From c4042b39a1a671abe2fa1badb67f5661a6d44214 Mon Sep 17 00:00:00 2001 From: Fabio Pliger Date: Fri, 9 Oct 2026 18:31:21 -0500 Subject: [PATCH 01/18] docs(migration): seed migration-tracking from the archived port --- migration-tracking/README.md | 60 + .../assets/kilo-v2-cli-2026-09-10.png | Bin 0 -> 135947 bytes .../assets/kilo-v2-vscode-2026-09-10.png | Bin 0 -> 234817 bytes .../github-drafts/issue-13750/README.md | 56 + .../github-drafts/issue-13750/issue-body.md | 242 +++ .../issue-13750/original-issue-body.md | 156 ++ .../issue-13750/progress-comment.md | 24 + .../issue-13750/published-subissues.md | 17 + .../issue-13750/shared-source-footprint.md | 3 + .../subissues/01-original-vscode.md | 33 + .../subissues/02-runtime-cli-tui.md | 29 + .../subissues/03-session-sharing.md | 30 + .../subissues/04-remote-sessions.md | 30 + .../subissues/05-distribution-cutover.md | 31 + .../subissues/06-editor-services-clients.md | 30 + .../issue-13750/subissues/07-jetbrains.md | 30 + .../subissues/08-cloud-agent-integration.md | 30 + .../09-anaconda-client-integration.md | 30 + .../issue-13750/tracking-policy.md | 21 + migration-tracking/marker-audit/README.md | 12 + .../kilo-override-marker-inventory.md | 954 +++++++++ .../v1-kilo-marker-port-assessment.md | 929 ++++++++ .../v1-kilo-marker-port-assessment.tsv | 817 +++++++ .../v2-current-shared-change-inventory.md | 87 + .../v2-shared-source-footprint-2026-09-10.md | 43 + .../plans/kilo-opencode-v2-execution-plan.md | 508 +++++ .../plans/kilo-opencode-v2-plan-progress.md | 245 +++ .../plans/kilo-opencode-v2-working-log.md | 1889 +++++++++++++++++ .../baseline/agent-policy-v2-parity.md | 48 + .../baseline/auto-packaging-v2-probe.md | 75 + .../baseline/cli-fixture-storage-incident.md | 96 + .../baseline/cli-remainder-next-gaps.md | 237 +++ .../baseline/cli-tui-closure-verification.md | 184 ++ .../baseline/cloud-agent-consumers.md | 187 ++ .../baseline/cloud-agent-contracts.md | 392 ++++ .../baseline/cloud-cli-v2-parity.md | 204 ++ .../baseline/config-mapping-v2-gap.md | 154 ++ .../baseline/credential-import-v2-parity.md | 91 + .../baseline/editor-services-inventory.md | 52 + .../baseline/existing-vscode-port.md | 193 ++ .../baseline/explore-policy-v2-parity.md | 107 + .../baseline/extension-cleanup-audit.md | 160 ++ .../baseline/extension-surface-inventory.md | 114 + .../baseline/external-resume-parity.md | 71 + .../baseline/goal79-scope-boundary.md | 56 + .../baseline/ide-preview-integration.md | 49 + .../baseline/indexing-v2-parity.md | 235 ++ .../baseline/memory-activity-next-slice.md | 75 + .../baseline/memory-sidebar-v2-parity.md | 87 + .../baseline/memory-ui-v2-parity.md | 48 + .../baseline/memory-v2-parity.md | 187 ++ .../baseline/model-info-bench-seam.md | 154 ++ .../baseline/model-loading-v2-parity.md | 167 ++ .../baseline/model-prompt-policy-v2-parity.md | 180 ++ .../baseline/model-prompt-v2-audit.md | 56 + .../baseline/model-sidebar-v2-parity.md | 73 + .../baseline/non-ide-closure-checkpoint.md | 387 ++++ .../baseline/non-ide-completion-audit.md | 94 + .../baseline/packaging-correction-plan.md | 175 ++ .../baseline/packaging-next-slice.md | 169 ++ .../baseline/pinned-v2-baseline.md | 250 +++ .../baseline/plan-v2-parity.md | 127 ++ .../reliability-checkpoint-2026-09-07.md | 46 + .../baseline/remote-share-next-contracts.md | 464 ++++ .../baseline/remote-v2-parity.md | 484 +++++ .../baseline/sandbox-linux-validation.md | 67 + .../baseline/sandbox-spawn-next-slice.md | 410 ++++ .../baseline/settings-v2-parity.md | 384 ++++ .../baseline/sidebar-bench-v2-parity.md | 100 + .../baseline/sidebar-indexing-v2-parity.md | 32 + .../baseline/sidebar-pr-v2-parity.md | 124 ++ .../baseline/sidebar-processes-v2-parity.md | 174 ++ .../baseline/sidebar-usage-v2-parity.md | 13 + .../baseline/skill-shell-parity.md | 61 + .../baseline/skills-v2-parity.md | 117 + .../baseline/startup-loading-v2.md | 87 + .../baseline/swarm-v2-parity.md | 71 + .../baseline/telemetry-v2-overlap.md | 172 ++ .../baseline/updater-local-parity.md | 253 +++ .../baseline/v1-fixture-generation.md | 167 ++ .../baseline/v1-session-schema-audit.md | 217 ++ .../baseline/v2-v1-schema-diffs.md | 201 ++ .../baseline/v2-v1-session-diffs.md | 208 ++ .../baseline/vscode-packaging.md | 121 ++ .../technical-notes/script/v2-baseline.ts | 96 + .../technical-notes/session-sharing.md | 92 + .../technical-notes/v2-fork-conventions.md | 168 ++ .../kilo-opencode-v2-test-plan-runtime.md | 1424 +++++++++++++ .../kilo-opencode-v2-test-plan-ui.md | 992 +++++++++ .../test-plans/kilo-opencode-v2-test-plan.md | 118 + 90 files changed, 18133 insertions(+) create mode 100644 migration-tracking/README.md create mode 100644 migration-tracking/assets/kilo-v2-cli-2026-09-10.png create mode 100644 migration-tracking/assets/kilo-v2-vscode-2026-09-10.png create mode 100644 migration-tracking/github-drafts/issue-13750/README.md create mode 100644 migration-tracking/github-drafts/issue-13750/issue-body.md create mode 100644 migration-tracking/github-drafts/issue-13750/original-issue-body.md create mode 100644 migration-tracking/github-drafts/issue-13750/progress-comment.md create mode 100644 migration-tracking/github-drafts/issue-13750/published-subissues.md create mode 100644 migration-tracking/github-drafts/issue-13750/shared-source-footprint.md create mode 100644 migration-tracking/github-drafts/issue-13750/subissues/01-original-vscode.md create mode 100644 migration-tracking/github-drafts/issue-13750/subissues/02-runtime-cli-tui.md create mode 100644 migration-tracking/github-drafts/issue-13750/subissues/03-session-sharing.md create mode 100644 migration-tracking/github-drafts/issue-13750/subissues/04-remote-sessions.md create mode 100644 migration-tracking/github-drafts/issue-13750/subissues/05-distribution-cutover.md create mode 100644 migration-tracking/github-drafts/issue-13750/subissues/06-editor-services-clients.md create mode 100644 migration-tracking/github-drafts/issue-13750/subissues/07-jetbrains.md create mode 100644 migration-tracking/github-drafts/issue-13750/subissues/08-cloud-agent-integration.md create mode 100644 migration-tracking/github-drafts/issue-13750/subissues/09-anaconda-client-integration.md create mode 100644 migration-tracking/github-drafts/issue-13750/tracking-policy.md create mode 100644 migration-tracking/marker-audit/README.md create mode 100644 migration-tracking/marker-audit/kilo-override-marker-inventory.md create mode 100644 migration-tracking/marker-audit/v1-kilo-marker-port-assessment.md create mode 100644 migration-tracking/marker-audit/v1-kilo-marker-port-assessment.tsv create mode 100644 migration-tracking/marker-audit/v2-current-shared-change-inventory.md create mode 100644 migration-tracking/marker-audit/v2-shared-source-footprint-2026-09-10.md create mode 100644 migration-tracking/plans/kilo-opencode-v2-execution-plan.md create mode 100644 migration-tracking/plans/kilo-opencode-v2-plan-progress.md create mode 100644 migration-tracking/plans/kilo-opencode-v2-working-log.md create mode 100644 migration-tracking/technical-notes/baseline/agent-policy-v2-parity.md create mode 100644 migration-tracking/technical-notes/baseline/auto-packaging-v2-probe.md create mode 100644 migration-tracking/technical-notes/baseline/cli-fixture-storage-incident.md create mode 100644 migration-tracking/technical-notes/baseline/cli-remainder-next-gaps.md create mode 100644 migration-tracking/technical-notes/baseline/cli-tui-closure-verification.md create mode 100644 migration-tracking/technical-notes/baseline/cloud-agent-consumers.md create mode 100644 migration-tracking/technical-notes/baseline/cloud-agent-contracts.md create mode 100644 migration-tracking/technical-notes/baseline/cloud-cli-v2-parity.md create mode 100644 migration-tracking/technical-notes/baseline/config-mapping-v2-gap.md create mode 100644 migration-tracking/technical-notes/baseline/credential-import-v2-parity.md create mode 100644 migration-tracking/technical-notes/baseline/editor-services-inventory.md create mode 100644 migration-tracking/technical-notes/baseline/existing-vscode-port.md create mode 100644 migration-tracking/technical-notes/baseline/explore-policy-v2-parity.md create mode 100644 migration-tracking/technical-notes/baseline/extension-cleanup-audit.md create mode 100644 migration-tracking/technical-notes/baseline/extension-surface-inventory.md create mode 100644 migration-tracking/technical-notes/baseline/external-resume-parity.md create mode 100644 migration-tracking/technical-notes/baseline/goal79-scope-boundary.md create mode 100644 migration-tracking/technical-notes/baseline/ide-preview-integration.md create mode 100644 migration-tracking/technical-notes/baseline/indexing-v2-parity.md create mode 100644 migration-tracking/technical-notes/baseline/memory-activity-next-slice.md create mode 100644 migration-tracking/technical-notes/baseline/memory-sidebar-v2-parity.md create mode 100644 migration-tracking/technical-notes/baseline/memory-ui-v2-parity.md create mode 100644 migration-tracking/technical-notes/baseline/memory-v2-parity.md create mode 100644 migration-tracking/technical-notes/baseline/model-info-bench-seam.md create mode 100644 migration-tracking/technical-notes/baseline/model-loading-v2-parity.md create mode 100644 migration-tracking/technical-notes/baseline/model-prompt-policy-v2-parity.md create mode 100644 migration-tracking/technical-notes/baseline/model-prompt-v2-audit.md create mode 100644 migration-tracking/technical-notes/baseline/model-sidebar-v2-parity.md create mode 100644 migration-tracking/technical-notes/baseline/non-ide-closure-checkpoint.md create mode 100644 migration-tracking/technical-notes/baseline/non-ide-completion-audit.md create mode 100644 migration-tracking/technical-notes/baseline/packaging-correction-plan.md create mode 100644 migration-tracking/technical-notes/baseline/packaging-next-slice.md create mode 100644 migration-tracking/technical-notes/baseline/pinned-v2-baseline.md create mode 100644 migration-tracking/technical-notes/baseline/plan-v2-parity.md create mode 100644 migration-tracking/technical-notes/baseline/reliability-checkpoint-2026-09-07.md create mode 100644 migration-tracking/technical-notes/baseline/remote-share-next-contracts.md create mode 100644 migration-tracking/technical-notes/baseline/remote-v2-parity.md create mode 100644 migration-tracking/technical-notes/baseline/sandbox-linux-validation.md create mode 100644 migration-tracking/technical-notes/baseline/sandbox-spawn-next-slice.md create mode 100644 migration-tracking/technical-notes/baseline/settings-v2-parity.md create mode 100644 migration-tracking/technical-notes/baseline/sidebar-bench-v2-parity.md create mode 100644 migration-tracking/technical-notes/baseline/sidebar-indexing-v2-parity.md create mode 100644 migration-tracking/technical-notes/baseline/sidebar-pr-v2-parity.md create mode 100644 migration-tracking/technical-notes/baseline/sidebar-processes-v2-parity.md create mode 100644 migration-tracking/technical-notes/baseline/sidebar-usage-v2-parity.md create mode 100644 migration-tracking/technical-notes/baseline/skill-shell-parity.md create mode 100644 migration-tracking/technical-notes/baseline/skills-v2-parity.md create mode 100644 migration-tracking/technical-notes/baseline/startup-loading-v2.md create mode 100644 migration-tracking/technical-notes/baseline/swarm-v2-parity.md create mode 100644 migration-tracking/technical-notes/baseline/telemetry-v2-overlap.md create mode 100644 migration-tracking/technical-notes/baseline/updater-local-parity.md create mode 100644 migration-tracking/technical-notes/baseline/v1-fixture-generation.md create mode 100644 migration-tracking/technical-notes/baseline/v1-session-schema-audit.md create mode 100644 migration-tracking/technical-notes/baseline/v2-v1-schema-diffs.md create mode 100644 migration-tracking/technical-notes/baseline/v2-v1-session-diffs.md create mode 100644 migration-tracking/technical-notes/baseline/vscode-packaging.md create mode 100644 migration-tracking/technical-notes/script/v2-baseline.ts create mode 100644 migration-tracking/technical-notes/session-sharing.md create mode 100644 migration-tracking/technical-notes/v2-fork-conventions.md create mode 100644 migration-tracking/test-plans/kilo-opencode-v2-test-plan-runtime.md create mode 100644 migration-tracking/test-plans/kilo-opencode-v2-test-plan-ui.md create mode 100644 migration-tracking/test-plans/kilo-opencode-v2-test-plan.md diff --git a/migration-tracking/README.md b/migration-tracking/README.md new file mode 100644 index 000000000000..d7cc829a6ae8 --- /dev/null +++ b/migration-tracking/README.md @@ -0,0 +1,60 @@ +# Kilo v2 migration tracking + +Planning, progress, validation, and source-parity documentation for the migration of Kilo onto OpenCode v2. + +## Structure + +```text +migration-tracking/ +├── README.md +├── plans/ # Execution plan, public progress plan and milestone history +├── test-plans/ # Test strategy, runtime checks, and UI scenarios +├── marker-audit/ # Source-change inventory and v1-to-v2 assessment +└── technical-notes/ # Supporting design, parity, and validation material + ├── baseline/ + ├── script/ + └── v2-fork-conventions.md +``` + +## Plans + +- [Execution plan](plans/kilo-opencode-v2-execution-plan.md): the index for finishing the migration: decisions, sequencing, gates (G1 CLI/TUI, G2 VS Code, G3 JetBrains), and the issue register. It changes only when a decision changes. Start here for how the remaining work is organized. +- [Plan and progress](plans/kilo-opencode-v2-plan-progress.md): the public-facing capability inventory, live status per row, and remaining acceptance requirements. Start here for an overview of current status. Its phase table and status line predate the execution plan until the N7 alignment lands. +- [Working log](plans/kilo-opencode-v2-working-log.md): the migration's milestone history. Under the execution plan, new entries are limited to milestones (upstream tag merges, delta sweeps, gate go/no-go decisions, decision changes, notable incidents) and are written by the coordinator only. Earlier entries hold the detailed implementation history; they describe their own checkpoints and may be superseded by later entries. + +The working log retains internal coordination references and machine-local paths. It needs editorial review before publication; it is not the public progress summary. + +## Test plans + +- [Main test plan](test-plans/kilo-opencode-v2-test-plan.md): overall validation plan. +- [Runtime test plan](test-plans/kilo-opencode-v2-test-plan-runtime.md): runtime and backend checks. +- [UI test plan](test-plans/kilo-opencode-v2-test-plan-ui.md): interface and interactive workflow checks. + +A listed test is a requirement, not proof that it has passed. Read the recorded execution status and its checkpoint before relying on a result. + +## Marker audit + +- [Marker inventory](marker-audit/kilo-override-marker-inventory.md): the historical inventory of Kilo change annotations, captured before marker cleanup. +- [Port assessment](marker-audit/v1-kilo-marker-port-assessment.md): the file-level assessment of v1 behavior, its v2 destination, and whether it is ported, native, partial, still needed, deferred, or unnecessary. +- [Assessment data](marker-audit/v1-kilo-marker-port-assessment.tsv): the corresponding tab-separated data for filtering and analysis. + +Source assessment and runtime acceptance are different measures. Fewer annotations can reflect relocation into Kilo-owned packages, obsolete changes, or behavior not yet ported; the count alone does not establish parity. + +## Technical notes + +This folder preserves the material previously collected under the repository's root `kilocode/` directory: + +- `baseline/`: focused parity assessments, design decisions, validation records, and remaining gaps. +- `script/`: supporting baseline tooling. +- `v2-fork-conventions.md`: ownership and upstream integration conventions. +- Other topic-specific notes, including session sharing. + +Use these notes to understand the reasoning and scope behind progress claims. They are checkpoint-specific supporting material, rather than a second capability checklist. + +## Organization and publication status + +This directory was assembled by copying existing files. The originals under root `plans/` and `kilocode/` remain intact. The originals are excluded from Git; this directory is the tracked migration collection. The copies are not automatically synchronized. Repository instructions point here, while historical links and tooling paths still need review. + +The public progress plan was prepared for external readers. The working log, technical notes, test plans, and audit documents retain their original content and need publication review for internal references, local paths, and outdated statements. Relative links and script paths inherited from the original locations may also need adjustment before the copies become the maintained versions. + +When updating this collection, keep decisions and sequencing in the execution plan, capability progress in the public plan, milestones in the working log, per-change evidence in the PR and its issue, test execution status in the test plans, and file-level mapping in the marker audit. Preserve the distinction between implemented behavior, source-reviewed equivalence, and verified end-to-end acceptance. diff --git a/migration-tracking/assets/kilo-v2-cli-2026-09-10.png b/migration-tracking/assets/kilo-v2-cli-2026-09-10.png new file mode 100644 index 0000000000000000000000000000000000000000..0c31f96261af80912f7f8e13dd1ad931242d1dc2 GIT binary patch literal 135947 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%0An*TIVyof53# z)l;1gdAzdSoirjO!4+HI8e>m=ol5b~sq;^k|boaPja4 ziSsNlFxI8|dJU8wJ3NIIzR{f73hk%zTL zSh!UN<8#WP-bdi8`$udxI;<0 zYr-~#Cde3;8bXH^7?$>rz?}CajcYE8Bnmz0gsHQIh`(has+(oz%;w(4Bx`%)d!OUH zLJ#mAM_CGs-Qi?Uu;~Y>Y%h7q5P^LniaFBN5d6qOH8{98{rEtBx#t{4Pe=P08W
    {Ej++RWkz80bl=yt% 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%0D<@n(_r1y%i|;UP;QV*0 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�|X3EQbb06y*l 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

    ~|E*;P}- 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%0DT+tRQ(6>?d>+ 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 Work in progress — status reviewed September 10, 2026. + +## Branch: [`kilo-v2`](https://github.com/Kilo-Org/kilocode/tree/kilo-v2) + +| Quick reference | Link / status | +|---|---| +| Start here | [Migration tracking](https://github.com/Kilo-Org/kilocode/tree/kilo-v2/migration-tracking) | +| Detailed progress | [Implementation and verification inventory](https://github.com/Kilo-Org/kilocode/blob/kilo-v2/migration-tracking/plans/kilo-opencode-v2-plan-progress.md) | +| Remaining work | [Subissues](#subissues) | +| Verification | End-to-end acceptance is incomplete; implemented does not mean fully verified. | +| Product lines | `kilo-v2`: migration work · `main`: v1 product line | + +## Summary + +The isolated Kilo v2 CLI/runtime foundation is implemented. The original Kilo VS Code interface now builds and has working backend adapters, but full original-client parity and acceptance remain incomplete. + +A source assessment of 816 v1 files containing 6,128 Kilo change annotations is complete. It identifies partial and missing behavior as well as native equivalents; it does not grant runtime acceptance. + +The inventory below retains the original 36 capability rows. The repository's expanded plan separates these into 88 finer-grained rows. + +**Continuing the work:** use the [`migration-tracking/` folder on `kilo-v2`](https://github.com/Kilo-Org/kilocode/tree/kilo-v2/migration-tracking) as your starting point. + +1. Read the [progress plan](https://github.com/Kilo-Org/kilocode/blob/kilo-v2/migration-tracking/plans/kilo-opencode-v2-plan-progress.md) and choose an open subissue below. +2. Follow `AGENTS.md` and the [fork conventions](https://github.com/Kilo-Org/kilocode/blob/kilo-v2/migration-tracking/technical-notes/v2-fork-conventions.md); use the relevant technical notes, source assessment and test plans to understand the remaining behavior. +3. Update the repository plan and subissue with changes, verification results and remaining gaps. Keep implementation status separate from end-to-end acceptance. + + +Migrate Kilo onto the OpenCode v2 architecture as a **target-shaped port**, not a merge of `main` into `v2`, and not a second `packages/opencode` host. + +- Branch: `kilo-v2`, tracking a **pinned** `upstream/v2` SHA (record in `migration-tracking/technical-notes/baseline/pinned-v2-baseline.md`). Merge forward once history is shared; perpetual rebase is not the standing policy. +- `main` stays the V1 product line. +- Related (different approach): #12887 + +## CLI preview +Kilo v2 CLI development preview, September 10, 2026. This screenshot illustrates the interface; end-to-end acceptance remains tracked separately. + +![Kilo v2 CLI showing a Plan session, session tabs, questions and usage sidebar](https://raw.githubusercontent.com/Kilo-Org/kilocode/kilo-v2/migration-tracking/assets/kilo-v2-cli-2026-09-10.png) + +## VS Code extension preview + +Original Kilo Code interface running against v2, September 10, 2026. Development preview; full original-client acceptance remains incomplete. + +**Known startup issue:** this preview is currently started in Restricted Mode as a workaround for a reported conflict with the main VS Code instance’s keys. The underlying conflict still needs investigation and a fix; Restricted Mode is not the intended final setup. + +![Kilo Code v2 VS Code extension showing sidebar conversation and Agent Manager panel](https://raw.githubusercontent.com/Kilo-Org/kilocode/kilo-v2/migration-tracking/assets/kilo-v2-vscode-2026-09-10.png) + +## What this is not + +- Wholesale `main` merge into the v2 branch +- Keeping `packages/opencode` as a legacy host on v2 (that package is gone on `origin/v2`) +- Dual-running V1 and V2 against the same SQLite file or the same live `kilo.jsonc` +- Ambient V1→V2 migration on preview startup +- Implementing the whole product as a stack of plugins (plugins are owner 3. Identity, SQLite path, daemon topology, and IDE backends are host. Protocol/Schema/Server edits are the exception, not the default.) + +## Placement + +Each Kilo capability maps to **one** owner: + +1. **Catalog / provider** — `catalog.transform`, or a native `packages/ai` provider, for model/protocol only. +2. **Host** — product binary and daemon topology. Current path is a from-scratch `packages/kilo-cli` (upstream `packages/cli` does not export a wrap surface for commands, service manager, updater, or ACP). Cost: re-own `kilo serve` / daemon / attach, updater, ACP. Alternative not taken: build the upstream CLI under Kilo identity (`OPENCODE_CLI_NAME` / channel defines) plus a narrow app-name hook. VS Code, JetBrains, and Agent Manager are Protocol clients of the local server — not OpenCode's `sdks/vscode`. +3. **Plugin** — Location-scoped `kilo-*` packages on upstream seams: `catalog.transform` / integration transforms, `Tool.Service` registration (executable tools; later transform wins on name), `session.context` (prompt/tool metadata only — that hook cannot invent executables), `tool.execute.before` / `.after`, `permission.evaluate`. Plugins do not become the host. Kilo-owned RPC registrations use the existing public RPC transport; add or change Protocol endpoints only when that transport and native interfaces are insufficient. + +Clients depend on Schema and Protocol only. Core and Server stay behind HTTP. Do not call private Core from Gateway or UI. + +Prefer, in order: `kilo-*` plugin seams → host `ServerOptions` / identity → Protocol / Schema / Server only when an upstream endpoint's shape must change → shared-file patch (marked, counted). + +## Fork boundary and upstream updates + +The dependency graph is the protection; markers are leftover debt. + +Prefer, in order: + +1. `kilo-*` packages (`kilo-gateway`, `kilo-memory`, `kilo-indexing`, `kilo-sandbox`, VS Code, JetBrains). These are Kilo-owned additions, not upstream patches. +2. Upstream v2 seams: `catalog.transform` or native `packages/ai`; Location-scoped plugin hooks; `ServerOptions` on the host; Kilo-owned Protocol / Schema / Server modules only when the public contract needs a Kilo field. +3. Kilo-prefixed paths inside packages we wrap (`packages/cli`, Protocol / Schema / Server). Do not create a new Kilo subtree inside `packages/core/src/session/runner`. +4. Shared-file patches only when no seam exists. Mark each one with the existing Kilo marker convention and keep an exact count. Any increase needs an explicit explanation of the missing interface and its maintenance cost. + +As of September 10, 2026, **24 upstream source files carry `kilocode_change` annotations: 5 Core, 4 theme, 1 plugin and 14 TUI files, plus `AGENTS.md`**. This counts marked source files, excluding Kilo-owned packages/subtrees, tests and README branding translations. The original bootstrap footprint was four files. The measured file list is in `migration-tracking/marker-audit/v2-shared-source-footprint-2026-09-10.md`; hook explanations remain beside the marked changes. Source-to-destination mappings are recorded in `migration-tracking/marker-audit/v1-kilo-marker-port-assessment.md`. + +Take `upstream/v2` in controlled steps. Do not merge `main` wholesale into `kilo-v2`. Avoid Core forks for the migrator, runner, and private APIs; those are the highest merge tax. + +## Store and config + +V1 session DB and live `kilo.jsonc` are not compatible with this port. Sharing them corrupts data. + +OpenCode v2's `V1Migration.transformSession` rewrites the SQLite file it opened (Bun only; Node/workerd is a no-op). OpenCode maps mixed v1/v2 config in memory and does not rewrite the file. + +**Rules** + +- **Preview identity is not a customer SKU.** Internally the preview uses a separate application identity (`kilo2`) so paths cannot collide with stable `kilo` or with OpenCode. Do not ship `kilo2` to customers. The product name stays `kilo`. When v2 becomes the default `kilo` binary, move v1 data/config dirs aside first, then take the `kilo` paths. +- Same project directory is Location, not the DB file. One SQLite per application graph. +- **Boot:** create/open only the `kilo2` file (empty if missing). No `session` table means the migrator does not run. If the resolved path is the live Kilo or OpenCode store, refuse to serve. Assert/test that upstream `V1Migration.layer` is a no-op on empty `kilo2` (no `session` table). The ambient fiber may still start; that is upstream debt to watch, not a Core fork. +- **Import (phase 6):** explicit and opt-in. Copy the live V1 DB, migrate the copy, never the original. Copy selected config keys into a new file; never rewrite live `kilo.jsonc`. The host (`packages/cli` / `kilo-cli`) owns detection, prompt, and copy-import — not a plugin. Do not ship the prompt until the importer exists. Precondition: a read-only schema diff of Kilo `main`'s store against the migrator's expected `session` / `session_message` / `event` tables, tested on a real Kilo `main` fixture. Note: upstream migrator also imports a sibling `opencode-next.db` when present. Also import `auth.json` → v2 credentials and map Kilo-only `kilo.jsonc` keys (upstream in-memory migrate only knows upstream v1 keys). +- **Rollback:** run the V1 `kilo` binary on the original files. There is no reverse migration. Leftover V1 tables on a migrated copy are not a way back. +- Do not fork `V1Migration` to take a destination path. Separate path is host identity (upstream already has `OPENCODE_DB` and channel DBs). + +## Phases + +Scope and exit criteria are unchanged; each phase now shows current status. Detailed acceptance is in the repository plan and subissues. + +### 0 — Isolated identity (internal preview) + +**Status:** Isolated preview identity implemented. + +Internal preview binary: own XDG, empty DB, branding, fail-closed. Not a customer release. + +**Done when:** stable `kilo` and the internal preview run side by side; two stores; boot does not migrate. + +### 1 — Kilo API seam + +**Status:** Gateway auth, organization selection and catalog integration use the public client and Kilo Gateway with no `@opencode-ai/core` import in `kilo-gateway`. Kilo contracts occupy ten owned Schema modules under `packages/schema/src/kilocode/`. + +One real Kilo-owned operation end-to-end through the generated client and a `kilo-*` plugin/host path, without importing Core. Upstream `@opencode-ai/client` already covers generic session/prompt/events — that alone is not this gate. Protocol / Schema / Server edits only when an upstream endpoint's shape must change; record each exception. + +**Done when:** e.g. gateway auth or org switch works through the client + plugin path with no Core import; any Protocol change is listed. + +### 2 — One native conversation + +**Status:** Native conversation foundation implemented; original-client acceptance continues. + +Stream, tools, permissions, interrupt, resume, restart through that contract. + +**Done when:** one headless path goes prompt → idle and survives reconnect. + +### 3 — Runtime + `kilo-*` packages + +**Status:** Partial: policy variants, original-client consumers and detailed acceptance remain. + +Memory, indexing, sandbox, telemetry, gateway policy, skills/agents. Reuse packages; change registration. + +**Done when:** those inventory rows are done or obsolete. + +### 4 — CLI / TUI remainder + +**Status:** Partial: themes, notifications, session-scope UI, remote/sharing acceptance and packaging remain. + +`/review`, privacy, themes, `kilo run --auto`, export, resume-claude/codex, `/remote`, `kilo cloud`. + +**Done when:** Phase 4 inventory rows are done or obsolete. + +### 5 — Product clients + +**Status:** Original VS Code port in progress; JetBrains and additional consumers remain. + +VS Code sidebar, editor tabs, Agent Manager, settings webview, JetBrains. Capability handshake. Never mix V1 and V2 routes in one session. + +**Done when:** Phase 5 inventory rows are done or obsolete. + +### 6 — Import / canary / cutover + +**Status:** Supported copy-import paths implemented; canary, distribution and production cutover acceptance remain. + +Host prompt + copy-then-migrate-the-copy. Originals untouched. Canary identity (`kilo2` vs `kilo` + channel) is an explicit gate before shipping the importer — not deferred silently. + +**Done when:** schema-diff + fixture pass; opt-in import works including credentials and Kilo config keys; V1 still opens the original store; canary is isolated. + +## Inventory (Kilo-Org/kilocode) + +> **End-to-end acceptance is incomplete.** Implementation describes code coverage, not confirmation that the full capability works end to end. Verification applies only to the tested scope; no row below claims complete E2E acceptance. + +Status: **implemented** = stated local slice verified, not release-wide approval; **native reuse** = native generic behavior; **partial** = concrete work or acceptance remains; **needs port** = required behavior missing; **deferred** = awaits a later implementation or an external dependency; **obsolete** = not required. Broader audit findings can reopen an implemented requirement. + +Verification: **Local checks passed** means recorded focused tests or isolated integration checks for the stated slice. **Pending** means acceptance remains open. Partial rows may have passing tests for individual paths; their scope notes describe the limits. + +| Capability | Owner | Phase | Implementation | Verification | Status detail / remaining work | +|---|---|---|---|---|---| +| Kilo logo / branding | 2 host + shared `home.logo` slot | 0 | implemented | Local checks passed; full E2E pending | Preview branding; full legacy theme catalogue remains under CLI/TUI parity. | +| Isolated `kilo2` identity + config/data dirs | 2 host | 0 | implemented | Local checks passed; full E2E pending | Separate preview stores; production cutover is a separate gate. | +| Gateway device auth / profile | 3 plugin (`integration.transform`) | 0–1 | implemented | Local checks passed; full E2E pending | Local authentication/profile covered; deployed verification remains. | +| Organization / team (`/teams`) | 3 plugin + client UI | 0–1 | implemented | Local checks passed; full E2E pending | Selection and scoped requests covered locally; original-client variants remain. | +| Gateway catalog, BYOK, org routing (`kilo-gateway`) | 3 Gateway plugin + host policy | 1 | implemented | Local checks passed; full E2E pending | Catalog/request policy and eight prompt selectors covered locally. | +| `kilo serve` / daemon / attach | 2 host | 1 | implemented | Local checks passed; full E2E pending | Native daemon and attach paths exist; client lifecycle acceptance is separate. | +| Generated JS SDK | Native Client + 3 Kilo RPC definitions | 1 | native reuse | Kilo integration E2E pending | Public typed client; no legacy SDK runtime required. | +| Session share / unshare / fork-from-share | 3 plugin + sharing backend + clients | 1 | partial | Full E2E pending; see scope notes | Local client paths exist; viewer, ownership, synchronization and deployed acceptance remain. | +| Memory (`/memory`) | 3 plugin + Kilo memory engine | 3 | partial | Full E2E pending; see scope notes | CLI/TUI and adapter slices exist; source-assessment gaps and original-client consumers remain. | +| Codebase indexing | 3 plugin + Kilo indexing engine | 3 | partial | Full E2E pending; see scope notes | Runtime implemented; original-client controls and detailed acceptance remain. | +| Sandbox tool shells (macOS/Linux) | 3 plugin + Kilo sandbox | 3 | partial | Full E2E pending; see scope notes | Shell confinement exists; complete legacy policy variants and platform acceptance. | +| Sandbox PTY / MCP / git spawn policy | 2 host decorators + shared MCP hook | 3 | implemented | Local checks passed; full E2E pending | Local MCP confinement and PTY create refusal; model-requested git uses shell confinement. | +| OpenTelemetry / telemetry | 2 host / 3 plugin | 3 | partial | Full E2E pending; see scope notes | Runtime integration exists; original extension proxy/consumer parity remains. | +| Kilo Swarm | 3 plugin + host permission policy | 3 | implemented | Local checks passed; full E2E pending | Scoped orchestration implemented; Agent Manager is a separate capability. | +| Skills, custom agents, `.kilo/agents` | 3 discovery + native agent/skill services | 3 | partial | Full E2E pending; see scope notes | Discovery works; original management/removal consumers remain. | +| Project config discovery (`.kilo/`, `kilo.jsonc` vs `.opencode`) | 2 host configuration integration | 0–1 | implemented | Local checks passed; full E2E pending | Native folding with Kilo project discovery. | +| Local `/review` | 3 command policy + native review | 4 | implemented | Local checks passed; full E2E pending | Generic review reused; Kilo policy integrated. | +| CLI TUI remainder | 2 host + owned TUI extensions | 4 | partial | Full E2E pending; see scope notes | Many workflows work; audit identifies remaining notification, theme, scope and presentation gaps. | +| `kilo run --auto`, session export/import, resume Claude/Codex | 2 host through native client | 4 | implemented | Local checks passed; full E2E pending | Headless run and supported transfers/import formats locally covered. | +| `/remote` | 2 adapter + Gateway | 4 | partial | Full E2E pending; see scope notes | Local transport/features covered; deployed relay/consumer acceptance remains. | +| `kilo cloud` CLI client | 2 host + Gateway | 4 | implemented | Local checks passed; full E2E pending | Local CLI contract covered; deployed-service acceptance remains. | +| Updater / update channel / packaging | 2 host + release tooling | 4 | partial | Full E2E pending; see scope notes | Real local directory update/rollback works; archives, signing and distribution remain. | +| ACP | 2 host over native ACP | 4 | implemented | Local checks passed; full E2E pending | Product launcher/host surface implemented. | +| Credential import (`auth.json` → v2) | 2 explicit import | 6 | implemented | Local checks passed; full E2E pending | Supported mappings preserve originals; unsupported inputs fail explicitly. | +| `kilo.jsonc` key mapping (Kilo-only keys) | 2 explicit import | 6 | implemented | Local checks passed; full E2E pending | Safe mapping implemented; unsupported feature keys remain documented refusals. | +| V1 schema-diff + fixture import test | 2 import tooling | 6 | implemented | Local checks passed; full E2E pending | Schema/source comparisons and fixture migration recorded. | +| VS Code sidebar chat | 2 original Kilo VS Code client | 5 | partial | Full E2E pending; see scope notes | Original UI builds; backend/lifecycle slices work; complete original-UI acceptance remains. | +| VS Code editor tabs | 2 original Kilo VS Code client | 5 | partial | Full E2E pending; see scope notes | Panel sources and adapters exist; verify original tab/restore workflows end to end. | +| Agent Manager | 2 original Kilo VS Code client | 5 | partial | Full E2E pending; see scope notes | UI builds; control-plane and notebook behavior remain incomplete. | +| VS Code settings webview | 2 original client + Kilo settings RPC | 5 | partial | Full E2E pending; see scope notes | Supported settings/collections/refresh work; complete original controls and acceptance. | +| Inline autocomplete / FIM | 2 editor client + service integration | 5 | needs port | Pending | Original autocomplete/FIM and next-edit consumer/service contracts remain. | +| Code actions, enhance prompt, git commit generation | 2 editor client + Kilo generation RPC | 5 | partial | Full E2E pending; see scope notes | Generation backend covered; original editor/SCM workflow acceptance remains. | +| Task timeline, diff viewer | 2 original client + native projections | 5 | partial | Full E2E pending; see scope notes | Complete original timeline and authoritative diff/restore details. | +| Voice / speech-to-text | 2 client + transcription provider | 5 | deferred | Pending | Client integration and transcription provider acceptance. | +| JetBrains plugin | 2 v2 client integration | 5 | deferred | Pending | Port and validate the original plugin against v2. | +| Kilo Console | — | — | obsolete | Not applicable | Not required by the target plan. | + +**Overlap with upstream v2** (do not rebuild): permissions, MCP, snapshots, compaction, session fork, ACP, plugins, subagents, `webfetch` / `websearch`, daemon HTTP+SSE. Port Kilo policy on these, not a second engine. + +### Additional consumers to assess + +Beyond the original inventory, these consumers need assessment before product-wide compatibility is claimed: + +| Consumer | Phase | Status | Required next step | +|---|---|---|---| +| Cloud agent runtime consumers | 5; rollout depends on 6 | Assessment required | Identify actual consumers and map execution, client and deployment contracts. Existing cloud CLI or remote coverage is insufficient. | +| Anaconda Desktop and related integrations | 5 | Assessment required | Identify the exact client and integration mode, then establish required changes or evidence-backed compatibility. | + +These rows are new scope under assessment. Add other clients as discovery identifies them. + +## Next milestones + +1. Complete the original VS Code contracts and typecheck gaps, then verify original chat, panels, settings, terminals, reconnect and interruption end to end. +2. Resolve remaining runtime and CLI/TUI behavior identified by the source assessment. +3. Complete sharing and remote integration contracts and their external acceptance when permitted. +4. Complete outstanding editor services and JetBrains scope, or record a product decision to reduce scope. +5. Finish platform distribution, signing, and opt-in import/canary acceptance before cutover. + +## Subissues + +- [#14016 — Complete original Kilo VS Code client parity](https://github.com/Kilo-Org/kilocode/issues/14016) +- [#14017 — Complete remaining runtime and CLI/TUI parity](https://github.com/Kilo-Org/kilocode/issues/14017) +- [#14018 — Complete v2 session sharing compatibility and acceptance](https://github.com/Kilo-Org/kilocode/issues/14018) +- [#14019 — Complete remote session consumer acceptance](https://github.com/Kilo-Org/kilocode/issues/14019) +- [#14020 — Complete distribution and safe migration cutover](https://github.com/Kilo-Org/kilocode/issues/14020) +- [#14021 — Complete remaining editor services](https://github.com/Kilo-Org/kilocode/issues/14021) +- [#14022 — Port the JetBrains plugin to v2](https://github.com/Kilo-Org/kilocode/issues/14022) +- [#14023 — Assess and integrate cloud agent consumers with v2](https://github.com/Kilo-Org/kilocode/issues/14023) +- [#14024 — Assess and integrate Anaconda Desktop and related clients with v2](https://github.com/Kilo-Org/kilocode/issues/14024) + +## Detailed tracking + +- Repository plan: `migration-tracking/plans/kilo-opencode-v2-plan-progress.md`. +- Source-to-destination assessment: `migration-tracking/marker-audit/v1-kilo-marker-port-assessment.md`. + +Use dated comments for completed checkpoints and changed decisions. Use subissues for independently owned remaining deliverables, with acceptance criteria and links back to the detailed inventory. diff --git a/migration-tracking/github-drafts/issue-13750/original-issue-body.md b/migration-tracking/github-drafts/issue-13750/original-issue-body.md new file mode 100644 index 000000000000..c4e621c6128a --- /dev/null +++ b/migration-tracking/github-drafts/issue-13750/original-issue-body.md @@ -0,0 +1,156 @@ +> Work In Progress + +## Summary + +Migrate Kilo onto the OpenCode v2 architecture as a **target-shaped port**, not a merge of `main` into `v2`, and not a second `packages/opencode` host. + +- Branch: `kilo-v2`, tracking a **pinned** `upstream/v2` SHA (record in the repo baseline doc). Merge forward once history is shared; perpetual rebase is not the standing policy. +- `main` stays the V1 product line. +- Related (different approach): #12887 + +## What this is not + +- Wholesale `main` merge into the v2 branch +- Keeping `packages/opencode` as a legacy host on v2 (that package is gone on `origin/v2`) +- Dual-running V1 and V2 against the same SQLite file or the same live `kilo.jsonc` +- Ambient V1→V2 migration on preview startup +- Implementing the whole product as a stack of plugins (plugins are owner 3. Identity, SQLite path, daemon topology, and IDE backends are host. Protocol/Schema/Server edits are the exception, not the default.) + +## Placement + +Each Kilo capability maps to **one** owner: + +1. **Catalog / provider** — `catalog.transform`, or a native `packages/ai` provider, for model/protocol only. +2. **Host** — product binary and daemon topology. Current path is a from-scratch `packages/kilo-cli` (upstream `packages/cli` does not export a wrap surface for commands, service manager, updater, or ACP). Cost: re-own `kilo serve` / daemon / attach, updater, ACP. Alternative not taken: build the upstream CLI under Kilo identity (`OPENCODE_CLI_NAME` / channel defines) plus a narrow app-name hook. VS Code, JetBrains, and Agent Manager are Protocol clients of the local server — not OpenCode's `sdks/vscode`. +3. **Plugin** — Location-scoped `kilo-*` packages on upstream seams: `catalog.transform` / integration transforms, `Tool.Service` registration (executable tools; later transform wins on name), `session.context` (prompt/tool metadata only — that hook cannot invent executables), `tool.execute.before` / `.after`, `permission.evaluate`. Plugins do not add Protocol endpoints or become the host. + +Clients depend on Schema and Protocol only. Core and Server stay behind HTTP. Do not call private Core from Gateway or UI. + +Prefer, in order: `kilo-*` plugin seams → host `ServerOptions` / identity → Protocol / Schema / Server only when an upstream endpoint's shape must change → shared-file patch (marked, counted). + +## Fork boundary and upstream updates + +The dependency graph is the protection; markers are leftover debt. + +Prefer, in order: + +1. `kilo-*` packages (`kilo-gateway`, `kilo-memory`, `kilo-indexing`, `kilo-sandbox`, VS Code, JetBrains). These are Kilo-owned additions, not upstream patches. +2. Upstream v2 seams: `catalog.transform` or native `packages/ai`; Location-scoped plugin hooks; `ServerOptions` on the host; Kilo-owned Protocol / Schema / Server modules only when the public contract needs a Kilo field. +3. Kilo-prefixed paths inside packages we wrap (`packages/cli`, Protocol / Schema / Server). Do not create a new Kilo subtree inside `packages/core/src/session/runner`. +4. Shared-file patches only when no seam exists. Mark each one with the existing Kilo marker convention and keep an exact count. The count must not grow without an explicit exception. + +**Current shared-patch count on `kilo-v2`: 4** (`AGENTS.md`; `packages/plugin/src/tui/context.ts` `home.logo` slot; `packages/tui/src/app.tsx` pluginDirectories; `packages/tui/src/routes/home.tsx` Slot `home.logo`). Growing past 4 needs an explicit exception. + +Take `upstream/v2` in controlled steps. Do not merge `main` wholesale into `kilo-v2`. Avoid Core forks for the migrator, runner, and private APIs; those are the highest merge tax. + +## Store and config + +V1 session DB and live `kilo.jsonc` are not compatible with this port. Sharing them corrupts data. + +OpenCode v2's `V1Migration.transformSession` rewrites the SQLite file it opened (Bun only; Node/workerd is a no-op). OpenCode maps mixed v1/v2 config in memory and does not rewrite the file. + +**Rules** + +- **Preview identity is not a customer SKU.** Internally the preview uses a separate application identity (`kilo2`) so paths cannot collide with stable `kilo` or with OpenCode. Do not ship `kilo2` to customers. The product name stays `kilo`. When v2 becomes the default `kilo` binary, move v1 data/config dirs aside first, then take the `kilo` paths. +- Same project directory is Location, not the DB file. One SQLite per application graph. +- **Boot:** create/open only the `kilo2` file (empty if missing). No `session` table means the migrator does not run. If the resolved path is the live Kilo or OpenCode store, refuse to serve. Assert/test that upstream `V1Migration.layer` is a no-op on empty `kilo2` (no `session` table). The ambient fiber may still start; that is upstream debt to watch, not a Core fork. +- **Import (phase 6):** explicit and opt-in. Copy the live V1 DB, migrate the copy, never the original. Copy selected config keys into a new file; never rewrite live `kilo.jsonc`. The host (`packages/cli` / `kilo-cli`) owns detection, prompt, and copy-import — not a plugin. Do not ship the prompt until the importer exists. Precondition: a read-only schema diff of Kilo `main`'s store against the migrator's expected `session` / `session_message` / `event` tables, tested on a real Kilo `main` fixture. Note: upstream migrator also imports a sibling `opencode-next.db` when present. Also import `auth.json` → v2 credentials and map Kilo-only `kilo.jsonc` keys (upstream in-memory migrate only knows upstream v1 keys). +- **Rollback:** run the V1 `kilo` binary on the original files. There is no reverse migration. Leftover V1 tables on a migrated copy are not a way back. +- Do not fork `V1Migration` to take a destination path. Separate path is host identity (upstream already has `OPENCODE_DB` and channel DBs). + +## Phases + +Completeness is the inventory below, not extra phases. Flip Status on a row when a slice lands. + +### 0 — Isolated identity (internal preview, current) + +Internal preview binary: own XDG, empty DB, branding, fail-closed. Not a customer release. + +**Done when:** stable `kilo` and the internal preview run side by side; two stores; boot does not migrate. + +### 1 — Kilo API seam + +One real Kilo-owned operation end-to-end through the generated client and a `kilo-*` plugin/host path, without importing Core. Upstream `@opencode-ai/client` already covers generic session/prompt/events — that alone is not this gate. Protocol / Schema / Server edits only when an upstream endpoint's shape must change; record each exception. + +**Done when:** e.g. gateway auth or org switch works through the client + plugin path with no Core import; any Protocol change is listed. + +### 2 — One native conversation + +Stream, tools, permissions, interrupt, resume, restart through that contract. + +**Done when:** one headless path goes prompt → idle and survives reconnect. + +### 3 — Runtime + `kilo-*` packages + +Memory, indexing, sandbox, telemetry, gateway policy, skills/agents. Reuse packages; change registration. + +**Done when:** those inventory rows are done or obsolete. + +### 4 — CLI / TUI remainder + +`/review`, privacy, themes, `kilo run --auto`, export, resume-claude/codex, `/remote`, `kilo cloud`. + +**Done when:** Phase 4 inventory rows are done or obsolete. + +### 5 — Product clients + +VS Code sidebar, editor tabs, Agent Manager, settings webview, JetBrains. Capability handshake. Never mix V1 and V2 routes in one session. + +**Done when:** Phase 5 inventory rows are done or obsolete. + +### 6 — Import / canary / cutover + +Host prompt + copy-then-migrate-the-copy. Originals untouched. Canary identity (`kilo2` vs `kilo` + channel) is an explicit gate before shipping the importer — not deferred silently. + +**Done when:** schema-diff + fixture pass; opt-in import works including credentials and Kilo config keys; V1 still opens the original store; canary is isolated. + +## Inventory (Kilo-Org/kilocode) + +Status: `not-started` · `in-progress` · `done` · `started` · `unknown` · `obsolete` + +| Capability | Owner | Phase | Status | +| --- | --- | --- | --- | +| Kilo logo / branding | 4 shared patch or upstream hook | 0 | started | +| Isolated `kilo2` identity + config/data dirs | 2 host | 0 | started | +| Gateway device auth / profile | 3 plugin (`integration.transform`) | 0–1 | started | +| Organization / team (`/teams`) | 3 plugin (+ TUI); Protocol only if shape must change | 0–1 | started | +| Gateway catalog, BYOK, org routing (`kilo-gateway`) | 3 plugin (`catalog.transform`); note upstream `KiloPlugin` headers | 1 | started | +| `kilo serve` / daemon / attach | 2 host | 1 | started | +| Generated JS SDK | upstream `@opencode-ai/client`; Kilo = plugin/RPC typings only if published | 1 | upstream-equivalent | +| Session share / unshare / fork-from-share | 3 plugin + Kilo backend; Protocol only if fork-from-share needs it | 1 | not-started | +| Memory (`/memory`) | 3 + `kilo-memory` | 3 | not-started | +| Codebase indexing | 3 + `kilo-indexing` | 3 | not-started | +| Sandbox tool shells (macOS/Linux) | 3 + `kilo-sandbox` (`shell.hook` / tool path) | 3 | not-started | +| Sandbox PTY / MCP / git spawn policy | 2 host, or explicitly out of scope | 3 | not-started | +| OpenTelemetry / telemetry | 2 or 3 | 3 | unknown | +| Kilo Swarm | unknown — resolve or `defer` before Phase 3 | 3 | unknown | +| Skills, custom agents, `.kilo/agents` | 3 | 3 | not-started | +| Project config discovery (`.kilo/`, `kilo.jsonc` vs `.opencode`) | 2 host seam or accept upstream names | 0–1 | not-started | +| Local `/review` | 2 host | 4 | not-started | +| CLI TUI remainder | 2 host | 4 | not-started | +| `kilo run --auto`, session export/import, resume Claude/Codex | 2 host | 4 | not-started | +| `/remote` | 2 + Gateway | 4 | not-started | +| `kilo cloud` CLI client | 2 + Gateway | 4 | not-started | +| Updater / update channel / packaging | 2 host (consequence of kilo-cli path) | 4 | not-started | +| ACP | 2 host (upstream in `packages/cli`, not exported) | 4 | not-started | +| Credential import (`auth.json` → v2) | 2 host | 6 | not-started | +| `kilo.jsonc` key mapping (Kilo-only keys) | 2 host | 6 | not-started | +| V1 schema-diff + fixture import test | 2 host | 6 | not-started | +| VS Code sidebar chat | 2 Protocol client | 5 | not-started | +| VS Code editor tabs | 2 client | 5 | not-started | +| Agent Manager | 2 client | 5 | not-started | +| VS Code settings webview | 2 client | 5 | not-started | +| Inline autocomplete / FIM | 2 client | 5 | unknown | +| Code actions, enhance prompt, git commit generation | 2 client | 5 | unknown | +| Task timeline, diff viewer | 2 client | 5 | not-started | +| Voice / speech-to-text | unknown | 5 | unknown | +| JetBrains plugin | 2 client | 5 | not-started | +| Kilo Console | — | — | obsolete | + +**Overlap with upstream v2** (do not rebuild): permissions, MCP, snapshots, compaction, session fork, ACP, plugins, subagents, `webfetch` / `websearch`, daemon HTTP+SSE. Port Kilo policy on these, not a second engine. + +## First slice + +Phase 0 on `kilo-v2` (internal): identity, empty store, branding (shared patch / hook), fail-closed. Not shipped to customers. + +Phase 1 in progress: gateway auth + catalog as owner-3 plugins; generated client is upstream-equivalent; prove one Kilo operation (auth/org) as the real seam gate. `kilo serve` partial (interactive + admission-only; no daemon attach). diff --git a/migration-tracking/github-drafts/issue-13750/progress-comment.md b/migration-tracking/github-drafts/issue-13750/progress-comment.md new file mode 100644 index 000000000000..0bf472b027d9 --- /dev/null +++ b/migration-tracking/github-drafts/issue-13750/progress-comment.md @@ -0,0 +1,24 @@ +## Migration checkpoint — September 10, 2026 + +The migration now has an isolated Kilo v2 CLI/runtime and an in-progress port of the **original Kilo VS Code interface**. The original interface builds; session, account, model, settings, memory, generation and terminal adapters have local coverage. Full original-client parity is still incomplete. + +Recent work includes configuration refresh that preserves active sessions and terminal processes, ticket-based terminal connections, and a Plan handoff fix that opens a new Code tab while retaining the Plan tab. + +The source assessment covers all **816 v1 files / 6,128 change-marker occurrences** at the pinned v1 revision: + +| Assessment | Files | +|---|---:| +| Already ported | 65 | +| Native equivalent | 137 | +| Partial | 233 | +| Needs port | 163 | +| Deferred | 68 | +| Not needed | 150 | + +These are source-review dispositions, not test results. + +The next work is original-client contract completion and acceptance, remaining runtime/TUI differences, sharing and remote integration, and distribution/cutover. Sharing still has viewer, ownership and synchronization gates; signing, hosted updates, distribution and cross-platform acceptance remain open. + +The issue body now shows per-phase status and current inventory statuses. The expanded repository plan carries detailed coverage, with source mapping in `migration-tracking/marker-audit/v1-kilo-marker-port-assessment.md`. + +Status reflects recorded local verification, not a fresh full-suite run. The parent issue tracks remaining deliverables and their acceptance criteria. diff --git a/migration-tracking/github-drafts/issue-13750/published-subissues.md b/migration-tracking/github-drafts/issue-13750/published-subissues.md new file mode 100644 index 000000000000..39968191affe --- /dev/null +++ b/migration-tracking/github-drafts/issue-13750/published-subissues.md @@ -0,0 +1,17 @@ +# Published subissues — September 10, 2026 + +All nine are linked as native subissues of [#13750](https://github.com/Kilo-Org/kilocode/issues/13750). + +## Subissues + +- [#14016 — Complete original Kilo VS Code client parity](https://github.com/Kilo-Org/kilocode/issues/14016) +- [#14017 — Complete remaining runtime and CLI/TUI parity](https://github.com/Kilo-Org/kilocode/issues/14017) +- [#14018 — Complete v2 session sharing compatibility and acceptance](https://github.com/Kilo-Org/kilocode/issues/14018) +- [#14019 — Complete remote session consumer acceptance](https://github.com/Kilo-Org/kilocode/issues/14019) +- [#14020 — Complete distribution and safe migration cutover](https://github.com/Kilo-Org/kilocode/issues/14020) +- [#14021 — Complete remaining editor services](https://github.com/Kilo-Org/kilocode/issues/14021) +- [#14022 — Port the JetBrains plugin to v2](https://github.com/Kilo-Org/kilocode/issues/14022) +- [#14023 — Assess and integrate cloud agent consumers with v2](https://github.com/Kilo-Org/kilocode/issues/14023) +- [#14024 — Assess and integrate Anaconda Desktop and related clients with v2](https://github.com/Kilo-Org/kilocode/issues/14024) + +Draft files are retained as the source used for publication; GitHub now holds their live execution status. diff --git a/migration-tracking/github-drafts/issue-13750/shared-source-footprint.md b/migration-tracking/github-drafts/issue-13750/shared-source-footprint.md new file mode 100644 index 000000000000..f49ff1eb10d8 --- /dev/null +++ b/migration-tracking/github-drafts/issue-13750/shared-source-footprint.md @@ -0,0 +1,3 @@ +# Shared source footprint + +The dated measurement is maintained in [v2-shared-source-footprint-2026-09-10.md](../../marker-audit/v2-shared-source-footprint-2026-09-10.md). diff --git a/migration-tracking/github-drafts/issue-13750/subissues/01-original-vscode.md b/migration-tracking/github-drafts/issue-13750/subissues/01-original-vscode.md new file mode 100644 index 000000000000..8acac6fa40e2 --- /dev/null +++ b/migration-tracking/github-drafts/issue-13750/subissues/01-original-vscode.md @@ -0,0 +1,33 @@ +# Complete original Kilo VS Code client parity + +## Outcome + +Port and validate the original Kilo webviews and host contracts against v2. + +## Scope + +Original sidebar and editor panels; lifecycle and streaming; permissions/questions; settings/accounts/models; timeline and diffs; Agent Manager/notebook host contracts. + +## Current position + +Original sources build and several adapters have focused local tests. Full client typechecking and original-UI acceptance remain incomplete. + +## Acceptance + +- [ ] Resolve remaining backend facade and consumer typecheck failures without no-op success shims. +- [ ] Verify prompt → response → idle, responsive Stop, reconnect and restart in the original UI. +- [ ] Complete original notifications and suggestions, including their request/reply lifecycle. +- [ ] Implement agent and skill removal and session message deletion required by the original client. +- [ ] Complete viewer presence registry/relay integration and validate original visibility reporting. +- [ ] Verify independent editor sessions, restore, settings persistence and account changes. +- [ ] Verify ticket-based terminal open, stream, resize, close and reconnect. +- [ ] Complete Agent Manager/notebook contracts or retain them explicitly as open scope. +- [ ] Record original-UI acceptance and remaining limitations in the repository plan. + +## Dependencies and boundaries + +Feature-specific FIM, speech and additional clients belong to the companion editor-services issue. Shared runtime defects belong to runtime parity. + +## Tracking + +Update the affected rows in `migration-tracking/plans/kilo-opencode-v2-plan-progress.md` and link acceptance evidence. diff --git a/migration-tracking/github-drafts/issue-13750/subissues/02-runtime-cli-tui.md b/migration-tracking/github-drafts/issue-13750/subissues/02-runtime-cli-tui.md new file mode 100644 index 000000000000..a8be709c4001 --- /dev/null +++ b/migration-tracking/github-drafts/issue-13750/subissues/02-runtime-cli-tui.md @@ -0,0 +1,29 @@ +# Complete remaining runtime and CLI/TUI parity + +## Outcome + +Resolve behavioral gaps from the source assessment using Kilo-owned seams where possible. + +## Scope + +Runtime policy and lifecycle differences; notifications, themes, scope/projection behavior and CLI/TUI workflows. + +## Current position + +Many runtime slices are implemented and tested locally. Source assessment still records partial and missing behavior. + +## Acceptance + +- [ ] Verify network-waiting, overflow and snapshot policy differences against v1 behavior at the pinned v1 revision. +- [ ] Complete remaining notifications, theme and location/session projection requirements. +- [ ] Keep regression coverage for the Plan-to-Code handoff. +- [ ] Explain each necessary shared hook and remove change annotations that are unnecessary under the Kilo-owned path conventions. +- [ ] Update partial/needs-port inventory dispositions with behavioral acceptance results, actual consumer evidence and test limits. + +## Dependencies and boundaries + +Do not reopen native engines merely to match v1 file layout. Client-specific rendering and distribution have separate work items. + +## Tracking + +Update the affected rows in `migration-tracking/plans/kilo-opencode-v2-plan-progress.md` and link acceptance evidence. diff --git a/migration-tracking/github-drafts/issue-13750/subissues/03-session-sharing.md b/migration-tracking/github-drafts/issue-13750/subissues/03-session-sharing.md new file mode 100644 index 000000000000..c684fcde2093 --- /dev/null +++ b/migration-tracking/github-drafts/issue-13750/subissues/03-session-sharing.md @@ -0,0 +1,30 @@ +# Complete v2 session sharing compatibility and acceptance + +## Outcome + +Complete the sharing contract across client, ingest service and public viewer. + +## Scope + +Viewer transcript compatibility, team ownership authorization, ongoing synchronization and deletion/revert semantics. + +## Current position + +Local share/unshare/fork paths and share metadata upload ahead of transcript content exist. Team sharing remains refused pending backend acceptance. + +## Acceptance + +- [ ] Verify the actual deployed token and metadata contracts when external testing is authorized. +- [ ] Implement or verify public viewer support for v2 self-contained messages. +- [ ] Establish backend team ownership authorization before enabling team uploads. +- [ ] Define and implement incremental synchronization with deletion/revert tombstones or an equivalent authoritative contract. +- [ ] Verify share, subsequent edits, revoke and fork with intentionally shareable data. +- [ ] Keep unsupported paths fail-closed and document cross-repository dependencies. + +## Dependencies and boundaries + +Requires cloud repository/backend work and later external acceptance. Local fixtures cannot stand in for deployed verification. + +## Tracking + +Update the affected rows in `migration-tracking/plans/kilo-opencode-v2-plan-progress.md` and link acceptance evidence. diff --git a/migration-tracking/github-drafts/issue-13750/subissues/04-remote-sessions.md b/migration-tracking/github-drafts/issue-13750/subissues/04-remote-sessions.md new file mode 100644 index 000000000000..3d7d3894c7fd --- /dev/null +++ b/migration-tracking/github-drafts/issue-13750/subissues/04-remote-sessions.md @@ -0,0 +1,30 @@ +# Complete remote session consumer acceptance + +## Outcome + +Validate the v2 remote adapter against actual relay and client behavior. + +## Scope + +Subscription scope, event/transcript projection, inline attachments, suggestions, reconnect and exit lifecycle. + +## Current position + +Local transport, attachment capability advertisement and suggestion lifecycle have focused coverage. Deployed consumer acceptance remains separate. + +## Acceptance + +- [ ] Verify actual relay/client contract compatibility when external access is authorized. +- [ ] Exercise streamed transcript, attachments and reconnect without duplicate or missing content. +- [ ] Exercise suggestion accept/dismiss/cancel and subscribed-session ownership. +- [ ] Verify exit and interruption semantics through the consuming client. +- [ ] Resolve remaining metadata differences from v1 or record a product decision. +- [ ] Keep unsupported capabilities unadvertised and preserve local regression coverage. + +## Dependencies and boundaries + +Session sharing persistence is tracked separately. + +## Tracking + +Update the affected rows in `migration-tracking/plans/kilo-opencode-v2-plan-progress.md` and link acceptance evidence. diff --git a/migration-tracking/github-drafts/issue-13750/subissues/05-distribution-cutover.md b/migration-tracking/github-drafts/issue-13750/subissues/05-distribution-cutover.md new file mode 100644 index 000000000000..d2c3eee77274 --- /dev/null +++ b/migration-tracking/github-drafts/issue-13750/subissues/05-distribution-cutover.md @@ -0,0 +1,31 @@ +# Complete distribution and safe migration cutover + +## Outcome + +Turn validated local preview mechanics into an accepted distribution and migration path. + +## Scope + +Portable payloads, update/rollback, signing, platforms, clean installation, explicit import and canary cutover. + +## Current position + +Real local directory update/rollback and supported copy-import paths are covered. Production archives, signing and hosted distribution are not accepted. + +## Acceptance + +- [ ] Complete Kilo CI and build hardening, including reproducible package and client builds. +- [ ] Complete a safe production payload format and its integrity/ownership boundaries. +- [ ] Verify update and rollback with actual supported platform artifacts. +- [ ] Complete signing/notarization and distribution automation where required. +- [ ] Verify clean install and source build dependency resolution. +- [ ] Exercise opt-in database/config/credential import while preserving v1 originals. +- [ ] Verify isolated canary and a documented production cutover/rollback procedure. + +## Dependencies and boundaries + +External publishing and platform acceptance require their own execution authorization. + +## Tracking + +Update the affected rows in `migration-tracking/plans/kilo-opencode-v2-plan-progress.md` and link acceptance evidence. diff --git a/migration-tracking/github-drafts/issue-13750/subissues/06-editor-services-clients.md b/migration-tracking/github-drafts/issue-13750/subissues/06-editor-services-clients.md new file mode 100644 index 000000000000..c9b7e8ad3b95 --- /dev/null +++ b/migration-tracking/github-drafts/issue-13750/subissues/06-editor-services-clients.md @@ -0,0 +1,30 @@ +# Complete remaining editor services + +## Outcome + +Complete feature-specific editor services beyond basic VS Code chat parity. + +## Scope + +Inline autocomplete/FIM and next edit; code actions and SCM generation workflows; speech and other source-assessed editor services. + +## Current position + +Stateless generation adapters have local coverage. That does not establish editor workflow, provider or additional-client acceptance. + +## Acceptance + +- [ ] Inventory each remaining service against its actual original consumer and v2 destination. +- [ ] Implement and validate FIM/next-edit producer and editor integration. +- [ ] Verify code actions, enhance prompt and commit generation in their real UI/SCM workflows. +- [ ] Complete speech capture/transcription integration and provider acceptance. +- [ ] Complete image generation and Claw consumer/service integration, with actual provider acceptance. +- [ ] Assign remaining feature services independent follow-ups when ownership is known; record any scope removal as a product decision. + +## Dependencies and boundaries + +Coordinate with original VS Code parity for shared host plumbing. JetBrains and additional runtime consumers have separate integration workstreams. Split editor services further by owner when useful. + +## Tracking + +Update the affected rows in `migration-tracking/plans/kilo-opencode-v2-plan-progress.md` and link acceptance evidence. diff --git a/migration-tracking/github-drafts/issue-13750/subissues/07-jetbrains.md b/migration-tracking/github-drafts/issue-13750/subissues/07-jetbrains.md new file mode 100644 index 000000000000..6a995699556f --- /dev/null +++ b/migration-tracking/github-drafts/issue-13750/subissues/07-jetbrains.md @@ -0,0 +1,30 @@ +# Port the JetBrains plugin to v2 + +## Outcome + +Preserve the original JetBrains experience through v2 public interfaces. + +## Scope + +Phase 5 — product clients. IDE plugin lifecycle, server discovery/authentication, session and terminal workflows. + +## Current position + +No port acceptance is recorded. Preserve the existing JetBrains product experience while replacing its backend integration; do not assume the VS Code adapter is reusable unchanged. + +## Acceptance + +- [ ] Inventory the existing plugin’s SDK, HTTP, event and terminal contracts and map each to a public v2 interface. +- [ ] Implement server startup/discovery, authentication and compatibility checks appropriate to the existing plugin lifecycle. +- [ ] Port session, streaming, cancellation, permissions, history and configuration workflows required by the original client. +- [ ] Identify dependencies on shared editor services such as FIM and generation without duplicating their implementation. +- [ ] Validate the real plugin in supported IDEs, including reconnect, restart and version mismatch handling. +- [ ] Update the JetBrains inventory row with remaining gaps and acceptance evidence. + +## Dependencies and boundaries + +Depends on stable public client contracts and applicable shared editor services. Completion of VS Code does not close this issue. + +## Tracking + +Update the affected rows in `migration-tracking/plans/kilo-opencode-v2-plan-progress.md` and link acceptance evidence. diff --git a/migration-tracking/github-drafts/issue-13750/subissues/08-cloud-agent-integration.md b/migration-tracking/github-drafts/issue-13750/subissues/08-cloud-agent-integration.md new file mode 100644 index 000000000000..46dffeb92def --- /dev/null +++ b/migration-tracking/github-drafts/issue-13750/subissues/08-cloud-agent-integration.md @@ -0,0 +1,30 @@ +# Assess and integrate cloud agent consumers with v2 + +## Outcome + +Establish and implement v2 compatibility for cloud agent consumers. + +## Scope + +Phase 5 — cloud agent startup, runtime/client contracts, execution lifecycle and persistence, with phase 6 deployment/cutover dependencies. + +## Current position + +Cloud agent integration needs a dedicated consumer/contract assessment. Existing kilo cloud CLI coverage, remote relay coverage, and Agent Manager UI work do not establish compatibility of cloud-hosted agent execution. + +## Acceptance + +- [ ] Identify actual cloud agent consumers, repositories, owners and deployed runtime versions; define which are in migration scope. +- [ ] Map startup, session admission, streaming, tools, credentials, persistence and cancellation contracts from actual consumers. +- [ ] Determine whether each consumer needs a v2 client adaptation, runtime packaging changes, or no change, with evidence. +- [ ] Implement the confirmed gaps through public interfaces and track cross-repository dependencies. +- [ ] Validate local contracts separately from hosted execution, recovery and deployment acceptance. +- [ ] Document rollout compatibility and update the detailed plan before claiming integration complete. + +## Dependencies and boundaries + +Coordinate with distribution, remote and sharing only where their contracts are actually consumed. Hosted acceptance remains outstanding until authorized and executed; do not substitute local fixtures for it. + +## Tracking + +Update the affected rows in `migration-tracking/plans/kilo-opencode-v2-plan-progress.md` and link acceptance evidence. diff --git a/migration-tracking/github-drafts/issue-13750/subissues/09-anaconda-client-integration.md b/migration-tracking/github-drafts/issue-13750/subissues/09-anaconda-client-integration.md new file mode 100644 index 000000000000..974f3a6c5429 --- /dev/null +++ b/migration-tracking/github-drafts/issue-13750/subissues/09-anaconda-client-integration.md @@ -0,0 +1,30 @@ +# Assess and integrate Anaconda Desktop and related clients with v2 + +## Outcome + +Establish and implement v2 compatibility for Anaconda Desktop. + +## Scope + +Phase 5 — additional clients. Desktop startup, authentication, session workflows and runtime compatibility; discovery establishes the exact integration mode. + +## Current position + +Discovery is required before implementation scope can be established. Inspect the existing desktop integration methods and their actual consumers; verify their v2 compatibility. + +## Acceptance + +- [ ] Identify the exact Anaconda Desktop product, source repository, owner and supported integration mode. +- [ ] Inspect its actual Kilo dependency: binary invocation, SDK/HTTP, ACP, extension bridge or another contract. +- [ ] Assess startup, identity/storage, authentication, session lifecycle and version negotiation where applicable. +- [ ] Record an evidence-backed no-change result or implement each required adaptation. +- [ ] Validate the actual client workflow, including failures and upgrade compatibility. +- [ ] Add any other discovered consumers as named inventory entries with owners and separate follow-ups when independently actionable. + +## Dependencies and boundaries + +Start with discovery rather than presumed implementation. Native ACP support or a working standalone Kilo CLI does not by itself prove this client’s compatibility. + +## Tracking + +Update the affected rows in `migration-tracking/plans/kilo-opencode-v2-plan-progress.md` and link acceptance evidence. diff --git a/migration-tracking/github-drafts/issue-13750/tracking-policy.md b/migration-tracking/github-drafts/issue-13750/tracking-policy.md new file mode 100644 index 000000000000..3981d3db6fca --- /dev/null +++ b/migration-tracking/github-drafts/issue-13750/tracking-policy.md @@ -0,0 +1,21 @@ +# Maintaining the migration plan + +Keep the repository plan. It provides versioned detail that can change alongside implementation and tests; the GitHub issue remains the public entry point. + +| Document | Owns | Update when | +|---|---|---| +| `migration-tracking/plans/kilo-opencode-v2-plan-progress.md` | Detailed capability inventory, behavior, destination and acceptance boundaries | A change adds behavior, closes a gap, or changes scope | +| GitHub #13750 body | Architecture, original phases, compact capability status and links to remaining work | A milestone or material status/decision changes | +| Subissues | Independently actionable remaining work, owner, dependencies and acceptance checklist | Work progresses or acceptance changes | +| Issue comments | Dated results, decisions, blockers and links to PRs/test evidence | A meaningful checkpoint occurs | +| `migration-tracking/test-plans/` | Reproducible acceptance procedures and recorded limitations | Verification procedures or results change | +| `migration-tracking/marker-audit/` | Source-to-destination mapping and rationale for removing or retaining patches | A mapped behavior or placement changes | +| `migration-tracking/plans/kilo-opencode-v2-working-log.md` | Historical working record | Append only when useful; do not treat old checkpoints as current status | + +The detailed repository inventory is the reference for capability coverage. The issue summarizes it; do not maintain another expanded inventory in comments. A subissue completion must update affected repository rows and the parent summary, without implying adjacent capabilities are complete. + +For each accepted milestone, update the detailed plan in the implementation PR, include acceptance evidence and outstanding limitations, then summarize the changed rows on GitHub. While a branch is unpublished, identify local-only evidence explicitly. Link milestone evidence to a commit when stable provenance matters. + +Do not make a marker count into a completion score. “Ported”, “native equivalent” and “not needed” need source-backed rationale; behavioral acceptance remains separate. “Deferred” remains outstanding unless an explicit product decision removes it from scope. Refusing unsupported imports is safe import behavior, not parity for the refused feature. + +Retain the working log for traceability, with a clear historical role. Do not copy its internal coordination or obsolete percentages into the public issue. This draft package does not rename or overwrite either existing plan. diff --git a/migration-tracking/marker-audit/README.md b/migration-tracking/marker-audit/README.md new file mode 100644 index 000000000000..629f96dd5f3b --- /dev/null +++ b/migration-tracking/marker-audit/README.md @@ -0,0 +1,12 @@ +# Kilo marker audits + +Start with the [current v2 shared-change inventory](v2-current-shared-change-inventory.md) to review modifications to upstream-owned files. It includes clickable source/marker links and the metadata exceptions that a marker-only search misses. + +| Document | Purpose | +|---|---| +| [Current v2 shared changes](v2-current-shared-change-inventory.md) | Upstream-diff inventory after marker coverage cleanup: 54 shared paths, 52 marked, 2 metadata exceptions. | +| [Shared source footprint](v2-shared-source-footprint-2026-09-10.md) | The narrower 24-production-source-file footprint and annotation rules. | +| [Historical marker inventory](kilo-override-marker-inventory.md) | Original pre-cleanup search; historical counts and line numbers are preserved. | +| [V1 port assessment](v1-kilo-marker-port-assessment.md) / [TSV](v1-kilo-marker-port-assessment.tsv) | 816 pinned v1 files / 6,128 occurrences, classified by behavior and destination. Not a current v2 marker count. | + +Kilo-owned implementations intentionally do not carry patch markers. The v1 assessment’s 65 “already ported” files are a source-assessment category, not the number of modified upstream v2 files. Keep historical assessments intact; refresh the current inventory when shared changes move or annotations change. diff --git a/migration-tracking/marker-audit/kilo-override-marker-inventory.md b/migration-tracking/marker-audit/kilo-override-marker-inventory.md new file mode 100644 index 000000000000..6bee828d7d5a --- /dev/null +++ b/migration-tracking/marker-audit/kilo-override-marker-inventory.md @@ -0,0 +1,954 @@ +> For current upstream v2 changes and marker links, use the [current shared-change inventory](v2-current-shared-change-inventory.md). This document preserves its original assessment scope and counts. + +> Historical snapshot: this inventory was captured before the 2026-09-08 cleanup of 350 redundant `kilocode_change` markers from 55 Kilo-owned files. Counts and line references below describe that pre-cleanup state, not the current checkout. Shared upstream markers were preserved. + +# Kilo override-marker inventory + +Generated 2026-09-08 from the working tree at `/Users/johnnyamancio/Workspace/kilo_workspace/kilo-opencode-v2-baseline`. + +## Scope and method + +This is a read-only inventory of tracked and untracked, non-ignored authored files returned by `git ls-files --cached --others --exclude-standard`. It excludes paths under `.git/`, `node_modules/`, `dist/`, `build/`, `coverage/`, `.cache/`, `cache/`, and `tmp/`, plus generated client/OpenAPI surfaces (`packages/client/src/generated*`, `packages/protocol/openapi.json`, and `packages/www/openapi.json`). Binary/unreadable files were skipped. + +Primary searches: + +```sh +git ls-files -z --cached --others --exclude-standard +rg -n --hidden 'kilocode_change' +rg -n -i --hidden 'kilocode[ _-]*change|kilo[ _-]*(override|patch|modification)' +``` + +The broader expression discovered actual near variants. Exact `kilocode_change` is the convention-defined marker. Classification is per occurrence: prose that names or explains the marker is a documentation mention, while comments attached to authored overrides are patch markers. Thus `AGENTS.md:1` is a patch marker but `AGENTS.md:3` is a documentation mention; the prose in `packages/schema/src/kilocode/presence.ts:6` is also a documentation mention. Source/config markers in Kilo-owned paths remain listed as patch/override markers even though the convention says markers there are unnecessary noise. + +Important limitation: this marker list is not a complete list of Kilo patches. Kilo-owned files under `kilocode/`, `packages//src/kilocode/`, and `packages//test/kilocode/` need no marker, and newly authored Kilo-owned packages may likewise contain unmarked Kilo implementation. + +## Totals + +- 441 broad-search occurrences in 93 unique files. +- 436 exact `kilocode_change` occurrences; 5 near-variant occurrences. +- 430 patch/override-marker occurrences in 85 files. +- 11 documentation mentions in 9 files. +- 32 tracked files and 61 untracked files represented. + +## Patch marker counts by package + +| Package/scope | Unique files | Occurrences | +|---|---:|---:| +| `(repository root / non-package)` | 1 | 1 | +| `packages/core` | 6 | 13 | +| `packages/kilo-ide-ui` | 54 | 349 | +| `packages/kilo-indexing` | 1 | 1 | +| `packages/kilo-ui` | 2 | 4 | +| `packages/kilo-vscode` | 1 | 1 | +| `packages/plugin` | 1 | 1 | +| `packages/theme` | 4 | 5 | +| `packages/tui` | 15 | 55 | + +## Full file list with line references + +### `.kilo/plans/1788595789852-glowing-panda.md` + +Classification: documentation mention; untracked. 1 occurrence. + +- L52 [documentation]: `Kilo-prefixed paths inside packages we wrap). No \`kilocode_change\` marker (owned` + +### `AGENTS.md` + +Classification: patch/override marker and documentation mention; tracked. 2 occurrences. + +- L1 [patch]: `` +- L3 [documentation]: `- Read \`kilocode/v2-fork-conventions.md\` before branching, merging upstream, or adding Kilo code. It overrides the branch-name rule below and defines Kilo-owned paths, shared-hook limits, and \`kilocode_change\` markers.` + +### `kilocode/baseline/config-mapping-v2-gap.md` + +Classification: documentation mention; untracked. 1 occurrence. + +- L23 [documentation]: `the Kilo-only nested fields marked \`kilocode_change\` in the pinned file.` + +### `kilocode/baseline/model-prompt-policy-v2-parity.md` + +Classification: documentation mention; untracked. 1 occurrence. + +- L85 [documentation]: `- \`packages/core/src/plugin/system-prompt.ts:38-42\` — a \`kilocode_change\` comment plus` + +### `kilocode/baseline/model-sidebar-v2-parity.md` + +Classification: documentation mention; tracked. 1 occurrence. + +- L35 [documentation]: `Each is marked \`kilocode_change\`; Kilo policy, transport, metadata and panels` + +### `kilocode/baseline/v1-session-schema-audit.md` + +Classification: documentation mention; tracked. 1 occurrence. + +- L112 [documentation]: `These are v1-main \`kilocode_change\` fields absent from v2's \`SessionV1\`` + +### `kilocode/v2-fork-conventions.md` + +Classification: documentation mention; tracked. 2 occurrences. + +- L127 [documentation]: `## \`kilocode_change\` markers` +- L129 [documentation]: `- Mark every Kilo edit inside a shared upstream file with a \`kilocode_change\`` + +### `packages/core/src/config.ts` + +Classification: patch/override marker; tracked. 3 occurrences. + +- L36 [patch]: `// kilocode_change - expose native refresh for hosts without filesystem watchers.` +- L322 [patch]: `// kilocode_change - retain location dependencies when refresh is invoked by a host RPC.` +- L325 [patch]: `// kilocode_change - reuse the serialized native loader for explicit refresh.` + +### `packages/core/src/mcp/index.ts` + +Classification: patch/override marker; tracked. 4 occurrences. + +- L185 [patch]: `// kilocode_change — host spawn policy with resources owned by the MCP connection scope.` +- L193 [patch]: `// kilocode_change — accept an optional host spawn policy.` +- L533 [patch]: `// kilocode_change — run the host policy under the existing connection scope and` +- L878 [patch]: `// kilocode_change — preserve the host spawn policy in Location node replacements.` + +### `packages/core/src/plugin/sdk.ts` + +Classification: patch/override marker; tracked. 2 occurrences. + +- L22 [patch]: `// kilocode_change - Let an embedding host enforce policy after user config without making that policy a builtin.` +- L36 [patch]: `// kilocode_change - Post registrations are an explicit host-only policy seam and never alter the default order.` + +### `packages/core/src/plugin/supervisor.ts` + +Classification: patch/override marker; tracked. 2 occurrences. + +- L27 [patch]: `// kilocode_change - Post SDK plugins are host-enforced policy: config cannot disable or shadow them.` +- L156 [patch]: `// kilocode_change - Host policy plugins registered for the post phase must win over config overlays.` + +### `packages/core/src/plugin/system-prompt.ts` + +Classification: patch/override marker; tracked. 1 occurrence. + +- L38 [patch]: `// kilocode_change - Public exports of the maintained Anthropic/Trinity assets so the Kilo CLI` + +### `packages/core/test/config/config.test.ts` + +Classification: patch/override marker; tracked. 1 occurrence. + +- L79 [patch]: `// kilocode_change - explicit refresh must retain dependencies outside the caller's Effect context.` + +### `packages/kilo-ide-ui/src/components/basic-tool.tsx` + +Classification: patch/override marker; untracked. 17 occurrences. + +- L6 [patch]: `import { Icon, type IconProps } from "./icon" // kilocode_change: added Icon` +- L36 [patch]: `retainDetails?: boolean // kilocode_change` +- L37 [patch]: `hasDetails?: boolean // kilocode_change` +- L40 [patch]: `allowPendingToggle?: boolean // kilocode_change` +- L95 [patch]: `const hasDetails = () => props.hasDetails ?? !!hasChildren() // kilocode_change` +- L137 [patch]: `if (!props.retainDetails) setState("ready", false) // kilocode_change` +- L180 [patch]: `if (pending() && !props.allowPendingToggle) return // kilocode_change` +- L181 [patch]: `if (props.hideDetails) return // kilocode_change` +- L184 [patch]: `props.onOpenChange?.(value) // kilocode_change` +- L187 [patch]: `// kilocode_change start` +- L193 [patch]: `// kilocode_change end` +- L202 [patch]: `{/* kilocode_change start */}` +- L206 [patch]: `{/* kilocode_change end */}` +- L265 [patch]: `{/* kilocode_change start */}` +- L276 [patch]: `{/* kilocode_change end */}` +- L317 [patch]: `{/* kilocode_change start */}` +- L323 [patch]: `{/* kilocode_change end */}` + +### `packages/kilo-ide-ui/src/components/button.css` + +Classification: patch/override marker; untracked. 1 occurrence. + +- L1 [patch]: `/* kilocode_change - reverted to main */` + +### `packages/kilo-ide-ui/src/components/card.css` + +Classification: patch/override marker; untracked. 4 occurrences. + +- L10 [patch]: `/* kilocode_change start — preserve Kilo's bordered card surface */` +- L16 [patch]: `/* kilocode_change end */` +- L88 [patch]: `/* kilocode_change start — preserve Kilo error and warning card variants */` +- L121 [patch]: `/* kilocode_change end */` + +### `packages/kilo-ide-ui/src/components/dock-surface.tsx` + +Classification: patch/override marker; untracked. 1 occurrence. + +- L28 [patch]: `data-component="prompt-input-form" // kilocode_change` + +### `packages/kilo-ide-ui/src/components/icon.tsx` + +Classification: patch/override marker; untracked. 2 occurrences. + +- L89 [patch]: `lock: \`\`, // kilocode_change` + +### `packages/kilo-ide-ui/src/components/list.tsx` + +Classification: patch/override marker; untracked. 2 occurrences. + +- L184 [patch]: `// kilocode_change start - fall back to first result when no item is active (noInitialSelection)` +- L187 [patch]: `// kilocode_change end` + +### `packages/kilo-ide-ui/src/components/markdown-shiki.worker.ts` + +Classification: patch/override marker; untracked. 2 occurrences. + +- L23 [patch]: `let theme = "Kilo" // kilocode_change - use the Kilo theme supplied during worker initialization` +- L32 [patch]: `theme = event.data.theme.name // kilocode_change` + +### `packages/kilo-ide-ui/src/components/markdown-worker.ts` + +Classification: patch/override marker; untracked. 2 occurrences. + +- L2 [patch]: `import { KiloTheme } from "../context/marked" // kilocode_change` +- L120 [patch]: `worker.postMessage({ type: "init", theme: KiloTheme } satisfies MarkdownWorkerRequest) // kilocode_change` + +### `packages/kilo-ide-ui/src/components/markdown.css` + +Classification: patch/override marker; untracked. 10 occurrences. + +- L69 [patch]: `margin-inline-start: 0; /* kilocode_change */` +- L70 [patch]: `padding-inline-start: 32px; /* kilocode_change */` +- L80 [patch]: `padding-inline-start: 2.25rem; /* kilocode_change */` +- L106 [patch]: `padding-inline-start: 1rem; /* Minimal indent for nesting only */ /* kilocode_change */` +- L110 [patch]: `padding-inline-start: 1.75rem; /* kilocode_change */` +- L115 [patch]: `border-inline-start: 2px solid var(--border-weak-base); /* kilocode_change */` +- L117 [patch]: `padding-inline-start: 0.5rem; /* kilocode_change */` +- L235 [patch]: `/* kilocode_change start */` +- L248 [patch]: `/* kilocode_change end */` +- L266 [patch]: `text-align: start; /* kilocode_change */` + +### `packages/kilo-ide-ui/src/components/markdown.tsx` + +Classification: patch/override marker; untracked. 37 occurrences. + +- L2 [patch]: `import { deferredHighlight, fnv1a } from "../context/marked" // kilocode_change` +- L29 [patch]: `// kilocode_change start: Mermaid rendering and morphdom guards for highlighted blocks` +- L32 [patch]: `// kilocode_change end` +- L46 [patch]: `src: string // kilocode_change - Mermaid consumes delimiter-free source while raw preserves stream identity` +- L380 [patch]: `// kilocode_change start: mermaid blocks are rendered as diagrams by` +- L389 [patch]: `src: block.src, // kilocode_change` +- L398 [patch]: `// kilocode_change end` +- L406 [patch]: `src: block.src, // kilocode_change` +- L452 [patch]: `// kilocode_change start: generation counter prevents stale deferredHighlight` +- L455 [patch]: `// kilocode_change end` +- L457 [patch]: `// kilocode_change start: Mermaid diagram rendering` +- L459 [patch]: `// kilocode_change end` +- L470 [patch]: `// kilocode_change start: Mermaid diagram rendering` +- L473 [patch]: `// kilocode_change end` +- L487 [patch]: `content.forEach((block, index) => updateBlock(container, index, block, labels, local.streaming ?? false)) // kilocode_change` +- L498 [patch]: `// kilocode_change start: progressive Shiki highlighting for non-streaming` +- L519 [patch]: `// kilocode_change end` +- L522 [patch]: `// kilocode_change start: progressive Shiki highlighting (issue #6221, PR #7102).` +- L543 [patch]: `// kilocode_change end` +- L545 [patch]: `// kilocode_change start: Mermaid diagram rendering` +- L560 [patch]: `// kilocode_change end` +- L563 [patch]: `// kilocode_change: cancel any in-flight deferredHighlight pass so its` +- L567 [patch]: `// kilocode_change start: Mermaid diagram rendering` +- L570 [patch]: `// kilocode_change end` +- L579 [patch]: `dir={"auto" /* kilocode_change */}` +- L609 [patch]: `src: block.src, // kilocode_change` +- L629 [patch]: `streaming: boolean, // kilocode_change` +- L664 [patch]: `// kilocode_change start: preserve "copied" visual state across re-renders` +- L666 [patch]: `// kilocode_change end` +- L670 [patch]: `// kilocode_change start: preserve rendered Mermaid diagrams across` +- L673 [patch]: `// kilocode_change end` +- L674 [patch]: `// kilocode_change start: preserve Shiki-highlighted blocks — don't let` +- L693 [patch]: `// kilocode_change end` +- L713 [patch]: `// kilocode_change start: mermaid blocks render as a source

     for`
    +- L723 [patch]: `codeElement.textContent = block.src // kilocode_change - Mermaid rejects fenced Markdown as diagram source`
    +- L732 [patch]: `// kilocode_change end`
    +- L768 [patch]: `pre.setAttribute("dir", "auto") // kilocode_change`
    +
    +### `packages/kilo-ide-ui/src/components/message-part.css`
    +
    +Classification: patch/override marker; untracked. 12 occurrences.
    +
    +- L24 [patch]: `gap: 8px; /* kilocode_change */`
    +- L46 [patch]: `cursor: pointer; /* kilocode_change */`
    +- L185 [patch]: `gap: 6px; /* kilocode_change */`
    +- L252 [patch]: `/* kilocode_change: always show copy button for the final turn response */`
    +- L485 [patch]: `/* kilocode_change start */`
    +- L495 [patch]: `/* kilocode_change end */`
    +- L513 [patch]: `/* kilocode_change start */`
    +- L523 [patch]: `/* kilocode_change end */`
    +- L1290 [patch]: `/* kilocode_change start */`
    +- L1300 [patch]: `/* kilocode_change end */`
    +- L1345 [patch]: `/* kilocode_change start */`
    +- L1354 [patch]: `/* kilocode_change end */`
    +
    +### `packages/kilo-ide-ui/src/components/message-part.tsx`
    +
    +Classification: patch/override marker; untracked. 12 occurrences.
    +
    +- L163 [patch]: `queued?: boolean // kilocode_change`
    +- L859 [patch]: `queued={props.queued} // kilocode_change`
    +- L1065 [patch]: `queued?: boolean // kilocode_change`
    +- L1155 [patch]: `data-queued={props.queued ? "" : undefined} // kilocode_change`
    +- L1181 [patch]: `
    {/* kilocode_change */}` +- L1184 [patch]: `{/* kilocode_change start */}` +- L1190 [patch]: `{/* kilocode_change end */}` +- L1555 [patch]: `// kilocode_change start` +- L1567 [patch]: `// kilocode_change end` +- L1570 [patch]: `.filter((item): item is TextPart => item?.type === "text" && !!item.text?.trim() && !item.synthetic) // kilocode_change` +- L1575 [patch]: `if (part().synthetic) return false // kilocode_change` +- L1593 [patch]: `` + +### `packages/kilo-ide-ui/src/components/popover.tsx` + +Classification: patch/override marker; untracked. 5 occurrences. + +- L23 [patch]: `contentLabel?: string // kilocode_change` +- L38 [patch]: `"contentLabel", // kilocode_change` +- L57 [patch]: `ready: true, // kilocode_change` +- L141 [patch]: `aria-label={local.contentLabel /* kilocode_change */}` +- L163 [patch]: `if (opened() \|\| state.dismiss === "outside") event.preventDefault() // kilocode_change` + +### `packages/kilo-ide-ui/src/components/resize-handle.tsx` + +Classification: patch/override marker; untracked. 4 occurrences. + +- L35 [patch]: `// kilocode_change start - set resize cursor on body during drag` +- L37 [patch]: `// kilocode_change end` +- L40 [patch]: `document.body.style.cursor = cursor // kilocode_change` +- L60 [patch]: `document.body.style.cursor = "" // kilocode_change` + +### `packages/kilo-ide-ui/src/components/select.css` + +Classification: patch/override marker; untracked. 1 occurrence. + +- L172 [patch]: `max-width: min(23rem, var(--kb-popper-content-available-width)); /* kilocode_change */` + +### `packages/kilo-ide-ui/src/components/select.test.ts` + +Classification: patch/override marker; untracked. 1 occurrence. + +- L1 [patch]: `// kilocode_change - new file` + +### `packages/kilo-ide-ui/src/components/select.tsx` + +Classification: patch/override marker; untracked. 3 occurrences. + +- L92 [patch]: `overlap={local.triggerVariant === "settings"} // kilocode_change` +- L93 [patch]: `fitViewport={local.triggerVariant === "settings"} // kilocode_change` +- L94 [patch]: `overflowPadding={local.triggerVariant === "settings" ? 12 : undefined} // kilocode_change` + +### `packages/kilo-ide-ui/src/components/session-turn.tsx` + +Classification: patch/override marker; untracked. 7 occurrences. + +- L162 [patch]: `queued?: boolean // kilocode_change` +- L209 [patch]: `if (typeof props.active === "boolean" && typeof props.queued === "boolean") return // kilocode_change` +- L234 [patch]: `// kilocode_change start — restore queued feature` +- L244 [patch]: `// kilocode_change end` +- L386 [patch]: `if (queued()) return false // kilocode_change` +- L415 [patch]: `{/* kilocode_change start */}` +- L417 [patch]: `{/* kilocode_change end */}` + +### `packages/kilo-ide-ui/src/components/switch.tsx` + +Classification: patch/override marker; untracked. 4 occurrences. + +- L8 [patch]: `inputProps?: ComponentProps // kilocode_change` +- L12 [patch]: `// kilocode_change start` +- L14 [patch]: `// kilocode_change end` +- L17 [patch]: ` {/* kilocode_change */}` + +### `packages/kilo-ide-ui/src/components/text-shimmer.css` + +Classification: patch/override marker; untracked. 2 occurrences. + +- L24 [patch]: `white-space: pre; /* kilocode_change - preserve inactive fallback whitespace */` +- L59 [patch]: `/* kilocode_change — gate animation on data-active directly (was data-run` + +### `packages/kilo-ide-ui/src/components/text-shimmer.tsx` + +Classification: patch/override marker; untracked. 2 occurrences. + +- L1 [patch]: `// kilocode_change start — the previous implementation used a createEffect that` +- L51 [patch]: `// kilocode_change end` + +### `packages/kilo-ide-ui/src/components/todo-panel-motion.stories.tsx` + +Classification: patch/override marker; untracked. 8 occurrences. + +- L2 [patch]: `import { createMemo, onCleanup } from "solid-js" // kilocode_change` +- L13 [patch]: `// kilocode_change start` +- L16 [patch]: `// kilocode_change end` +- L142 [patch]: `collapsed: false, // kilocode_change` +- L184 [patch]: `// kilocode_change start` +- L191 [patch]: `// kilocode_change end` +- L254 [patch]: `{/* kilocode_change start */}` +- L280 [patch]: `{/* kilocode_change end */}` + +### `packages/kilo-ide-ui/src/components/tool-error-card.css` + +Classification: patch/override marker; untracked. 2 occurrences. + +- L3 [patch]: `background-color: transparent; /* kilocode_change — override restored card background */` +- L4 [patch]: `border: none; /* kilocode_change — override restored card border */` + +### `packages/kilo-ide-ui/src/context/data.tsx` + +Classification: patch/override marker; untracked. 17 occurrences. + +- L44 [patch]: `// kilocode_change start` +- L49 [patch]: `before?: string // kilocode_change - optional, kilo uses \`patch\`` +- L50 [patch]: `after?: string // kilocode_change - optional, kilo uses \`patch\`` +- L51 [patch]: `patch?: string // kilocode_change` +- L58 [patch]: `export type OpenContentFn = (content: string, language?: string) => void // kilocode_change` +- L60 [patch]: `export type ValidateFilesFn = (paths: string[]) => Promise // kilocode_change` +- L61 [patch]: `// kilocode_change end` +- L70 [patch]: `onOpenFile?: OpenFileFn // kilocode_change` +- L71 [patch]: `onOpenDiff?: OpenDiffFn // kilocode_change` +- L72 [patch]: `onOpenUrl?: OpenUrlFn // kilocode_change` +- L73 [patch]: `onOpenContent?: OpenContentFn // kilocode_change` +- L74 [patch]: `onValidateFiles?: ValidateFilesFn // kilocode_change` +- L85 [patch]: `openFile: props.onOpenFile, // kilocode_change` +- L86 [patch]: `openDiff: props.onOpenDiff, // kilocode_change` +- L87 [patch]: `openUrl: props.onOpenUrl, // kilocode_change` +- L88 [patch]: `openContent: props.onOpenContent, // kilocode_change` +- L89 [patch]: `validateFiles: props.onValidateFiles, // kilocode_change` + +### `packages/kilo-ide-ui/src/context/index.ts` + +Classification: patch/override marker; untracked. 1 occurrence. + +- L2 [patch]: `export * from "./data" // kilocode_change - Kilo typechecks packages/ui, whose components import useData from here` + +### `packages/kilo-ide-ui/src/context/marked.tsx` + +Classification: patch/override marker; untracked. 33 occurrences. + +- L2 [patch]: `// kilocode_change: marked-shiki highlighted code blocks synchronously during` +- L9 [patch]: `// kilocode_change start: import types for double-dollar math extension` +- L11 [patch]: `// kilocode_change end` +- L13 [patch]: `import { parseFilePath } from "../file-path" // kilocode_change` +- L15 [patch]: `import { getSharedHighlighter, type ThemeRegistrationResolved } from "@pierre/diffs" // kilocode_change` +- L16 [patch]: `import { ensureKiloDiffTheme, KILO_DIFF_THEME } from "../pierre/kilo-diff-theme" // kilocode_change` +- L18 [patch]: `// kilocode_change start: the "Kilo" diff/highlight theme registration moved to` +- L25 [patch]: `// kilocode_change end` +- L27 [patch]: `// kilocode_change start: theme object consumed by the streaming Shiki worker` +- L401 [patch]: `// kilocode_change end` +- L403 [patch]: `// kilocode_change start: double-dollar-only math rules for marked.` +- L406 [patch]: `// kilocode_change end` +- L408 [patch]: `// kilocode_change start: isolate KaTeX from the markdown root dir=auto.` +- L413 [patch]: `// kilocode_change end` +- L422 [patch]: `// kilocode_change start` +- L427 [patch]: `// kilocode_change end` +- L433 [patch]: `// kilocode_change: removed single-dollar inline math ($...$) rendering.` +- L499 [patch]: `// kilocode_change: parseFilePath imported from ../file-path` +- L501 [patch]: `// kilocode_change start: highlight cache for deferred highlighting` +- L541 [patch]: `const dir = pre.getAttribute("dir") // kilocode_change` +- L542 [patch]: `if (dir) highlighted.setAttribute("dir", dir) // kilocode_change` +- L652 [patch]: `// kilocode_change end` +- L654 [patch]: `// kilocode_change start: expose the parser setup for Kilo markdown tests.` +- L656 [patch]: `// kilocode_change start: two-pass parser — first pass skips Shiki highlighting` +- L667 [patch]: `// kilocode_change start` +- L698 [patch]: `// kilocode_change end` +- L700 [patch]: `// kilocode_change start: Marked accepts a tilde preceded by an opening` +- L710 [patch]: `// kilocode_change end` +- L712 [patch]: `// kilocode_change start: enable only double-dollar math.` +- L760 [patch]: `// kilocode_change end` +- L761 [patch]: `// kilocode_change: markedShiki removed — the custom \`code\` renderer` +- L766 [patch]: `// kilocode_change end` +- L786 [patch]: `// kilocode_change end` + +### `packages/kilo-ide-ui/src/file-path.test.ts` + +Classification: patch/override marker; untracked. 1 occurrence. + +- L1 [patch]: `// kilocode_change - new file` + +### `packages/kilo-ide-ui/src/file-path.ts` + +Classification: patch/override marker; untracked. 1 occurrence. + +- L1 [patch]: `// kilocode_change - new file` + +### `packages/kilo-ide-ui/src/i18n/ar.ts` + +Classification: patch/override marker; untracked. 6 occurrences. + +- L108 [patch]: `// kilocode_change start` +- L119 [patch]: `// kilocode_change end` +- L124 [patch]: `"ui.tool.swePruned": "SWE-Pruner · تم الاحتفاظ بـ {{kept}} من {{total}} سطرًا", // kilocode_change` +- L160 [patch]: `"ui.message.deleteQueued": "حذف الرسالة من قائمة الانتظار", // kilocode_change` +- L176 [patch]: `"ui.question.subtitle.dismissed": "{{count}} dismissed", // kilocode_change` +- L178 [patch]: `"ui.question.answer.dismissed": "Dismissed", // kilocode_change` + +### `packages/kilo-ide-ui/src/i18n/br.ts` + +Classification: patch/override marker; untracked. 6 occurrences. + +- L108 [patch]: `// kilocode_change start` +- L119 [patch]: `// kilocode_change end` +- L124 [patch]: `"ui.tool.swePruned": "SWE-Pruner · {{kept}} de {{total}} linhas mantidas", // kilocode_change` +- L160 [patch]: `"ui.message.deleteQueued": "Excluir mensagem na fila", // kilocode_change` +- L176 [patch]: `"ui.question.subtitle.dismissed": "{{count}} dismissed", // kilocode_change` +- L178 [patch]: `"ui.question.answer.dismissed": "Dismissed", // kilocode_change` + +### `packages/kilo-ide-ui/src/i18n/bs.ts` + +Classification: patch/override marker; untracked. 6 occurrences. + +- L112 [patch]: `// kilocode_change start` +- L123 [patch]: `// kilocode_change end` +- L128 [patch]: `"ui.tool.swePruned": "SWE-Pruner · zadržano {{kept}} od {{total}} redova", // kilocode_change` +- L164 [patch]: `"ui.message.deleteQueued": "Obriši poruku iz reda", // kilocode_change` +- L180 [patch]: `"ui.question.subtitle.dismissed": "{{count}} dismissed", // kilocode_change` +- L182 [patch]: `"ui.question.answer.dismissed": "Dismissed", // kilocode_change` + +### `packages/kilo-ide-ui/src/i18n/da.ts` + +Classification: patch/override marker; untracked. 6 occurrences. + +- L107 [patch]: `// kilocode_change start` +- L118 [patch]: `// kilocode_change end` +- L123 [patch]: `"ui.tool.swePruned": "SWE-Pruner · {{kept}} af {{total}} linjer beholdt", // kilocode_change` +- L159 [patch]: `"ui.message.deleteQueued": "Slet besked i kø", // kilocode_change` +- L175 [patch]: `"ui.question.subtitle.dismissed": "{{count}} dismissed", // kilocode_change` +- L177 [patch]: `"ui.question.answer.dismissed": "Dismissed", // kilocode_change` + +### `packages/kilo-ide-ui/src/i18n/de.ts` + +Classification: patch/override marker; untracked. 6 occurrences. + +- L113 [patch]: `// kilocode_change start` +- L124 [patch]: `// kilocode_change end` +- L129 [patch]: `"ui.tool.swePruned": "SWE-Pruner · {{kept}} von {{total}} Zeilen behalten", // kilocode_change` +- L165 [patch]: `"ui.message.deleteQueued": "Nachricht in Warteschlange löschen", // kilocode_change` +- L181 [patch]: `"ui.question.subtitle.dismissed": "{{count}} dismissed", // kilocode_change` +- L183 [patch]: `"ui.question.answer.dismissed": "Dismissed", // kilocode_change` + +### `packages/kilo-ide-ui/src/i18n/en.ts` + +Classification: patch/override marker; untracked. 6 occurrences. + +- L114 [patch]: `// kilocode_change start` +- L125 [patch]: `// kilocode_change end` +- L130 [patch]: `"ui.tool.swePruned": "SWE-Pruner · kept {{kept}} of {{total}} lines", // kilocode_change` +- L172 [patch]: `"ui.message.deleteQueued": "Delete queued message", // kilocode_change` +- L189 [patch]: `"ui.question.subtitle.dismissed": "{{count}} dismissed", // kilocode_change` +- L191 [patch]: `"ui.question.answer.dismissed": "Dismissed", // kilocode_change` + +### `packages/kilo-ide-ui/src/i18n/es.ts` + +Classification: patch/override marker; untracked. 6 occurrences. + +- L108 [patch]: `// kilocode_change start` +- L119 [patch]: `// kilocode_change end` +- L124 [patch]: `"ui.tool.swePruned": "SWE-Pruner · {{kept}} de {{total}} líneas conservadas", // kilocode_change` +- L160 [patch]: `"ui.message.deleteQueued": "Eliminar mensaje en cola", // kilocode_change` +- L176 [patch]: `"ui.question.subtitle.dismissed": "{{count}} dismissed", // kilocode_change` +- L178 [patch]: `"ui.question.answer.dismissed": "Dismissed", // kilocode_change` + +### `packages/kilo-ide-ui/src/i18n/fr.ts` + +Classification: patch/override marker; untracked. 6 occurrences. + +- L108 [patch]: `// kilocode_change start` +- L119 [patch]: `// kilocode_change end` +- L124 [patch]: `"ui.tool.swePruned": "SWE-Pruner · {{kept}} lignes conservées sur {{total}}", // kilocode_change` +- L160 [patch]: `"ui.message.deleteQueued": "Supprimer le message en file d'attente", // kilocode_change` +- L176 [patch]: `"ui.question.subtitle.dismissed": "{{count}} dismissed", // kilocode_change` +- L178 [patch]: `"ui.question.answer.dismissed": "Dismissed", // kilocode_change` + +### `packages/kilo-ide-ui/src/i18n/it.ts` + +Classification: patch/override marker; untracked. 5 occurrences. + +- L1 [patch]: `// kilocode_change - new file` +- L134 [patch]: `"ui.tool.swePruned": "SWE-Pruner · {{kept}} di {{total}} righe mantenute", // kilocode_change` +- L173 [patch]: `"ui.message.deleteQueued": "Elimina il messaggio in coda", // kilocode_change` +- L191 [patch]: `"ui.question.subtitle.dismissed": "{{count}} dismissed", // kilocode_change` +- L193 [patch]: `"ui.question.answer.dismissed": "Dismissed", // kilocode_change` + +### `packages/kilo-ide-ui/src/i18n/ja.ts` + +Classification: patch/override marker; untracked. 6 occurrences. + +- L107 [patch]: `// kilocode_change start` +- L118 [patch]: `// kilocode_change end` +- L123 [patch]: `"ui.tool.swePruned": "SWE-Pruner · {{total}} 行中 {{kept}} 行を保持", // kilocode_change` +- L159 [patch]: `"ui.message.deleteQueued": "キュー内のメッセージを削除", // kilocode_change` +- L175 [patch]: `"ui.question.subtitle.dismissed": "{{count}} dismissed", // kilocode_change` +- L177 [patch]: `"ui.question.answer.dismissed": "Dismissed", // kilocode_change` + +### `packages/kilo-ide-ui/src/i18n/ko.ts` + +Classification: patch/override marker; untracked. 6 occurrences. + +- L108 [patch]: `// kilocode_change start` +- L119 [patch]: `// kilocode_change end` +- L124 [patch]: `"ui.tool.swePruned": "SWE-Pruner · {{total}}줄 중 {{kept}}줄 유지", // kilocode_change` +- L160 [patch]: `"ui.message.deleteQueued": "대기 중인 메시지 삭제", // kilocode_change` +- L176 [patch]: `"ui.question.subtitle.dismissed": "{{count}} dismissed", // kilocode_change` +- L178 [patch]: `"ui.question.answer.dismissed": "Dismissed", // kilocode_change` + +### `packages/kilo-ide-ui/src/i18n/nl.ts` + +Classification: patch/override marker; untracked. 8 occurrences. + +- L56 [patch]: `// kilocode_change start - complete upstream usage-exceeded translations` +- L65 [patch]: `// kilocode_change end` +- L111 [patch]: `// kilocode_change start` +- L122 [patch]: `// kilocode_change end` +- L137 [patch]: `"ui.tool.swePruned": "SWE-Pruner · {{kept}} van {{total}} regels behouden", // kilocode_change` +- L176 [patch]: `"ui.message.deleteQueued": "Bericht in wachtrij verwijderen", // kilocode_change` +- L194 [patch]: `"ui.question.subtitle.dismissed": "{{count}} dismissed", // kilocode_change` +- L196 [patch]: `"ui.question.answer.dismissed": "Dismissed", // kilocode_change` + +### `packages/kilo-ide-ui/src/i18n/no.ts` + +Classification: patch/override marker; untracked. 6 occurrences. + +- L111 [patch]: `// kilocode_change start` +- L122 [patch]: `// kilocode_change end` +- L127 [patch]: `"ui.tool.swePruned": "SWE-Pruner · {{kept}} av {{total}} linjer beholdt", // kilocode_change` +- L163 [patch]: `"ui.message.deleteQueued": "Slett melding i kø", // kilocode_change` +- L179 [patch]: `"ui.question.subtitle.dismissed": "{{count}} dismissed", // kilocode_change` +- L181 [patch]: `"ui.question.answer.dismissed": "Dismissed", // kilocode_change` + +### `packages/kilo-ide-ui/src/i18n/pl.ts` + +Classification: patch/override marker; untracked. 6 occurrences. + +- L107 [patch]: `// kilocode_change start` +- L118 [patch]: `// kilocode_change end` +- L123 [patch]: `"ui.tool.swePruned": "SWE-Pruner · zachowano {{kept}} z {{total}} wierszy", // kilocode_change` +- L159 [patch]: `"ui.message.deleteQueued": "Usuń wiadomość z kolejki", // kilocode_change` +- L175 [patch]: `"ui.question.subtitle.dismissed": "{{count}} dismissed", // kilocode_change` +- L177 [patch]: `"ui.question.answer.dismissed": "Dismissed", // kilocode_change` + +### `packages/kilo-ide-ui/src/i18n/ru.ts` + +Classification: patch/override marker; untracked. 6 occurrences. + +- L107 [patch]: `// kilocode_change start` +- L118 [patch]: `// kilocode_change end` +- L123 [patch]: `"ui.tool.swePruned": "SWE-Pruner · сохранено {{kept}} из {{total}} строк", // kilocode_change` +- L159 [patch]: `"ui.message.deleteQueued": "Удалить сообщение из очереди", // kilocode_change` +- L175 [patch]: `"ui.question.subtitle.dismissed": "{{count}} dismissed", // kilocode_change` +- L177 [patch]: `"ui.question.answer.dismissed": "Dismissed", // kilocode_change` + +### `packages/kilo-ide-ui/src/i18n/th.ts` + +Classification: patch/override marker; untracked. 6 occurrences. + +- L109 [patch]: `// kilocode_change start` +- L120 [patch]: `// kilocode_change end` +- L125 [patch]: `"ui.tool.swePruned": "SWE-Pruner · เก็บไว้ {{kept}} จาก {{total}} บรรทัด", // kilocode_change` +- L161 [patch]: `"ui.message.deleteQueued": "ลบข้อความที่อยู่ในคิว", // kilocode_change` +- L177 [patch]: `"ui.question.subtitle.dismissed": "{{count}} dismissed", // kilocode_change` +- L179 [patch]: `"ui.question.answer.dismissed": "Dismissed", // kilocode_change` + +### `packages/kilo-ide-ui/src/i18n/tr.ts` + +Classification: patch/override marker; untracked. 6 occurrences. + +- L114 [patch]: `// kilocode_change start` +- L125 [patch]: `// kilocode_change end` +- L130 [patch]: `"ui.tool.swePruned": "SWE-Pruner · {{total}} satırdan {{kept}} tanesi korundu", // kilocode_change` +- L166 [patch]: `"ui.message.deleteQueued": "Kuyruktaki mesajı sil", // kilocode_change` +- L182 [patch]: `"ui.question.subtitle.dismissed": "{{count}} dismissed", // kilocode_change` +- L184 [patch]: `"ui.question.answer.dismissed": "Dismissed", // kilocode_change` + +### `packages/kilo-ide-ui/src/i18n/uk.ts` + +Classification: patch/override marker; untracked. 28 occurrences. + +- L15 [patch]: `// kilocode_change start` +- L18 [patch]: `// kilocode_change end` +- L29 [patch]: `"ui.fileMedia.state.unavailable": "Попередній перегляд {{kind}} недоступний.", // kilocode_change` +- L31 [patch]: `// kilocode_change start` +- L34 [patch]: `// kilocode_change end` +- L52 [patch]: `"ui.sessionTurn.retry.retrying": "повтор спроби", // kilocode_change` +- L53 [patch]: `"ui.sessionTurn.retry.inSeconds": "через {{seconds}}с", // kilocode_change` +- L56 [patch]: `"ui.sessionTurn.retry.geminiHot": "Gemini зараз перевантажений", // kilocode_change` +- L60 [patch]: `// kilocode_change start - complete upstream usage-exceeded translations` +- L69 [patch]: `// kilocode_change end` +- L72 [patch]: `// kilocode_change start` +- L75 [patch]: `// kilocode_change end` +- L89 [patch]: `// kilocode_change start` +- L92 [patch]: `// kilocode_change end` +- L94 [patch]: `"ui.messagePart.title.write": "Записати", // kilocode_change` +- L96 [patch]: `"ui.messagePart.review.title": "Перегляньте свої відповіді", // kilocode_change` +- L99 [patch]: `// kilocode_change start` +- L102 [patch]: `// kilocode_change end` +- L126 [patch]: `// kilocode_change start` +- L137 [patch]: `// kilocode_change end` +- L138 [patch]: `"ui.scrollView.ariaLabel": "вміст з прокруткою", // kilocode_change` +- L142 [patch]: `"ui.tool.swePruned": "SWE-Pruner · збережено {{kept}} з {{total}} рядків", // kilocode_change` +- L147 [patch]: `"ui.tool.webfetch": "Веб-запит", // kilocode_change` +- L149 [patch]: `"ui.tool.codesearch": "Пошук коду", // kilocode_change` +- L185 [patch]: `"ui.message.deleteQueued": "Видалити повідомлення з черги", // kilocode_change` +- L199 [patch]: `"ui.patch.action.patched": "Застосовано патч", // kilocode_change` +- L202 [patch]: `"ui.question.subtitle.dismissed": "{{count}} dismissed", // kilocode_change` +- L204 [patch]: `"ui.question.answer.dismissed": "Dismissed", // kilocode_change` + +### `packages/kilo-ide-ui/src/i18n/zh.ts` + +Classification: patch/override marker; untracked. 6 occurrences. + +- L111 [patch]: `// kilocode_change start` +- L122 [patch]: `// kilocode_change end` +- L127 [patch]: `"ui.tool.swePruned": "SWE-Pruner · 保留 {{total}} 行中的 {{kept}} 行", // kilocode_change` +- L163 [patch]: `"ui.message.deleteQueued": "删除排队中的消息", // kilocode_change` +- L179 [patch]: `"ui.question.subtitle.dismissed": "{{count}} dismissed", // kilocode_change` +- L181 [patch]: `"ui.question.answer.dismissed": "Dismissed", // kilocode_change` + +### `packages/kilo-ide-ui/src/i18n/zht.ts` + +Classification: patch/override marker; untracked. 6 occurrences. + +- L111 [patch]: `// kilocode_change start` +- L122 [patch]: `// kilocode_change end` +- L127 [patch]: `"ui.tool.swePruned": "SWE-Pruner · 保留 {{total}} 行中的 {{kept}} 行", // kilocode_change` +- L163 [patch]: `"ui.message.deleteQueued": "刪除排隊中的訊息", // kilocode_change` +- L179 [patch]: `"ui.question.subtitle.dismissed": "{{count}} dismissed", // kilocode_change` +- L181 [patch]: `"ui.question.answer.dismissed": "Dismissed", // kilocode_change` + +### `packages/kilo-ide-ui/src/pierre/index.ts` + +Classification: patch/override marker; untracked. 2 occurrences. + +- L4 [patch]: `import { KILO_DIFF_THEME } from "./kilo-diff-theme" // kilocode_change` +- L156 [patch]: `theme: KILO_DIFF_THEME, // kilocode_change` + +### `packages/kilo-ide-ui/src/pierre/kilo-diff-theme.ts` + +Classification: patch/override marker; untracked. 1 occurrence. + +- L1 [patch]: `// kilocode_change - new file` + +### `packages/kilo-ide-ui/src/pierre/worker.ts` + +Classification: patch/override marker; untracked. 4 occurrences. + +- L3 [patch]: `import { ensureKiloDiffTheme, KILO_DIFF_THEME } from "./kilo-diff-theme" // kilocode_change` +- L5 [patch]: `// kilocode_change start: register the "Kilo" theme as a precondition of creating` +- L11 [patch]: `// kilocode_change end` +- L31 [patch]: `theme: KILO_DIFF_THEME, // kilocode_change` + +### `packages/kilo-ide-ui/src/styles/index.css` + +Classification: patch/override marker; untracked. 1 occurrence. + +- L37 [patch]: `@import "../kilocode/markdown-mermaid.css" layer(components); /* kilocode_change */` + +### `packages/kilo-ide-ui/src/styles/theme.css` + +Classification: patch/override marker; untracked. 2 occurrences. + +- L135 [patch]: `--surface-critical-base: #feefeb; /* kilocode_change */` +- L395 [patch]: `--surface-critical-base: #42120b; /* kilocode_change */` + +### `packages/kilo-ide-ui/vite.config.ts` + +Classification: patch/override marker; untracked. 2 occurrences. + +- L39 [patch]: `if (!process.env.KILO_FETCH_PROVIDER_ICONS) return // kilocode_change` +- L43 [patch]: `if (!process.env.KILO_FETCH_PROVIDER_ICONS) return // kilocode_change` + +### `packages/kilo-indexing/src/tree-sitter/queries/java.ts` + +Classification: patch/override marker; tracked. 1 occurrence. + +- L42 [patch]: `type: (_) @definition.method.start ; kilocode_change` + +### `packages/kilo-ui/src/styles/index.css` + +Classification: patch/override marker; untracked. 2 occurrences. + +- L1 [patch]: `/* Wraps upstream styles and adds Kilo overrides */` — near variant +- L7 [patch]: `/* Per-component Kilo overrides */` — near variant + +### `packages/kilo-ui/src/styles/tailwind/index.css` + +Classification: patch/override marker; untracked. 2 occurrences. + +- L1 [patch]: `/* Wraps upstream tailwind styles and adds Kilo overrides */` — near variant +- L7 [patch]: `/* Per-component Kilo overrides */` — near variant + +### `packages/kilo-vscode/webview-ui/src/context/language.tsx` + +Classification: patch/override marker; untracked. 1 occurrence. + +- L4 [patch]: `* Merges UI translations from @opencode-ai/ui and Kilo overrides from @kilocode/kilo-i18n.` — near variant + +### `packages/plugin/src/tui/context.ts` + +Classification: patch/override marker; tracked. 1 occurrence. + +- L179 [patch]: `readonly "home.logo": Readonly> // kilocode_change - host-owned Home branding` + +### `packages/schema/src/kilocode/presence.ts` + +Classification: documentation mention; untracked. 1 occurrence. + +- L6 [documentation]: `* a kilocode_change route in the pinned baseline). This module carries no` + +### `packages/theme/src/tui/defaults.ts` + +Classification: patch/override marker; tracked. 2 occurrences. + +- L112 [patch]: `// kilocode_change - text.logo light default` +- L335 [patch]: `// kilocode_change - text.logo dark default` + +### `packages/theme/src/tui/fallback.ts` + +Classification: patch/override marker; tracked. 1 occurrence. + +- L11 [patch]: `logo: DEFAULT_THEME[mode].text.logo, // kilocode_change - retain branding in themes without a logo override` + +### `packages/theme/src/tui/schema.ts` + +Classification: patch/override marker; tracked. 1 occurrence. + +- L109 [patch]: `// kilocode_change - text.logo semantic color role` + +### `packages/theme/src/tui/types.ts` + +Classification: patch/override marker; tracked. 1 occurrence. + +- L31 [patch]: `// kilocode_change - text.logo semantic color role` + +### `packages/tui/src/app.tsx` + +Classification: patch/override marker; tracked. 2 occurrences. + +- L192 [patch]: `pluginDirectories?: string[] // kilocode_change - allow isolated hosts to supply their discovery roots` +- L219 [patch]: `input.pluginDirectories ?? (yield* Effect.promise(() => localPluginDirectories(process.cwd(), global.config))) // kilocode_change - preserve upstream discovery unless the host overrides it` + +### `packages/tui/src/component/dialog-agent.tsx` + +Classification: patch/override marker; tracked. 1 occurrence. + +- L14 [patch]: `title: item.name ?? item.id, // kilocode_change - display agent names; selection keeps the canonical ID` + +### `packages/tui/src/component/dialog-integration.tsx` + +Classification: patch/override marker; tracked. 3 occurrences. + +- L30 [patch]: `kilo: -1, // kilocode_change - recommend Kilo Gateway in Connect` +- L51 [patch]: `Number(b.id === "kilo") - Number(a.id === "kilo") \|\| // kilocode_change - Kilo stays first across categories` +- L117 [patch]: `description: methods.length === 0 ? "Environment only" : integration.id === "kilo" ? "Recommended" : undefined, // kilocode_change - identify the recommended provider` + +### `packages/tui/src/component/dialog-model.tsx` + +Classification: patch/override marker; tracked. 19 occurrences. + +- L1 [patch]: `import { createRenderEffect, createMemo, createSignal, onCleanup } from "solid-js" // kilocode_change - scoped display metadata lifecycle` +- L2 [patch]: `import { useTerminalDimensions } from "@opentui/solid" // kilocode_change - stable picker bounds during metadata loading` +- L14 [patch]: `import { useTuiApp, type TuiModelGroup } from "../context/runtime" // kilocode_change - host-owned picker presentation` +- L22 [patch]: `const presentation = useTuiApp().modelPicker // kilocode_change - do not fork native picker/preferences` +- L23 [patch]: `const dimensions = useTerminalDimensions() // kilocode_change` +- L33 [patch]: `// kilocode_change - metadata is display-only but must not reorder an actionable cold list` +- L75 [patch]: `// kilocode_change - a host-hidden model leaves favorites and recents too` +- L91 [patch]: `// kilocode_change - host disclosures also follow native favorites and recents` +- L119 [patch]: `// kilocode_change - host presentation policy removes flagged Kilo models entirely` +- L130 [patch]: `// kilocode_change - display-only grouping supplied by the host` +- L139 [patch]: `category: connected() ? (group?.category ?? provider?.name ?? model.providerID) : undefined, // kilocode_change` +- L140 [patch]: `groupOrder: group?.order, // kilocode_change - metadata ordering, never model identity` +- L141 [patch]: `footer: [free(model) ? "Free" : undefined, group?.footer].filter(Boolean).join(" · ") \|\| undefined, // kilocode_change - host-owned disclosures` +- L161 [patch]: `presentation?.preferredProviderID, // kilocode_change` +- L198 [patch]: `// kilocode_change - reserve the native list viewport plus search/footer chrome before metadata arrives` +- L273 [patch]: `// kilocode_change - optional host priority; preserve the upstream default` +- L275 [patch]: `const group = (a.groupOrder ?? Infinity) - (b.groupOrder ?? Infinity) // kilocode_change` +- L276 [patch]: `if (group && !Number.isNaN(group)) return group // kilocode_change` +- L277 [patch]: `// kilocode_change - preserve explicit model selection independently of presentation` + +### `packages/tui/src/component/prompt/autocomplete.tsx` + +Classification: patch/override marker; tracked. 2 occurrences. + +- L522 [patch]: `// kilocode_change - mirror dispatch precedence: local names/aliases shadow server commands and skills.` +- L528 [patch]: `if (commandNames.has(serverCommand.name)) continue // kilocode_change - show one executable command` + +### `packages/tui/src/component/prompt/index.tsx` + +Classification: patch/override marker; tracked. 1 occurrence. + +- L1559 [patch]: `agentLabel: agent ? (agent.name ?? Locale.titlecase(agent.id)) : undefined, // kilocode_change - honor host/config presentation without changing agent identity` + +### `packages/tui/src/context/local.tsx` + +Classification: patch/override marker; tracked. 2 occurrences. + +- L26 [patch]: `import { Locale } from "../util/locale" // kilocode_change - display names are independent of durable agent IDs` +- L96 [patch]: `// kilocode_change - honor configured names in transcript and subagent presentation too.` + +### `packages/tui/src/context/runtime.tsx` + +Classification: patch/override marker; tracked. 5 occurrences. + +- L2 [patch]: `import type { LocationRef } from "@opencode-ai/client" // kilocode_change - public host model-presentation boundary` +- L4 [patch]: `// kilocode_change - additive host presentation; native preferences and selection remain TUI-owned` +- L11 [patch]: `// kilocode_change - host presentation policy can hide a model from every dialog section` +- L26 [patch]: `sessionEpilogue?: (input: { title: string; sessionID?: string }) => string // kilocode_change - let isolated hosts own their exit branding` +- L27 [patch]: `modelPicker?: TuiModelPicker // kilocode_change - optional, upstream defaults remain unchanged` + +### `packages/tui/src/feature-plugins/sidebar/context.tsx` + +Classification: patch/override marker; tracked. 1 occurrence. + +- L11 [patch]: `// kilocode_change - collapsible sidebar section with clickable triangle heading and action token` + +### `packages/tui/src/routes/home.tsx` + +Classification: patch/override marker; tracked. 1 occurrence. + +- L85 [patch]: `{/* kilocode_change - Home branding is supplied by the host, with no OpenCode fallback */}` + +### `packages/tui/src/routes/session/composer/subagents-tab.tsx` + +Classification: patch/override marker; tracked. 4 occurrences. + +- L9 [patch]: `import { useLocal } from "../../../context/local" // kilocode_change - names, not canonical IDs, label agents` +- L25 [patch]: `const local = useLocal() // kilocode_change` +- L48 [patch]: `? local.agent.name(session.agent, session.location) // kilocode_change` +- L50 [patch]: `? local.agent.name(agentMatch[1], session.location) // kilocode_change` + +### `packages/tui/src/routes/session/index.tsx` + +Classification: patch/override marker; tracked. 9 occurrences. + +- L206 [patch]: `setEpilogue((app.sessionEpilogue ?? sessionEpilogue)({ title, sessionID: session()?.id })) // kilocode_change - preserve upstream presentation unless the host overrides it` +- L2056 [patch]: `{local.agent.name(props.message.agent) /* kilocode_change - show the name, not its compatibility ID */}` +- L2078 [patch]: `const local = useLocal() // kilocode_change - resolve display names for both sides of a switch` +- L2091 [patch]: `const agent = local.agent.name(props.message.agent) // kilocode_change` +- L2093 [patch]: `return \`Switched agent from ${local.agent.name(props.message.previous)} to ${agent}\` // kilocode_change` +- L2109 [patch]: `const local = useLocal() // kilocode_change - use registered names for subagent notices` +- L2122 [patch]: `const actor = () => (source() === "shell" ? "Shell" : local.agent.name(stringValue(metadata()?.agent) ?? "Subagent")) // kilocode_change` +- L3463 [patch]: `const local = useLocal() // kilocode_change - preserve agent identity while displaying its configured name` +- L3493 [patch]: `: \`${local.agent.name(stringValue(props.input.agent) ?? stringValue(props.input.subagent_type) ?? "General")} Subagent — ${description() ?? "Subagent"}\` /* kilocode_change */` + +### `packages/tui/src/util/renderer.ts` + +Classification: patch/override marker; tracked. 1 occurrence. + +- L8 [patch]: `renderer.screenMode = "main-screen" // kilocode_change - restore the shell before the host prints its epilogue` + +### `packages/tui/test/component/session-tabs-mouse.test.tsx` + +Classification: patch/override marker; tracked. 3 occurrences. + +- L70 [patch]: `// kilocode_change - a terminal context menu can consume the right-button release.` +- L135 [patch]: `// kilocode_change - dismissing rename must release the tab menu's mouse capture.` +- L163 [patch]: `// kilocode_change - closing the context menu must leave ordinary tab clicks functional.` + +### `packages/tui/test/feature-plugins/sidebar-context.test.tsx` + +Classification: patch/override marker; tracked. 1 occurrence. + +- L11 [patch]: `// kilocode_change - provide semantic action token for collapsible heading` + +### `plans/kilo-opencode-v2-port.md` + +Classification: documentation mention; tracked. 2 occurrences. + +- L562 [documentation]: `- Keep shared upstream changes minimal and mark them with \`kilocode_change\`.` +- L584 [documentation]: `- Preserve \`kilocode_change\` markers in shared upstream files and keep Kilo` + diff --git a/migration-tracking/marker-audit/v1-kilo-marker-port-assessment.md b/migration-tracking/marker-audit/v1-kilo-marker-port-assessment.md new file mode 100644 index 000000000000..c129f448fe16 --- /dev/null +++ b/migration-tracking/marker-audit/v1-kilo-marker-port-assessment.md @@ -0,0 +1,929 @@ +> For current upstream v2 changes and marker links, use the [current shared-change inventory](v2-current-shared-change-inventory.md). This document preserves its original assessment scope and counts. + +# V1 Kilo marker port assessment + +> Completed source-assessment inventory: all 816 rows have a disposition. This is a read-only port assessment, not implementation or runtime acceptance. Partial and needs-port rows retain the specific gaps described below; no capability completion credit is added. + +## Totals + +Pinned v1 `d99662338e3ddbd2613ab41fc0369837a6eb4be9`; 816 files / 6128 occurrences. Dispositions: already ported 65; deferred 68; native equivalent 137; needs port 163; not needed 150; partial 233; unresolved 0. + +## Final TUI assessment + +The final 23 TUI rows were assessed directly from pinned marker blocks against current v2 runtime, plugin, and test consumers. Mixed legacy surfaces remain partial where concrete host/native behavior coexists with missing Kilo invariants; session scope switching, list-level empty-state actions, export options, and Kilo presentation regressions are explicit needs-port items rather than inferred from adjacent UI. + +## Completed packages/opencode and docs-automation handoffs + +An initial packages/opencode handoff was withdrawn after semantic review established that it used path/file-presence heuristics and generic rationales; all affected rows were restored before the replacement worker re-audited the complete slice from marker invariants and concrete consumers. The docs-automation handoff assessed 15 rows as deferred because pinned v1 workflows directly consume automation modules absent from v2; the packages/kilo-docs tree is also absent, so adjacent runtime features are not treated as parity. Nine historical authored-plan rows are not needed because their marker-only statements are non-executable completed-work notes and v2 has active ownership rules. + +## TUI model and integration dialogs + +| v1 file / marker lines | marker kind | behavior area | disposition | concrete v2 destination | evidence | +|---|---|---|---|---|---| +| `packages/tui/src/component/dialog-model.tsx`
    1,2,5,10,13,14,21,25,33,37,74,78,83,97,99,133,139,141,143,147,162,165,181,194,224,257,268,276,281,283,286,290,296,298 | shared-upstream patch marker | Provider/model routing or UI | partial | packages/tui/src/component/dialog-model.tsx; packages/tui/src/context/runtime.tsx; packages/kilo-cli/src/model-picker.ts; packages/kilo-cli/src/tui-plugin/model-info-dialog.tsx | Ported core picker policy through the v2 host-owned TuiApp.modelPicker seam: Kilo catalog groups/ranking, hidden models (including favorite/recent removal), preferred provider ordering, disclosures, native favorites/recents and a separate model-info dialog are concrete consumers. Partial because the v1 inline wide selection preview/ModelInfoPanel and its exact per-section fuzzy-search/header behavior are not reproduced in the native v2 dialog; the owned info dialog is a separate command flow, not selection-preserving inline parity. Evidence: Ported core picker policy through the v2 host-owned TuiApp.modelPicker seam: Kilo catalog groups/ranking, hidden models (including favorite/recent removal), preferred provider ordering, disclosures, native favorites/recents and a separate model-info dialog are concrete consumers. Partial because the v1 inline wide selection preview/ModelInfoPanel and its exact per-section fuzzy-search/header behavior are not reproduced in the native v2 dialog; the owned info dialog is a separate command flow, not selection-preserving inline parity. | +| `packages/tui/src/component/dialog-provider.tsx`
    15,18,21,56,89,127,132,135,137,140,143,190,201,214,223,382,387,388,390,391,396,405 | shared-upstream patch marker | Provider/model routing or UI | partial | packages/tui/src/component/dialog-integration.tsx; packages/server/src/integration; packages/kilo-cli/src/integration.ts | V2 replaces the provider-auth dialog with the Integration workflow and concretely preserves Kilo-first/recommended placement, method selection, key/form/OAuth/command flows, connection refresh, and provider-to-model return routing. Partial because the v1 KiloProvider adapter's per-provider titles/descriptions, failed-provider gutters/details, provider-specific automatic method rendering, local-provider optional-key placeholder/description, and custom-provider kilo.json credential guidance are not all evidenced in the v2 integration UI; adjacent generic connection support is not parity for those remainders. Evidence: V2 replaces the provider-auth dialog with the Integration workflow and concretely preserves Kilo-first/recommended placement, method selection, key/form/OAuth/command flows, connection refresh, and provider-to-model return routing. Partial because the v1 KiloProvider adapter's per-provider titles/descriptions, failed-provider gutters/details, provider-specific automatic method rendering, local-provider optional-key placeholder/description, and custom-provider kilo.json credential guidance are not all evidenced in the v2 integration UI; adjacent generic connection support is not parity for those remainders. | + +## TUI data projection and sync fixtures + +V2 replaces the monolithic SyncProvider with location-keyed client projections. Most session, shell/background, VCS, config, indexing, and owned Kilo consumers are native or relocated, while network-wait/tool-terminal state, released-v1 Sync folding, and generic subscriber isolation remain gaps, making the source partial. The old fixture nesting is obsolete; the workspace VCS regression is native through location-keyed storage and the current data test. + +## TUI event routing pair + +Native v2 event shapes remove the legacy sync-envelope adapter and preserve location metadata, but direct `useEvent` subscribers are no longer filtered to the current project. The paired v2 suite adds strong reconnect and durable-event coverage while omitting the old foreign-project rejection case, so source is partial and the isolation regression needs a port. + +## Mixed TUI status and footer surfaces + +All four rows are partial. V2 removes obsolete plugin-path parsing and preserves typed MCP status, child-session state, navigation, location, and Kilo sidebar branding through native or owned seams. Missing invariants remain explicit: Kilo version/config paths and auth commands in the status dialog, free-model onboarding, remote-presence indication, and the agent-colored subagent footer spinner. + +## TUI connectivity and branded guidance tests + +Typed integration connections natively supersede v1's provider-ID/cost heuristic, with renamed tests covering credential and environment connections. Kilo-specific model/MCP error guidance and its tests remain unported. The old provider ordering regression also lacks a v2 policy or consumer-backed equivalent. + +## TUI session utilities and small dialog slice + +Rename completion and Kilo sidebar branding are native equivalents through durable data and owned slots. Network recovery, multiline alerts, and fixed-width Markdown tables need ports. Dialog centering and embedded terminals are partial: v2 exposes stronger generic primitives, but does not preserve the old global/default or tool-snapshot-specific behavior. + +## TUI config, bindings, themes, and path formatting + +V2 natively strengthens portable home-path formatting, while the old cross-tree TypeScript aliases and a Kilo-branded internal mode-stack error are not required under the owned-package architecture. Kilo title/Vim/attention config is partial. Feedback, Vim-toggle and news bindings, the Kilo/colorblind theme catalogue, and the Kilo default-sound regression remain unported; generic input, feedback colors, branded logo tokens, or static footer content are not treated as parity. + +## Focused TUI notifications, branding, and tips + +Kilo's logo and exit epilogue were relocated cleanly into the owned `packages/kilo-cli` TUI plugin, with renderer and resume-command tests; their removed shared markers do not represent lost behavior. The old home-tip catalogue and persistent toggle have no v2 consumer and need an owned plugin contribution. Notifications are only partial: v2 has stronger lifecycle infrastructure, but currently treats interruption as completion, sounds for completed subagents, and lacks Kilo's manual-abort and timeout-message policy; the corresponding old regression cases need a port. + +## Shared TUI attention and dialog slice + +The v2 agent dialog preserves display labels with canonical IDs under its renamed schema. Attention behavior is stronger and tested, but Kilo title/sound-pack branding is absent. The Kilo-pricing-specific retry/upgrade dialog branch has no current component and needs a port. + +## Focused TUI service slice + +The cost-nudge, thumbs-feedback and no-display link-dialog consumers have no v2 equivalents and need owned TUI ports. The ordered embedded-worker helper is architecture-obsolete rather than deferred: v2 service/interactive-server ownership preserves ordered shutdown without that worker. Native projection tests cover much of old SyncV2, while its resolved-project filtering proof remains unlocated. + +## Delegation and web-search tool slice + +Native v2 subagents cover durable child execution and configurable depth, but only partially replace Kilo's task policy because persisted model precedence and inherited denial ceilings are not proven. The interactive Agent Manager's batching/worktrees/sections/events need a port. V2's provider abstraction can host a Kilo search integration, but Tavily does not replace the marked authenticated Kilo Exa route and response/error contract. + +## Missing specialized-tool slice + +Chart rendering, image generation/editing, and outbound remote file delivery have no current v2 tool consumers. The paired image and file-delivery regressions also need ports: generic MCP output and inbound user attachments do not cover their renderer, provider, filesystem-authorization, or delivery-only invariants. + +## Remote ownership source slice + +V2 natively replaces the extracted attached-session state machine with heartbeat reconciliation, detach fencing and reconnect deduplication in the owned relay. The broader sender and WebSocket files are partial because their active command/heartbeat behavior is present while spawn-capable instance creation and advertisement are absent; the standalone instance-advertisement file is deferred with that retired product surface. + +## Remote command/protocol and delivery-contract slice + +The v2 relay natively preserves the unconditional exit capability and detach semantics. Heartbeats, reconnect and presence are present, while the old spawn-capable instance advertisement is intentionally absent. Prompt enqueue dismisses remote suggestions, but exact question dismissal is unproven; likewise, typed user attachment handling does not prove the old `send_file` delivery-only replay exclusion. + +## Plan and historical-recall tool slice + +Native v2 `plan_exit` policy and tests supersede the v1 forwarding hook. Kilo memory provides a permissioned recall subset, but it is not equivalent to v1's arbitrary historical session transcript search/read across the worktree family; the missing transcript-specific behavior and regression tests are recorded separately. + +## Network and overflow session slice + +The v1 offline wait/reconnect state machine and race-focused tests need a Kilo-owned v2 port; generic retry and simulation networking are not equivalent. V2 has provider-driven context-overflow compaction and extensive runner tests, but the pinned Kilo token counter and proactive/economic threshold are not proven, so overflow is partial. + +## Session execution follow-on and claim refinements + +Legacy run-state/status/todo Layer composition is superseded by native v2 execution and projection services; workflow-specific variant precedence remains unported. Sharing claims are narrowed to local-fixture acceptance because deployed viewer/team/incremental-sync gates were not exercised. Variant persistence is native: Kilo injects its `kilo2/` state root at runtime, overriding the generic source comment. + +## Permission and variant follow-on + +The v2 mini TUI carries the permission state machine and model-variant selection logic. Permission remains partial because skill-shell requests no longer remove persistent approval from the shared footer. Variant persistence is native because Kilo runtime storage injection overrides the generic upstream path comment. + +## Suggestions, sharing and sandbox batch + +Suggestion lifecycle, tool admission, replay and accept/dismiss behavior are carried by the owned v2 remote adapter and its integration tests. Kilo public sharing is Gateway-backed and tested end to end. Sandbox is intentionally partial: v2 has launch-scoped shell/local-MCP OS confinement and PTY refusal, not v1's persisted per-session toggle, inheritance and all-file-tool decoration; the reusable Linux CI setup action is also absent. + +## Memory/usage and initial remote-control batch + +V2 replaces parent-message cost propagation with durable root-and-descendant aggregation, carries throughput in the native TUI session-row seam, and replaces memory HTTP routes with location-scoped Memory RPC backed by the owned memory engine. The relay adapter preserves protocol-v1 command and detach invariants, but the old standalone `kilo remote` spawn-capable host and instance advertisement are intentionally outside the current preview surface, so those rows remain partial. + +## Account/Gateway/model-policy batch + +Direct source review mapped the v1 Core Kilo provider hook to the location/account-scoped Gateway plugin, model catalog, request isolation and routed-model seams. The large v1 owned provider helper remains partial because its Gateway behaviors are ported but unrelated custom loaders are mixed into the same file. Credential lifecycle is partial pending presence-side reconciliation. The old backend model-search helper has shipped client equivalents in native CLI search and the original VS Code model-selector utilities. Four coherent v1 tests map to current Gateway/Core tests for auth fallback, local providers, cache isolation and failed-catalog withdrawal. + +## Previously completed batches + +Packages/UI: 54 ported. Marker-bearing packages/kilo-*: indexing query ported, policy native equivalent, VS Code manifest partial guard, UI contract partial test coverage. Config/settings/import: 15 source-backed dispositions. + +## File-by-file assessment + +| V1 file + lines | Ownership | Purpose | Disposition | Destination/seam | Evidence | +|---|---|---|---|---|---| +| `.github/actions/setup-bun/action.yml`
    36,37,39,48,74,82,86,90,92,99,101,105,107 | shared-upstream patch marker | CI/repository tooling | partial | .github/actions/setup-bun/action.yml (v2, 80 lines): Windows hoisted-linker install native; cache skip, node-gyp headers, frozen-lockfile and Windows retry absent | V1 markers: skip the ~1 GB Bun cache on Windows for restore and save (L36-39,105-107), set up Node 24 and node-gyp headers for native dependency builds on non-Windows (L48-74,82-86), install with `--frozen-lockfile` (L93,101), and retry a failed Windows install with streaming disabled (L92-99). V2 action keeps the upstream Windows `--linker hoisted` install (L68-72) and caches on every OS without frozen-lockfile, Node header setup or retry. No Kilo v2 CI lane exists yet (workflows target upstream `dev`/`v2` and the Kilo fork runs no Kilo-specific CI, kilocode/baseline/pinned-v2-baseline.md:92-120), so the Windows-cache/native-build hardening is only needed once Kilo CI runs these actions with Kilo's native dependencies. Evidence: L36: # Restoring and extracting the ~1 GB cache took 2m23s on Windows, while a # kilocode_change / L37: # fresh install took 1m27s. Keep Windows off this cache until that reverses. # kilocode_change | +| `.github/actions/setup-git-committer/action.yml`
    4,11,26,29,42,50 | shared-upstream patch marker | CI/repository tooling | needs port | .github/actions/setup-git-committer/action.yml:3-25 (app-id/private-key inputs) plus the includeIf credential cleanup | V1 renamed the GitHub App inputs to `kilo-maintainer-app-id`/`kilo-maintainer-app-secret` (L4-11,26-29) and removed stale `includeIf.gitdir` git-credentials includes before committing (L42-50). V2 action uses `opencode-app-id`/`opencode-app-secret` and has no includeIf cleanup. Any Kilo v2 workflow that commits as the Kilo maintainer bot (release/version/nix-hash bumps) needs the Kilo App inputs; the cleanup guards against leaked credential includes on shared runners. Evidence: L4: # kilocode_change start / L11: # kilocode_change end | +| `.github/actions/setup-kilo/action.yml`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | .github/actions/setup-kilo/action.yml when Kilo v2 CI needs a released Kilo CLI on runners | V1 new-file action installs the Kilo CLI on runners for workflows that drive Kilo (docs automation, review). V2 has no Kilo CI lane and the kilo2 CLI is not yet published as an installable release, so there is no consumer; keep with the deferred docs/automation lane. Evidence: L1: # kilocode_change - new file | +| `.github/actions/setup-linux-sandbox/action.yml`
    1 | shared-upstream patch marker | Linux sandbox CI setup | partial | `packages/kilo-cli/script/sandbox-linux-smoke.ts` and Kilo CLI sandbox tests | V2 owns Linux Bubblewrap support and a dedicated smoke script, but the pinned reusable GitHub setup action is absent; runtime parity does not by itself port the CI bootstrap seam. Evidence: L1: # kilocode_change - new file | +| `.github/codeql/codeql-config.yml`
    1 | shared-upstream patch marker | Configuration/settings | deferred | .github/codeql/codeql-config.yml with .github/workflows/codeql.yml when Kilo security scanning is enabled for the v2 line | V1 new-file CodeQL 'critical-only' configuration used by codeql.yml/codeql-kotlin.yml. V2 has no CodeQL workflow or config; security scanning for the Kilo v2 repository is a CI-lane decision, not a runtime behavior. Evidence: L1: # kilocode_change - new file | +| `.github/docs-sync/collect.mjs`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 | Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. Evidence: L1: // kilocode_change - new file | +| `.github/docs-sync/edit.mjs`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 | Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. Evidence: L1: // kilocode_change - new file | +| `.github/docs-sync/extract-json.mjs`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 | Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. Evidence: L1: // kilocode_change - new file | +| `.github/docs-sync/filter-worthy.mjs`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 | Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. Evidence: L1: // kilocode_change - new file | +| `.github/docs-sync/learn.mjs`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 | Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. Evidence: L1: // kilocode_change - new file | +| `.github/docs-sync/lib.mjs`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 | Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. Evidence: L1: // kilocode_change - new file | +| `.github/docs-sync/prepare-branch.mjs`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 | Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. Evidence: L1: // kilocode_change - new file | +| `.github/docs-sync/redact-stream.mjs`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 | Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. Evidence: L1: // kilocode_change - new file | +| `.github/docs-sync/reverts.mjs`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 | Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. Evidence: L1: // kilocode_change - new file | +| `.github/docs-sync/selftest.mjs`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 | Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. Evidence: L1: // kilocode_change - new file | +| `.github/docs-sync/triage.mjs`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 | Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. Evidence: L1: // kilocode_change - new file | +| `.github/docs-sync/upsert-pr.mjs`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 | Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. Evidence: L1: // kilocode_change - new file | +| `.github/docs-sync/watermark.mjs`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 | Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. Evidence: L1: // kilocode_change - new file | +| `.github/workflows/auto-docs.yml`
    29,40 | shared-upstream patch marker | CI/repository tooling | deferred | .github/workflows/auto-docs.yml or the chosen v2 documentation-intake automation | Pinned v1 gates a pull_request_target webhook to merged feat PRs in Kilo-Org/kilocode and forwards metadata only; v2 has no workflow or consumer for that intake. It is Kilo repository automation and remains deferred rather than a runtime gap. Evidence: L29: # kilocode_change start / L40: # kilocode_change end | +| `.github/workflows/beta.yml`
    17,19,29,32 | shared-upstream patch marker | CI/repository tooling | deferred | .github/workflows/beta.yml (Kilo beta release trigger) once the Kilo v2 release lane exists | V1 markers pin checkout@v6 with `persist-credentials: false` and pass the Kilo maintainer App secrets to setup-git-committer (L17-19,29-32) for the beta release trigger. V2 has no beta.yml; the v2 publish lane is upstream's (`opencode2` artifacts, publish.yml:171). Kilo v2 release automation is not established. Evidence: L17: uses: actions/checkout@v6 # kilocode_change / L19: persist-credentials: false # kilocode_change | +| `.github/workflows/check-forbidden-strings.yml`
    1,14 | shared-upstream patch marker | CI/repository tooling | deferred | .github/workflows/check-forbidden-strings.yml + script/check-forbidden-strings.ts when Kilo v2 CI is set up | V1 new-file workflow runs the Kilo forbidden-strings guard on PRs. V2 has neither the workflow nor the script (see script/check-forbidden-strings.ts row); the guard protects Kilo branding/leak invariants and belongs to the deferred Kilo CI lane. Evidence: L1: # kilocode_change - new file / L14: - uses: actions/checkout@v6 # kilocode_change | +| `.github/workflows/check-kilo-generated-artifacts.yml`
    1,20 | authored tooling/check | CI/repository tooling | deferred | .github/workflows/check-kilo-generated-artifacts.yml + script/check-kilo-generated-artifacts.ts; v2 equivalent gate is `bun run check:generated` in packages/client and packages/www (kilocode/baseline/pinned-v2-baseline.md:97-98) | V1 Kilo-owned workflow verifies Kilo generated artifacts are committed. V2 has no Kilo CI lane; upstream's generated-client check (`check:generated`) is run manually per the baseline. Kilo-specific generated artifacts (kilo-vscode/kilo-ui outputs) have no v2 CI verification yet. Evidence: L1: # kilocode_change - new file / L20: - uses: actions/checkout@v6 # kilocode_change | +| `.github/workflows/check-md-table-padding.yml`
    17 | shared-upstream patch marker | CI/repository tooling | deferred | .github/workflows/check-md-table-padding.yml + script/check-md-table-padding.ts (kilocode/v2-fork-conventions.md notes the rule is currently upheld by review only) | V1 marker pins checkout@v6 in the Kilo markdown-table-padding workflow. V2 has neither workflow nor script; the fork conventions explicitly record that no v2 workflow enforces compact tables yet. Deferred with the Kilo CI lane. Evidence: L17: - uses: actions/checkout@v6 # kilocode_change | +| `.github/workflows/check-opencode-annotations.yml`
    19,23,30,40,52 | shared-upstream patch marker | CI/repository tooling | deferred | .github/workflows/check-opencode-annotations.yml + script/check-opencode-annotations.ts, check-opencode-promise-facades.ts, check-architecture.ts, check-model-tool-network.ts, check-workflows.ts | V1 workflow (Kilo-only via `github.repository == 'Kilo-Org/kilocode'`) checks `kilocode_change` annotations in shared upstream files and runs four additional Kilo architecture guards (L19-52). V2 has none of these scripts or the workflow, although the marker convention itself is documented in kilocode/v2-fork-conventions.md. The marker-enforcement invariant is a Kilo CI concern deferred with that lane; see the script/ rows for per-script status. Evidence: L19: name: Check kilocode_change annotations / L23: - uses: actions/checkout@v6 # kilocode_change | +| `.github/workflows/check-org-member.yml`
    21 | shared-upstream patch marker | CI/repository tooling | not needed | none — v2 has no check-org-member workflow; runner choice is a CI-lane detail | V1 marker only switches the runner to `blacksmith-2vcpu-ubuntu-2404` (L21). V2 has no such workflow; if one is added for the Kilo v2 repo the runner label is an infrastructure choice, not a behavior to port. Evidence: L21: runs-on: blacksmith-2vcpu-ubuntu-2404 # kilocode_change | +| `.github/workflows/codeql-kotlin.yml`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | .github/workflows/codeql-kotlin.yml when the JetBrains plugin lane (packages/kilo-jetbrains) is restored on v2 | V1 new-file CodeQL workflow for the Kotlin JetBrains plugin. V2 has no packages/kilo-jetbrains and no CodeQL; deferred together with the JetBrains lane (see the containers/jetbrains deferral already recorded). Evidence: L1: # kilocode_change - new file | +| `.github/workflows/codeql.yml`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | .github/workflows/codeql.yml when Kilo security scanning is enabled for the v2 line | V1 new-file CodeQL workflow (critical-only config). V2 has no security scanning workflow; CI-lane decision. Evidence: L1: # kilocode_change - new file | +| `.github/workflows/containers.yml`
    6,19,22,28,30,50,53 | shared-upstream patch marker | CI/repository tooling | needs port | .github/workflows/containers.yml:5,21 (default branch trigger and lowercase registry owner) plus the Kilo repository guard | V1 markers trigger on `main`, guard builds to `Kilo-Org/kilocode` or manual dispatch, lowercase the repository owner for the GHCR registry, and pin the Ubuntu 24.04 tag (L6,19,22-28,50-53). V2 containers.yml triggers on upstream branches and uses `ghcr.io/${{ github.repository_owner }}` unlowercased (L21) — `ghcr.io/Kilo-Org/...` is an invalid image reference, so a Kilo fork run would fail to push. The containers workflow does exist in v2 and would run in a Kilo fork, so this is a concrete gap rather than a deferred lane (consistent with the containers Dockerfile rows already marked needs port). Evidence: L6: - main # kilocode_change / L19: if: github.event_name == 'workflow_dispatch' || github.repository == 'Kilo-Org/kilocode' # kilocode_change | +| `.github/workflows/disabled/duplicate-issues.yml.disabled`
    143 | shared-upstream patch marker | CI/repository tooling | not needed | none — v2 ships an active upstream duplicate-issues.yml; Kilo disabled its own copy | V1 kept a disabled copy with a `needs:compliance` recheck tweak (L143). V2 has upstream's live duplicate-issues.yml and no disabled directory; whether to disable issue automation for the Kilo v2 repo is a repository-settings decision, and the marked tweak has no consumer. Evidence: L143: if: github.event.action == 'edited' && contains(github.event.issue.labels.*.name, 'needs:compliance') # kilocode_change | +| `.github/workflows/disabled/notify-discord.yml.disabled`
    9,11 | shared-upstream patch marker | CI/repository tooling | not needed | none — Kilo disabled OpenCode Discord notifications; v2 fork should disable/remove upstream notify-discord.yml at CI setup | V1 disabled upstream's Discord notification workflow via `if: false` (L9-11). V2 contains the live upstream notify-discord.yml; a Kilo v2 repository must not notify OpenCode's Discord, but that is achieved by not carrying the upstream workflow (or repository secrets) rather than porting a disabled file. Evidence: L9: # kilocode_change start - disabled for kilo-cli fork (OpenCode Discord notifications) / L11: # kilocode_change end | +| `.github/workflows/disabled/review.yml.disabled`
    9,11 | shared-upstream patch marker | CI/repository tooling | not needed | none — see notify-discord row; upstream review.yml requires OpenCode install/API keys | V1 disabled upstream's AI review workflow (L9-11). V2 has upstream's live review.yml; for a Kilo v2 repo it would fail/no-op without OpenCode secrets. Disposition of upstream automation is a CI-setup decision; no Kilo behavior is encoded in the disabled file. Evidence: L9: # kilocode_change start - disabled for kilo-cli fork (requires OpenCode install and API keys) / L11: # kilocode_change end | +| `.github/workflows/docs-build.yml`
    16,19 | shared-upstream patch marker | CI/repository tooling | deferred | .github/workflows/docs-build.yml for packages/kilo-docs when the docs lane is restored (docs-sync/auto-docs rows already deferred) | V1 markers only change runner and checkout pin (L16,19) in the Kilo docs build workflow. V2 has no docs-build workflow though packages/kilo-docs exists; belongs with the deferred docs automation lane. Evidence: L16: runs-on: blacksmith-4vcpu-ubuntu-2404 # kilocode_change / L19: uses: actions/checkout@v6 # kilocode_change | +| `.github/workflows/docs-check-links.yml`
    20 | shared-upstream patch marker | CI/repository tooling | deferred | .github/workflows/docs-check-links.yml with the docs lane | V1 marker pins checkout@v6 (L20) in the Kilo docs link-check workflow. V2 has no such workflow; deferred with docs automation. Evidence: L20: - uses: actions/checkout@v6 # kilocode_change | +| `.github/workflows/docs-sync.yml`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | .github/workflows/docs-sync.yml with its .github/docs-sync/* modules | Pinned v1 owns the complete scheduled/manual docs bot (collect, learn, triage, edit, verify, rolling PR) and invokes every docs-sync module; v2 retains packages/kilo-docs but has neither this workflow nor .github/docs-sync. Deferred repository automation, not implicitly ported. Evidence: L1: # kilocode_change - new file | +| `.github/workflows/generate.yml`
    17,18,21,30,33 | shared-upstream patch marker | CI/repository tooling | deferred | .github/workflows/generate.yml (automated SDK regeneration commits) once the Kilo v2 release lane exists; manual equivalent is `bun run generate` in packages/client | V1 markers pin checkout with `persist-credentials: false` and pass Kilo maintainer App secrets (L17-21,30-33). V2 has no generate workflow; regeneration is manual per AGENTS.md. Automated regeneration commits require the Kilo App identity (setup-git-committer row). Evidence: L17: uses: actions/checkout@v6 # kilocode_change / L18: # kilocode_change start | +| `.github/workflows/kilo-auto-close.yml`
    1 | Kilo-owned inherited marker | CI/repository tooling | deferred | .github/workflows/kilo-auto-close.yml when Kilo v2 issue automation is enabled | V1 Kilo-owned 366-line workflow auto-closes issues/PRs per Kilo policy. V2 has upstream close-issues.yml/close-prs.yml/compliance-close.yml instead; Kilo's policy automation is a repository-lane decision. Evidence: L1: # kilocode_change - new file | +| `.github/workflows/nix-hashes.yml`
    86,99,109,112,119 | shared-upstream patch marker | CI/repository tooling | needs port | .github/workflows/nix-hashes.yml:105 (setup-git-committer inputs) — pass Kilo maintainer App secrets; action version pins are cosmetic | V1 markers pin upload/download-artifact and checkout versions (L86,99,119) and pass the Kilo maintainer App secrets to setup-git-committer (L109-112). V2 nix-hashes.yml exists and calls setup-git-committer with upstream `opencode-app-*` inputs; in a Kilo fork the hash-bump commit would need the Kilo App identity. Follows the setup-git-committer row. Evidence: L86: uses: actions/upload-artifact@v7 # kilocode_change / L99: uses: actions/checkout@v6 # kilocode_change | +| `.github/workflows/prepare-jetbrains-release.yml`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | .github/workflows/prepare-jetbrains-release.yml with the JetBrains lane | V1 Kilo-owned JetBrains release-preparation workflow. V2 has no packages/kilo-jetbrains; deferred with that product lane. Evidence: L1: # kilocode_change - new file | +| `.github/workflows/publish-jetbrains-bundled.yml`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | .github/workflows/publish-jetbrains-bundled.yml with the JetBrains lane | V1 Kilo-owned bundled JetBrains publish workflow (341 lines). No v2 JetBrains package; deferred. Evidence: L1: # kilocode_change - new file | +| `.github/workflows/publish-jetbrains.yml`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | .github/workflows/publish-jetbrains.yml with the JetBrains lane | V1 Kilo-owned JetBrains publish workflow (222 lines). No v2 JetBrains package; deferred. Evidence: L1: # kilocode_change - new file | +| `.github/workflows/publish.yml`
    25,31,38,40,47,53,60,83,89,98,110,128,133,234,238,239,240,241,242,261,322,331,335,343,354,364,369,382,389,390,394,395,398,415,419,428,431,433,444,451 | shared-upstream patch marker | CI/repository tooling | needs port | .github/workflows/publish.yml (v2, 696 lines, publishes upstream `opencode`/`opencode2` artifacts from packages/opencode and packages/cli) — a Kilo v2 release workflow must re-add pre-release input, Kilo App secrets, Zig/bwrap sandbox helper build, tar.zst artifact packing, validate-cli matrices incl. Alpine libstdc++, VSIX build from the packed CLI, and the smoke-test gate | V1 markers (40) add: `pre_release` dispatch input for VS Code marketplace + npm rc channel (L25-31), issues/pull-requests write permissions for JetBrains pin-bump PRs (L38,40), Kilo CLI install for version bumps (L53-60), Zig setup for Linux sandbox helpers (L89-98), tar.zst packing of the CLI dist (L110-128) with matching unpack in build-vscode/publish (L343-354,433-444), `validate-cli-unix`/`validate-cli-windows` matrices executing every published binary incl. bwrap license/version checks and Alpine libstdc++ install (L133-322), a pre-publish smoke-test gate (L369-382,389-391), Kilo App secrets (L428-431) and action pins. V2 publish.yml is upstream's release pipeline for `opencode`; none of the Kilo release invariants exist and the artifact names differ (`opencode2`, L171). Because the v2 workflow is live and would publish upstream packages from a Kilo fork, the Kilo release lane must replace it before any Kilo v2 release — hence needs port rather than deferred, although sequencing depends on the packaging plan (kilocode/baseline/packaging-correction-plan.md). Evidence: L25: # kilocode_change start / L31: # kilocode_change end | +| `.github/workflows/smoke-test.yml`
    1,50,143 | shared-upstream patch marker | CI/repository tooling | deferred | .github/workflows/smoke-test.yml (kilo-bench pre-publish smoke) with the Kilo release lane | V1 Kilo-owned reusable workflow checks out kilo-bench and smoke-tests released CLI assets (L1,50,143). V2 has no smoke-test workflow and the kilo2 CLI has its own local `service-smoke.ts` (pinned-v2-baseline.md:101-102) but no bench-based release gate. Deferred with publish.yml. Evidence: L1: # kilocode_change - new file / L50: uses: actions/checkout@v6 # kilocode_change | +| `.github/workflows/source-check-links.yml`
    19 | shared-upstream patch marker | CI/repository tooling | deferred | .github/workflows/source-check-links.yml with the docs lane | V1 marker pins checkout@v6 (L19) in the Kilo source link-check workflow. V2 has no such workflow. Evidence: L19: - uses: actions/checkout@v6 # kilocode_change | +| `.github/workflows/test-jetbrains.yml`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | .github/workflows/test-jetbrains.yml with the JetBrains lane | V1 Kilo-owned JetBrains test workflow. No v2 JetBrains package. Evidence: L1: # kilocode_change - new file | +| `.github/workflows/test-vscode.yml`
    24,30,53 | shared-upstream patch marker | CI/repository tooling | deferred | .github/workflows/test-vscode.yml for packages/kilo-vscode (unit tests + `kilocode_change` marker check) when Kilo v2 CI is set up | V1 markers set the runner, pin checkout and run a `kilocode_change` marker check for the VS Code package (L24,30,53-54). V2 has packages/kilo-vscode but no workflow testing it (upstream publish-vscode.yml targets upstream's extension). Deferred with the Kilo CI lane; note the fork conventions now forbid markers in Kilo-owned packages, so the marker check semantics would invert. Evidence: L24: runs-on: blacksmith-4vcpu-ubuntu-2404 # kilocode_change / L30: uses: actions/checkout@v6 # kilocode_change | +| `.github/workflows/test.yml`
    6,11,13,24,68,70,73,77,79,84,88,91,92,96,104,106,112,115,118,122,124,125,129,132,133,135,136,137,142,144,149,150,152,157,159,164,167,169,189,191,233,235,237,245,247,259,260,271,283 | shared-upstream patch marker | CI/repository tooling | needs port | .github/workflows/test.yml (v2, 262 lines): default-branch concurrency (L14 keys on `dev`), unit matrix (L56-119) — a Kilo v2 CI must add Kilo default-branch handling, sandbox helper setup, Kilo bot git identity, junit publishing and required-check aggregation; JetBrains/CLI shard/httpapi jobs follow their lanes | V1 markers (49) add: `main` trigger and run-id concurrency for the default branch (L6,11-13), a paths-filter `changes` job that skips general unit tests for isolated product/docs changes (L24-68,70-88), Node 24 setup with Windows retry (L96-112), Linux sandbox helper setup (L118-122), Kilo maintainer bot git identity (L123-129), Turbo cache keys (L131-142), split non-CLI vs sharded CLI test runs with KILO_EXPERIMENTAL_DISABLE_FILEWATCHER/KILO_TEST_PROFILE/KILO_TEST_SHARD (L144-167), junit report publishing/artifacts (L169-189), an HttpApi exerciser job with generated-client check (L191-235), a JetBrains job (L237-245) and `unit-required`/`required` aggregation jobs (L247-283). V2 test.yml is upstream's: keys concurrency on `dev`, runs `GITHUB_ACTIONS=false bun turbo test` (L116-119), has no sandbox setup, Kilo identity, junit publishing, sharding or required aggregation, and its e2e job is skipped on v2 (baseline:116). The v2 workflow exists and would run in a Kilo fork, so Kilo's CI invariants (sandbox helper for sandbox tests, default-branch concurrency, required checks) are concrete gaps; the CLI shard/profile/httpapi jobs depend on v1 test infrastructure that v2 does not have. Evidence: L6: - main # kilocode_change / L11: # Keep every run on main so cancelled checks do not pollute the default branch # kilocode_change | +| `.github/workflows/typecheck.yml`
    13,16,21,27,29,102 | shared-upstream patch marker | CI/repository tooling | partial | .github/workflows/typecheck.yml (v2, 21 lines): `bun typecheck` native; Kilo Console build and JetBrains typecheck/required jobs absent | V1 markers set the runner/checkout, exclude the JetBrains package from `bun turbo typecheck` then build Kilo Console (L13-27), add a JetBrains path filter + Gradle typecheck (L29-79) and a `required` aggregation job (L80-102). V2 typecheck.yml runs `bun typecheck` on blacksmith (matching the AGENTS.md command). Preserved: TypeScript typecheck gate; missing: Kilo Console build (no packages/kilo-console in v2), JetBrains typecheck (no package) and the required-status aggregation. Evidence: L13: runs-on: blacksmith-4vcpu-ubuntu-2404 # kilocode_change / L16: uses: actions/checkout@v6 # kilocode_change | +| `.github/workflows/visual-regression.yml`
    1,15,61,94,223,258 | shared-upstream patch marker | CI/repository tooling | deferred | .github/workflows/visual-regression.yml for packages/kilo-ui and packages/kilo-vscode webview stories when Kilo v2 CI is set up | V1 Kilo-owned 412-line workflow runs visual regression for kilo-ui and the kilo-vscode webview via setup-bun (L1,15,61,94,223,258). V2 has both packages and the vscode-visual-regression skill/tests but no workflow (upstream storybook.yml targets upstream storybook). Deferred with the Kilo CI lane. Evidence: L1: # kilocode_change - new file / L15: runs-on: ubuntu-latest # kilocode_change | +| `.github/workflows/watch-opencode-releases.yml`
    15 | shared-upstream patch marker | CI/repository tooling | deferred | .github/workflows/watch-opencode-releases.yml — upstream-release watcher for the Kilo fork; v2 line pins `upstream/v2` SHAs manually (kilocode/v2-fork-conventions.md 'Upstream snapshot tracking') | V1 marker gates the release watcher to the Kilo repo or manual dispatch with an `OPENCODE_WATCH_ENABLED` variable (L15). V2 has no watcher; the fork conventions describe a manual pinned-SHA adoption process. Automating upstream-release detection for the v2 line is deferred. Evidence: L15: if: vars.OPENCODE_WATCH_ENABLED != 'false' && (github.event_name == 'workflow_dispatch' || github.repository == 'Kilo-Org/kilocode') # kilocode_change | +| `.husky/pre-push`
    21,50 | shared-upstream patch marker | Package behavior indicated by path | partial | .husky/pre-push (v2, 20 lines: bun-version guard + `bun typecheck`); JetBrains split not applicable | V1 block (L21-50) splits the pre-push typecheck: always typecheck non-JetBrains packages, and typecheck `@kilocode/kilo-jetbrains` only when files outside .changeset/kilo-vscode/kilo-docs changed (computed from the pushed refs against `/main`). V2's hook keeps the bun-version guard and runs `bun typecheck` for every package. Preserved: typecheck gate on push (the AGENTS.md/baseline-authoritative command). Missing: change-scoped skipping, which only matters once a heavy Kilo package (JetBrains, absent in v2) is added. Evidence: L21: # kilocode_change start / L50: # kilocode_change end | +| `.kilo/agent/upstream-merge.md`
    75,125,145,146,149,175,181,185,201,303,307,352,379,380,414 | documentation/reference | Documentation/agent guidance | native equivalent | AGENTS.md pointer plus kilocode/v2-fork-conventions.md sections Upstream snapshot tracking, Narrow shared hooks, and kilocode_change markers | Pinned v1 is merge-agent guidance: inspect marker intent, preserve Kilo behavior, minimize shared diffs, and justify removals. V2's AGENTS.md requires reading kilocode/v2-fork-conventions.md before merges; that document explicitly requires pinned-SHA merge accounting, owned seams, preserved markers, and an explanation for marker removal. The prose is reorganized for v2 rather than copied. Evidence: L75: - does a `kilocode_change` marker in HEAD encode a bug fix, a feature, or a / L125: `kilocode_change` comments when possible). | +| `.kilo/command/review-upstream-merge.md`
    15,37,53,81 | documentation/reference | Documentation/agent guidance | native equivalent | kilocode/v2-fork-conventions.md sections Dev-fix triage lane, Kilo-owned paths, Narrow shared hooks, and kilocode_change markers | The four marker occurrences instruct reviewers to detect lost/redundant markers, trace producer-to-consumer behavior, and preserve Kilo tests. V2 conventions retain the behavioral classification, owned-seam trace, preservation/removal rules, and baseline validation; the obsolete v1 command file has no runtime consumer. Evidence: L15: Review carefully, file by file, whether we accidentally removed any `kilocode_change` marker. / L37: Check whether any merged files now use `kilocode_change` markers without any actual difference to upstream. | +| `.kilo/plans/1779980012659-happy-circuit.md`
    24 | documentation/reference | Documentation/agent guidance | not needed | No v2 destination; historical implementation plan only | This pinned-v1 file is an authored historical plan, not executable configuration or a runtime/CI consumer. Its marker occurrence only states repository ownership/marker policy for that completed v1 work; v2 has its own active plan and ownership rules in plans/kilo-opencode-v2-issue-13750.md and kilocode/v2-fork-conventions.md. Evidence: L24: - If shared upstream files must be edited, keep the change narrow and add `kilocode_change` markers where required. | +| `.kilo/plans/1779980572375-proud-river.md`
    25 | documentation/reference | Documentation/agent guidance | not needed | No v2 destination; historical implementation plan only | This pinned-v1 file is an authored historical plan, not executable configuration or a runtime/CI consumer. Its marker occurrence only states repository ownership/marker policy for that completed v1 work; v2 has its own active plan and ownership rules in plans/kilo-opencode-v2-issue-13750.md and kilocode/v2-fork-conventions.md. Evidence: L25: - If shared upstream-owned files are involved, use the repository’s `kilocode_change` marker guidance. | +| `.kilo/plans/1779987162764-misty-rocket.md`
    68 | documentation/reference | Documentation/agent guidance | not needed | No v2 destination; historical implementation plan only | This pinned-v1 file is an authored historical plan, not executable configuration or a runtime/CI consumer. Its marker occurrence only states repository ownership/marker policy for that completed v1 work; v2 has its own active plan and ownership rules in plans/kilo-opencode-v2-issue-13750.md and kilocode/v2-fork-conventions.md. Evidence: L68: - This change is in `packages/kilo-jetbrains/`, a Kilo-owned package, so `kilocode_change` markers are not needed. | +| `.kilo/plans/1783518847053-prompt-rollback-redo-jetbrains-parity.md`
    109 | documentation/reference | Documentation/agent guidance | not needed | No v2 destination; historical implementation plan only | This pinned-v1 file is an authored historical plan, not executable configuration or a runtime/CI consumer. Its marker occurrence only states repository ownership/marker policy for that completed v1 work; v2 has its own active plan and ownership rules in plans/kilo-opencode-v2-issue-13750.md and kilocode/v2-fork-conventions.md. Evidence: L109: - Confirm no `kilocode_change` markers are needed for the VS Code i18n edits (Kilo-owned webview path — none required). | +| `.kilo/plans/1783550396285-jetbrains-rollback-hardening.md`
    8 | documentation/reference | Documentation/agent guidance | not needed | No v2 destination; historical implementation plan only | This pinned-v1 file is an authored historical plan, not executable configuration or a runtime/CI consumer. Its marker occurrence only states repository ownership/marker policy for that completed v1 work; v2 has its own active plan and ownership rules in plans/kilo-opencode-v2-issue-13750.md and kilocode/v2-fork-conventions.md. Evidence: L8: so no `kilocode_change` markers are needed. | +| `.kilo/plans/1784057744472-jetbrains-custom-provider-dialog.md`
    12 | documentation/reference | Documentation/agent guidance | not needed | No v2 destination; historical implementation plan only | This pinned-v1 file is an authored historical plan, not executable configuration or a runtime/CI consumer. Its marker occurrence only states repository ownership/marker policy for that completed v1 work; v2 has its own active plan and ownership rules in plans/kilo-opencode-v2-issue-13750.md and kilocode/v2-fork-conventions.md. Evidence: L12: All work is in `packages/kilo-jetbrains/frontend/`. No backend, RPC, or DTO changes. `saveCustom` already upserts, and `ProviderSettingsDto.config[id] | +| `.kilo/plans/1784058434633-consolidate-jetbrains-model-pickers.md`
    19 | documentation/reference | Documentation/agent guidance | not needed | No v2 destination; historical implementation plan only | This pinned-v1 file is an authored historical plan, not executable configuration or a runtime/CI consumer. Its marker occurrence only states repository ownership/marker policy for that completed v1 work; v2 has its own active plan and ownership rules in plans/kilo-opencode-v2-issue-13750.md and kilocode/v2-fork-conventions.md. Evidence: L19: Kilo-owned (no `kilocode_change` markers). | +| `.kilo/plans/agent-manager-multi-project-sidebar-density.md`
    10 | documentation/reference | Documentation/agent guidance | not needed | No v2 destination; historical implementation plan only | This pinned-v1 file is an authored historical plan, not executable configuration or a runtime/CI consumer. Its marker occurrence only states repository ownership/marker policy for that completed v1 work; v2 has its own active plan and ownership rules in plans/kilo-opencode-v2-issue-13750.md and kilocode/v2-fork-conventions.md. Evidence: L10: - Do **not** add `kilocode_change` markers. This package is Kilo-owned and CI fails if you do. | +| `.kilo/plans/jetbrains-mdview-vscode-styling.md`
    68 | documentation/reference | Documentation/agent guidance | not needed | No v2 destination; historical implementation plan only | This pinned-v1 file is an authored historical plan, not executable configuration or a runtime/CI consumer. Its marker occurrence only states repository ownership/marker policy for that completed v1 work; v2 has its own active plan and ownership rules in plans/kilo-opencode-v2-issue-13750.md and kilocode/v2-fork-conventions.md. Evidence: L68: - No `kilocode_change` markers are needed for JetBrains or Kilo UI paths. | +| `.kilo/skills/kilocode-merge-minimizer/SKILL.md`
    3,8,22,74,78,80,86 | documentation/reference | Documentation/agent guidance | native equivalent | AGENTS.md and kilocode/v2-fork-conventions.md sections Kilo-owned paths, Narrow shared hooks, and kilocode_change markers | Pinned v1's skill requires Kilo logic in owned files, narrow hooks in shared files, inline/block markers only around the changed lines, and no markers in Kilo-owned paths. Those same operational invariants are explicit in the v2 conventions consumed through the root AGENTS.md pointer; the legacy repository-local skill itself is unnecessary. Evidence: L3: description: Use when changing shared upstream-owned files to add Kilo-specific behavior, editing `kilocode_change` markers in shared code, or moving / L8: Use this skill whenever a normal development task touches shared upstream-owned code and includes Kilo-specific behavior, especially for marker cleanu | +| `.opencode/agent/triage.md`
    4 | documentation/reference | Documentation/agent guidance | needs port | .opencode/agent/triage.md Kilo-owned model selection for repository triage | Pinned v1 explicitly selects kilo/openai/gpt-5-nano for the hidden triage agent. Current v2 selects opencode/gpt-5.4-mini and the same agent invokes github-triage, so the Kilo provider/cost-routing invariant is absent at the actual consumer. Evidence: L4: model: kilo/openai/gpt-5-nano # kilocode_change | +| `.opencode/tool/github-triage.ts`
    4,13,52,55 | documentation/reference | Documentation/agent guidance | needs port | .opencode/tool/github-triage.ts Kilo repository owner/repo and current Kilo team roster | Pinned v1 replaces the upstream team table and sends assignments to Kilo-Org/kilocode. Current v2 still executes this tool but targets anomalyco/opencode with the upstream roster, so neither Kilo repository routing nor assignee ownership is preserved; both marked subfeatures must be ported together. Evidence: L4: // kilocode_change start / L13: // kilocode_change end | +| `AGENTS.md`
    22,23,187,192,202,204,205,206,207 | documentation/reference | Documentation/agent guidance | native equivalent | AGENTS.md lines 1-4 and kilocode/v2-fork-conventions.md sections Kilo-owned paths, Narrow shared hooks, and kilocode_change markers | The v1 occurrences combine marker-check commands, shared-file minimization, marker syntax, and a cloud-schema reminder tied to removed packages/opencode Config.Info. V2 replaces the shared guidance with a marked root pointer and explicit conventions covering ownership, minimal hooks, syntax, preservation, and removal evidence. The packages/opencode cloud-mirror path is not applicable to v2's Schema/Core layout; checker automation is assessed in its own script/workflow rows. Evidence: L22: - **kilocode_change check**: `bun run check-kilocode-change` from `packages/kilo-vscode/`. CI runs this — `kilocode_change` is a marker for upstream m / L23: - **opencode annotation check**: `bun run script/check-opencode-annotations.ts --worktree` from repo root when verifying local agent changes. CI runs | +| `CONTRIBUTING.md`
    110 | documentation/reference | Documentation/agent guidance | native equivalent | CONTRIBUTING.md instruction to follow AGENTS.md, then AGENTS.md pointer to kilocode/v2-fork-conventions.md | Pinned v1's single occurrence says shared packages/opencode edits need narrow kilocode_change markers and Kilo-owned paths do not. V2 has no packages/opencode tree; current CONTRIBUTING.md delegates repository style to AGENTS.md, whose fork header routes contributors to the v2 ownership and marker rules. Evidence: L110: - When editing shared `packages/opencode/` files, keep Kilo changes small and mark Kilo-only edits with `// kilocode_change` for a single line or `// | +| `REVIEW.md`
    14,65 | documentation/reference | Documentation/agent guidance | partial | Add a v2 review checklist only if desired; invariant source is kilocode/v2-fork-conventions.md and baseline checks are in kilocode/baseline/pinned-v2-baseline.md | The marker-review half has a native v2 equivalent: conventions require evidence for marker removal and forbid markers in owned paths, while the baseline document owns validation. The second v1 occurrence is a cloud Config.Info mirror reminder tied to removed packages/opencode and is not applicable as written. What is absent is only the standalone reviewer-oriented checklist document, not the ownership invariant or a runtime capability. Evidence: L14: - `kilocode_change` marker rules — both directions: / L65: When `Config.Info` in `packages/opencode/src/config/config.ts` gains a new `kilocode_change` field, the matching JSON Schema entry must also be added | +| `install`
    3,10,29,70,227,243,286,298,336,339,344,353,360,365,369,387,389,527,578 | shared-upstream patch marker | Package behavior indicated by path | needs port | `install` (v2 script hard-codes APP=opencode2, `$HOME/.opencode/bin`, opencode.ai URLs and `@opencode-ai/cli-*` packages; a Kilo v2 installer must re-apply the Kilo branding, `~/.kilo/bin`, `@kilocode/*` package resolution, tree-sitter payload move and logo) | V1 markers (19) brand the installer as Kilo: APP=kilo, usage text with kilo.ai URLs (L10-29), `INSTALL_DIR=$HOME/.kilo/bin` (L70), `check_version` against the installed `kilo` binary (L227-243), Kilo temp paths and curl tracing (L286,298), Kilo-branded install messages, moving the `kilo` binary plus the `tree-sitter` directory into place (L336-369), `# kilo` PATH comment (L387-389), and a KILO_UNICODE_LOGO-aware logo/getting-started epilogue (L527-578). V2's `install` is upstream's opencode2 installer (L3,23-25,68,169-183) with none of the Kilo branding or the tree-sitter payload step. A Kilo v2 release needs its own installer; the packaging plan (kilocode/baseline/packaging-correction-plan.md) governs timing, but the shared file as-is would install OpenCode. Evidence: L3: APP=kilo # kilocode_change / L10: # kilocode_change start - usage text | +| `packages/client/test/import-boundaries.test.ts`
    15,23,68,72 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | packages/client/test/import-boundaries.test.ts:7 (`realpathSync(resolve(.../node_modules/effect))`) | V1 replaced the effect-path check with a separator-normalized `/node_modules/effect/` substring match to support hoisted dependencies on Windows (L15,23,68-72). V2 resolves the effect package through `realpathSync` of the package-local node_modules and compares with `within`, which handles hoisting/symlinks natively; the Windows separator normalization is not present but `within` operates on resolved absolute paths. Evidence: L15: expect(dependency(root, "effect")).toEqual([]) // kilocode_change / L23: expect(dependency(network, "effect").length).toBeGreaterThan(0) // kilocode_change | +| `packages/containers/README.md`
    11 | documentation/reference | Documentation/agent guidance | partial | packages/containers/README.md alongside a restored packages/containers/jetbrains/Dockerfile | The v2 README still documents and is consumed with the same build script/images directory, but its JetBrains image entry is gone. Base/bun-node/rust/tauri/publish documentation remains natively equivalent; only the marked JetBrains image documentation is missing with that deferred image. Evidence: L11: - `jetbrains`: `bun-node` plus Java 21, JBR font libraries, and pre-cached Gradle and Bun packages | +| `packages/containers/bun-node/Dockerfile`
    1,3 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/containers/bun-node/Dockerfile ARG REGISTRY default | Pinned v1 changes the parent-image registry default to ghcr.io/kilo-org. Current v2 builds the same bun-node image but defaults to ghcr.io/anomalyco, so a Kilo build without explicit REGISTRY pulls the wrong organization image; the executable consumer is packages/containers/script/build.ts. Evidence: L1: # kilocode_change start / L3: # kilocode_change end | +| `packages/containers/jetbrains/Dockerfile`
    1,52 | shared-upstream patch marker | Package behavior indicated by path | deferred | packages/containers/jetbrains/Dockerfile when the JetBrains client build lane is restored | Pinned v1 defines the complete Java 21/JBR/Gradle/Bun-cache build image and its Kilo registry parent. V2 has no file and its build.ts image list omits jetbrains; this is a coherent deferred JetBrains build capability, not an adjacent container equivalent. Evidence: L1: # kilocode_change start / L52: # kilocode_change end | +| `packages/containers/publish/Dockerfile`
    1,3 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/containers/publish/Dockerfile ARG REGISTRY default | The publish container still exists and is built by v2 build.ts, but its marked Kilo parent registry default reverted from ghcr.io/kilo-org to ghcr.io/anomalyco. Explicit REGISTRY can override it; default Kilo publishing behavior is not preserved. Evidence: L1: # kilocode_change start / L3: # kilocode_change end | +| `packages/containers/rust/Dockerfile`
    1,3 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/containers/rust/Dockerfile ARG REGISTRY default | The rust container still exists and is built by v2 build.ts, but the marked Kilo parent registry default is now ghcr.io/anomalyco. The Rust toolchain body is native-equivalent; only registry ownership remains missing. Evidence: L1: # kilocode_change start / L3: # kilocode_change end | +| `packages/containers/script/build.ts`
    10,20,25,31 | shared-upstream patch marker | Package behavior indicated by path | partial | packages/containers/script/build.ts Kilo registry/builder defaults; JetBrains image remains deferred with its Dockerfile | The same v2 build consumer retains the image build loop, but defaults to ghcr.io/anomalyco, names its buildx builder opencode, and omits jetbrains. Thus common image mechanics are native-equivalent while all three marked Kilo subfeatures are absent; JetBrains is deferred and registry/builder identity needs porting. Evidence: L10: const reg = process.env.REGISTRY ?? "ghcr.io/kilo-org" // kilocode_change / L20: const images = ["base", "bun-node", "jetbrains", "rust", "tauri-linux", "publish"] // kilocode_change | +| `packages/containers/tauri-linux/Dockerfile`
    1,3 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/containers/tauri-linux/Dockerfile ARG REGISTRY default | The tauri-linux image still exists and is built by v2 build.ts, but its parent registry default reverted from the marked ghcr.io/kilo-org to ghcr.io/anomalyco. Tauri dependency installation is otherwise retained. Evidence: L1: # kilocode_change start / L3: # kilocode_change end | +| `packages/core/src/config.ts`
    9,143,178,182,190,196,198,205 | shared-upstream patch marker | Configuration/settings | partial | packages/kilo-cli/src/project-config.ts (ProjectConfig.configured) wired through packages/kilo-cli/src/host.ts:30; native packages/core/src/config/discovery.ts stays upstream | V1 markers add kilo.json/kilo.jsonc/config.json to the discovered names, walk .kilo/.kilocode directories, honor Flag.KILO_DISABLE_PROJECT_CONFIG, and order global < project files < Kilo config-directory files. V2 leaves upstream discovery untouched (discovery.ts:11 still opencode.json/.opencode) and instead replaces Config.node with the owned ProjectConfig service (project-config.ts:31-32 files kilo.json/kilo.jsonc, directories .kilo/.kilocode; readProjectEntries walks project boundary lowest-to-highest priority incl. agent markdown and skills). Preserved: Kilo file/dir names, ancestor precedence, project-boundary confinement. Different/missing: native global+project discovery is disabled (`Config.configured({global:false, project:false})`), so opencode.json/.opencode compatibility and the native global config directory are not read; the disable flag became the host opt-in `projectConfig` option (commands.ts:199 `--project-config`, interactive-server.ts:140) rather than an env flag; project plugins are explicitly dropped. The three v2 markers in config.ts (L36,322,325) are a different Kilo hook (host-driven refresh), not this invariant. Evidence: L9: import { Flag } from "./flag/flag" // kilocode_change / L143: const names = ["config.json", "kilo.json", "kilo.jsonc", "opencode.json", "opencode.jsonc"] // kilocode_change | +| `packages/core/src/config/command.ts`
    6 | shared-upstream patch marker | Configuration/settings | needs port | packages/schema/src/config/command.ts (template optionality) plus an owned override step in the command plugin or a Kilo-owned config plugin | V1 makes `template` optional so kilo.json can partially override an existing command (description/agent/model/variant/subtask) without restating its template. V2 schema packages/schema/src/config/command.ts:8 keeps `template: Schema.String` required, and no Kilo consumer (kilo-cli ProjectConfig decodes through the native Info schema at project-config.ts:113) relaxes it, so a partial command override document fails decoding. Paired with packages/core/src/config/plugin/command.ts. Evidence: L6: template: Schema.String.pipe(Schema.optional), // kilocode_change - allow partial command overrides | +| `packages/core/src/config/plugin/command.ts`
    30,55 | shared-upstream patch marker | Configuration/settings | needs port | packages/core/src/config/plugin/command.ts editor transform or a Kilo-owned plugin applying overrides after native registration | V1 block L30-55 applies partial workflow overrides after every command template is registered: template-only updates first, then description/agent/model/variant/subtask onto existing drafts, skipping template-less commands that do not exist. V2 packages/core/src/config/plugin/command.ts:66-72 only `editor.add`s each document command wholesale with a required template; there is no post-registration override pass and no owned plugin calls `ctx.command.transform` for commands (only memory-plugin.ts registers its own commands). Kilo partial override semantics are absent. Evidence: L30: // kilocode_change start - apply partial workflow overrides after every command template is registered / L55: // kilocode_change end | +| `packages/core/src/config/plugin/reference.ts`
    25,28 | shared-upstream patch marker | Configuration/settings | native equivalent | packages/core/src/config/plugin/reference.ts:24 (per-document directory) — no Kilo seam needed | V1 L25-28 resolved local reference paths against `location.project.directory` (falling back to `location.directory` only when the project is the filesystem root) so opening a subdirectory did not break repo-relative references. V2 resolves each reference relative to the directory of the config document that declared it (`doc.path ? path.dirname(doc.path) : location.directory`, reference.ts:24) — for repo-root kilo.json documents this yields the project directory, and for nested .kilo documents it is more precise than v1. The v1 behavior of resolving even inline `content` documents against the project directory is the only divergence (falls back to location.directory), which is not a Kilo-specific invariant. Evidence: L25: // kilocode_change start / L28: // kilocode_change end | +| `packages/core/src/credential.ts`
    3,4,6,12,20,41,86,109,193,224,226,244,311,333,337,349,354,367,378,383,403,408,423,430,433,440,442,450,451 | shared-upstream patch marker | Package behavior indicated by path | partial | packages/kilo-cli/src/import-v1-config.ts + packages/kilo-cli/src/credential-import.ts (`kilo2 import-v1 --auth --apply`, commands.ts:216-236) and packages/kilo-cli/src/paths.ts:87 preflight | V1 markers: (a) legacy auth.json / account.json / auth-v2.json import with data-migration markers and startup reconciliation of the active imported account (legacyImportLayer L109-224); (b) write-back of credential changes into auth.json for released v1 clients (writeLegacy L289-333, L403); (c) KILO_AUTH_CONTENT process-local injected credentials that bypass storage (L244-283, L337-383); (d) retained `layer` export for Kilo test layers. V2 core credential.ts has no markers. Preserved through an owned seam: one-shot explicit import of v1 auth.json credentials (api keys, kilo device OAuth, openai/github-copilot/xai OAuth) via `kilo2 import-v1` writing through Credential.create; automatic import is deliberately refused when legacy files are found in preview storage (paths.ts:87-89). Missing: startup reconciliation of a released client's later auth.json edits, auth.json write-back for v1 clients (kilo2 uses its own kilo2.db, so shared-store compatibility is intentionally out of scope), and the KILO_AUTH_CONTENT isolated-workspace credential injection (no v2 reference in packages/*/src). Evidence: L3: import { asc, desc, eq } from "drizzle-orm" // kilocode_change / L4: // kilocode_change start | +| `packages/core/src/cross-spawn-spawner.ts`
    4,5,6,7,8,255,261,275,280,288,335,345,390,391,393,399,401,414,417,426,429,433,437,444,450,459,476 | shared-upstream patch marker | Package behavior indicated by path | partial | packages/util/src/cross-spawn-spawner.ts (upstream, unmarked) with Kilo confinement moved to packages/kilo-cli/src/sandbox.ts, sandbox-mcp.ts, sandbox-pty.ts and packages/kilo-cli/src/host.ts:36-47 | V1 markers thread every StandardCommand through `@kilocode/sandbox` prepareCommand (L393-426), validate spawn targets (SpawnValidation, L390,402-414), opt selected commands into direct-exit settlement with descendant group cleanup (SpawnExit, L288,335-345,437-444), tap stdio at spawn time because Bun drops buffered stdio on close (L255,261,280), preserve prepared kill options (L450-459), and settle signal termination as 128+signum (L476). V2 util cross-spawn-spawner.ts carries none of these (0 markers; no tap/settle/direct). Preserved partially through owned seams: sandbox confinement for MCP local server spawns (sandbox-mcp.ts wraps argv with a launcher) and PTY refusal (sandbox-pty.ts), matching the earlier 'Sandbox intentionally partial' assessment. Missing: generic shell/tool-command sandbox preparation at the spawner, spawn-target validation, direct-exit settlement/descendant cleanup, stdio tap, kill-option propagation, and 128+signum exit settlement. Evidence: L4: import { prepareCommand as prepareSandbox } from "@kilocode/sandbox" // kilocode_change / L5: import { tap as tapStdio, tapped } from "./kilocode/stdio-tap" // kilocode_change - Bun drops buffered stdio on close | +| `packages/core/src/database/database.ts`
    9,10,11,37,44,59,65,68 | shared-upstream patch marker | Package behavior indicated by path | partial | packages/kilo-cli/src/paths.ts:26 (`kilo2.db` injected via Database options) and paths.ts preflight; no v2 DbPreflight/compat | V1 markers: DbPreflight.assertWritable for an actionable read-only-db error with self-heal (L44), `compat(db)` to keep the shared DB usable by released CLIs (L37), kilo-branded `kilo-.db` with fallback to a pre-existing opencode-named DB (L59-65), and lazy path resolution so KILO_DB overrides apply (L68). V2 database.ts is unmarked but takes `Options.path` at layer build time (database.ts:66-79), and the Kilo host injects its own `kilo2.db` inside an isolated profile root (paths.ts:26, host.ts:22) — this natively satisfies branding and late path resolution. Released-CLI compat is not needed because kilo2 never shares v1's database. Missing: the read-only database preflight/self-heal (paths.ts preflight checks protected paths, symlinks and hard links but not writability). Evidence: L9: import { existsSync } from "fs" // kilocode_change / L10: import { DbPreflight } from "../kilocode/db-preflight" // kilocode_change | +| `packages/core/src/database/migration/20260603040000_session_message_projection_order.ts`
    11 | shared-upstream patch marker | Session lifecycle/history/UI | native equivalent | packages/core/src/database/migration/20260603040000_session_message_projection_order.ts:11 | V1 marker relaxed `ADD COLUMN seq integer` (nullable) so released Kilo writers lacking `seq` could still insert into the shared table. V2 keeps upstream `seq integer NOT NULL` (L11) and adds a type+seq index. Because kilo2 owns an isolated database that no released v1 writer opens, the nullable relaxation has no consumer; the upstream projection order invariant itself is present natively. Evidence: L11: yield* tx.run(`ALTER TABLE \`session_message\` ADD COLUMN \`seq\` integer;`) // kilocode_change | +| `packages/core/src/database/migration/20260714141136_session-message-legacy-writer-compat.ts`
    1 | shared-upstream patch marker | Session lifecycle/history/UI | not needed | none — kilo2 isolated database (packages/kilo-cli/src/paths.ts:26) | V1 new-file migration exists solely to keep `session_message` writable by legacy Kilo writers sharing one database (see v1 database.ts `compat`). V2 has no such migration and no shared-database scenario: the Kilo host uses its own `kilo2.db` under a profile root and refuses legacy stores in preview storage (paths.ts:87). Historical shared-DB compatibility is not a v2 behavior. Evidence: L1: // kilocode_change - new file | +| `packages/core/src/effect/layer-node.ts`
    333,361 | shared-upstream patch marker | Package behavior indicated by path | not needed | packages/util/src/effect/layer-node.ts (upstream) — no Kilo consumer | V1 added `LayerNode.suspend` to break circular node dependency chains introduced by Kilo v1 modules (consumers at pinned SHA: packages/opencode/src/kilo-sessions/kilo-sessions.ts:611, packages/opencode/src/kilocode/bootstrap.ts:113, packages/opencode/src/tool/registry.ts:481). V2 util layer-node.ts has no `suspend` export and no Kilo v2 module needs it (Kilo nodes use makeLocationNode/replace in kilo-cli). The helper served v1-only composition; nothing to port unless a future owned node reintroduces a cycle. Evidence: L333: // kilocode_change start - defer node construction to break circular dependency chains / L361: // kilocode_change end | +| `packages/core/src/event.ts`
    9,60,103,252,458 | shared-upstream patch marker | Package behavior indicated by path | not needed | none — packages/core/src/event/* stores current shapes; v1 rows enter only through packages/kilo-cli/src/import-v1-session.ts | V1 markers route durable tool `content` through kilocode/event-storage encode/decode (L9,60,103,252,458) so rows persisted by pre-1.17.13 released Kilo readers/writers stay readable in a shared database. V2 core event storage (packages/core/src/event/sql.ts, session/event.ts) has no such codec and no Kilo marker. The compat only matters when v2 reads v1-written rows in the same DB; kilo2 uses an isolated database and imports v1 sessions through an explicit conversion path instead. Paired Kilo-owned module below. Evidence: L9: import * as EventStorage from "./kilocode/event-storage" // kilocode_change - released tool content shapes / L60: data: Schema.decodeUnknownSync(definition.data)(EventStorage.decode(definition.type, event.data)), // kilocode_change | +| `packages/core/src/filesystem.ts`
    5,11,35,36,37,77,78,86,87,110,114,115,137 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/core/src/filesystem.ts read/list paths or an owned FileSystem replacement node in packages/kilo-cli | V1 markers: bounded searches (`MAX_SEARCH_LIMIT = 100`, `SearchLimit` capped schema, L35-37), TOCTOU-hardened read that opens the validated descriptor and compares dev/ino before reading (L86-110), and list() that validates the directory target before and after enumeration via kilocode/search-target (L114-137). V2 filesystem.ts keeps `DEFAULT_SEARCH_LIMIT = 100` but its Glob/Grep inputs accept any PositiveInt limit (L41,50), read() does a stat-then-readFile by path (L96-103), and list() only stats once (L105-108). No Kilo-owned FileSystem override exists in packages/kilo-cli. The Kilo hardening invariants (limit cap, descriptor-bound read, swap detection) are absent. Evidence: L5: import { Context, Effect, Layer, Option, Schema } from "effect" // kilocode_change / L11: import * as SearchTarget from "./kilocode/search-target" // kilocode_change | +| `packages/core/src/filesystem/search.ts`
    14,17,34,45,68,71,77,82,95,98,103,106,111,150,168,169,216,219,223,225,232,234,243,246,250,252,261,264,284,288,289 | shared-upstream patch marker | Package behavior indicated by path | partial | packages/core/src/filesystem/search.ts (ripgrepLayer/fff layer); missing canonical target inspection, spawn-bound validate, realpath result filtering | V1 markers confine every glob/grep to the canonical active Location via SearchTarget.inspect (L34-45,68-71,95-98,150-168), pass spawn-bound `validate` (L77,106), defer FFF index creation until first search with scoped release (L169-216), and post-filter FFF results by realpath containment (L235-236,265-266). V2 search.ts (202 lines) keeps a ripgrep layer and an FFF layer chosen by option/platform (L191) with location-scoped indexes, but has no SearchTarget/realpath post-filter or validate hook. Preserved: location-scoped search and FFF availability gating; missing: symlink-escape hardening and swap detection around search execution; FFF laziness was not compared line-by-line. Evidence: L14: // kilocode_change start / L17: // kilocode_change end | +| `packages/core/src/flag/flag.ts`
    2,9,30,42,52,56,60,66,70,72,74,80,85,87,89,91,93,95,97,99,101,103,109,113,115,117,123,125,127,129,155,159 | shared-upstream patch marker | Package behavior indicated by path | partial | Kilo-owned env reads in packages/kilo-cli/src (sandbox.ts, updater.ts, presence-service.ts, interactive-server.ts, daemon-entry.ts, skill-shell.ts) and host options in packages/kilo-cli/src/host.ts; no shared Flag module | V1 markers add ~30 KILO_* flags (unstable-channel defaults, KILO_EXPERIMENTAL umbrella, Claude Code/skills opt-outs, EXA, plan mode, scout, parallel, markdown, embedded web UI, channel DB, skip migrations, strict config deps, session switching, FFF win32 default, project-config disable, session retry limit). V2 core has no flag.ts at all (upstream moved flags out) and the Kilo host reads a different, smaller env surface directly (KILO_DB/KILO_CONFIG_DIR as protected paths in paths.ts, KILO_DISABLE_SKILL_SHELL, KILO_SANDBOX_ROOT/KILO_BWRAP_PATH, KILO_TELEMETRY_*, KILO_UPDATE_MANIFEST_URL, KILO_EVENT_SERVICE_URL, KILO_PLATFORM). Preserved in spirit: env-driven Kilo host configuration and project-config gating (now the `--project-config` option). Missing: every v1 flag whose feature exists in v2 but has no env toggle (models fetch/autocompact remain host options `models.fetch`; retry limit, experimental models, LSP download, default plugins, question tool, FFF override have no Kilo toggle), and v1-only feature flags (Claude Code, EXA, scout, plan-mode, parallel, session switcher) are moot until those features are ported. A per-flag consumer inventory should accompany any port. Evidence: L2: import { InstallationChannel } from "../installation/version" // kilocode_change / L9: // kilocode_change start | +| `packages/core/src/fs-util.ts`
    2,56,108,123,205 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/util/src/fs-util.ts FSUtil layer or a Kilo-owned FSUtil.node replacement in packages/kilo-cli/src/host.ts overrides | V1 markers decorate the Effect FileSystem with `@kilocode/sandbox` (decorateFileSystem L56; ensureDirectory for ensureDir/writeJson L108,123) so every file-tool write is sandbox-confined, and export a `defaultLayer` for legacy runtime compatibility (L205). V2 util fs-util.ts has no decoration, and the Kilo sandbox (packages/kilo-cli/src/sandbox*.ts) confines only MCP spawns and PTY; consistent with the earlier sandbox assessment ('all-file-tool decoration' missing). The legacy `defaultLayer` export is not needed (no v1 runtime in v2). Evidence: L2: import { decorateFileSystem, ensureDirectory } from "@kilocode/sandbox" // kilocode_change / L56: const fs = decorateFileSystem(yield* FileSystem.FileSystem) // kilocode_change | +| `packages/core/src/global.ts`
    7,8,12,13,26,31,48,49,50,51,52,53,54,57,59 | shared-upstream patch marker | Package behavior indicated by path | partial | packages/kilo-cli/src/paths.ts layout() + `Global.node.replace(Global.layerWith(input.paths))` in host.ts:27 | V1 markers: app name `kilo` (L12), newline-sanitized XDG roots and trimmed home (L13-31 incl. KILO_TEST_HOME), ensureRealDir for every root (L48-54), and macOS Spotlight no-index marking of data/cache/state (L57-59). V2 util global.ts stays `opencode` (L12) and is replaced at runtime by the owned kilo2 layout (paths.ts:6-33, distinct `kilo2/` roots, canonical realpath resolution in preflight). Preserved: Kilo-branded roots and real-directory resolution (preflight `canonical`). Missing: newline sanitization of $HOME/$XDG_* values, the test-home override (v2 uses upstream OPENCODE_TEST_HOME), and Spotlight no-index marking (no `markNoIndex`/metadata_never_index anywhere in v2). Evidence: L7: import { markNoIndex } from "./kilocode/spotlight" // kilocode_change / L8: import { ensureRealDir } from "./kilocode/global" // kilocode_change | +| `packages/core/src/image.ts`
    21,33,55 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/core/src/image.ts + packages/core/src/image/photon.ts (pixel-limit pre-check) or Kilo-owned image size guard | V1 adds `PixelLimitError` (L21-33) surfaced from the resizer contract (L55) so decompression-bomb images are rejected before native decode. V2 image.ts has no PixelLimitError and no dimension check in photon.ts (grep for dimension/MAX_/bomb empty). Paired with packages/core/src/image/photon.ts. Evidence: L21: // kilocode_change start - report images rejected before native decode allocation / L33: // kilocode_change end | +| `packages/core/src/image/photon.ts`
    7,8,31,45,48 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/core/src/image/photon.ts:31-48 pre-decode dimension check backed by a Kilo-owned image-size helper | V1 block L31-45 parses image header dimensions and rejects images over MAX_DIMENSION/MAX_PIXELS before `PhotonImage.new_from_byteslice` allocates native pixels. V2 photon.ts decodes directly with no dimension guard; the v1 helper `kilocode/image-size` has no v2 counterpart. Decompression-bomb protection is absent. Evidence: L7: import { DecodeError, PixelLimitError, ResizerUnavailableError, SizeError } from "../image" // kilocode_change / L8: import { allowed, dimensions, MAX_DIMENSION, MAX_PIXELS } from "../kilocode/image-size" // kilocode_change | +| `packages/core/src/installation/version.ts`
    4,10,13 | shared-upstream patch marker | Package behavior indicated by path | not needed | packages/kilo-cli/src/host.ts:22 (`app.channel` from the kilo2 layout) covers release/preview identity | V1 adds `KILO_BUILD_KIND` and `InstallationBuildKind` (source vs release) for v1 CLI build-time distinction. V2 core has no installation/version.ts and no BUILD_KIND reference; the Kilo host identifies itself via `identity`/`channel` in the layout (paths.ts:6-7) passed as `app.channel`. No v2 consumer needs a separate build-kind flag; revisit only if release packaging introduces one. Evidence: L4: const KILO_BUILD_KIND: string // kilocode_change / L10: // kilocode_change start - distinguish release builds from source / local builds | +| `packages/core/src/integration.ts`
    201,207,324,348,381,389,472,482,488,529,541,553,558 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | packages/core/src/integration.ts:380-433 (`persisting` flag, uninterruptible settle) | V1 markers add a `settling` flag, a 30s settlement timeout and `Effect.uninterruptibleMask` so OAuth credential persistence is atomic with cancel/expiry and an interrupted callback still settles (L201,207,324-381,389,472,482-488,529,541-553,558). Upstream v2 now implements the same invariant natively: `settle` is `Effect.uninterruptible`, marks the attempt `persisting` so cancel/expiry/claim cannot overtake persistence ('Persisting attempts cannot be cancelled, expired, or claimed again', integration.ts:427), and terminal state is written after createCredential resolves. The explicit 30s timeout is not present, but the atomicity and persist-before-expose invariants are. Evidence: L201: const settlementTimeout = Duration.seconds(30) // kilocode_change - bound retained OAuth attempt secrets / L207: settling: boolean // kilocode_change - cancellation and expiry cannot overtake credential persistence | +| `packages/core/src/kilocode/event-storage.ts`
    1,30 | Kilo-owned inherited marker | Package behavior indicated by path | not needed | none (see packages/core/src/event.ts) | Kilo-owned v1 module implementing the released tool-content shape codec (`{type:"media"}`, `{type:"file",source}`) for shared-database compatibility. V2 has no packages/core/src/kilocode directory and no consumer reading released v1 durable rows in place; the isolated kilo2.db plus explicit `import-v1` flow make the in-place codec unnecessary. Evidence: L1: // kilocode_change start - released readers persist tool content in the pre-1.17.13 shapes / L30: // kilocode_change end | +| `packages/core/src/models-dev.ts`
    11,48,64,73,96,102,131,227,232,253 | shared-upstream patch marker | Provider/model routing or UI | partial | packages/core/src/models-dev.ts:42,192 (native reasoning_options); packages/schema/src/kilocode/models.ts + packages/kilo-gateway/src/models.ts (Kilo catalog metadata); no v2 cache re-check/refresh notify | V1 markers: (a) models.dev `reasoning_options` schema snatched from upstream (L48-73); (b) Kilo catalog metadata fields recommendedIndex/prompt/isFree/mayTrainOnYourPrompts/ai_sdk_provider and provider `description` (L96-102,131); (c) re-read the disk cache under the lock before refetching to avoid duplicate network calls (L227-232); (d) ModelsRefresh.notify after refresh (L253). V2: (a) is native — `reasoning_options` is a first-class SourceModel field consumed at models-dev.ts:192/289. (b) is relocated: Kilo model metadata lives in the owned Gateway schema (schema/src/kilocode/models.ts:8-17 recommendedIndex, mayTrainOnYourPrompts, ...) consumed by packages/kilo-gateway, so the shared models.dev schema no longer needs those fields. Missing: (c) the under-lock cache re-check (v2 models-dev.ts has no rechecked/loadFromDisk path) and (d) the Kilo refresh notification hook (no ModelsRefresh in v2); these are minor robustness/notification invariants without a v2 consumer. Evidence: L11: import * as ModelsRefresh from "./kilocode/models-refresh" // kilocode_change / L48: // kilocode_change start - models.dev reasoning_options (snatched from upstream | +| `packages/core/src/plugin/internal.ts`
    112 | shared-upstream patch marker | Package behavior indicated by path | partial | packages/kilo-cli/src/skill-policy.ts (adds `kilo-config`, KILO_CONFIG_ID L10, transform L40-46); packages/core/src/plugin/skill.ts:28-36 still registers builtin `opencode` | V1 marker skips registering upstream's redundant `opencode` skill because Kilo's registry supplies `kilo-config`. V2 preserves the Kilo half: the owned skill-policy plugin registers/updates the `kilo-config` builtin skill. Missing: the native `opencode.skill` plugin still adds the builtin `opencode` skill (skill.ts:28-36) and skill-policy.ts only removes project skills escaping the project realpath (L47-55), so agents see both `opencode` and `kilo-config` skills. Evidence: L112: // kilocode_change - Kilo's CLI registry supplies `kilo-config`; do not register the redundant opencode skill. | +| `packages/core/src/plugin/provider/kilo.ts`
    1,3,6,14,16,26,28,33,38,45 | shared-upstream patch marker | Provider/model routing or UI | native equivalent | `packages/kilo-gateway/src/plugin.ts`, `models.ts`, and Kilo routed-model host plugin | V1 shared provider hook is superseded by location/account-scoped Gateway catalog transforms, request isolation, routed protocols, and credential-event refresh; current Core Kilo plugin retains only upstream-compatible header behavior. Evidence: L1: import { createKilo, KILO_OPENROUTER_BASE } from "@kilocode/kilo-gateway" // kilocode_change / L3: import { ProviderV2 } from "../../provider" // kilocode_change | +| `packages/core/src/plugin/provider/llmgateway.ts`
    4,17,21,24 | shared-upstream patch marker | Provider/model routing or UI | needs port | packages/kilo-gateway or a Kilo-owned catalog transform overriding attribution headers for llmgateway/nvidia/openrouter/vercel/zenmux | V1 sets Kilo attribution headers (HTTP-Referer https://kilo.ai/, X-Title 'Kilo Code', X-Source 'kilo') and guards on the `llmgateway` provider ID. V2 llmgateway.ts:21-23 sends https://opencode.ai/ / 'opencode' / 'opencode' and no Kilo-owned transform rewrites them (grep for kilo.ai/X-Title in kilo-gateway and kilo-cli returns none; the Gateway plugin only manages the `kilo` provider's baseURL/organization header, plugin.ts:352-366). Kilo attribution is absent at the executing consumer. Evidence: L4: import { ProviderV2 } from "../../provider" // kilocode_change / L17: if (item.provider.id !== ProviderV2.ID.make("llmgateway")) continue // kilocode_change | +| `packages/core/src/plugin/provider/nvidia.ts`
    3,14,16,17,20 | shared-upstream patch marker | Provider/model routing or UI | needs port | same owned attribution transform as llmgateway.ts | V1 sends Kilo attribution (HTTP-Referer kilo.ai, X-Title 'Kilo Code') and defaults X-BILLING-INVOKE-ORIGIN to 'KiloCode'. V2 nvidia.ts:16-18 sends opencode.ai / 'opencode' / 'OpenCode'. No Kilo consumer overrides these headers, so NVIDIA billing attribution reports OpenCode for Kilo users. Evidence: L3: import { ProviderV2 } from "../../provider" // kilocode_change / L14: if (item.provider.id !== ProviderV2.ID.make("nvidia")) continue // kilocode_change | +| `packages/core/src/plugin/provider/openai.ts`
    9,58,63,70,78,84,200,279 | shared-upstream patch marker | Provider/model routing or UI | partial | packages/core/src/plugin/provider/openai.ts:75-90 (native state check, callback page); missing Kilo-branded originator/User-Agent | V1 markers: reject callback requests whose `state` mismatches without terminating the active attempt (L58-63), Kilo-branded OAuth callback pages (L70,78,84), `kilo/` User-Agent (L200), and `originator: "kilo"` in the ChatGPT authorize URL (L279). V2 natively validates state (openai.ts:79-84 'Invalid OAuth state') and returns 404 for unrelated paths (L67), using the upstream OauthCallbackPage. Missing: Kilo-branded callback page, `kilo/` User-Agent header, and `originator: kilo` — the ChatGPT flow identifies as opencode. Evidence: L9: import { KiloOauthCallbackPage } from "../../kilocode/oauth/page" // kilocode_change / L58: // kilocode_change start - unrelated localhost requests must not terminate the active OAuth attempt | +| `packages/core/src/plugin/provider/opencode.ts`
    5,44 | shared-upstream patch marker | Provider/model routing or UI | needs port | packages/kilo-gateway/src/plugin.ts (Zen suppression, L136-147,384) plus a Kilo decision on removing the OpenCode Console auth methods from the native opencode plugin | V1 replaced the whole plugin with only the free-tier catalog gate: no OpenCode Console OAuth device flow (client id opencode-cli), no 'OpenCode Console account'/'service account' methods, no console-driven remote config; paid zen models disabled unless a key is present and the provider marked `public` otherwise (L5-44, with an explicit 'do not restore without product decision'). V2 opencode.ts (343 lines) is upstream's full plugin: clientID 'opencode-cli' (L14), OAuth method registration (L34-110), `public` apiKey and paid-model disabling (L194-201). The Kilo Gateway plugin suppresses the uncredentialed Zen catalog (plugin.ts:384 suppressUncredentialedZen) but does not remove the competitor sign-in methods, so the v1 product invariant (no OpenCode Console account system offered) is not met. Evidence: L5: // kilocode_change start - Kilo keeps only the free-tier catalog gate from upstream's opencode plugin. / L44: // kilocode_change end | +| `packages/core/src/plugin/provider/openrouter.ts`
    4,14,16,17 | shared-upstream patch marker | Provider/model routing or UI | needs port | same owned attribution transform as llmgateway.ts | V1 sends HTTP-Referer https://kilo.ai/ and X-Title 'Kilo Code' for the `openrouter` provider. V2 openrouter.ts:14 sends https://opencode.ai/ and 'opencode'; no Kilo override exists. Evidence: L4: import { ProviderV2 } from "../../provider" // kilocode_change / L14: if (item.provider.id !== ProviderV2.ID.openrouter) continue // kilocode_change | +| `packages/core/src/plugin/provider/vercel.ts`
    3,13,15,16 | shared-upstream patch marker | Provider/model routing or UI | needs port | same owned attribution transform as llmgateway.ts | V1 sets lowercase http-referer https://kilo.ai/ and x-title 'Kilo Code' for the `vercel` provider. V2 vercel.ts:13 sends opencode.ai/'opencode'; no Kilo override exists. Evidence: L3: import { ProviderV2 } from "../../provider" // kilocode_change / L13: if (item.provider.id !== ProviderV2.ID.make("vercel")) continue // kilocode_change | +| `packages/core/src/plugin/provider/zenmux.ts`
    3,14,16,17 | shared-upstream patch marker | Provider/model routing or UI | needs port | same owned attribution transform as llmgateway.ts | V1 defaults HTTP-Referer to https://kilo.ai/ and X-Title to 'Kilo Code' (with `??=` so user headers win). V2 zenmux.ts:15-16 sends opencode.ai/'opencode'; no Kilo override exists. Evidence: L3: import { ProviderV2 } from "../../provider" // kilocode_change / L14: if (item.provider.id !== ProviderV2.ID.make("zenmux")) continue // kilocode_change | +| `packages/core/src/project.ts`
    66,125 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/core/src/project.ts:237 (cached id file name) — Kilo-owned project id file name or accept `opencode` file | V1 stores and reads the cached project ID in `/kilo` (L66,125) so Kilo and OpenCode installs do not share/clobber each other's project identity file. V2 project.ts:237 reads `/opencode` and there is no Kilo override (the kilo2 host only replaces Global/Config/InstructionDiscovery/Mcp/Pty nodes, host.ts:27-47). Because the id file lives inside the location's git store, a v2 Kilo install writes/reads the OpenCode file name — a behavior difference (shared identity with an OpenCode install) rather than a crash; port only if separate identity is still required. Evidence: L66: return yield* fs.readFileString(path.join(dir, "kilo")).pipe( // kilocode_change / L125: yield* fs.writeFileString(path.join(input.store, "kilo"), input.id).pipe(Effect.ignore) // kilocode_change | +| `packages/core/src/provider.ts`
    6 | shared-upstream patch marker | Provider/model routing or UI | native equivalent | packages/kilo-gateway/src/plugin.ts:339-364 (string `kilo` provider ID) and packages/core/src/plugin/provider/kilo.ts | V1 attached a well-known `Provider.ID.kilo` constant without forking the schema. V2 provider.ts has no such constant, but every Kilo consumer addresses the provider by the literal `"kilo"` ID through the owned Gateway plugin (editor.provider.get/update("kilo")), and upstream itself ships a `kilo` provider plugin keyed on the Gateway baseURL. The routing identity is present; only the convenience constant is gone. Evidence: L6: // kilocode_change - preserve Kilo's well-known routing ID without forking the shared schema | +| `packages/core/src/pty.ts`
    11,14,15,40,43,54,59,71,129,137,141,147,157,168,186,197,203,205,209,217,227,245,269,294,296,309,318 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/core/src/pty.ts create/teardown/attach or a Kilo-owned Pty.node replacement (packages/kilo-cli/src/sandbox-pty.ts shows the replacement pattern) | V1 markers: terminate the full PTY process tree on teardown and await it during service teardown (L129-147), uninterruptible remove+deleted event (L157-168), resolve Kilo self-commands to the real binary/cwd (L186-197), KILO_PTY_ID env for nested processes (L203), tombstone KILO_SERVER_PASSWORD/USERNAME so terminals never inherit local server credentials (L205-209), spawn with initial cols/rows (L217-227), `stopping` guard against double exit (L40,245,269), `sessionID` association on update (L54,294-296), and `allowExited` attach with replay (L71,309,318). V2 pty.ts is upstream: teardown calls `process.kill()` only (L117-126), no uninterruptible removal, no self-command resolution, env sets OPENCODE_TERMINAL only (L163-169), spawn without initial size (L176), no sessionID/allowExited. The only Kilo Pty seam in v2 is sandbox-pty.ts (refusal). Credential-tombstone and tree-termination invariants are absent. Evidence: L11: import { SessionSchema } from "./session/schema" // kilocode_change / L14: import { KiloPtySelfCommand } from "./kilocode/pty-self-command" // kilocode_change | +| `packages/core/src/pty/driver.ts`
    1 | shared-upstream patch marker | Package behavior indicated by path | not needed | none — v2 core imports `#pty` directly (packages/core/src/pty.ts:4,17) | V1 re-exported the conditional PTY driver for Kilo's legacy interactive terminal in packages/opencode. V2 has no legacy interactive terminal consumer; the kilo-cli TUI plugin uses the server PTY API and native-pty.ts only resolves the bundled binary path. No consumer for a driver re-export. Evidence: L1: // kilocode_change - expose the conditional PTY driver to Kilo's legacy interactive terminal | +| `packages/core/src/reference.ts`
    38,116,124 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | packages/core/src/config/plugin/reference.ts (ConfigEntryObserver over Config.Service entries) fed by packages/kilo-cli/src/project-config.ts | V1 added `Reference.replace(sources)` so Kilo's effective-config reconciliation (v1 packages/opencode/src/kilocode/reference.ts:190) could rewrite references without a request-scoped transform slot. V2 Kilo config enters through the owned ProjectConfig extension of `Config.Service.entries`, and the native reference plugin observes those entries and reloads via `ctx.reference.reload()`/transform (reference.ts:22-24), so kilo.json references are materialized without a bespoke replace API. Evidence: L38: readonly replace: (sources: readonly (readonly [string, Source])[]) => Effect.Effect // kilocode_change / L116: // kilocode_change start - reconcile Kilo's effective config without a request-scoped transform slot. | +| `packages/core/src/ripgrep.ts`
    7,8,9,26,42,71,75,82,87,88,91,97,120,121,126,131,134,136,142,143,148,158,162,163,179,185,195,213,223,260,266,286,294,301,326 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/core/src/ripgrep.ts run/glob/grep (SearchResult truncation state, context rows, spawn-bound validation, bounded stop) or a Kilo-owned Ripgrep.node replacement in packages/kilo-cli | V1 markers: return `SearchResult{items,truncated,partial}` from glob/grep so model-facing tools see truncation (L87-97,213-223,301-326), retain ripgrep `context` records and decorate matches via kilocode/ripgrep-grep options/flags/select (L26,42,74,260-266,286-294), stop bounded grep at the overflow match with a 1s force-kill and direct-exit settlement (L120,131-134,142-163), bind approved search targets at spawn via SpawnValidation (L71,82,121,126-136,195), and surface the underlying failure reason (L179-185). V2 ripgrep.ts (273 lines, unmarked) returns plain arrays with `rows.length > input.limit` slicing (L140-141), has no context-record support, no stop/forceKill, no validate hook and no Kilo grep option module; no owned Ripgrep replacement exists in packages/kilo-cli. Paired with tool/glob.ts and tool/grep.ts rows. Evidence: L7: import * as KiloGrep from "./kilocode/ripgrep-grep" // kilocode_change / L8: import * as SpawnExit from "./kilocode/spawn-exit" // kilocode_change | +| `packages/core/src/ripgrep/binary.ts`
    94 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/core/src/ripgrep/binary.ts:98 (skip system `rg` on win32) | V1 skipped the system `rg` lookup on Windows because Git for Windows exposes an incompatible MSYS rg.exe, always using the downloaded binary. V2 binary.ts:98 resolves `rg.exe` from PATH/global.bin on win32 and prefers it when present, so the incompatible MSYS binary can be selected again. Small, Windows-only regression. Evidence: L94: // kilocode_change - Git for Windows may expose an incompatible MSYS rg.exe | +| `packages/core/src/schema.ts`
    14,17 | shared-upstream patch marker | Package behavior indicated by path | not needed | none — no v2 consumer of `optionalOmitUndefined`/`withStatics` | V1 exported compatibility aliases for Kilo-owned schemas written against older helper names. V2 core schema.ts lacks them and no Kilo v2 package references either alias (repo-wide grep of packages/*/src is empty); Kilo v2 schemas use the native `optional`/`statics` helpers directly. Evidence: L14: // kilocode_change start - compatibility aliases for Kilo-owned schemas / L17: // kilocode_change end | +| `packages/core/src/session.ts`
    6,35,40,137,142,196,199,319,335,359 | shared-upstream patch marker | Session lifecycle/history/UI | not needed | none — kilo2 uses an isolated database; imported v1 rows are rebuilt by packages/core/src/database/migration/20260804233008_loose_psylocke.ts (kilocode/baseline/v1-session-schema-audit.md:65,122) | V1 markers exist only for released-data compatibility in a shared store: normalize legacy tool content on paginated reads (L35,197-203), filter `isNotNull(seq)` because Kilo kept `session_message.seq` nullable (L319,335), and widen the durable event stream/history to the released `SessionDurable` key manifest (L40,137,142,196,359). V2 session.ts is unmarked, `seq` is NOT NULL, and the explicit `import-v1` path copies the v1 store and rebuilds `session_message` from `message`/`part` (dropping nullable-seq rows) rather than reading released Kilo rows in place. No v2 consumer needs the compatibility filters. Evidence: L6: import { and, asc, desc, eq, gt, isNotNull, like, lt, or, type SQL } from "drizzle-orm" // kilocode_change / L35: import { normalize } from "./kilocode/session-message" // kilocode_change | +| `packages/core/src/session/compaction.ts`
    227 | shared-upstream patch marker | Session lifecycle/history/UI | not needed | none (released-reader compatibility; see packages/core/src/session.ts) | V1 duplicated `recent` into a compatibility `include` field so released v1 readers could parse newer compaction rows in the shared database. V2 compaction.ts writes only the native `recent` field (L260) and no released reader shares kilo2's database. Evidence: L227: include: selected.recent, // kilocode_change - released v1 readers recognize this compatibility field | +| `packages/core/src/session/context-epoch.ts`
    13,19,30,40,50,58,90,94,133,135,152 | shared-upstream patch marker | Session lifecycle/history/UI | not needed | none — v2 removed context epochs (packages/core/src/database/migration/20260622142730_simplify_session_context_epoch.ts, 20260622170816_reset_v2_session_state.ts); Location guard lives in packages/core/src/session/context.ts:123 | V1 markers thread the expected `Location.Ref` into epoch initialization and re-check Session placement inside the insert so a concurrent move cannot write an epoch for the source Location (L13-152). V2 has no context-epoch module or table: instruction state is persisted durably per the V2 Session Core rules and `SessionContext.select` interrupts when the Session's stored Location differs from the running Location (context.ts:123). The v1 race (epoch row written for the wrong Location) has no v2 write to protect; a v2-specific move/instruction-state race was not separately proven here. Evidence: L13: import { Location } from "../location" // kilocode_change / L19: class LocationMismatch extends Error {} // kilocode_change - a concurrent Session move must abort the epoch | +| `packages/core/src/session/history.ts`
    1,8,22,45,65,109 | shared-upstream patch marker | Session lifecycle/history/UI | not needed | none (released-data compatibility; see packages/core/src/session.ts) | V1 markers add `isNotNull(seq)` filters, legacy tool-content normalization at the row boundary, and a die on legacy rows (L1,8,22,45,65-67,109). V2 history.ts is unmarked with NOT NULL `seq`; nullable/legacy rows never exist in kilo2's isolated database. Evidence: L1: import { and, asc, desc, eq, gt, gte, isNotNull, ne, or } from "drizzle-orm" // kilocode_change / L8: import { normalize } from "../kilocode/session-message" // kilocode_change | +| `packages/core/src/session/message-updater.ts`
    378,386 | shared-upstream patch marker | Session lifecycle/history/UI | not needed | none (released-data compatibility) | V1 tolerated released compaction events lacking `messageID`/`reason`/`recent` (L378,386). V2 message-updater.ts consumes only native compaction events; released v1-line durable events are not replayed into kilo2 (import rebuilds projections from message/part, kilocode/baseline/v1-session-schema-audit.md:83-84). Evidence: L378: if (event.data.messageID === undefined || event.data.reason === undefined) return Effect.void // kilocode_change / L386: recent: event.data.recent ?? "", // kilocode_change - current v1 writes include recent; released rows omit it | +| `packages/core/src/session/projector.ts`
    3,13,19,24,27,62,120,146,152,173,188,196,397,449 | shared-upstream patch marker | Session lifecycle/history/UI | not needed | none — released-data compatibility is obsolete in kilo2's isolated store; sharing is a Gateway RPC (packages/kilo-gateway/src/session.ts:132-177), not a projected session column | V1 markers: released-shape encode/normalize of stored messages (L13,24-27,120), `isNotNull(seq)` filters (L146-152,173,188-196), nullable-seq revert boundary guard (L449-451), replay of released two-step prompt promotions via PromptCompat (L19,397), and `share_url: info.share?.url ?? null` so full session updates clear removed shares (L62). The compatibility markers are not needed (isolated database, NOT NULL seq, projections rebuilt from message/part on import). The share-clearing marker also has no v2 consumer: `share_url` survives only as an unused column (session/sql.ts:39; no writer in packages/core/src) and Kilo sharing is served by the owned Gateway session RPC rather than a session-row projection. Evidence: L3: import { and, desc, eq, isNotNull, gt, or, sql } from "drizzle-orm" // kilocode_change / L13: import * as StoredMessage from "../kilocode/session-message" // kilocode_change | +| `packages/core/src/session/revert.ts`
    11,36,56 | shared-upstream patch marker | Session lifecycle/history/UI | not needed | none (released-data compatibility) | V1 markers guard nullable `seq` boundaries and normalize legacy tool content when collecting snapshot files (L11,36-37,54-56). V2 revert.ts is unmarked over NOT NULL `seq` rows in kilo2's isolated store. Evidence: L11: import * as StoredMessage from "../kilocode/session-message" // kilocode_change / L36: // kilocode_change - Kilo keeps session_message.seq nullable for released-data compatibility | +| `packages/core/src/session/runner/llm.ts`
    178,193 | shared-upstream patch marker | Session lifecycle/history/UI | not needed | none — see packages/core/src/session/context-epoch.ts | V1 passed the expected Location into SessionContextEpoch.initialize/prepare (L178,193-194). V2 runner/llm.ts has no context epoch calls; Location scoping is enforced by `SessionContext.select` (context.ts:123). Evidence: L178: const initialized = yield* SessionContextEpoch.initialize(db, loadSystemContext(agent), session.id, location) // kilocode_change / L193: // kilocode_change - pass the expected Location so a concurrent move aborts the epoch | +| `packages/core/src/session/runner/model.ts`
    141,150 | shared-upstream patch marker | Session lifecycle/history/UI | native equivalent | packages/kilo-gateway/src/plugin.ts:40,190-193,352-357 (X-KILOCODE-ORGANIZATIONID header from the stored organization selection) | V1 injected the migrated OAuth account id into the request body as `kilocodeOrganizationId` for Kilo Gateway organization routing (L141-150). V2 routes organization identity through the owned Gateway plugin instead: the selected organizationID is read from credential metadata/stored selection and applied as the `X-KILOCODE-ORGANIZATIONID` header on provider, model and variant headers (plugin.ts:352-394). The routing invariant is preserved through a Kilo-owned seam; the body-field mechanism is intentionally different. Evidence: L141: // kilocode_change start - Kilo Gateway consumes the migrated OAuth account as its organization route. / L150: // kilocode_change end | +| `packages/core/src/session/sql.ts`
    16,49,100,132 | shared-upstream patch marker | Session lifecycle/history/UI | partial | packages/core/src/session/sql.ts (part index) paired with the unported historical-recall tool; revert workspace status and nullable seq not needed | V1 markers: nullable `seq` for released clients (L132) — not needed in kilo2's isolated store; `revert.workspace` restore status persisted on the session revert record (L49-51) — v2 Session.Revert schema has no workspace field and no Kilo consumer writes one (grep empty), tied to the v1 workspace-restore feature; `RecallPartIndex` on `part` (L16,100) — a search index backing v1's historical session recall tool, which the canonical 'Plan and historical-recall tool slice' already records as needing a port. Preserved: nothing Kilo-specific is required today; missing: the recall index (only meaningful once the recall tool is ported) and revert workspace status. Evidence: L16: import { RecallPartIndex } from "../kilocode/session/recall-part-index" // kilocode_change / L49: // kilocode_change - Kilo also persists a workspace restore status on the revert record | +| `packages/core/src/session/store.ts`
    3,13,50,56 | shared-upstream patch marker | Session lifecycle/history/UI | not needed | none (released-data compatibility) | V1 markers filter `isNotNull(seq)` and normalize legacy tool content when loading a message by id (L3,13,50,56). V2 store.ts is unmarked; kilo2 never reads released Kilo rows in place. Evidence: L3: import { and, eq, isNotNull } from "drizzle-orm" // kilocode_change / L13: import { normalize } from "../kilocode/session-message" // kilocode_change | +| `packages/core/src/shell.ts`
    11,180,194,199 | shared-upstream patch marker | Package behavior indicated by path | partial | packages/core/src/shell/select.ts:163-167 (PowerShell args) and packages/core/src/shell.ts invocation; missing UTF-8 prologue and `kilo` $0 | V1 markers: pass `kilo` as `$0` for the eval-wrapped bash/zsh command (L180,194) and route PowerShell through kilocode/powershell.ts which injects a UTF-8 console-encoding setup after any `param(...)` prologue (L11,199). V2 ShellSelect.args natively uses `-NoLogo -NoProfile -NonInteractive -Command` for PowerShell (select.ts:166) but does not inject the UTF-8 InputEncoding/OutputEncoding prologue or preserve `param()` ordering, and the eval wrapper `$0` is upstream's. Preserved: PowerShell detection and non-interactive invocation; missing: UTF-8 encoding guarantee for PowerShell tool output and Kilo-branded `$0` (cosmetic). Evidence: L11: import { PowerShell } from "./kilocode/powershell" // kilocode_change / L180: "kilo", // kilocode_change | +| `packages/core/src/state.ts`
    59,68,136,142 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | packages/kilo-cli/src/project-config.ts (Config.Service entries extension) replacing out-of-fold mutation | V1 added `State.mutate` so Kilo could reconcile config-derived state (references) outside the replayable transform fold (L59-68,136-142); its only consumer was v1 Reference.replace. V2 state.ts has no `mutate`, and Kilo's effective config now flows through the native Config.Service entries (ProjectConfig) which every config plugin observes and reloads from, so no out-of-fold mutation is required. See packages/core/src/reference.ts row. Evidence: L59: // kilocode_change start - Kilo reconciles config-derived state outside the transform fold / L68: // kilocode_change end | +| `packages/core/src/tool/apply-patch.ts`
    13,38,51,91,92 | shared-upstream patch marker | Tool behavior/UI | needs port | packages/core/src/tool/plugin/patch.ts (`toStructuredOutput`/structured output compaction against the tool-output store limit) | V1 adds a `compact` step that drops per-file diff previews only when the encoded output exceeds ToolOutputStore.MAX_BYTES, keeping durable/SSE tool records bounded (L13,38-51,91-92). V2 tool/plugin/patch.ts (and file-diff.ts) has no compact/toStructuredOutput/MAX_BYTES logic (grep empty), so oversized patch outputs are persisted/streamed unbounded. Paired with tool/edit.ts. Evidence: L13: import { ToolOutputStore } from "../tool-output-store" // kilocode_change / L38: // kilocode_change start - keep durable/SSE tool records bounded without hiding normal-sized diff previews | +| `packages/core/src/tool/edit.ts`
    8,20,45,58,124,125 | shared-upstream patch marker | Tool behavior/UI | needs port | packages/core/src/tool/plugin/edit.ts (`toStructuredOutput` compaction) — the named-reference doc marker is not needed | V1 markers: the same bounded structured-output `compact` as apply-patch (L20,45-58,124-125) and a doc-comment marker preserving named-reference mutation-constraint documentation (L8). V2 tool/plugin/edit.ts has no compaction step. The named-reference constraint documentation is moot because v2 tools do not accept `reference` inputs (see glob/grep rows). Evidence: L8: // kilocode_change - preserve named-reference mutation constraint documentation / L20: import { ToolOutputStore } from "../tool-output-store" // kilocode_change | +| `packages/core/src/tool/glob.ts`
    8,11,13,28,33,38,54,60,63,76,81,93,100,113,116,118,121,124,139,153 | shared-upstream patch marker | Tool behavior/UI | partial | packages/core/src/tool/plugin/glob.ts:95-134 (native containment, truncation flag, timeout); missing `reference` input, `partial` status, capped limit schema, spawn-bound validation | V1 markers: optional `reference` input to search a named project reference instead of the active Location (L28-31,93,100-105), `SearchLimit`-capped limit schema (L32), typed `Result{items,truncated,partial}` with model text noting truncation/partial reads (L38-54,76-81,124-139), canonical SearchTarget inspection of base and target with escape checks (L100-113), and spawn-bound `validate` (L118-121). V2 glob plugin natively resolves the target through LocationMutation, fails on escaped/non-directory paths (L95-98), requests `limit+1` to compute `truncated` and reports it in model content and metadata (L99-134), and adds a 30s timeout. Missing: named-reference search, `partial` (unreadable files) status, the 100-cap on model-supplied limits (v2 accepts any PositiveInt), and TOCTOU validation of the search root at spawn. Evidence: L8: // kilocode_change start / L11: // kilocode_change end | +| `packages/core/src/tool/grep.ts`
    9,12,14,31,35,39,44,69,76,79,92,100,112,120,137,140,142,144,147,150,171,183 | shared-upstream patch marker | Tool behavior/UI | partial | packages/core/src/tool/plugin/grep.ts:95-162 (native containment/truncation); missing `reference`, single-file targets, managed-data retention, `partial`, capped limit, spawn validation | V1 markers mirror glob.ts plus: grep may target a single file (`file:` basename with cwd = dirname, L136-142), absolute paths under Kilo's global data directory are retained via SearchTarget.managed (L127-135), and `Global.Service` is a dependency (L76,183). V2 grep plugin natively handles containment via LocationMutation, `limit+1` truncation reporting (L116-162) and timeout. Missing: named-reference search, file-target grep, managed-data path retention, `partial` status, 100-cap limit schema, and spawn-bound validation. Evidence: L9: // kilocode_change start / L12: // kilocode_change end | +| `packages/core/src/tool/read-filesystem.ts`
    93,110,177,181,185,199,203,346 | shared-upstream patch marker | Tool behavior/UI | needs port | packages/core/src/tool/read-filesystem.ts inspect/read/list (identity-bound Target) consumed by packages/core/src/tool/plugin/read.ts | V1 binds approved reads to filesystem identity: `inspect` returns a Target carrying dev/ino (L93-110,177-181), `read` opens the approved path and verifies the descriptor identity before reading (L185-203), and `list` re-verifies directory identity (L346-348). V2 read-filesystem.ts (389 lines, unmarked) has no dev/ino tracking or verify step (grep for ino/verify/open( empty) and tool/plugin/read.ts authorizes by resolved path only (read.ts:60-80). The permission-approved-target TOCTOU hardening is absent. Evidence: L93: // kilocode_change start - bind approved reads to filesystem identity / L110: // kilocode_change end | +| `packages/core/src/tool/read.ts`
    8,34,71,86 | shared-upstream patch marker | Tool behavior/UI | needs port | packages/core/src/tool/plugin/read.ts (thread the approved Target from read-filesystem inspect through permission.assert to reader.read) | V1 markers inspect the resolved target before permission and pass the approved identity into the read (L8,34,71-86). V2 tool/plugin/read.ts resolves via LocationMutation, asserts permission on the resource string, then calls `reader.read(AbsolutePath.make(target.absolute), ...)` by path (read.ts:73-84); no identity carried. Paired with read-filesystem.ts. Evidence: L8: import { LocationMutation } from "../location-mutation" // kilocode_change / L34: const mutation = yield* LocationMutation.Service // kilocode_change | +| `packages/core/src/tool/write.ts`
    8 | shared-upstream patch marker | Tool behavior/UI | not needed | none — v2 tools have no `reference` input (packages/core/src/tool/plugin/write.ts) | V1's only marker preserves a doc comment stating that named references are read-oriented and not accepted by mutation tools. V2 write.ts is upstream and no v2 tool exposes named-reference inputs, so the constraint documentation has nothing to document until reference search is ported (see glob/grep rows). Evidence: L8: // kilocode_change - preserve named-reference mutation constraint documentation | +| `packages/core/src/util/encode.ts`
    2,3 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/util/src/encode.ts:1-4 (`base64Encode` path normalization) | V1 normalizes backslashes to `/` before encoding so Windows paths produce the same base64url IDs as POSIX paths (L2). V2 util encode.ts encodes UTF-8 bytes (the second v1 marker is native) but does not normalize Windows separators, so IDs derived from paths differ per platform. Evidence: L2: const normalized = value.replace(/\\/g, "/") // kilocode_change - normalize Windows paths / L3: const bytes = new TextEncoder().encode(normalized) // kilocode_change - encode as UTF-8 bytes | +| `packages/core/src/util/log.ts`
    4,9,10,68,73,124 | shared-upstream patch marker | Package behavior indicated by path | partial | packages/util/src/observability/logging.ts:49-60 (append-mode file logger per channel); no rotation or dev.log truncation | V1 markers replace the log sink with a rotating-file-stream (50M, 10 files, `.log-history`) that truncates `dev.log` once per Kilo run (KILO_RUN_ID) and guards ENOENT on rotation (L4-10,68,73-124). V2 util logging.ts writes `opencode-.log` via Effect `Logger.toFile` in append mode with no rotation or size bound; the Kilo host only toggles stderr logging (interactive-server.ts:315) and points Global at the kilo2 log directory (paths.ts:19). Preserved: per-channel log file under Kilo-owned roots. Missing: size-based rotation/retention and per-run dev-log truncation. Evidence: L4: import { existsSync, writeFileSync } from "fs" // kilocode_change / L9: import { createStream } from "rotating-file-stream" // kilocode_change | +| `packages/core/src/v1/config/agent.ts`
    12,66,70,71,75,76,77,78,83,87,89,96,104,108,111,125,126,135,155,158 | shared-upstream patch marker | Configuration/settings | partial | Retained `packages/core/src/v1/config/agent.ts` consumed by `packages/kilo-cli/src/import-v1-config.ts`; target `packages/schema/src/config/agent.ts` | Standard agent migration is live, but pinned skills/MCP/VS Code requirement fields are absent from retained schema/target and remain unmapped. Evidence: L12: // kilocode_change start - agent skill/MCP/VS Code extension requirements schema / L66: // kilocode_change end | +| `packages/core/src/v1/config/command.ts`
    6 | shared-upstream patch marker | Configuration/settings | needs port | Retained schema + `packages/core/src/config/plugin/command.ts`; importer consumer | Pinned optional-template workflow override is not retained (current template required), so model/variant-only global workflow overrides cannot import exactly. Evidence: L6: template: Schema.optional(Schema.String), // kilocode_change - allow global workflow model/variant overrides | +| `packages/core/src/v1/config/config.ts`
    19,23,32,35,41,43,45,55,64,67,99,100,181,197,201,213,214,216,238,245,264,269,289,307,314,335 | shared-upstream patch marker | Configuration/settings | partial | Retained v1 decoder → `packages/kilo-cli/src/import-v1-config.ts` → schema/Core config plugins | Config mapping report verifies native-carried and Kilo-owned mappings, while explicitly enumerating refused Kilo keys (sandbox, indexing, remote_control, display, commit-message, compaction, telemetry, image/notebook/task-model and others); refusal is not parity. Evidence: L19: // kilocode_change start / L23: // kilocode_change end | +| `packages/core/src/v1/config/mcp.ts`
    3,4,6,9,28,60 | shared-upstream patch marker | Configuration/settings | partial | Retained MCP decoder/migrator → v2 `packages/schema/src/config/mcp.ts` and Core MCP plugin | Local/remote MCP configuration migrates, but pinned `env` alias normalization is absent from retained decoder; only canonical environment is accepted. Evidence: L3: import { Schema, SchemaGetter } from "effect" // kilocode_change / L4: import { zod } from "@opencode-ai/core/effect-zod" // kilocode_change | +| `packages/core/src/v1/config/migrate.ts`
    39,40,88,111,173,197,252 | shared-upstream patch marker | Configuration/settings | native equivalent | Current retained migrator + `packages/kilo-cli/src/import-v1-config.ts:819-946` | Pinned null-delete handling is replaced by boundary normalizeNulls before decoding plus current migration; importer directly invokes ConfigMigrateV1.migrate and compares reduced mappings. Evidence: L39: model: info.model ?? undefined, // kilocode_change - v1 null delete sentinel is not valid in v2 / L40: default_agent: info.default_agent ?? undefined, // kilocode_change | +| `packages/core/src/v1/config/permission.ts`
    5,28,36,37,41,49 | shared-upstream patch marker | Configuration/settings | partial | Retained permission decoder/migrator → v2 permissions and settings collections | Null sentinels are normalized at importer boundary, but pinned markdown_source, agent_manager, and notebook permission keys lack exact v2 import targets. Evidence: L5: export const Action = Schema.NullOr(Schema.Literals(["ask", "allow", "deny"])) // kilocode_change - nullable allows null as a delete sentinel / L28: markdown_source: Schema.optional(Rule), // kilocode_change - explicitly authorize external agent/command sources | +| `packages/core/src/v1/config/provider.ts`
    4,13,14,15,18,71,126 | shared-upstream patch marker | Configuration/settings | partial | Retained provider migration + `packages/kilo-gateway` catalog/routing and v2 provider schema | Standard provider/model data migrates and Gateway supplies model metadata, but pinned prompt enum and Kilo-specific provider schema extensions are not retained field-for-field. Evidence: L4: import { PROMPTS, AI_SDK_PROVIDERS } from "@kilocode/kilo-gateway" // kilocode_change / L13: prompt: Schema.optional(Schema.Literals(PROMPTS)), // kilocode_change | +| `packages/core/src/v1/config/server.ts`
    13 | shared-upstream patch marker | Configuration/settings | needs port | No profile-config target; server launch remains host/CLI controlled | Pinned custom mDNS domain is in a top-level server config that the config-mapping audit verifies v2 refuses as unsupported; no runtime profile consumer exists. Evidence: L13: description: "Custom domain name for mDNS service (default: kilo.local)", // kilocode_change | +| `packages/core/test/catalog.test.ts`
    30,47,73,105 | shared-upstream patch marker | Package behavior indicated by path | not needed | packages/core/test/catalog.test.ts (upstream; no auth.json import to isolate) | V1 markers only inject a temporary Global data dir because Kilo's Credential layer imported Global.data/auth.json on startup (L30-32,47,73,105). V2 Credential has no startup legacy import (see packages/core/src/credential.ts row), so the isolation shim is obsolete; the v2 suite (445 lines, unmarked) runs against in-memory credentials. Evidence: L30: // kilocode_change - Credential imports Global.data/auth.json on startup, so without this the suite / L47: [Global.node, globalLayer()], // kilocode_change | +| `packages/core/test/config/config.test.ts`
    164,201,251,267,767,854 | shared-upstream patch marker | Configuration/settings | partial | packages/kilo-cli/test/project-config.test.ts, project-config-reader.test.ts, config-refresh.test.ts (owned ProjectConfig coverage); native config.test.ts covers upstream discovery | V1 markers add three Kilo cases: project discovery disabled via KILO_DISABLE_PROJECT_CONFIG keeps only global config (L164-201), legacy config.json still loads (L251-267), and Kilo roots/precedence (.kilocode/.kilo directories, kilo.json vs opencode.json ordering up to the project boundary, L767-854). V2 relocates the Kilo discovery to kilo-cli ProjectConfig and its owned tests cover kilo.json/.kilo/.kilocode boundary walking and refresh; the single v2 marker in config.test.ts (L79) tests the separate refresh hook. Missing: no v2 test asserts the disable path (now the host `projectConfig` option) against mixed global+project documents, legacy config.json loading is intentionally dropped, and the v1 mixed opencode.json+kilo.json precedence matrix has no equivalent because v2 disables native discovery in the Kilo host. Evidence: L164: // kilocode_change start / L201: // kilocode_change end | +| `packages/core/test/credential.test.ts`
    2,4,7,10,14,23,27,35,69,338 | shared-upstream patch marker | Package behavior indicated by path | partial | packages/kilo-cli/test/import-v1-config.test.ts (explicit auth.json import planning/apply); no v2 test for KILO_AUTH_CONTENT or startup reconciliation | V1 markers add a process-local KILO_AUTH_CONTENT isolation test (L69-138) and startup legacy auth.json reconciliation/import tests (L140-338) plus fixture plumbing (L2-35). V2 credential.test.ts (202 lines) is upstream-only. The explicit import replacement is tested in kilo-cli import-v1-config.test.ts (api-key normalization, Kilo OAuth device method, provider OAuth inventory, host-writer requirement). Missing: KILO_AUTH_CONTENT isolation and startup auth.json reconciliation cases, consistent with those behaviors being absent (credential.ts row). Evidence: L2: import { Database as SQLite } from "bun:sqlite" // kilocode_change / L4: import { eq } from "drizzle-orm" // kilocode_change | +| `packages/core/test/event.test.ts`
    25,27,86,92 | shared-upstream patch marker | Package behavior indicated by path | not needed | none — v2 core has no test/event.test.ts; database isolation is native (packages/core/src/database/database.ts:66-79 per-path locks, in-memory default) | V1 markers only swapped in an in-memory `Database.layerFromPath(":memory:")` to isolate concurrent tests from process-wide migrations (L25-27,86,92). V2 Database defaults to `:memory:` per layer build and locks per file path, so no test-side isolation shim is needed; the v1 EventV2 suite itself does not exist in v2. Evidence: L25: // kilocode_change start - keep concurrent tests isolated from process database migrations / L27: // kilocode_change end | +| `packages/core/test/filesystem/search.test.ts`
    26,27,39,40,41 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/core/test/filesystem/search.test.ts or a Kilo-owned ripgrep test once SearchResult is ported | V1 markers assert the `SearchResult{items,truncated}` shape returned by Ripgrep.glob/grep (L26-27,39-41). V2 search.test.ts (247 lines) tests plain arrays because v2 Ripgrep returns arrays (ripgrep.ts row: needs port). The assertions follow the source port. Evidence: L26: expect(result.items.map((item) => item.path)).toEqual([RelativePath.make("src/match.ts")]) // kilocode_change / L27: expect(result.truncated).toBe(false) // kilocode_change | +| `packages/core/test/filesystem/watcher.test.ts`
    19,231,235 | shared-upstream patch marker | Package behavior indicated by path | partial | packages/core/test/filesystem/watcher.test.ts:28 (`describeNative` skips on CI) | V1 markers gate the native watcher suite to non-CI or the darwin KILO_TEST_PROFILE (L19) and accept FSEvents classifying a HEAD overwrite as `add` (L231-235). V2 skips native watcher tests entirely on CI (L28) — a broader gate that subsumes v1's, so the CI invariant is preserved but the darwin CI profile opt-in is gone. The FSEvents add/change tolerance was not located in the v2 suite (690 lines); it matters only if the HEAD-overwrite case exists there. Evidence: L19: Watcher.hasNativeBinding() && (!process.env.CI || process.env.KILO_TEST_PROFILE === "darwin") // kilocode_change / L231: // kilocode_change start - FSEvents may classify this overwrite as an add. | +| `packages/core/test/global.test.ts`
    9 | shared-upstream patch marker | Package behavior indicated by path | not needed | packages/kilo-cli/src/paths.ts layout tests (packages/kilo-cli/test/preview.test.ts) cover Kilo roots | V1 asserted `Global.Path.tmp` ends in `kilo` (L9). V2 has no core/test/global.test.ts; util global stays `opencode` and the Kilo host injects `kilo2/` roots via Global.layerWith, exercised by kilo-cli preview/preflight tests. The v1 assertion targets a branding choice v2 intentionally makes elsewhere. Evidence: L9: expect(Global.Path.tmp).toBe(path.join(os.tmpdir(), "kilo")) // kilocode_change | +| `packages/core/test/integration.test.ts`
    15,36 | shared-upstream patch marker | Package behavior indicated by path | not needed | packages/core/test/integration.test.ts (upstream; no auth.json dual-write to isolate) | V1 markers set KILO_AUTH_CONTENT and a temp Global data dir so Kilo's auth.json dual-write/re-import did not leak between tests (L15-36). V2 Credential neither dual-writes nor re-imports auth.json, so the isolation harness is obsolete; the v2 suite (676 lines) is unmarked. Evidence: L15: // kilocode_change start - Kilo dual-writes stored credentials into Global.data/auth.json and re-imports / L36: // kilocode_change end | +| `packages/core/test/location-filesystem.test.ts`
    70,91 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/core/test/location-filesystem.test.ts (symlink-escape rejection for read/list/glob/grep) | V1 adds a test that in-worktree symlinks to outside paths are rejected for read, list, glob and grep (L70-91). V2 tests lexical escapes (L133) and, notably, allows listing through an external symlink while rejecting file reads (L148-167) — the opposite of v1's list rejection — and does not exercise glob/grep escape. Follows the packages/core/src/filesystem.ts hardening row; a port must reconcile v2's deliberate list-through-symlink behavior. Evidence: L70: // kilocode_change start - canonical containment must reject in-worktree links to outside paths / L91: // kilocode_change end | +| `packages/core/test/plugin/command.test.ts`
    40,41,42 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/core/src/plugin/command/initialize.txt (Kilo-branded init template) and its test in packages/core/test/plugin/command.test.ts | V1 asserts the built-in `init` command template mentions 'future Kilo sessions' and `kilo.json` and never 'OpenCode' (L40-42). V2 initialize.txt says 'future OpenCode sessions' and `opencode.json` (L3,15,59) and command.test.ts asserts the upstream template; no Kilo-owned override of the init command exists in packages/kilo-cli. Branding of the init workflow is absent. Evidence: L40: expect((yield* command.get("init"))?.template).toContain("future Kilo sessions") // kilocode_change / L41: expect((yield* command.get("init"))?.template).toContain("`kilo.json`") // kilocode_change | +| `packages/core/test/plugin/fixture.ts`
    25,59 | shared-upstream patch marker | Package behavior indicated by path | not needed | packages/core/test/plugin/fixture.ts (upstream) | V1 marker injects a temp Global data dir to avoid Credential's startup auth.json import (L25-27,59). Obsolete in v2 (no startup import). Evidence: L25: // kilocode_change - Credential imports Global.data/auth.json on startup, so without this the suite / L59: [Global.node, globalLayer], // kilocode_change | +| `packages/core/test/plugin/models-dev.test.ts`
    27,34 | shared-upstream patch marker | Provider/model routing or UI | not needed | packages/core/test/plugin/models-dev.test.ts (upstream) | V1 marker injects a temp Global data dir for the same auth.json import reason (L27-34). Obsolete in v2. Evidence: L27: // kilocode_change - Catalog pulls Credential, which imports Global.data/auth.json on startup, so / L34: [Global.node, globalLayer], // kilocode_change | +| `packages/core/test/plugin/provider-kilo.test.ts`
    3,5,22,43,68,90,119,125,206 | shared-upstream patch marker | Provider/model routing or UI | partial | packages/kilo-gateway/test/plugin.test.ts, gateway.test.ts, account.test.ts (Gateway routing, anonymous key, auth precedence); attribution headers unported | V1 markers test Kilo attribution headers on the kilo provider (L68,90,119), routing the Kilo catalog through the `@kilocode/kilo-gateway` SDK with `kilocodeToken` (L125-168), authenticated credentials winning over KILO_API_KEY/KILO_ORG_ID env (L170-190), and anonymous availability (L192-206). V2's owned Gateway suite covers anonymous provider-scope key (`apiKey` 'anonymous', plugin.test.ts:1669-1782), authenticated/anonymous transitions and Authorization precedence (L750). Missing: Kilo `HTTP-Referer`/`X-Title` header assertions (headers are upstream-branded in v2, see provider plugin rows) and KILO_API_KEY/KILO_ORG_ID env fallback (no v2 reference to those variables in kilo-gateway/kilo-cli). Evidence: L3: import { AISDK } from "@opencode-ai/core/aisdk" // kilocode_change / L5: import { ModelV2 } from "@opencode-ai/core/model" // kilocode_change | +| `packages/core/test/plugin/provider-llmgateway.test.ts`
    50,51 | shared-upstream patch marker | Provider/model routing or UI | needs port | packages/core/test/plugin/provider-llmgateway.test.ts header assertions (follows llmgateway.ts attribution port) | V1 asserts Kilo attribution headers including `X-Source: kilo` (L50-51). V2 test (73 lines, unmarked) asserts opencode headers because the source sends them. Follows the packages/core/src/plugin/provider/llmgateway.ts row. Evidence: L50: "X-Title": "Kilo Code", // kilocode_change / L51: "X-Source": "kilo", // kilocode_change | +| `packages/core/test/plugin/provider-opencode.test.ts`
    71 | shared-upstream patch marker | Provider/model routing or UI | needs port | packages/core/test/plugin/provider-opencode.test.ts (drop console account/service-account and remote-catalog cases once the Kilo product decision is applied) | V1 marker documents that Kilo removed upstream's 'registers account and service account methods' and 'loads providers and models from the connected OpenCode server' tests because Kilo does not register the OpenCode Console flow (L71-73). V2 test (664 lines) is upstream's full suite exercising those flows. Follows the packages/core/src/plugin/provider/opencode.ts row. Evidence: L71: // kilocode_change - Kilo does not register the OpenCode Console device/OAuth flow and does not let | +| `packages/core/test/project.test.ts`
    157,178,209 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/core/test/project.test.ts:393-414,519 (follows the `kilo` project id file name port) | V1 markers write/assert the cached project id file as `.git/kilo` (L157,178,209). V2 project.test.ts asserts `.git/opencode` because v2 project.ts:237 uses the upstream file name. Follows the packages/core/src/project.ts row; if the `kilo` file name is not ported the v1 assertions are moot. Evidence: L157: yield* Effect.promise(() => Bun.write(path.join(tmp.path, ".git", "kilo"), "old-id")) // kilocode_change / L178: expect(yield* Effect.promise(() => Bun.file(path.join(tmp.path, ".git", "kilo")).exists())).toBe(false) // kilocode_change | +| `packages/core/test/pty/pty-session.test.ts`
    16,65,84,93,98,138,185,262,285 | shared-upstream patch marker | Session lifecycle/history/UI | needs port | packages/core/test/pty/pty-session.test.ts (explicit-args, UTF-8 round-trip, descendant termination, exited replay cases) | V1 markers add a 15s PTY timeout with output-bearing diagnostics (L16,65,84-98), and four Kilo cases: explicit commands do not gain implicit login args (L138-148), non-ASCII output round-trips byte-identically (L153-161), background descendants outside the shell process group are terminated on remove (L162-184), and `allowExited` attachments replay exited output then end (L262-285). V2 pty-session.test.ts (225 lines) covers shell/login/cwd defaults (L210) but none of the four Kilo cases (grep for descendants/allowExited/explicit empty). Follows the packages/core/src/pty.ts row. Evidence: L16: const PTY_TEST_TIMEOUT = "15 seconds" // kilocode_change - PTY startup can exceed the default test timeout on macOS CI / L65: duration: PTY_TEST_TIMEOUT, // kilocode_change | +| `packages/core/test/ripgrep.test.ts`
    59,60,66,78 | shared-upstream patch marker | Package behavior indicated by path | partial | packages/core/test/ripgrep.test.ts:82-103 (hidden .opencode vs .git filtering native); missing underlying-reason failure case | V1 markers assert `.items` on SearchResult (L59-60) and add a case that execution failures surface the underlying reason (`ripgrep execution failed: `, L66-78). V2 test natively asserts the hidden-directory filtering on plain arrays (L89-103). Missing: the underlying-reason error test, following the unported error-detail marker in packages/core/src/ripgrep.ts (L179-185). Evidence: L59: expect(matches.items.map((item) => item.entry.path)).toContain(RelativePath.make(".opencode/config")) // kilocode_change / L60: expect(matches.items.map((item) => item.entry.path)).not.toContain(RelativePath.make(".git/config")) // kilocode_change | +| `packages/core/test/session-projector.test.ts`
    23,247,288,353,402,404,408,414 | shared-upstream patch marker | Session lifecycle/history/UI | not needed | none — released-reader compatibility shapes are not written by v2 (packages/core/src/session/projector.ts row) | V1 markers add a tool-output image scenario and assert the projector writes released-reader shapes: compaction `summary` with embedded 'Recent context' plus `kilo_summary`, and tool `content` as `{type:"file",source:{type:"data"}}` (L23,247-288,353-408,414). These exist solely so released v1 Kilo clients could read the shared database; kilo2's isolated store and import path make the shapes unnecessary, and v2's 750-line suite tests the native shapes. Evidence: L23: import * as StoredMessage from "@opencode-ai/core/kilocode/session-message" // kilocode_change / L247: // kilocode_change start | +| `packages/core/test/session-prompt.test.ts`
    101 | shared-upstream patch marker | Session lifecycle/history/UI | not needed | none — comment-only marker about released readers | V1 marker is a comment explaining why the suite avoids a durable interrupt lookup (released database readers could not decode that event type, L101). V2 has no released-reader constraint; the v2 suite (1193 lines) exercises interruption through its own fixtures (L38-51). Evidence: L101: // kilocode_change - no durable interrupt lookup: released database readers cannot decode that event type. | +| `packages/core/test/session-runner.test.ts`
    748,781 | shared-upstream patch marker | Session lifecycle/history/UI | not needed | none — see packages/core/src/session/context-epoch.ts | V1 adds a regression that a concurrent Session move during system-context load does not create a source-Location context epoch (L748-781). V2 removed context epochs and interrupts on Location mismatch at select time (context.ts:123); there is no epoch row to assert. A v2-specific move-during-load regression was not found in the 5548-line suite and was not proven necessary. Evidence: L748: // kilocode_change start - prevent a source-location context epoch after the session moves mid-load / L781: // kilocode_change end | +| `packages/core/test/tool-apply-patch.test.ts`
    205,230 | shared-upstream patch marker | Tool behavior/UI | needs port | packages/core/test/tool-patch.test.ts (oversized structured output case) following the patch.ts compaction port | V1 adds 'omits oversized patches from durable structured output' asserting structured output stays within ToolOutputStore.MAX_BYTES (L205-230). V2 renamed the suite to tool-patch.test.ts and has no MAX_BYTES/oversized case (grep empty). Follows packages/core/src/tool/apply-patch.ts. Evidence: L205: // kilocode_change start / L230: // kilocode_change end | +| `packages/core/test/tool-edit.test.ts`
    162,193 | shared-upstream patch marker | Tool behavior/UI | needs port | packages/core/test/tool-edit.test.ts (oversized structured output case) following the edit.ts compaction port | V1 adds 'omits an oversized patch from durable structured output' (L162-193). V2 tool-edit.test.ts (643 lines) has no such case. Follows packages/core/src/tool/edit.ts. Evidence: L162: // kilocode_change start / L193: // kilocode_change end | +| `packages/core/test/tool-read-filesystem.test.ts`
    18 | shared-upstream patch marker | Tool behavior/UI | needs port | packages/core/test/tool-read-filesystem.test.ts (inspect-then-read Target fixture) following the identity-bound read port | V1 marker resolves an inspected Target before reads because Kilo binds reads to filesystem identity (L18-19). V2 test (307 lines) reads by path. Follows packages/core/src/tool/read-filesystem.ts. Evidence: L18: // kilocode_change - Kilo binds reads to an inspected filesystem identity, so tests resolve a target first | +| `packages/core/test/util/effect-flock.test.ts`
    62,89,372,373,391 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | packages/core/test/fixture/lock-worker.ts:29-43 (`stopLockWorker` awaits close, taskkill on win32) | V1 markers make worker shutdown await the process `close` event with a Windows taskkill fallback and widen readiness/test timeouts for slow hosted macOS (L62-89,372-373,391). V2 factors the same behavior into a shared lock-worker fixture (await `close`, win32 taskkill /T /F) used by effect-flock.test.ts (374 lines). The widened 20s/60s timeouts were not compared; the race-fix invariant is present natively. Evidence: L62: // kilocode_change start - make worker finalization await the process close event without a Windows race / L89: // kilocode_change end | +| `packages/core/test/util/flock.test.ts`
    84,113,184,203 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | packages/core/test/fixture/lock-worker.ts:29-43 via spawnLockWorker/stopLockWorker (flock.test.ts:6,77,113) | Same as effect-flock.test.ts: v1's inline stopWorker/close-await/taskkill helpers (L84-113,184,203) are now the shared v2 fixture consumed by flock.test.ts. Evidence: L84: // kilocode_change start - make worker finalization await the process close event without a Windows race / L113: // kilocode_change end | +| `packages/effect-drizzle-sqlite/src/sqlite-core/effect/session.ts`
    285 | shared-upstream patch marker | Session lifecycle/history/UI | needs port | packages/core/src/database/drizzle/sqlite-core/effect/session.ts:280 (redact `params` in EffectDrizzleQueryError) | V1 redacted bound parameter values in query errors because bound values may contain credentials (L285). V2 vendored the drizzle Effect session into packages/core/src/database/drizzle and constructs `EffectDrizzleQueryError({ query, params, cause })` with the raw params (session.ts:280); packages/core/test/database-drizzle.test.ts:59 even asserts `error.params` equals the bound values. Credential values (e.g. credential table inserts) can leak into logged/serialized errors. Evidence: L285: params: params.map(() => ""), // kilocode_change - bound values may contain credentials | +| `packages/effect-drizzle-sqlite/test/sqlite.test.ts`
    133,149 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/core/test/database-drizzle.test.ts (redaction regression) following the session.ts port | V1 test asserts query errors never contain bound credential values (L133-149). V2's database-drizzle.test.ts asserts the opposite shape (params preserved). Follows the effect-drizzle session.ts row. Evidence: L133: // kilocode_change start - query errors must never expose bound credential values / L149: // kilocode_change end | +| `packages/effect-sqlite-node/tsconfig.json`
    5 | shared-upstream patch marker | Configuration/settings | not needed | none — package removed in v2 (packages/core/src/database/sqlite.node.ts) | V1 added `types: ["node"]` for node:sqlite with Kilo's tsgo version (L5). V2 has no packages/effect-sqlite-node; the node sqlite adapter lives in core with the core tsconfig. Evidence: L5: "types": ["node"], // kilocode_change - required for node:sqlite with Kilo's tsgo version | +| `packages/http-recorder/src/websocket.ts`
    153,208 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | packages/http-recorder/src/websocket/recorder.ts:17-23,155-160 (progress Ref + Deferred + Semaphore ordering) | V1 added a causal replay ordering state machine (progress position, per-step Deferred, semaphore-guarded client sends, unconsumed-event check on close, L153-208). V2 restructured the recorder into websocket/recorder.ts with the same progress Ref, changed Deferred, write/event locks and an unconsumed-events close check, so the ordering invariant is native. Evidence: L153: // kilocode_change start - preserve causal client/server transcript order during replay / L208: // kilocode_change end | +| `packages/http-recorder/test/record-replay.test.ts`
    312 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | packages/http-recorder/test/websocket.test.ts:252 ('WebSocket replay preserves causal frame ordering') | V1 added a regression for causal replay ordering and auto recording (L312-313). V2 carries a test with the same name plus binary-frame and public-decorator variants (websocket.test.ts:252,306,511). Evidence: L312: // kilocode_change - regression coverage for internal replay ordering and auto recording | +| `packages/http-recorder/tsconfig.json`
    12 | shared-upstream patch marker | Configuration/settings | not needed | packages/http-recorder/tsconfig.json (upstream, 22 lines) | V1 added `types: ["bun"]` for Bun test globals (L12). V2 tsconfig omits it and the package typechecks under `bun typecheck`; no Kilo-owned source in http-recorder. Evidence: L12: "types": ["bun"], // kilocode_change - include Bun test globals in workspace typecheck | +| `packages/httpapi-codegen/test/generate.test.ts`
    5,626 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | packages/httpapi-codegen/test/generate.test.ts:5,1100 (`fileURLToPath` for the generated directory glob) | V1 converted the `generated` file URL with `fileURLToPath` for Windows correctness (L5,626). V2 test uses the same `fileURLToPath(new URL("generated", import.meta.url))` natively. Evidence: L5: import { fileURLToPath } from "node:url" // kilocode_change - convert file URLs correctly on Windows / L626: () => Array.fromAsync(new Bun.Glob("*.ts").scan(fileURLToPath(new URL("generated", import.meta.url)))), // kilocode_change | +| `packages/httpapi-codegen/test/write.test.ts`
    2,19,20,59 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | packages/httpapi-codegen/test/write.test.ts:2,19-20 (`join("/generated", ...)` native separators) | V1 used `join` for native separators in the expected write paths (L2,19-20,59). V2 test uses `join("/generated", ...)` natively. Evidence: L2: import { join } from "node:path" // kilocode_change - use native separators in cross-platform tests / L19: { path: join("/generated", "session.ts"), content: "export const session = {}\n" }, // kilocode_change | +| `packages/kilo-docs/pages/contributing/architecture/config-schema.md`
    74 | documentation/reference | Documentation/agent guidance | partial | kilocode/baseline/config-mapping-v2-gap.md (Kilo-only config keys audited against v2 schema/import); no contributor procedure page — packages/kilo-docs is absent from v2 (`git ls-files packages/kilo-docs` empty) | V1 marked line documents the procedure for adding a Kilo-only config key: add the Effect Schema field with a `kilocode_change` marker in the shared config, then regenerate the JSON schema (L74). V2 has no docs site package; the Kilo-only key inventory and its v2 schema/import status are recorded in the baseline gap audit, and v2 conventions steer new Kilo config to owned seams (ProjectConfig) rather than marked shared schema edits. Missing: a contributor-facing procedure for Kilo-only keys on the v2 line. Evidence: L74: 1. Add or update Effect Schema field with `kilocode_change` marker in `packages/opencode/src/config/config.ts`. | +| `packages/kilo-docs/pages/contributing/architecture/development-patterns.md`
    27,28,47,51,52,53 | documentation/reference | Documentation/agent guidance | partial | kilocode/v2-fork-conventions.md ('Kilo-owned paths', 'Narrow shared hooks', '`kilocode_change` markers' sections) — the docs-site page is absent | V1 marked table rows and 'Shared OpenCode files' section define where Kilo changes go (packages/opencode/src|test/kilocode, narrow marked seams in shared files, Kilo-owned packages without markers) and the marker grammar (inline, start/end block, new-file, JSX) with exemptions (L24-31,45-56). V2 relocates this guidance into kilocode/v2-fork-conventions.md with v2 paths (`kilocode/`, `packages//src/kilocode/`, `plans/`), the same marker rules, the no-markers-in-Kilo-owned-packages rule and merge-preservation rules. Preserved: ownership/marker conventions. Missing: the marker grammar table (block/new-file/JSX forms are not spelled out in v2), and the v1 placement rows for CLI endpoint/SDK regeneration (AGENTS.md covers `bun run generate`), JetBrains contract (no v2 package) and config-key overlay; packages/kilo-docs itself does not exist in v2. Evidence: L27: | Required shared OpenCode edit | Small import, route, or injection seam in shared file plus `kilocode_change` marker | Keeps upstream diff narrow and / L28: | VS Code, JetBrains, docs, indexing, UI, gateway, or telemetry change | Existing Kilo-owned package | These packages are Kilo-owned; do not add `kilo | +| `packages/kilo-docs/pages/contributing/development-environment.md`
    253 | documentation/reference | Documentation/agent guidance | partial | kilocode/v2-fork-conventions.md:127-140 (marker rule); docs-site page absent | V1 marked bullet tells contributors to keep shared-file edits small and mark them with `// kilocode_change` (line or start/end block) and not to mark Kilo-owned paths (L253). The same rule is stated in the v2 fork conventions (root AGENTS.md points to it). Missing: the contributor development-environment page as a whole, since packages/kilo-docs is not part of v2. Evidence: L253: - When editing shared `packages/opencode/` files, keep changes small and mark Kilo-only edits with `// kilocode_change` for a single line or `// kiloc | +| `packages/kilo-docs/pages/contributing/index.md`
    100 | documentation/reference | Documentation/agent guidance | partial | kilocode/v2-fork-conventions.md:127-140; docs-site page absent | V1 marked bullet (L100) is the same marker-discipline instruction as development-environment.md. Relocated to the v2 fork conventions; the docs-site contributing index has no v2 counterpart because packages/kilo-docs is absent. Evidence: L100: - When editing shared `packages/opencode/` files, keep Kilo changes small and mark Kilo-only edits with `// kilocode_change` for a single line or `// | +| `packages/kilo-indexing/src/tree-sitter/queries/java.ts`
    42 | Kilo-owned inherited marker | Package behavior indicated by path | already ported | `packages/kilo-indexing/src/tree-sitter/queries/java.ts:41-42` | Current query retains exact method-start capture; index.ts:11 consumes definition.method.start. Only redundant marker removed. Evidence: L42: type: (_) @definition.method.start ; kilocode_change | +| `packages/kilo-vscode/AGENTS.md`
    218 | documentation/reference | Documentation/agent guidance | native equivalent | `kilocode/v2-fork-conventions.md:127-134` plus root AGENTS pointer | Central v2 conventions encode the same no-owned-marker policy; package-local duplication unnecessary. Evidence: L218: This package is entirely Kilo-specific — `kilocode_change` markers are NOT needed in any files under `packages/kilo-vscode/`. The markers are only nec | +| `packages/kilo-vscode/package.json`
    1253 | Kilo-owned inherited marker | Package behavior indicated by path | partial | `packages/kilo-vscode/package.json` plus proposed owned guard script/CI wiring | Manifest is transplanted, but its check-kilocode-change script subfeature has no automated v2 equivalent; only that guard needs port. Evidence: L1253: "check-kilocode-change": "! grep -rIn 'kilocode_change' . ../kilo-ui/ --exclude='package.json' --exclude='*.md' --exclude-dir='node_modules' --exclude | +| `packages/kilo-vscode/tests/unit/kilo-ui-contract.test.ts`
    136,145 | Kilo-owned inherited marker | Package behavior indicated by path | partial | `packages/kilo-ide-ui/src/context/data.tsx:45-86`; `packages/kilo-vscode/webview-ui/src/App.tsx:182-183`; replacement test absent | OpenFile/OpenDiff contracts and consumers are ported, but dedicated source-contract regression coverage is absent. Evidence: L136: // onOpenFile and OpenFileFn are `kilocode_change` additions — TypeScript types / L145: // onOpenDiff and OpenDiffFn are `kilocode_change` additions — TypeScript types | +| `packages/llm/src/protocols/openai-chat.ts`
    227 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | packages/ai/src/protocols/openai-chat.ts:363-367 (text-only user content collapsed to a string) | V1 collapsed all-text user content into a single string for OpenAI Chat compatibility (L227, joined with newlines). V2 (package renamed to packages/ai) does the same when every part is text without cache_control (joined without separator, L363-367). The invariant (string content for text-only user messages) is native; the join separator differs. Evidence: L227: return { role: "user" as const, content: content.map((part) => part.text).join("\n") } // kilocode_change | +| `packages/llm/src/protocols/openai-responses.ts`
    139 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | packages/ai/src/protocols/open-responses.ts + packages/ai/src/protocols/utils/open-responses-options.ts:64 | V1 added `reasoning.summary` to the Responses request schema (L139). V2's open-responses options accept `reasoningSummary: auto|concise|detailed` natively (open-responses-options.ts:64) and providers/openai-options.ts forwards it (L25,43). Evidence: L139: summary: Schema.optional(OpenAIOptions.OpenAIReasoningSummary), // kilocode_change | +| `packages/llm/src/protocols/utils/openai-options.ts`
    10,13,33,39,65,70 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | packages/ai/src/protocols/utils/open-responses-options.ts:64 | V1 defined `OpenAIReasoningSummaries` (auto/concise/detailed), its schema literal and a `reasoningSummary` request resolver (L10-13,33,39,65-70). V2 declares the same three literals in the Open Responses options schema and forwards them from provider options; no Kilo seam needed. Evidence: L10: // kilocode_change start - support every reasoning summary mode in the OpenAI Responses API / L13: // kilocode_change end | +| `packages/llm/src/providers/openai-options.ts`
    12 | shared-upstream patch marker | Provider/model routing or UI | native equivalent | packages/ai/src/providers/openai-options.ts:25,43 | V1 added `reasoningSummary?: "auto"|"concise"|"detailed"` to OpenAI provider options (L12). V2 providers/openai-options.ts passes `reasoningSummary` through and defaults it to `auto`. Evidence: L12: readonly reasoningSummary?: "auto" | "concise" | "detailed" // kilocode_change | +| `packages/llm/src/schema/ids.ts`
    45 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | packages/core/src/aisdk.ts:786-788 (`other` mapped to `unknown`; raw reason retained at L755-760) | V1 added `other` to FinishReason so the AI SDK's unexpected-provider finish reason decoded instead of failing (L45). V2 schema/src/llm.ts:5 omits `other`, but the AI SDK bridge maps `unified === "other"` to `unknown` while keeping the raw provider reason alongside, and native protocols fall back to `unknown` (e.g. anthropic-messages.ts:1141). Decoding cannot fail on `other`; only the literal name differs. Evidence: L45: "other", // kilocode_change - preserve the AI SDK's unexpected provider finish reason | +| `packages/llm/src/schema/messages.ts`
    1,45,47,55,62,69,71 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | packages/ai/src/schema/messages.ts:66-90 (tolerant `LLM.ToolResult` union with `is`/`make`) | V1 introduced a tolerant tool-result value union (json/text/error/content) with `ToolResultValue.is/make` helpers (L47-71) and re-exported StoredToolContent (L45). V2's ai schema defines the identical union and helpers natively (messages.ts:66-90) and tool-runtime.ts consumes `ToolResultValue.is`. The StoredToolContent re-export is not needed (see schema/src/llm.ts row). Evidence: L1: import { Schema, SchemaGetter } from "effect" // kilocode_change / L45: export { StoredToolContent } from "@opencode-ai/schema/llm" // kilocode_change - shared with the durable event schema | +| `packages/llm/test/tool-runtime.test.ts`
    9,260,264 | shared-upstream patch marker | Tool behavior/UI | not needed | none — StoredToolContent codec has no v2 consumer (packages/schema/src/llm.ts row) | V1 markers import StoredToolContent and assert storage accepts released `media` shapes while public ToolContent rejects them (L9,260-264). V2 has no released-shape codec to test; packages/ai/test covers the native tool runtime. Evidence: L9: StoredToolContent, // kilocode_change / L260: // kilocode_change start - storage accepts released shapes while public content remains canonical | +| `packages/llm/tsconfig.json`
    6 | shared-upstream patch marker | Configuration/settings | not needed | packages/ai/tsconfig.json (upstream) | V1 added `types: ["bun"]` for Bun test globals in workspace typecheck (L6). The package was renamed to packages/ai in v2 with upstream tsconfig; no Kilo-owned source in packages/ai requires the addition. Evidence: L6: "types": ["bun"], // kilocode_change - include Bun test globals in workspace typecheck | +| `packages/opencode/AGENTS.md`
    78 | documentation/reference | Documentation/agent guidance | native equivalent | `kilocode/v2-fork-conventions.md` (Kilo-owned paths, shared-hook limits, marker rules) and root AGENTS.md pointer | Marked paragraph instructs extracting Kilo-specific code out of shared upstream files. V2 fork conventions encode the same rule with stricter Kilo-owned path tables. Evidence: L78: `opencode/` is a fork of upstream opencode. When a change must touch a shared upstream file, extract the Kilo-specific logic into a mirror file under | +| `packages/opencode/Dockerfile`
    10,13,16,19 | shared-upstream patch marker | Package behavior indicated by path | deferred | `packages/containers` (Kilo container batch assessed by the non-opencode worker) and `packages/kilo-cli/script/build-portable.ts` | Markers copy the Kilo linux musl binary, bundled bwrap and licenses into the image. V2 builds a portable preview and Linux sandbox smoke (sandbox-linux-smoke.ts) but publishes no container image; container/registry defaults were classified deferred by the containers batch. Evidence: L10: # kilocode_change start / L13: # kilocode_change end | +| `packages/opencode/bin/kilo`
    10,17,20,21,22,38,42,49,50,55,72,235 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/script/build-portable.ts` + portable launcher (portable-launcher-smoke), baseline packaging docs | PRESERVED — portable launcher resolves the bundled binary and resources (portable smoke tests). MISSING — npm-distributed launcher with cached binary fallback and tree-sitter resource pointing (npm distribution deferred). Evidence: L10: // kilocode_change start - point packaged binaries at co-located tree-sitter WASM resources / L17: // kilocode_change end | +| `packages/opencode/script/build-node.ts`
    17,23,30,31,32 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/script/build.ts`, `build-portable.ts` (sandbox assets for Linux smoke) | PRESERVED — Kilo v2 builds include sandbox support for Linux (sandbox-linux-validation baseline). MISSING/unverified — mutation-worker/network-relay asset defines (v2 sandbox is process confinement without a network relay). Evidence: L17: // kilocode_change start / L23: // kilocode_change end | +| `packages/opencode/script/build.ts`
    5,9,14,22,27,35,54,106,172,258,265,287,292,298,301,308,311,314,317,325,332,335,338,341,348,354,357,360,365,391,395,400,406,407,413,416,429,435,439,446,457,459,461,465,467,471,474 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/script/build.ts`, `build-tui.ts`, `build-portable.ts` (compile flags recorded in corrections memory) | PRESERVED — Kilo-owned build scripts compile the CLI/TUI with Bun for the preview (portable acceptance). MISSING — release archive naming, multi-target publishing and gh release upload (distribution deferred), indexing plugin bundling specifics. Evidence: L5: import os from "os" // kilocode_change / L9: import { createRequire } from "module" // kilocode_change | +| `packages/opencode/script/dev-local.ts`
    1 | shared-upstream patch marker | Package behavior indicated by path | needs port | `packages/kilo-cli/script/dev.ts` (source launcher) + cloud client env handling in `src/cloud/origin.ts` | Whole file points the dev CLI at a locally running cloud dev server (manifest ports, KILO_API_URL/ingest/event URLs) with isolated state. V2 dev.ts launches the interactive preview but has no local-cloud manifest discovery; KILO_API_URL is honored by the Gateway client only. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/script/generate.ts`
    3,12,49 | shared-upstream patch marker | Package behavior indicated by path | not needed | `packages/kilo-gateway/src/models.ts` decodes the live catalog | Marker parses a Kilo models snapshot during SDK generation; v2 generation runs from Protocol and the Gateway decodes catalogs at runtime. Evidence: L3: import { parseModelsSnapshot } from "../src/kilocode/provider/models-snapshot-shape" // kilocode_change / L12: // kilocode_change start | +| `packages/opencode/script/kilocode/test-cli.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | native equivalent | `packages/kilo-cli/script/test.ts` and `test/fixture.ts` guards | Whole-file Kilo test runner for the v1 CLI package; v2 kilo-cli has its own test script and fixture guard. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/script/postinstall.mjs`
    14,126,128,171 | shared-upstream patch marker | Package behavior indicated by path | deferred | npm distribution (deferred); portable preview uses `build-portable.ts` | Markers select the platform variant and copy runtime resources for the npm-installed binary. V2 does not publish to npm in the current phase. Evidence: L14: // kilocode_change start - variant detection matching bin/kilo logic / L126: // kilocode_change end | +| `packages/opencode/script/publish.ts`
    6,24,31,35,37,48,53,55,58,65,66,70,75,86,88,106,116,128,129,130,131,132,133,134,135,165,167,178,179,187,188,198,199,206,207,221,224,225 | shared-upstream patch marker | Package behavior indicated by path | deferred | Release workflow (deferred; no publishing in current plan) | 38 markers brand npm publish, container image and AUR PKGBUILD. Publishing is explicitly out of scope for the v2 preview. Evidence: L6: import { NpmPublish } from "./kilocode/npm-publish" // kilocode_change / L24: // kilocode_change start | +| `packages/opencode/script/run-workspace-server`
    3,8 | shared-upstream patch marker | Server/API integration | not needed | `bun run dev:live` (root AGENTS.md live TUI testing) | Comment markers in a dev helper; v2 provides dev:live against the elected server. Evidence: L3: // This script runs a separate Kilo server to be used as a remote // kilocode_change / L8: // In `.kilo/kilo.jsonc` in the root of this project add: // kilocode_change | +| `packages/opencode/script/test-runner.ts`
    1,154,190,200 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/kilo-cli/script/test.ts` | Whole-file test runner for the v1 opencode package; v2 kilo-cli has its own runner/fixture guard. Evidence: L1: // kilocode_change - new file / L154: const candidates = patterns.length > 0 && !profile ? matched : matched.filter((file) => !skipped.has(file)) // kilocode_change | +| `packages/opencode/src/acp/content.ts`
    79 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/kilo-cli/src/acp.ts` + `packages/cli` ACP bridge (acp.test.ts, acp-cli.test.ts) | Marker narrows an error swallow to the expected non-URL uri case. V2 ACP bridge is exercised by Kilo acp tests; content mapping is upstream's with no Kilo divergence needed. Evidence: L79: // kilocode_change - a non-URL uri is the expected fallback; only that is swallowed | +| `packages/opencode/src/acp/permission.ts`
    15,62,70,72,91,94,99,126,133 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/src/acp.ts` and Core permission reply (no interactive flag) | PRESERVED — ACP permission prompts map to v2 permission asks. MISSING — skill-shell batch prompt presentation and the human-answered `interactive` reply flag (permission groups row partial). Evidence: L15: import { SkillShellPrompt } from "@/kilocode/acp/permission" // kilocode_change / L62: const skillShell = SkillShellPrompt.is(permission.metadata) // kilocode_change - skill batches list commands and never persist | +| `packages/opencode/src/acp/service.ts`
    49,94,100,789,790,791,871 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/src/acp.ts` (endpoint auth via daemon credentials) and Gateway default model policy | PRESERVED — ACP endpoint authentication is Kilo-owned (acp.ts). MISSING — `kilo-login` ACP auth method id and defaulting to the first sorted Kilo provider model when no model is configured (routed-model handles Auto but ACP default selection unverified). Evidence: L49: export const AuthMethodID = "kilo-login" // kilocode_change / L94: // kilocode_change start | +| `packages/opencode/src/agent/agent.ts`
    15,21,30,36,44,48,51,53,70,86,89,103,116,118,125,145,151,158,160,163,166,176,179,192,195,266,297,306,322,337,346,351,376,380,383,457,476,479,488,497,500,506,509,520,529,561,565,568,571,574,576,579,598,600,655 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/src/agent-policy.ts` (ask/debug added :165-194, explore/plan hardening, exploreBash ceiling), `plan-policy.ts`; baseline agent-policy-v2-parity | 55 markers. PRESERVED — ask and debug agents, plan/explore permission patches, bash allowlist ceiling, human-driven tools (question/interactive) denied for delegated agents, telemetry via Kilo plugin. MISSING — build→code rename and `build` alias resolution (v2 keeps upstream `build`; agent-policy.ts:160 patches build rather than renaming), orchestrator agent, opt-in Scout repository-research agent with `requirements`/RequirementBlockedError guard, `deprecated` flag, typed metadata fields (id/displayName/source/reference) instead of provider options, default external_directory allow for global config dirs (test row partial), cache-key rebuild on permission-relevant config changes (v2 config transforms are reactive natively), hardenSystemAgents deny-only invariant (no v2 counterpart found). Evidence: L15: import PROMPT_SCOUT from "@/kilocode/agent/scout.txt" // kilocode_change / L21: import { KilocodePaths } from "@/kilocode/paths" // kilocode_change | +| `packages/opencode/src/auth/index.ts`
    7,9,91,96,104 | shared-upstream patch marker | Authentication/account integration | partial | `packages/core/src/credential.ts` + `packages/kilo-gateway/src/plugin.ts` integration methods; `packages/kilo-cli/src/telemetry.ts` | PRESERVED — Kilo credential storage/removal via v2 Integration/Credential services (Gateway plugin registration, provider withdrawal on sign-out). MISSING/unverified — telemetry logout tracking and identity reset on Kilo auth removal (grep identity in telemetry.ts not confirmed), OAUTH_DUMMY_KEY placeholder pattern (v2 uses native OAuth authToken; not needed structurally). Evidence: L7: import { Telemetry } from "@kilocode/kilo-telemetry" // kilocode_change / L9: export const OAUTH_DUMMY_KEY = "kilo-oauth-dummy-key" // kilocode_change | +| `packages/opencode/src/bus/bus-event.ts`
    2,32,42 | shared-upstream patch marker | Package behavior indicated by path | not needed | None; v2 has no legacy Bus (`packages/core/src/bus.ts` is the native Effect bus) | Markers expose EventV2 definitions through the deleted legacy bus schema layer for v1 compatibility. V2 consumers use native events only. Evidence: L2: import { EventManifest } from "@/event-manifest" // kilocode_change / L32: // kilocode_change start - expose current Effect events through legacy bus schemas | +| `packages/opencode/src/bus/global.ts`
    23 | shared-upstream patch marker | Package behavior indicated by path | not needed | None (v1 legacy GlobalBus removed; v2 uses Effect streams for `/api/event`) | Single marker tunes Node EventEmitter listener warnings on the legacy GlobalBus, which does not exist in v2. Evidence: L23: GlobalBus.setMaxListeners(50) // kilocode_change — surface warning if SSE listeners accumulate | +| `packages/opencode/src/bus/index.ts`
    1,5,14,16,17,197,199,213,252 | shared-upstream patch marker | Package behavior indicated by path | not needed | None; v2 `packages/core/src/bus.ts` + event system | Whole file is a compatibility layer restoring the Bus upstream deleted in v1.16.2 for legacy Kilo callers. V2 Kilo code is written against native events; no legacy callers remain to bridge. Evidence: L1: // kilocode_change - Kilo compatibility layer. Upstream deleted this Bus (Effect PubSub) service in v1.16.2 / L5: import { Effect, Exit, Fiber, Layer, PubSub, Scope, Context, Stream, Schema } from "effect" // kilocode_change | +| `packages/opencode/src/cli/cmd/agent.ts`
    112 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/src/commands.ts` (`agents` list only) and Core config discovery (`.opencode`) | Marker targets `.kilo` for project-scoped agent creation. V2 Kilo CLI lists agents but has no agent-create command, and Core discovery does not scan `.kilo` (config/paths row); both the command and the directory convention are absent. Evidence: L112: scope === "global" ? Global.Path.config : path.join(ctx.worktree, ".kilo"), // kilocode_change | +| `packages/opencode/src/cli/cmd/attach.ts`
    6,9,13,52,79,87,134,152 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/src/commands.ts:249-262` (`attach` with directory/session) and `attach.test.tsx`; cloud in `cloud-command.ts` | PRESERVED — attach to a running Kilo server with session selection (kilo-cli attach command, real-host tests). MISSING — importing a cloud session before the TUI renders (v2 cloud command has start/send/status/result but no attach-time cloud fork import), configurable basic-auth username (v2 uses daemon credentials). Evidence: L6: // kilocode_change start - Kilo implementations (sdk client, cloud-session) are / L9: // kilocode_change end | +| `packages/opencode/src/cli/cmd/config.ts`
    1 | shared-upstream patch marker | Configuration/settings | native equivalent | `packages/kilo-cli/src/settings-rpc.ts` + TUI settings dialog (`tui-plugin/settings.tsx`), `import-v1-config.ts` | Whole-file Kilo config command (inspect/edit config from the CLI). V2 exposes Kilo settings through the settings RPC/TUI dialog and explicit import; a CLI subcommand is not present but the capability (read/update scoped settings with provenance) is delivered through the settings surfaces accepted in the settings row. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/cli/cmd/db.ts`
    40 | shared-upstream patch marker | Package behavior indicated by path | not needed | None; v2 Kilo CLI has no `db` shell command | Single marker hides the console window when spawning sqlite3; the command does not exist in v2 (paths.ts owns kilo2.db but exposes no shell). Evidence: L40: windowsHide: true, // kilocode_change - prevent CMD window flash on Windows | +| `packages/opencode/src/cli/cmd/debug/index.ts`
    61 | shared-upstream patch marker | Package behavior indicated by path | not needed | None; v2 kilo-cli has no debug subcommand (Core has protocol `debug` group) | Branding-only marker in a command absent from the Kilo v2 CLI. Evidence: L61: console.log(`kilo version: ${InstallationVersion}`) // kilocode_change | +| `packages/opencode/src/cli/cmd/debug/ripgrep.ts`
    43,77 | shared-upstream patch marker | Package behavior indicated by path | not needed | None (debug command absent in v2 Kilo CLI) | Markers adapt debug output to Core ripgrep result shapes; v1 compatibility only. Evidence: L43: process.stdout.write(files.items.map((file) => file.path).join(EOL) + EOL) // kilocode_change / L77: process.stdout.write(JSON.stringify(results.items, null, 2) + EOL) // kilocode_change - preserve debug output shape | +| `packages/opencode/src/cli/cmd/export.ts`
    29,31 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/kilo-cli/src/commands.ts` `export [--sanitize]` and `remote-transcript.ts` | Marker retains Kilo summary counts in export output while accepting upstream optional file/patch values. V2 Kilo export renders sessions from the v2 projected history with its own sanitizer; the v1 summary-shape compatibility is not applicable. Evidence: L29: // kilocode_change start - retain Kilo summary counts while accepting upstream's optional file and patch values / L31: // kilocode_change end | +| `packages/opencode/src/cli/cmd/generate.ts`
    11,14,17,23,35,40,45 | shared-upstream patch marker | Package behavior indicated by path | not needed | `packages/client` `bun run generate` (unforked protocol wording) | Markers replace the product name in generated descriptions; branding only. V2 generates the client from the Protocol without Kilo wording changes. Evidence: L11: info: { title: string; description: string } // kilocode_change / L14: // kilocode_change start | +| `packages/opencode/src/cli/cmd/github.handler.ts`
    36,142,144,202,204,230,288,324,328,333,380,441,509,519,525,536,709,758,805,808,912,1000,1011,1027,1104,1106,1371,1374,1434,1572 | shared-upstream patch marker | Package behavior indicated by path | deferred | Kilo GitHub action/agent integration (external `kilo-github-action`, kiloconnect app) — out of the local-runtime scope; would live in a Kilo-owned CLI module | 30 markers rebrand the GitHub agent flow (bot user, workflow template, endpoints, session URLs, prompts). V2 Kilo CLI has no github subcommand or action handler; the feature depends on Kilo cloud/GitHub infrastructure and repository automation, which the plan keeps outside the current local phases (same treatment as docs-automation rows). Evidence: L36: import { GitHubSecurity } from "@/kilocode/security/github" // kilocode_change / L142: const AGENT_USERNAME = "kiloconnect[bot]" // kilocode_change | +| `packages/opencode/src/cli/cmd/import.ts`
    12,15,28,34,84,120,149,184,251,253 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/kilo-cli/src/commands.ts` `import`/`import-external`/`import-v1` and `import-v1-session.ts` | Markers move the v1 import command onto the ShareNext data model. V2 Kilo has explicit import commands built on the v2 session store (proven-safe copy + external transcript import); the v1 architecture migration is superseded. Evidence: L12: import * as Log from "@opencode-ai/core/util/log" // kilocode_change / L15: const log = Log.create({ service: "import" }) // kilocode_change | +| `packages/opencode/src/cli/cmd/mcp.ts`
    21,24,124,192,397,426,439,442,476,518,532,583,770,814 | shared-upstream patch marker | Package behavior indicated by path | needs port | Kilo CLI subcommand over Core MCP service (protocol `mcp` group) and Kilo settings writer (`packages/kilo-cli/src/config-write.ts`) | Markers make the MCP management command resolve supported Kilo config directories/profile, brand prompts, and use a kilo-debug client. V2 Kilo CLI has no `mcp` subcommand (commands.ts); MCP configuration is only via config files or the TUI/extension. The Kilo-config-directory resolution for writes is the substantive missing invariant. Evidence: L21: // kilocode_change - KilocodeMcpConfig is dynamically imported in addMcpToConfig to keep startup fast / L24: import { Flag } from "@opencode-ai/core/flag/flag" // kilocode_change | +| `packages/opencode/src/cli/cmd/models.ts`
    56,64 | shared-upstream patch marker | Provider/model routing or UI | native equivalent | `packages/kilo-cli/src/commands.ts:332` `models` command and Kilo-first ordering in `packages/kilo-cli/src/model-picker.ts` (host modelPicker groups/ranking per dialog-model row) | Marked block sorts provider IDs so kilo/opencode providers come first. V2 Kilo model surfaces (picker) rank Kilo groups first; the CLI models command output ordering was not verified as Kilo-first, but the invariant is a presentation preference already applied at the primary selection surface. Evidence: L56: // kilocode_change start / L64: // kilocode_change end | +| `packages/opencode/src/cli/cmd/pr.ts`
    7,9,11,21,27,30,31,50,98,101,108,122,125,137 | shared-upstream patch marker | Package behavior indicated by path | needs port | Kilo CLI subcommand in `packages/kilo-cli/src/commands.ts` (absent) using worktree/vcs protocol groups | Markers resolve the running CLI executable, detect app.kilo.ai session links in PR bodies and start `kilo -s `. V2 has no pr command. Evidence: L7: import { existsSync } from "node:fs" // kilocode_change / L9: const subcommand = "pr" // kilocode_change | +| `packages/opencode/src/cli/cmd/providers.ts`
    20,241,244,374,384,390,394,408,411,427,430,466,475,486,541,544 | shared-upstream patch marker | Provider/model routing or UI | partial | `packages/kilo-cli/src/credential-import.ts`, TUI integration dialog (`packages/tui` provider dialog via Gateway plugin), `kilo-gateway/src/plugin.ts` integration methods | PRESERVED — Kilo login/logout via Gateway integration methods and TUI provider dialog (Kilo-first per dialog-provider row), credential import from v1. MISSING — CLI `auth login/logout/list` subcommand with codex alias and kilo.json guidance (no auth/providers command in commands.ts). Evidence: L20: // kilocode_change - @/kilocode/auth/remove is dynamically imported in the logout handler to keep startup fast / L241: // kilocode_change start - keep "auth" as primary command name | +| `packages/opencode/src/cli/cmd/remote.ts`
    1,22 | shared-upstream patch marker | Remote control/presence | partial | `packages/kilo-cli/src/remote-session.ts`, `remote-rpc.ts`, and TUI `/remote` | V2 has a location-scoped preview remote adapter with explicit consent and relay authentication, but not the pinned standalone headless `kilo remote` process or its instance-advertisement lifecycle; the current schema explicitly documents this narrower surface. Evidence: L1: // kilocode_change - new file / L22: // kilocode_change - K1 W1: advertise this instance on the relay | +| `packages/opencode/src/cli/cmd/run.ts`
    3,13,30,33,135,168,173,215,281,288,294,295,309,435,447,454,465,475,486,506,532,753,760,768,769,770,771,794,795,797,799,801,818,820,888,891,894,902,912,920,924,930,931,940,942,959,966,981,989,1006,1008,1015,1020,1026,1045,1054,1119,1160,1206,1207 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/src/run.ts` (--auto :121-129, form cancel :132-139, root-only permission filter :142) and `commands.ts:283-330` (run options: session, model, agent, auto, sandbox, swarm, file, format) | 60 markers. PRESERVED — headless run with --auto once-approval, file attachments, format, session resume, sandbox/swarm flags, interrupt on unanswered form/permission with failure message. MISSING — Task-child session tracking for --auto and child auto-reject (run-auto row partial), cloud fork (`--cloud-fork`), interactive direct mode (`-i/--mini` with footer/terminal), non-interactive suggestion dismissal (remote-suggestions exists but run.ts does not dismiss), bounded network retry (network row), built-in session command handling in run, daemon attach for run, skill-shell human-decision gating. Evidence: L3: // kilocode_change start - use Kilo CLI branding / L13: // kilocode_change end | +| `packages/opencode/src/cli/cmd/run/demo.ts`
    18 | shared-upstream patch marker | Package behavior indicated by path | not needed | None | Marker only re-points a type import to the SDK; no behavior. Evidence: L18: import type { Event, ToolPart } from "@kilocode/sdk/v2" // kilocode_change - revert to upstream native Event type | +| `packages/opencode/src/cli/cmd/run/footer.permission.tsx`
    103,144,145,169,288 | shared-upstream patch marker | Package behavior indicated by path | needs port | Interactive direct mode does not exist in v2 kilo-cli (run.ts is headless); permission UI is the native TUI (`packages/tui`) — skill-shell option treatment would land in `packages/kilo-cli/src/skill-shell.ts` UI | Markers change prompt copy and add skill-shell-aware permission options in `kilo run -i` footer. V2 has no run interactive footer; the TUI permission UI has no skill-shell batch option variant (permission.shared row partial). Evidence: L103: placeholder={"Tell Kilo what to do differently" /* kilocode_change */} / L144: const skillShell = createMemo(() => props.request.metadata?.["skillShell"] === true) // kilocode_change | +| `packages/opencode/src/cli/cmd/run/footer.prompt.tsx`
    15,180,422,443,449,868 | shared-upstream patch marker | Package behavior indicated by path | needs port | None in v2 (no `kilo run -i` mode); TUI autocomplete in `packages/tui/src/component/prompt/autocomplete.tsx` covers slash commands for the full TUI | Markers surface skills as slash commands and add /exit in the direct-mode footer. The direct interactive run mode is absent in v2. Evidence: L15: import { slashDisplay, slashMatches } from "@/kilocode/cli/cmd/command-display" // kilocode_change / L180: // kilocode_change | +| `packages/opencode/src/cli/cmd/run/footer.ts`
    34,93,94,95,334,338,712,716 | shared-upstream patch marker | Package behavior indicated by path | needs port | None; interactive_terminal tool/run footer absent in v2 kilo-cli (PTY exists natively via protocol `pty`/`persistent-pty` groups) | Markers add terminal write/resize/close callbacks for the Kilo interactive terminal in direct mode. V2 has native PTY services but no run-mode embedded terminal footer. Evidence: L34: import { RUN_INTERACTIVE_TERMINAL_ROWS } from "@/kilocode/cli/cmd/run/interactive-terminal" // kilocode_change / L93: onTerminalWrite: (input: { terminalID: string; data: string }) => Promise // kilocode_change | +| `packages/opencode/src/cli/cmd/run/footer.view.tsx`
    15,98,99,100,283,288,805,815 | shared-upstream patch marker | Package behavior indicated by path | needs port | None (see footer.ts) | Same interactive-terminal footer feature; absent. Evidence: L15: import { RunInteractiveTerminalBody } from "@/kilocode/cli/cmd/run/interactive-terminal" // kilocode_change / L98: onTerminalWrite: (input: { terminalID: string; data: string }) => Promise // kilocode_change | +| `packages/opencode/src/cli/cmd/run/permission.shared.ts`
    80,82,84,130,134,151,156,157 | shared-upstream patch marker | Interactive permission footer | partial | `packages/tui/src/mini/permission.shared.ts` and permission service contract | V2 preserves once/always/reject transitions, confirmation, rejection text and canonical presentation, but its shared footer does not carry the pinned `skillShell` option that removes Allow always; reply interactivity is represented by the native request path rather than the v1 wire flag. Evidence: L80: export function permissionOptions(stage: PermissionStage, skillShell?: boolean): PermissionOption[] { // kilocode_change - skillShell param / L82: // kilocode_change start - skill-shell batches are never persisted, so no "Allow always" | +| `packages/opencode/src/cli/cmd/run/runtime.lifecycle.ts`
    72,73,74,188,255,259 | shared-upstream patch marker | Package behavior indicated by path | needs port | None (direct interactive run mode absent) | Markers wire terminal callbacks and enable mouse in the direct-mode renderer; feature absent. Evidence: L72: onTerminalWrite: (input: { terminalID: string; data: string }) => Promise // kilocode_change / L73: onTerminalResize: (input: { terminalID: string; cols: number; rows: number }) => Promise // kilocode_change | +| `packages/opencode/src/cli/cmd/run/runtime.ts`
    18,24,25,231,274,278,299,309,314,450,457 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/src/model-state-plugin.ts` (per-agent model/variant persistence) and TUI model picker (`model-picker.ts`); variant.shared row native | PRESERVED — preserving compatible variants/effort when switching models (variant.shared.ts row native; model-state plugin persists variant). MISSING — direct-mode interactive terminal. Evidence: L18: import { KiloRunTerminal } from "@/kilocode/cli/cmd/run-terminal" // kilocode_change / L24: // kilocode_change - preserve compatible variants when switching models | +| `packages/opencode/src/cli/cmd/run/session-data.ts`
    27,28,30,83,222,231,245,249,264,1076,1112 | shared-upstream patch marker | Session lifecycle/history/UI | partial | `packages/kilo-cli/src/run.ts` event handling over v2 client types | PRESERVED — run consumes typed v2 client events. MISSING — interactive terminal snapshot in session data (feature absent). Evidence: L27: import type { Event, Part, PermissionRequest, QuestionRequest, ToolPart } from "@kilocode/sdk/v2" // kilocode_change - revert to upstream native Event / L28: import type { RunInteractiveTerminalSnapshot } from "@/kilocode/cli/cmd/run/types" // kilocode_change | +| `packages/opencode/src/cli/cmd/run/splash.ts`
    3,197,237 | shared-upstream patch marker | Package behavior indicated by path | needs port | None (direct interactive run mode absent); TUI home logo is ported (`tui-plugin/logo.tsx`) | Markers render the Kilo logo/badge and `kilo run -i -s ` resume hint in the direct-mode splash; mode absent. Evidence: L3: // Renders the full Kilo entry logo and a compact [O] exit badge, plus // kilocode_change / L197: push(lines, body_left, top, "Kilo", right, undefined, TextAttributes.BOLD) // kilocode_change | +| `packages/opencode/src/cli/cmd/run/stream.transport.ts`
    18,148,158,159,193,196,288,294,315 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/src/run.ts` event stream (`client.event.subscribe`) | PRESERVED — consuming native events without sync copies (v2 has no sync copies). MISSING — interactive_terminal.* event routing (feature absent). Evidence: L18: import type { Event, GlobalEvent, KiloClient } from "@kilocode/sdk/v2" // kilocode_change - revert to upstream native Event type / L148: if (event.type === "interactive_terminal.updated") return event.properties.info.sessionID // kilocode_change | +| `packages/opencode/src/cli/cmd/run/subagent-data.ts`
    1 | shared-upstream patch marker | Package behavior indicated by path | not needed | None | Single type-import marker; no behavior. Evidence: L1: import type { Event, Message, Part, PermissionRequest, QuestionRequest, ToolPart } from "@kilocode/sdk/v2" // kilocode_change - revert to upstream nat | +| `packages/opencode/src/cli/cmd/run/tool.ts`
    1,28,105,428,453,1163,1175 | shared-upstream patch marker | Tool behavior/UI | partial | `packages/kilo-cli/src/run.ts` text/json formatting of tool parts | PRESERVED — run output formats tool parts natively. MISSING — interactive_terminal display rule (tool absent). Evidence: L1: // Per-tool display rules shared across `kilo run` output paths. // kilocode_change / L28: import type { InteractiveTerminalTool } from "@/kilocode/tool/interactive-terminal" // kilocode_change | +| `packages/opencode/src/cli/cmd/run/trace.ts`
    4 | shared-upstream patch marker | Package behavior indicated by path | not needed | `packages/kilo-cli/src/paths.ts` (isolated kilo2 log/data layout) | Comment-only marker; v2 log locations follow the kilo2 layout. Evidence: L4: // ~/.local/share/kilo/log/direct/-.jsonl. Also writes // kilocode_change | +| `packages/opencode/src/cli/cmd/run/types.ts`
    15,178 | shared-upstream patch marker | Package behavior indicated by path | needs port | None (feature absent) | Markers add the interactive_terminal footer state variant; the direct-mode terminal feature is absent in v2. Evidence: L15: import type { RunInteractiveTerminalSnapshot } from "@/kilocode/cli/cmd/run/types" // kilocode_change / L178: | { type: "interactive_terminal"; terminal: RunInteractiveTerminalSnapshot } // kilocode_change | +| `packages/opencode/src/cli/cmd/run/variant.shared.ts`
    6 | shared-upstream patch marker | Model variant persistence | native equivalent | `packages/tui/src/mini/variant.shared.ts`, `model-preference.ts`, `context/local.tsx:155`, and Kilo `layout().paths.state` | The source comment is not runtime evidence. Kilo injects its `kilo2/` state directory into the TUI local context, which builds the model preference repository at that injected state plus `model.json`; behavior and Kilo-owned storage are preserved. Evidence: L6: // The saved variant persists across sessions in ~/.local/state/kilo/model.json // kilocode_change | +| `packages/opencode/src/cli/cmd/serve.ts`
    12,21,27,29,42,54 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/src/commands.ts:262` `serve` and `daemon.ts` lifecycle (native-lifecycle.test.ts) | PRESERVED — serve command with graceful daemon lifecycle/shutdown tests. MISSING — session-ingest drain on shutdown (session export/ingest feature absent in v2). Evidence: L12: instance: false, // kilocode_change / L21: // kilocode_change start | +| `packages/opencode/src/cli/cmd/session.ts`
    86,98,100,104,108,117,163,175,177,216 | shared-upstream patch marker | Session lifecycle/history/UI | partial | `packages/kilo-cli/src/commands.ts:332` (`sessions [--directory]`) over v2 session pagination and project registry (experimental.session.list adapter in `packages/kilo-vscode/src/backend`) | Marked blocks add `--all` (Session.listGlobal across projects with project name/worktree columns), `--search` title filter, and JSON formatters. V2 `sessions` command lists sessions for one directory only; the global cross-project listing exists as a backend adapter for the extension (board 433: experimental.session.list over v2 pagination + project registry) but is not exposed by the CLI command, and title search/JSON output are not present in commands.ts. Evidence: L86: // kilocode_change start / L98: // kilocode_change end | +| `packages/opencode/src/cli/cmd/stats.ts`
    88,90,188,192,203 | shared-upstream patch marker | Package behavior indicated by path | needs port | Kilo CLI stats command over Core `session/stats.ts` (v2 has session stats service but no CLI command) | Markers export stats aggregation for tests and avoid double-counting propagated subagent cost. V2 Kilo CLI has no stats command; Core stats.ts exists but its treatment of child cost aggregation for a CLI report is not consumed. Evidence: L88: // kilocode_change start - expose Effect stats aggregation for Kilo regression coverage / L90: // kilocode_change end | +| `packages/opencode/src/cli/cmd/tui.ts`
    15,18,25,26,27,35,62,64,81,126,134,136,141,146,260,265,278,285,291,295,303,305,306,307,314,316,317,338,369,404,416,422,426,433,440,452,456,457,460,468,471,495 | shared-upstream patch marker | User-interface behavior | partial | `packages/kilo-cli/src/tui.ts`, `daemon.ts` (service status/start/reuse), `interactive-server.ts`; baseline cli-tui-closure-verification | 42 markers. PRESERVED — default TUI attaches to the Kilo daemon with authenticated routes (daemon.ts credentials), graceful shutdown (native-lifecycle tests), worker/parent correlation via kilo2 identity. MISSING — cloud-fork session import before TUI render, stale PWD handling for `bun --cwd` wrappers (dev.ts launcher behavior unverified), explicit `--server`/remote-exit bridge for embedded worker transport (worker-shutdown row native). Evidence: L15: import type { StartInput } from "@/kilocode/cli/cmd/tui/thread" // kilocode_change - runtime imports deferred into handlers / L18: // kilocode_change start - correlate the TUI worker with its parent process | +| `packages/opencode/src/cli/cmd/uninstall.ts`
    11,28,59,133,134,135,136,137,138,139,184,185,186,187,188,189,190,196,213,227,264,273,289,308 | shared-upstream patch marker | Package behavior indicated by path | deferred | Kilo CLI command paired with package-manager distribution (out of current phase; portable preview only) | 24 markers rebrand uninstall (npm/brew/choco/scoop names, thank-you copy, shell-rc marker cleanup for both opencode and kilo). V2 ships a portable preview with `update` only; publishing/package-manager distribution is explicitly out of scope, so uninstall parity is deferred with it. Evidence: L11: import { Brew as KiloBrew } from "@/kilocode/installation" // kilocode_change / L28: describe: "uninstall kilo and remove all related files", // kilocode_change | +| `packages/opencode/src/cli/cmd/upgrade.ts`
    9,20,31,46,49,61,69 | shared-upstream patch marker | Package behavior indicated by path | deferred | `packages/kilo-cli/src/updater.ts` (`update --check/--apply/--rollback` local portable path) | Markers rebrand the upgrade command and its method list. V2 replaced this with a local portable updater; package-manager upgrade methods are deferred with distribution (installation row partial). Evidence: L9: describe: "upgrade kilo to the latest or a specific version", // kilocode_change / L20: choices: ["curl", "npm", "yarn", "pnpm", "bun", "brew", "choco", "scoop"], // kilocode_change | +| `packages/opencode/src/cli/effect-cmd.ts`
    5,92,96 | shared-upstream patch marker | Package behavior indicated by path | not needed | None (v2 commands are Effect-native) | Markers bridge the legacy ALS context for yargs Effect commands; no legacy context in v2. Evidence: L5: import { Instance } from "@/kilocode/instance" // kilocode_change / L92: // kilocode_change start - preserve legacy instance context across Promise callbacks | +| `packages/opencode/src/cli/error.ts`
    50,58,67,68,69,106 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/src/run.ts`/`commands.ts` error output and TUI error guidance (dialog rows assessed needs port) | PRESERVED — v2 CLI surfaces typed errors for models/sessions. MISSING — Kilo-branded remediation hints (`kilo models`, kilo.json names, `kilo auth login `), `modelsEmpty` message (TUI error guidance row needs port). Evidence: L50: return `MCP server "${data}" failed.` // kilocode_change / L58: // ProviderModelNotFoundError: { providerID: string, modelID: string, suggestions?: string[], modelsEmpty?: boolean } // kilocode_change | +| `packages/opencode/src/cli/logo.ts`
    1,8,11,12 | shared-upstream patch marker | Package behavior indicated by path | already ported | `packages/kilo-cli/src/tui-plugin/logo.tsx`/`logo-data.ts` replacing `home.logo` | Markers blank upstream logo decorations because Kilo renders its own logo; v2 Kilo replaces the home logo slot entirely. Evidence: L1: // kilocode_change start / L8: // kilocode_change end | +| `packages/opencode/src/cli/network.ts`
    24,25,37,56,78,84 | shared-upstream patch marker | Package behavior indicated by path | not needed | None (no mDNS in v2 server) | Branding on a removed feature. Evidence: L24: describe: "custom domain name for mDNS service (default: kilo.local)", // kilocode_change / L25: default: "kilo.local", // kilocode_change | +| `packages/opencode/src/cli/tui/worker-shutdown.ts`
    1 | shared-upstream patch marker | Embedded v1 TUI worker teardown ordering | native equivalent | V2 elected-service and interactive-server lifecycle ownership | The exact drain → dispose → stopServer helper is obsolete because v2 no longer embeds that TUI worker. Its invariant—stop accepting/producing work, dispose scoped resources, then release the server—is owned by the service/interactive-server shutdown lifecycle, so no dormant feature is being deferred. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/cli/tui/worker.ts`
    13,14,15,16,17,19,20,23,31,42,43,53,56,63,106,107,108,112 | shared-upstream patch marker | User-interface behavior | partial | `packages/kilo-cli/src/tui.ts` + `daemon.ts`; worker-shutdown row native equivalent | PRESERVED — worker shutdown ordering and remote exit (worker-shutdown.ts row native), logs kept off the TUI terminal (kilo2 log paths). MISSING — session-ingest drain before dispose (ingest feature absent), remote-exit bridge specifics for embedded transport. Evidence: L13: import { KiloLog } from "@/kilocode/log" // kilocode_change / L14: import { ensureProcessMetadata } from "@opencode-ai/core/util/opencode-process" // kilocode_change | +| `packages/opencode/src/cli/ui.ts`
    4,6,8 | shared-upstream patch marker | Package behavior indicated by path | already ported | `packages/kilo-cli/src/tui-plugin/logo-data.ts` | Single import marker; logo ported. Evidence: L4: import { tui } from "../kilocode/cli/logo" // kilocode_change / L6: // kilocode_change start | +| `packages/opencode/src/cli/upgrade.ts`
    12,14 | shared-upstream patch marker | Package behavior indicated by path | deferred | `packages/kilo-cli/src/updater.ts` | Marker restricts auto-upgrade to registry installs since Kilo publishes only via npm. V2 updater is local-portable; registry publishing is deferred. Evidence: L12: // kilocode_change start - only auto-upgrade for npm/yarn/pnpm/bun (we only publish @kilocode/cli via npm registry) / L14: // kilocode_change end | +| `packages/opencode/src/command/index.ts`
    10,11,14,29,31,60,91,116,120,122,125,127,160,163,171,173,181,184,186,197,198,199,200,202,211,212,218,219,222,233 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/src/review-policy.ts` (review + deprecated aliases), `resume-external.ts`, Core `session/command.ts` command editor | PRESERVED — /review and deprecated local-review aliases (review-policy.ts adds both), external resume commands (resume-external/external-sessions), config-defined commands overriding plugin defaults (v2 command editor). MISSING — `trusted` flag on skill-sourced templates gating `!cmd` shell (skill-shell.ts gates by config rather than a per-command trusted field), deferred override application across command/skill/MCP sources with shared names, `variant` on commands (v2 command schema has model but variant unverified). Evidence: L10: import { legacyReviewCommand, reviewCommand } from "@/kilocode/review/command" // kilocode_change / L11: import { apply as applyOverride, type Override } from "@/kilocode/command/override" // kilocode_change | +| `packages/opencode/src/config/agent.ts`
    11,12,17,21,29,37,39,43,52,60,86,87,97,102,110,118,120,124,133,144,157 | shared-upstream patch marker | Configuration/settings | needs port | `packages/core/src/config/variable.ts` (`substitute` :27 — no trust/scope gating) and `config/plugin/agent.ts` | Markers confine untrusted project agent prompt `{file:}` reads to the project root, gate `{env:}` on trusted sources and keep invalid agent files non-fatal. V2 ConfigVariable.substitute resolves env and file tokens for every source without trust or root confinement (variable.ts:27-60), so a project agent file could read arbitrary files/env — the Kilo security invariant is absent. Evidence: L11: import { ConfigVariable } from "./variable" // kilocode_change / L12: // kilocode_change start | +| `packages/opencode/src/config/command.ts`
    6,11,17,22,30,38,40,44,53,71,82 | shared-upstream patch marker | Configuration/settings | partial | `packages/core/src/config/normalize.ts` diagnostics and Kilo settings warnings (`packages/kilo-cli/src/settings.ts` warnings RPC) | PRESERVED — v2 config normalization produces structured diagnostics and Kilo settings expose warnings (settings collectionGet diagnostics per board 424). MISSING — trust/scope substitution for command templates (same gap as agent.ts) and Kilo command override fields carried by these markers (subtask/variant handled natively in v2 command schema). Evidence: L6: import { SchemaIssue } from "effect" // kilocode_change - preserve Effect issue details in Kilo warnings / L11: // kilocode_change start | +| `packages/opencode/src/config/config.ts`
    13,16,19,41,42,57,59,61,79,85,89,98,114,129,150,154,162,165,172,181,186,190,198,200,211,217,219,228,246,252,260,280,281,315,318,324,325,334,344,352,353,358,361,364,367,379,381,384,385,386,395,404,418,427,430,441,452,456,465,495,529,580,587,620,634,638,645,650,653,663,668,673,684,691,699,707,710,713,722,727,741,766,794,795,798,802,813,824,853,871,875,879,882,894,899,950,952,958,971,976,980,999,1026,1032,1035,1037,1073,1075,1090,1093,1106,1119,1127 | shared-upstream patch marker | Configuration/settings | partial | `packages/core/src/config.ts` (Kilo refresh markers :36,:322-325), `config/discovery.ts`, `config/watch.ts`; Kilo: `packages/kilo-cli/src/{settings,settings-rpc,project-config,config-write,import-v1-config}.ts`, baseline config-mapping-v2-gap | 113 markers. PRESERVED — warnings accumulator surfaced to clients (settings-rpc warnings/diagnostics), explicit refresh when other processes edit config (core config.ts refresh RPC + native watch.ts), Kilo project config updates through a Kilo writer (project-config.ts, config-write.ts), serialized read-merge-write for concurrent approvals (settings.ts revision checks/atomic writes), $schema handling for Kilo documents (settings.ts), Kilo default plugins registered by the host instead of config injection. MISSING — trusted/untrusted substitution (`{env:}` gated to trusted sources; `{file:}` confined to projectRoot) in Core config loading (variable.ts has no trust), discovery of kilo.json/kilo.jsonc and .kilo/.kilocode config directories by Core (discovery.ts only .opencode/opencode.json), well-known org and console-managed org config sources, MDM/managed directory loading, primary-checkout config directories for worktrees, retireIndexingFlag/legacy Kilo config compatibility warnings, dispose-skipping for permission-only updates, extra .gitignore entries (pnpm-lock/yarn.lock/agent-manager.json). Evidence: L13: import { applyEdits, findNodeAtLocation, modify, parseTree } from "jsonc-parser" // kilocode_change - parseTree/findNodeAtLocation used in patchJsonc / L16: // kilocode_change start | +| `packages/opencode/src/config/managed.ts`
    23,25,27 | shared-upstream patch marker | Configuration/settings | needs port | Kilo paths in `packages/kilo-cli/src/paths.ts` and Core config discovery (`config/discovery.ts` has no managed/system directory) | Markers point managed config to /Library/Application Support/kilo, ProgramData\kilo, /etc/kilo. V2 Core discovery reads global/explicit/project sources only (discovery.ts:24-70; grep ProgramData|/etc/kilo: none), so enterprise managed config has no location at all, let alone a Kilo-branded one. Evidence: L23: return "/Library/Application Support/kilo" // kilocode_change / L25: return path.join(process.env.ProgramData || "C:\\ProgramData", "kilo") // kilocode_change | +| `packages/opencode/src/config/markdown.ts`
    4,57,59,70,75,77,80,85,97,100,109 | shared-upstream patch marker | Configuration/settings | partial | `packages/core/src/config/markdown.ts` | PRESERVED — frontmatter parsing with substitution exists natively (config/markdown.ts uses ConfigVariable). MISSING/unverified — preserving unquoted colon values as exact strings, retrying invalid frontmatter with permissive sanitization, and trust/fileScope confinement for `{file:}` in project markdown (variable.ts has no trust gating). Paired test/config/markdown.test.ts pending. Evidence: L4: import { KilocodeMarkdown } from "../kilocode/config/markdown" // kilocode_change / L57: // kilocode_change start - preserve unquoted colon values as exact strings | +| `packages/opencode/src/config/paths.ts`
    29,35 | shared-upstream patch marker | Configuration/settings | partial | "`packages/kilo-cli/src/project-config.ts:32` (`["".kilo"", "".kilocode""]`) for Kilo settings; Core `config/discovery.ts:41` still scans `.opencode`/opencode.json only" | Kilo settings/project config on v2 read .kilo and .kilocode directories, but Core config discovery for agents, commands, skills and opencode.json does not include .kilo/.kilocode names, so project-local Kilo agent/command definitions are not discovered natively (config-mapping-v2-gap covers import, not discovery). Evidence: L29: targets: [".kilocode", ".kilo"], // kilocode_change / L35: targets: [".kilocode", ".kilo"], // kilocode_change | +| `packages/opencode/src/config/tui-migrate.ts`
    2,12,91,99,127,132 | shared-upstream patch marker | Configuration/settings | not needed | `packages/tui/src/config/v1/index.tsx` (v1 TUI config compatibility) and `packages/kilo-cli/src/import-v1-config.ts` | Markers migrate legacy TUI config files into the kilo directory with the app.kilo.ai schema URL. V2 handles v1 TUI config via the retained v1 schema and explicit Kilo import; in-place migration of the legacy layout is intentionally not performed (copy-then-migrate policy), so this shim is not carried. Evidence: L2: import { access, constants } from "fs/promises" // kilocode_change / L12: const TUI_SCHEMA_URL = "https://app.kilo.ai/tui.json" // kilocode_change | +| `packages/opencode/src/config/tui.ts`
    24,44,101,108,110,114,142,144,159,162,164,166,186,189,202,208,214,217,224,227,233,244,246,252,256,257,287,293 | shared-upstream patch marker | Configuration/settings | partial | `packages/tui/src/config/index.tsx` + `config/v1/index.tsx:122` (sound_pack default opencode.default), Kilo plugin registration in `packages/kilo-cli/src/tui-plugin/tui.tsx` | PRESERVED — Kilo default TUI plugins are registered by the Kilo host (tui.tsx kilo.preview plugin) rather than injected via config. MISSING — `kilo.default` sound pack default (v2 defaults to opencode.default; attention row partial already notes missing Kilo pack), loading tui.json from Kilo config directories (.kilo/.kilocode, KILO_CONFIG_DIR), trust-gated {env:}/{file:} substitution, `info()` editable config for Kilo Console. Evidence: L24: import { KilocodeDefaultPlugins } from "@/kilocode/config/default-plugins" // kilocode_change / L44: readonly info: () => Effect.Effect // kilocode_change - editable config for Kilo console | +| `packages/opencode/src/config/variable.ts`
    6,19,21,26,27,30,42,47,52,53,77,79,84,101,107,115,120,141,143 | shared-upstream patch marker | Configuration/settings | needs port | `packages/core/src/config/variable.ts:27-60` (`substitute`/`substituteFiles`) — Kilo hardening would need an upstream-facing patch or a Kilo config plugin gate | Markers make untrusted project config unable to read env vars, confine `{file:}` to the project root, reject server-credential substitution, skip commented tokens, and read one opened file to avoid TOCTOU. V2 substitute() resolves `{env:}` from process.env for all sources and resolves `{file:}` to any absolute or config-relative path with only the commented-line skip present (variable.ts:44-58). The trust/confinement invariants are absent. Evidence: L6: import { ConfigVariableGuard } from "@/kilocode/config/variable" // kilocode_change / L19: // kilocode_change start | +| `packages/opencode/src/effect/app-runtime.ts`
    7,17,49,56,59,64,65,66,67,68,70,73,77,126,133 | shared-upstream patch marker | Package behavior indicated by path | not needed | `packages/kilo-cli/src/runtime.ts`/`interactive-server.ts` host composition | Markers keep Credential/ModelCache/Memory/EventV2/etc. in the v1 AppRuntime layer graph. V2 Kilo composes services through the host and plugin contexts; this v1 layer-graph glue has no counterpart. Evidence: L7: import { Credential } from "@opencode-ai/core/credential" // kilocode_change / L17: import { ModelCache } from "@/provider/model-cache" // kilocode_change | +| `packages/opencode/src/effect/bridge.ts`
    6,15,24,37,46,51,53,57,62,76,78,82,91 | shared-upstream patch marker | Package behavior indicated by path | not needed | None; v2 Kilo code is Effect-native with Location services | Markers restore legacy Instance/workspace ALS contexts around Effect runs for callback code. V2 has no legacy ALS instance context. Evidence: L6: import { Instance, type InstanceContext } from "@/kilocode/instance" // kilocode_change / L15: // kilocode_change start - preserve legacy Kilo contexts across Promise callbacks | +| `packages/opencode/src/effect/instance-state.ts`
    2,15 | shared-upstream patch marker | Package behavior indicated by path | not needed | None (v2 uses `Location.Service`/`InstanceRef` natively) | Two markers fall back to a captured legacy instance context; v1-only compatibility. Evidence: L2: import { capture, type InstanceContext } from "@/kilocode/instance" // kilocode_change / L15: const ctx = (yield* InstanceRef) ?? capture() // kilocode_change | +| `packages/opencode/src/effect/run-service.ts`
    7,8,29,41,59,65 | shared-upstream patch marker | Package behavior indicated by path | not needed | None; v2 service lifetimes are scoped Layers | Markers bridge the ALS instance context and allow Kilo runtimes to release resources. V2 Kilo services use scoped layers/plugin lifetimes. Evidence: L7: import { context as instanceContext } from "@/project/instance-context" // kilocode_change / L8: import { LocalContext } from "@/util/local-context" // kilocode_change | +| `packages/opencode/src/effect/runner.ts`
    2,84,94,96,110,119,147 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/core/src/session/run-coordinator.ts` and execution claim (write-ahead claim marks busy period per AGENTS Session Core) | Markers ensure work publishes busy only after the Running state is committed. V2 SessionRunCoordinator commits a durable execution claim before running and publishes execution events from the coordinator, matching the ordering invariant. Evidence: L2: import { KiloRunner } from "@/kilocode/effect/runner" // kilocode_change / L84: // kilocode_change start - do not let work publish busy before the Running state is committed | +| `packages/opencode/src/effect/runtime-flags.ts`
    20,23,25,45,53 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/src/skill-shell.ts` (kill-switch via config), `packages/kilo-cli/src/paths.ts` env handling | PRESERVED — skill-shell disable exists as Kilo policy configuration (skill-shell parity baseline). MISSING — KILO_DISABLE_CHANNEL_DB, KILO_SKIP_MIGRATIONS, experimental Scout and session-switcher flags (features absent in v2). Evidence: L20: disableChannelDb: bool("KILO_DISABLE_CHANNEL_DB"), // kilocode_change / L23: disableSkillShell: bool("KILO_DISABLE_SKILL_SHELL"), // kilocode_change - disable shell injection in skill bodies | +| `packages/opencode/src/event-v2-bridge.ts`
    6,7,41,42,44,46,49,53,63,75 | shared-upstream patch marker | Package behavior indicated by path | not needed | None; v2 event stream is native EventV2 | Markers encode EventV2 payloads for legacy bus and SSE consumers and tag instance-less events as global. V2 has no legacy consumers. Evidence: L6: import { EventManifest } from "@/event-manifest" // kilocode_change / L7: import * as EventWire from "@/kilocode/event-wire" // kilocode_change | +| `packages/opencode/src/format/index.ts`
    12,89,90 | shared-upstream patch marker | Package behavior indicated by path | needs port | `packages/core/src/formatter.ts`/`formatter/*` spawn environment (no env scrubbing found) — Kilo could apply via sandbox env policy in `packages/kilo-cli/src/sandbox.ts` | Markers run formatters with a model-facing sanitized environment (extendEnv:false). V2 formatter spawns inherit the process environment; sandbox.ts confines processes only when the sandbox is enabled and does not scrub credentials for formatters specifically. Evidence: L12: import { model as modelEnv } from "@/kilocode/process/env" // kilocode_change / L89: env: modelEnv(item.environment), // kilocode_change - formatters must not inherit backend credentials | +| `packages/opencode/src/id/id.ts`
    10 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/kilo-cli/src/remote-suggestions.ts` suggestion IDs | Marker adds a suggestion ID prefix. V2 Kilo suggestions generate their own IDs in the remote-suggestions holder; the upstream ID table need not change. Evidence: L10: suggestion: "sug", // kilocode_change | +| `packages/opencode/src/ide/index.ts`
    35,39 | shared-upstream patch marker | Package behavior indicated by path | not needed | None; v2 `packages/kilo-vscode` bundles/launches the CLI itself | Marker disables CLI-driven IDE extension installation because the Kilo extension bundles the CLI. Same architecture in v2; no install path to disable. Evidence: L35: // kilocode_change start - Kilo's VS Code extension bundles the CLI; auto-install from CLI is not applicable / L39: // kilocode_change end | +| `packages/opencode/src/image/image.ts`
    10,15,98 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/core/src/image.ts` (Image.Service normalization) | PRESERVED — image validation/normalization service exists (core/image.ts; session prompt normalizeImageAttachment). MISSING/unverified — 5 MiB shared cap constant and Photon-unavailable fallback preserving valid images (not found). Evidence: L10: export const MAX_BASE64_BYTES = 5 * 1024 * 1024 // kilocode_change - share user file pre-read limit / L15: // kilocode_change start - preserve valid in-limit images when Photon is unavailable | +| `packages/opencode/src/index.ts`
    5,25,32,33,34,37,39,51,53,84,85,92,103,109,120,123,157 | shared-upstream patch marker | Package behavior indicated by path | not needed | `packages/kilo-cli/src/index.ts` + `commands.ts` (Kilo owns its CLI entry on v2) | 17 markers thread KiloCli hooks into the upstream entrypoint. V2 Kilo has a separate CLI package with its own entry and command tree, so the upstream-entry patching is structurally unnecessary; individual Kilo commands are tracked by their own rows/plan phases. Evidence: L5: // kilocode_change - upstream account console intentionally omitted; KiloCli registers `kilo console` for local settings / L25: // kilocode_change - upstream web command intentionally omitted; Kilo does not ship an embedded web UI | +| `packages/opencode/src/installation/index.ts`
    16,24,51,139,140,141,142,143,160,188,210,214,218,231,240,259,271,274,284,298,305,317,325,332,334,337,343,348,365,368 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/src/updater.ts` (local portable staging/rollback; baseline updater-local-parity) and `App.useragent` via `packages/kilo-cli/src/host.ts:21` | PRESERVED — user agent derived from Kilo app identity; local updater with check/apply/rollback for the portable preview. MISSING — package-manager upgrade paths (brew tap/formula, npm/pnpm/bun global, choco, scoop) and Kilo release/install endpoints (updater.ts has no package-manager methods; publishing/release channels are out of scope per plan). Evidence: L16: // kilocode_change start / L24: // kilocode_change end | +| `packages/opencode/src/kilo-sessions/attached-state.ts`
    1,93,109,116,123,131,133,155,183,190,212,217,223,228,267,269,276,318,366,373 | Kilo-owned inherited marker | Remote attached-session ownership state | native equivalent | `packages/kilo-cli/src/remote-session.ts:118-126,386-460,1183-1221` and `test/remote-session.test.ts:924-1139,1555-1723` | The v2 relay owns authoritative heartbeat reconciliation, detach tombstones/fences, reconnect deduplication and fresh ownership removal; focused integration tests exercise ACK loss, reconnect and duplicate subscription behavior. Evidence: L1: // kilocode_change - extracted state machine that separates presence-owned / L93: // kilocode_change - collision-safe union key. The historical "\|" join | +| `packages/opencode/src/kilo-sessions/instance-advertisement.ts`
    1 | Kilo-owned inherited marker | Spawn-capable remote instance advertisement | deferred | No current v2 destination; intentionally outside the preview relay surface | This payload served the retired standalone spawn-capable `kilo remote` host. Current v2 advertises session capabilities but deliberately does not expose cloud-clone or instance-spawn metadata; revisit only if that product surface returns. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilo-sessions/kilo-sessions.ts`
    47,267,615,658,698,722,750,759,823,858,876,882 | Kilo-owned inherited marker | Session lifecycle/history/UI | partial | `packages/kilo-cli/src/remote-session.ts`, `remote-status.ts`, `remote-plugin.ts` (heartbeat with capabilities; baseline remote-v2-parity, remote-share-next-contracts) | Marked lines (12) cover: AppNodeBuilder shim (not needed on v2); module-level instance advertisement flag with default advertisement on enableRemote, immediate heartbeat on set/reconnect; per-session `platform` in heartbeat; attach/detach/has/ownedCount seams for exit_cli with negative-containment fence; lazy import cycle break. V2 remote-session/remote-plugin implement heartbeat (now with `capabilities.attachments`), attached-session state was assessed native (attached-state.ts row) and instance advertisement deferred (instance-advertisement.ts row). Preserved: attach/detach ownership and heartbeat; missing: advertisement flag/out-of-band heartbeat and per-session platform field pending the deferred advertisement row. Evidence: L47: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change / L267: // kilocode_change - K1 W1: module-level instance advertisement flag. | +| `packages/opencode/src/kilo-sessions/remote-command.ts`
    65,163,206 | Kilo-owned inherited marker | Remote command protocol | native equivalent | `packages/kilo-cli/src/remote-session.ts:863-873,1183-1228` and `test/remote-session.test.ts:189-248,924-1139` | V2's adapter preserves bounded protocol-v1 command listing, always advertises `canExitSession: true`, rejects uncatalogued commands, and treats `exit_cli` as validated session detach; current integration tests exercise those invariants. Evidence: L65: // kilocode_change - K1 W1: `canExitSession` is an INDEPENDENT producer / L163: // kilocode_change - K1 W1: always advertise `canExitSession: true`. This | +| `packages/opencode/src/kilo-sessions/remote-protocol.ts`
    13,21,46 | Kilo-owned inherited marker | Remote relay wire protocol | partial | `packages/kilo-cli/src/remote-protocol.ts` and `remote-session.ts` | V2 carries the strict protocol-v1 session/heartbeat/command schemas and platform-aware presence behavior, but intentionally omits the pinned spawn-capable instance advertisement because the preview adapter does not support cloud-session cloning or the standalone headless host. Evidence: L13: // kilocode_change - K1 W1: per-session platform advertises the platform the / L21: // kilocode_change - K1 W1: instance advertisement. Presence on a heartbeat | +| `packages/opencode/src/kilo-sessions/remote-sender.ts`
    13,52,81,85,91,93,104,152,155,160,162,164,273,284,285,323,324,327,621,708,796,886,887,929 | Kilo-owned inherited marker | Remote session command dispatcher | partial | `packages/kilo-cli/src/remote-session.ts` and `remote-suggestions.ts` | V2 ports command discovery, model listing, inline prompt admission, suggestion lifecycle, detach and redacted failure handling. The pinned in-process create_session spawn/rollback/inheritance path and instance-picker host surface are intentionally absent. Evidence: L13: import { Suggestion } from "@/kilocode/suggestion" // kilocode_change / L52: // kilocode_change start - create_session: strict v1 request with optional inheritance fields | +| `packages/opencode/src/kilo-sessions/remote-ws.ts`
    17,254,275 | Kilo-owned inherited marker | Remote WebSocket heartbeat fencing | partial | `packages/kilo-cli/src/remote-session.ts:118-126,257-347,386-460,1183-1221` | The v2 relay implements immediate/reconnect heartbeats and negative-containment detach fencing, but it does not propagate the pinned optional instance advertisement through heartbeat gathering and the wire. Evidence: L17: // kilocode_change - K1 W1: widened return type so the optional `instance` / L254: // kilocode_change - K1 W1: spread optional `instance` so the | +| `packages/opencode/src/kilocode/agent-manager/event.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | needs port | Kilo-owned schema module (e.g. `packages/schema/src/kilocode/agent-manager.ts`) emitted by the still-missing agent-manager tool; consumer contract already exists in `packages/kilo-vscode/src/agent-manager/tool-start.ts` (ToolRequest/ToolTask mirror AgentManagerStart/AgentManagerTask) | Marked file is the whole AgentManagerTask/AgentManagerMode/AgentManagerStart bus-event schema (requestID, worktree|local mode, tasks with prompt/name/branchName/model/variant). V2 has the transplanted extension consumer expecting exactly these fields, but no v2 Core/CLI producer or event definition exists (grep AgentManager in kilo-cli/core/schema: none); it depends on the agent-manager tool row already classified needs port. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/agent/index.ts`
    1,485,511 | Kilo-owned inherited marker | Package behavior indicated by path | partial | `packages/kilo-cli/src/agent-policy.ts` (Kilo agent policy) plus native v2 permission defaults; suggest tool permission at `packages/kilo-cli/src/remote-suggestions.ts` tool seam | "Only marked lines (L485, L511) add `suggest: ""allow""` to Kilo's default and build-agent permission rulesets; the new-file marker covers the fork's own Agent module. V2 agent-policy.ts owns Kilo agent behavior and baseline agent-policy-v2-parity records that seam, and the v2 suggest tool is Kilo-owned and registered without a per-agent permission gate, so the marked default has no exact counterpart: allowed-by-default is preserved in effect, but the explicit per-agent `suggest` permission rule does not exist in v2's permission vocabulary." Evidence: L1: // kilocode_change - new file / L485: suggest: "allow", // kilocode_change | +| `packages/opencode/src/kilocode/agent/options.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | needs port | Kilo agent-policy transform or a request-boundary hook in `packages/core/src/session/model-request.ts` (providerOptions assembly at :312-341) | Marked file strips id/displayName/source/reference/resolved from agent.options so strict providers (NVIDIA NIM 400) do not reject them. V2 model-request builds providerOptions from context only and no Kilo code writes displayName/source metadata into agent options (grep stripInternalOptions/INTERNAL_OPTION_KEYS: none). If the v2 Kilo agent policy re-introduces org/marketplace agent metadata on options, this boundary strip must be ported; today the invariant is unimplemented rather than natively covered. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/bash-hierarchy.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | needs port | Kilo permission policy over v2 `packages/core/src/permission.ts` saved rules (`permission/saved.ts`) | Marked file generates `npm *`, `npm install *`, exact-text always patterns from BashArity prefixes for the v1 permission 'always' reply. V2 permission saved rules (core/src/permission/saved.ts) store request-shaped rules and no v2 code expands a shell command into arity-level wildcard patterns (grep BashHierarchy/hierarch in kilo-cli/core: none besides unrelated model-prompt-policy). Invariant absent. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/bootstrap.ts`
    23 | Kilo-owned inherited marker | Package behavior indicated by path | not needed | None; v2 composes Kilo services through plugin `define`/Layer nodes in `packages/kilo-cli/src/interactive-server.ts` and `runtime.ts` | The only marked line is the `AppNodeBuilder` import used to build v1 LayerNode graphs for Kilo bootstrap; v2's Kilo bootstrap is the host composition in kilo-cli (interactive-server/runtime/daemon) using v2 plugin contexts, so this fork-specific effect-plumbing shim has no v2 counterpart to port. The unmarked bootstrap responsibilities are tracked by their own feature rows. Evidence: L23: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change | +| `packages/opencode/src/kilocode/claw/client.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | needs port | New Kilo-owned TUI plugin module under `packages/kilo-cli/src/tui-plugin/` reusing `packages/kilo-cli/src/cloud/*` HTTP/WebSocket clients; extension counterpart exists at `packages/kilo-vscode/src/kiloclaw/` | Whole file: KiloClaw chat client wrapper (list/create/select/rename/delete conversations, WS subscription rotation, legacy ChatMessage translation). V2 CLI/TUI has no Claw surface (grep claw in kilo-cli/src: only presence-service.ts string); master plan lists Claw among remaining feature-specific services. The transplanted VS Code KiloClaw provider is an IDE consumer, not the TUI client. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/claw/hooks.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | needs port | Same new Kilo-owned TUI Claw plugin under `packages/kilo-cli/src/tui-plugin/` | Whole file provides Solid signals/resources over the Claw client for the TUI sidebar/dialogs; no v2 TUI Claw consumer exists (grep claw in packages/tui/src and kilo-cli tui-plugin: none). Port together with claw/client.ts. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/claw/kilo-chat-client.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | needs port | Kilo-owned client under `packages/kilo-cli/src/cloud/` (alongside client.ts/http.ts/websocket-stream.ts) | Whole file is the HTTP client for the kilo-chat worker (conversations, tokens, bot status). V2 cloud/ owns Kilo Cloud tRPC/stream-ticket/WebSocket clients but has no kilo-chat endpoints (grep claw/kilo-chat: none). Concrete missing transport for the Claw feature. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/claw/sidebar.tsx`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | needs port | Kilo-owned `sidebar.content` claim in `packages/kilo-cli/src/tui-plugin/` (same seam as sidebar-pr.tsx/sidebar-usage.tsx) | Whole file renders Claw status in the v1 TUI sidebar. V2 Kilo sidebar registration in tui-plugin/tui.tsx installs usage/memory/indexing/PR/processes/bench/routed-model/account sections only; no Claw section. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/claw/types.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | needs port | Kilo-owned schema in `packages/schema/src/kilocode/` or with the Claw plugin | Whole file defines Kilo Chat protocol types (ChatMessage, ConversationListItem, status/typing events). V2 kilo-vscode/src/kiloclaw/types.ts holds the extension's own types but nothing in CLI/TUI/schema; needed by the Claw TUI port. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/cli/cmd/tui/app.tsx`
    285 | Kilo-owned inherited marker | User-interface behavior | partial | `packages/tui/src/app.tsx` / `packages/tui/src/routes/session/index.tsx` auto-approve command and `packages/kilo-vscode/src/kilo-provider/auto-approve.ts` | Marked lines (L285-287) document that the /auto-approve toggle writes an allow-everything rule to global config so VS Code/JetBrains/headless runs stop prompting too. V2 TUI has an auto-approve command (tui/src/app.tsx, config/keybind.ts) and the transplanted extension has its own auto-approve module, but I found no evidence the v2 TUI toggle persists a server-side global permission rule shared with other clients (v2 permission saved rules are per-request-shape in core/src/permission/saved.ts). Preserved: TUI toggle exists; missing: the cross-client global-config persistence invariant. Evidence: L285: // kilocode_change - the saved rule is server side, so it also stops VS Code, JetBrains and | +| `packages/opencode/src/kilocode/cli/cmd/tui/component/dialog-headless-link.tsx`
    1 | Kilo-owned inherited marker | No-display documentation-link dialog | needs port | Owned v2 TUI dialog/plugin route | No current v2 component presents the v1 no-display state with a readable documentation URL and keyboard/mouse close behavior; headless execution cancellation is a different consumer. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/cli/cmd/tui/context/nudge.tsx`
    1 | Kilo-owned inherited marker | Per-session soft maximum-cost alert | needs port | Owned v2 TUI context/plugin consuming durable session cost events | Current v2 shows usage but has no consumer that seeds cumulative message cost, rearms on idle→running, prompts Stop/Continue at a configurable threshold, snoozes dismissal, and aborts on Stop. Evidence: L1: // kilocode_change - new file: soft max-cost nudge wiring for the TUI | +| `packages/opencode/src/kilocode/cli/cmd/tui/feedback.ts`
    1 | Kilo-owned inherited marker | Per-message thumbs feedback telemetry | needs port | Owned v2 TUI action/plugin plus Kilo telemetry feedback event | No current v2 session action selects the latest non-reverted assistant message, gates on telemetry, adds Kilo session/message lineage, submits an up/down feedback event and reports success/error via toast. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/cli/logo.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | already ported | `packages/kilo-cli/src/tui-plugin/logo-data.ts` (`supports()`, modern/fallback artwork) and `logo.tsx` replacing `home.logo` in `tui-plugin/tui.tsx:33`; `test/logo.test.tsx` | Whole file: modern/fallback logo variants selected by `supports(env, platform)` (KILO_UNICODE_LOGO override, TERM=dumb, Windows terminal detection, ConEmu/ANSICON). V2 logo-data.ts reproduces the same supports() rules and both artwork sets, and logo.tsx renders them into the native home.logo slot with a test. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/cli/run-auto.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | partial | `packages/kilo-cli/src/run.ts` permission reply loop (:112-145) over native `permission.asked` events | Marked file tracks the root session plus Task-tool child sessionIDs so --auto approves (and suggestions are accepted from) exactly those sessions. V2 run.ts implements --auto by replying once to permission.asked, but filters `event.data.sessionID === sessionID` (root only) and has no child-session tracking, so subagent permission requests are neither approved nor auto-rejected-with-failure as in v1 (v1 run.ts L931-981). Preserved: root --auto; missing: tracked child scope and the failing auto-reject semantics. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/cli/server-urls.ts`
    1 | Kilo-owned inherited marker | Server/API integration | native equivalent | `packages/server/src/server-info.ts` (uses os.networkInterfaces for advertised addresses) | Whole file formats localhost plus non-internal IPv4 interface URLs (IPv6 bracket-safe) for `kilo serve` output. V2 server-info.ts enumerates networkInterfaces for the same purpose in the native server; the Kilo-specific wrapper is redundant. Bracketed IPv6 formatting is only relevant when hostname is IPv6, which the native code also handles via URL. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/cli/setup.ts`
    36 | Kilo-owned inherited marker | Package behavior indicated by path | not needed | `packages/kilo-cli/src/index.ts`/`commands.ts` (Kilo owns its own CLI entry on v2) | The single marked line is a comment stating that Kilo CLI customization is centralized here so upstream src/index.ts has few kilocode_change call-sites. On v2 Kilo owns a separate CLI package (packages/kilo-cli) and does not patch the upstream entrypoint, so the merge-minimization invariant is satisfied structurally. Individual v1 Kilo subcommands (cloud, daemon, profile, console, roll-call, dev-setup) are tracked by their own rows/plan phases, not by this marker. Evidence: L36: // (src/index.ts) only needs a handful of thin call-sites behind kilocode_change markers. | +| `packages/opencode/src/kilocode/command/override.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | native equivalent | v2 command editor `ctx.command.transform` (used by `packages/kilo-cli/src/review-policy.ts`) and Core command config merge | Whole file applies user config overrides (template/description/agent/model/variant/subtask) onto an existing command while recomputing hints. V2 exposes a command editor transform where config-defined commands override plugin defaults (review-policy comment: 'Config-defined commands can still override this default'), and Core `session/command.ts` resolves configured commands natively; no separate Kilo helper is required. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/commit-message/git-context.ts`
    136 | Kilo-owned inherited marker | Package behavior indicated by path | native equivalent | `packages/kilo-vscode/src/backend/generation.ts` / `src/util/process.ts` (windowsHide) and CLI generation-plugin | Only marked line sets `windowsHide: true` on the commit-message git spawn to avoid a cmd.exe flash. The v2 extension's generation and process utilities already pass windowsHide (grep windowsHide: kilo-vscode backend/generation.ts, util/process.ts, cli/src/server-process.ts). Commit-message generation itself lives in `packages/kilo-cli/src/generation-plugin.ts` via the Core git service rather than raw spawnSync. Evidence: L136: windowsHide: true, // kilocode_change - prevent cmd.exe flash on Windows | +| `packages/opencode/src/kilocode/components/dialog-claw-setup.tsx`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | needs port | Kilo-owned dialog via `ctx.ui`/dialog primitives in `packages/kilo-cli/src/tui-plugin/` (same seam as memory-dialog.tsx) | Whole file is the Claw setup dialog; no v2 TUI Claw dialog exists. Part of the Claw TUI port. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/components/dialog-claw-upgrade.tsx`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | needs port | Same Kilo-owned Claw TUI plugin | Whole file is the Claw upgrade prompt dialog; absent in v2 TUI. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/config-validation.ts`
    1 | Kilo-owned inherited marker | Configuration/settings | native equivalent | `packages/core/src/config/normalize.ts` + `packages/kilo-cli/src/settings.ts:732-739` | V2 settings writes run substitute/parse/normalize/decode verification and surface diagnostics before write, replacing the v1 owned validator. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/config/overlay.ts`
    136,191,195,245,256,259,269,271 | Kilo-owned inherited marker | Configuration/settings | native equivalent | `packages/kilo-cli/src/settings.ts` and SettingsRpc collection/field operations | V2 settings store provides global/project scope folding, revision checks, atomic writes, strict verification, and project-root confinement for config substitution; baseline settings audit identifies these consumers. Evidence: L136: // kilocode_change - project config is untrusted; confine {file:} reads to the project root / L191: // kilocode_change start - project agents untrusted, {file:} confined to the project root; global agents trusted | +| `packages/opencode/src/kilocode/plan-followup.ts`
    27 | Kilo-owned inherited marker | Package behavior indicated by path | not needed | None for the marker; plan flow itself lives in `packages/kilo-cli/src/plan-policy.ts` (baseline plan-v2-parity) | The single marked line imports the fork's AppNodeBuilder to build Agent/Provider runtimes lazily. V2 plan-policy is a plugin `define` with ctx services, so this layer-building shim is structurally unnecessary. The plan hand-over behavior is assessed under the plan tool rows (tool/plan.ts native equivalent). Evidence: L27: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change | +| `packages/opencode/src/kilocode/plugins/home-footer.tsx`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | partial | `packages/kilo-cli/src/tui-plugin/tui.tsx:68` (`home.footer` append) and `tui-plugin/remote.tsx` remote status UI | Whole file replaces upstream home_footer to render directory/branch, MCP counts, version plus RemoteIndicator. V2 appends a Kilo preview/account label to the native home.footer and remote.tsx exposes remote status through a dialog/command (`Remote control · Connected/Enabled/Disabled`), but no persistent remote indicator is rendered in the home footer, and Kilo does not replace the upstream footer contents. Preserved: Kilo footer slot; missing: always-visible remote connection indicator. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/plugins/model-usage.ts`
    95 | Kilo-owned inherited marker | Session-family model usage UI | native equivalent | `packages/core/src/session/stats.ts:100-182`, `packages/schema/src/kilocode/session-usage.ts:21-38`, and Kilo CLI usage UI | V2 derives durable per-model cost/token aggregates and root-plus-descendant session usage from projected data, exposes it through browser-safe RPC, and renders the resulting family usage; inherited markers are unnecessary in owned destinations. Evidence: L95: // When samples carry `elapsedMs` (kilocode_change: persisted on the | +| `packages/opencode/src/kilocode/plugins/sidebar-pr.tsx`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | already ported | `packages/kilo-cli/src/tui-plugin/sidebar-pr.tsx` installed from `tui.tsx` (`installPrSidebar`), documented in `kilocode/baseline/sidebar-pr-v2-parity.md` | Whole file: sidebar section resolving the current branch's PR via cached gh probe. Baseline doc records a source-backed, isolated-host-tested port of this exact v1 file onto the native sidebar.content slot. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/provider/provider.ts`
    1,7,270 | Kilo-owned inherited marker | Provider/model routing or UI | partial | Split across `packages/kilo-gateway/src/{plugin,models,gateway}.ts`, `packages/ai/src/kilocode/`, and native Core provider plugins | Gateway catalog fields, privacy request policy, Auto routing, variants and account refresh are source-verified in v2; the mixed v1 file also contains non-Gateway custom-loader behaviors that are not all reconciled in this batch. Evidence: L1: // kilocode_change - new file / L7: // calls at well-defined injection points (each marked with kilocode_change). | +| `packages/opencode/src/kilocode/review/worktree-diff.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | needs port | Kilo-owned review data source over Core `packages/core/src/git.ts` / `plugin/vcs/git.ts` (`git diff --unified` :549/:383) feeding `packages/kilo-cli/src/review-policy.ts` | Whole file computes WorktreeDiff items (before/after, tracked, generatedLike, summarized, stamp) honoring ignore rules for the review command. V2 review-policy only rewrites the prompt text (`reviewPrompt`) and relies on the model to gather diffs; Core has git diff primitives but no equivalent generated-file/summarization classification or WorktreeDiff schema (grep WorktreeDiff/cumulative: none). Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/server/httpapi/server.ts`
    10,12,69 | Kilo-owned inherited marker | Server/API integration | not needed | Presence is Kilo-owned in `packages/kilo-cli/src/presence-service.ts`/`kilo-presence.ts`; v2 host composition in `interactive-server.ts` | Marked lines import AppNodeBuilderV1 (because v1 defaultLayer aliases were removed) and provide KiloViewers.defaultLayer into the fork's HTTP server layer stack. V2 does not fork the upstream HttpApi server; Kilo presence is composed as a separate service in kilo-cli (presence-service.ts), so these layer-wiring shims have no counterpart. Presence semantics themselves are tracked by the presence rows/baseline, not this marker. Evidence: L10: import { AppNodeBuilderV1 } from "@/effect/app-node-builder-v1" // kilocode_change - defaultLayer aliases are gone / L12: import { KiloViewers } from "@/kilocode/presence/service" // kilocode_change | +| `packages/opencode/src/kilocode/server/provider-auth-lifecycle.ts`
    3,13,18 | Kilo-owned inherited marker | Provider/model routing or UI | partial | Gateway `packages/kilo-gateway/src/plugin.ts:567-568` plus CLI TUI/remote credential event consumers | Credential changes refresh scoped catalogs and downstream clients, replacing global instance disposal; the specific v1 presence invalidateAuth side effect has not yet been reconciled with the v2 presence service. Evidence: L3: import { KiloViewers } from "@/kilocode/presence/service" // kilocode_change / L13: // kilocode_change start - drop the old presence socket; callers invoke this for the "kilo" provider only | +| `packages/opencode/src/kilocode/server/reference-reconciler.ts`
    8,19 | Kilo-owned inherited marker | Server/API integration | not needed | None; v2 has no ported reference reconciler (grep ReferenceReconciler: none), and the marker is only the v1 plugin-readiness wait | Marked lines import PluginV2 and wait for `core/config-reference` before syncing references; this is v1 effect-runtime ordering glue, not product behavior. The underlying Kilo reference (Scout) feature is not present in v2 and is not marked here, so the marker itself has nothing to port. Evidence: L8: import { PluginV2 } from "@opencode-ai/core/plugin" // kilocode_change / L19: yield* (yield* PluginV2.Service).wait(PluginV2.ID.make("core/config-reference")) // kilocode_change | +| `packages/opencode/src/kilocode/server/server.ts`
    1,3 | Kilo-owned inherited marker | Server/API integration | native equivalent | v2 server OpenAPI generation in `packages/server` plus Kilo app-name/branding hooks in `packages/kilo-cli` | File only exports DOC_TITLE 'kilo' and DOC_DESCRIPTION 'kilo api' for the upstream OpenAPI document. V2 Kilo does not patch upstream server.ts; the preview uses its own branding via the CLI/app-name hooks recorded in the master plan (Kilo Gateway/app-name hook), so a doc-title constant is not a behavior gap. Evidence: L1: // kilocode_change - new file / L3: // Imported by ../../server/server.ts with minimal kilocode_change markers. | +| `packages/opencode/src/kilocode/session-export/org-sources.ts`
    5 | Kilo-owned inherited marker | Session lifecycle/history/UI | not needed | None for the marker; org selection lives in `packages/kilo-gateway/src/account.ts` and `packages/kilo-cli/src/indexing.ts` | Single marked line is the AppNodeBuilder import for building Config runtime. V2 Kilo services read organization state through the Gateway account service directly; session export itself is a separate unported feature not covered by this marker. Evidence: L5: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change | +| `packages/opencode/src/kilocode/session-import/service.ts`
    4,48 | Kilo-owned inherited marker | Session lifecycle/history/UI | not needed | `packages/kilo-cli/src/import-v1-session.ts` (Kilo v1 session-store copy) and Core `session/revert.ts` types | Marked lines only adopt the shared `SessionMessage.ID` brand for revert state when inserting imported sessions. V2 import-v1-session.ts copies the store without re-typing rows and Core owns revert typing, so this brand-alignment patch has no counterpart to port. Evidence: L4: import { SessionMessage } from "@opencode-ai/core/session/message" // kilocode_change - shared Revert.State brand / L48: messageID: SessionMessage.ID.make(input.revert.messageID), // kilocode_change - shared Revert.State brand | +| `packages/opencode/src/kilocode/session-portability/cumulative-diff.ts`
    6 | Kilo-owned inherited marker | Session lifecycle/history/UI | not needed | None for the marker; cumulative session diff feature untracked in v2 (grep cumulativeSessionDiff: none) | Single marked line is the AppNodeBuilder import; the cumulative-diff behavior itself is unmarked v1 code consumed by kilo-sessions remote and is not part of this marker row. Evidence: L6: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change | +| `packages/opencode/src/kilocode/session/cost-propagation.ts`
    1 | Kilo-owned inherited marker | Descendant-session cost accounting | native equivalent | `packages/core/src/session/stats.ts` plus `packages/kilo-cli/src/session-usage.ts` | V2 reads durable session-family usage instead of mutating parent-message costs, preserving descendant-inclusive totals without the v1 process-local lock and lost-update risk. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/session/metrics.ts`
    1,28,47 | Kilo-owned inherited marker | Model throughput metrics | native equivalent | `packages/tui/src/routes/session/rows.ts` and `packages/tui/test/cli/tui/session-rows.test.ts:29-52` | Current TUI measures output throughput across model steps from stream timing, excludes tool time, and omits it when the stream boundary is unavailable; focused current tests cover both cases. Evidence: L1: // kilocode_change - new file / L28: // kilocode_change start - tokens/second throughput for #6579. | +| `packages/opencode/src/kilocode/session/processor.ts`
    1 | Kilo-owned inherited marker | Session lifecycle/history/UI | partial | `packages/kilo-cli/src/telemetry.ts` and Core `session/runner/*`; offline handling tracked under session/network.ts (needs port) | Whole file is Kilo's processor side-module: ReviewCommand telemetry emission, SessionNetwork offline waits, and status updates. V2 has Kilo telemetry (telemetry.ts, telemetry-command.ts) and native runner status, but the review-command telemetry hook (grep ReviewCommand in kilo-cli: none) and offline/reconnect wait are absent (network.ts row is needs port). Preserved: generic telemetry/status; missing: review telemetry and network gating hooks. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/session/prompt.ts`
    1,265,278 | Kilo-owned inherited marker | Session lifecycle/history/UI | partial | Core `packages/core/src/permission.ts` rule evaluation; Kilo provenance reporting in `packages/kilo-cli/src/settings.ts`/`project-config.ts` (config provenance only) | Marked block (L265-278) tags agent/session permission rules with their origin before merge so the winning rule reports its true source (PermissionProvenance.classify). V2 permission evaluation (core/src/permission.ts) returns the matched saved rule without origin tagging, and v2 'provenance' code concerns config/settings scopes, not permission approvals (grep PermissionProvenance: none). Preserved: guard/hard permission concepts exist natively; missing: rule-origin provenance in approvals. Evidence: L1: // kilocode_change - new file / L265: // kilocode_change start - tag every rule with its true origin before merging, so the winning | +| `packages/opencode/src/kilocode/session/workflow-variant.ts`
    1 | Kilo-owned inherited marker | Workflow command variant resolution | needs port | Kilo-owned command/session admission policy seam | V2 supports model variants generally, but the pinned precedence among command.variant, matching agent.variant and inherited input variant is absent with the unported workflow-command override surface; ordinary TUI selection is not equivalent. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/skills/builtin.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | already ported | `packages/kilo-cli/src/skill-policy.ts` (`BUILTIN_LOCATION`, skill-policy-kilo-config.md imported with type text) — baseline skills-v2-parity | Whole file inlines built-in skill markdown at compile time and registers it before discovery so user skills override. V2 skill-policy imports the builtin skill markdown as text, registers it after config discovery, and reports collisions; the reserved builtin cannot be replaced by project definitions (comment L25-26), which is the v2 policy decision for the same seam. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/snapshot/diff-full.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | native equivalent | `packages/core/src/plugin/vcs/git.ts:383-401` and `core/src/git.ts:549` (`git diff --unified=N`, batched and capped) | Whole file shells out to `git diff --unified=3` for snapshot patches to avoid O(N*M) JS diffs. V2 Core snapshot/VCS diffs already run git diff with unified context, batched per invocation and capped (vcs/git.ts:49), so the performance invariant is native. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/snapshot/index.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | native equivalent | `packages/core/src/snapshot.ts` | Two-line barrel re-exporting track/diff-full; assessed through those rows. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/snapshot/track.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | partial | `packages/core/src/snapshot.ts` capture (`maximumUntrackedFileBytes: 2 MiB` :126, warning-and-skip :131) plus Kilo project-config writer `packages/kilo-cli/src/project-config.ts` | Whole file forks Snapshot.track with a timeout, asks the user to continue/disable snapshots for the project (writing `snapshot:false` to .kilo/kilo.json without disposing the instance), and injects an 'Initializing snapshot…' part. V2 snapshot capture bounds untracked file size and swallows failures with a warning, which mitigates huge-repo stalls, but there is no timeout-driven question, per-project disable persistence, or transient progress part (grep in core/snapshot.ts: no timeout/prompt). Preserved: bounded capture; missing: interactive guard. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/storage/json-migration.ts`
    17 | Kilo-owned inherited marker | Package behavior indicated by path | not needed | None; v2 owns explicit v1 import in `packages/kilo-cli/src/import-v1-session.ts`/`import-v1-config.ts` | Single marked line imports AppNodeBuilder for the v1 JSON→SQLite migration runtime. V2 never migrates legacy JSON storage in-process (import is a copy-then-migrate host command), so the shim and its target have no v2 counterpart. Evidence: L17: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change | +| `packages/opencode/src/kilocode/suggestion/tui/bar.tsx`
    2 | Kilo-owned inherited marker | Remote suggestion presentation | native equivalent | `packages/kilo-cli/src/remote-session.ts:595-622,1297-1334` plus Kilo VS Code suggestion handler/UI | V2 emits replayable shown/accepted/dismissed frames to the subscribed remote viewer, while VS Code routes pending cards and accept/dismiss actions through its owned UI seam; no inherited marker is needed. Evidence: L2: // kilocode_change - new file | +| `packages/opencode/src/kilocode/system-prompt.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | partial | `packages/core/src/plugin/system-prompt.ts` (Kilo-modified per git status) and `packages/kilo-cli/src/model-prompt-policy.ts` | Whole file composes Kilo system-prompt sections from editor context (staticEnvLines) and Global paths. V2 has Kilo prompt policy (model-prompt-policy.ts, baseline model-prompt-policy-v2-parity) and a Kilo-touched core system-prompt plugin, but no editor-context injection producer (grep staticEnvLines/EditorContext in kilo-cli/core: none; hits are TUI editor UI). Preserved: prompt selector/policy seam; missing: IDE editor-context environment lines. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/tool/agent-manager.ts`
    1 | Kilo-owned inherited marker | Interactive multi-session agent manager | needs port | New owned v2 orchestration tool, durable manager service and TUI card surface | Native subagents cover child execution, but no v2 consumer implements this tool's worktree/local batch start, section assignment, list/filter, prompt/stop/move protocol, model search, or manager event UI. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/tool/chart.ts`
    1 | Kilo-owned inherited marker | Chart rendering tool | needs port | New owned v2 tool plus typed tool-result/UI rendering seam | No v2 tool accepts and validates Chart.js configuration, records chart telemetry, or returns chart metadata for a dedicated renderer; generic MCP text-content handling is not equivalent. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/tool/generate-image.ts`
    1 | Kilo-owned inherited marker | Image generation/editing tool | needs port | New owned v2 tool integrated through Core tool registry and Kilo Gateway/account policy | V1 resolves Kilo/OpenRouter credentials, submits multimodal image requests, validates data URLs and writes authorized output paths. No current v2 consumer implements this tool or these boundaries. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/tool/model-search.ts`
    1 | Kilo-owned inherited marker | Provider/model routing or UI | native equivalent | CLI native model picker search plus original VS Code `webview-ui/src/components/shared/model-selector-utils.ts:72-111` | Both shipped clients implement punctuation-insensitive collapsed model/provider matching; the v1 agent-manager helper is replaced by client-owned consumers rather than a backend tool helper. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/tool/send-file.ts`
    54,98 | Kilo-owned inherited marker | Remote file-delivery tool | needs port | New owned v2 tool and typed delivery-only attachment outcome | The v1 tool gates on a live remote session, applies external-directory plus read authorization before target inspection, enforces size/MIME rules and emits delivery-only data attachments. V2 accepts user multipart input but has no equivalent outbound `send_file` tool. Evidence: L54: // kilocode_change start — authorize missing and directory paths with the same / L98: // kilocode_change end | +| `packages/opencode/src/kilocode/tool/task.ts`
    1 | Kilo-owned inherited marker | Kilo task/subagent policy | partial | `packages/core/src/tool/plugin/subagent.ts:129-145`, config normalization/migration, and Kilo plan/model policy | V2 has native durable subagent execution, configurable depth and Kilo planning restrictions. The pinned helper additionally applies persisted per-agent model/variant precedence and inherited mutation/MCP denial ceilings; exact parity is not established. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/tool/websearch-kilo-exa.ts`
    1 | Kilo-owned inherited marker | Authenticated Kilo Exa gateway search | needs port | Owned Kilo web-search provider registered in the v2 WebSearch abstraction | The marked file posts to Kilo `/api/exa/search` with the Kilo bearer token, caps results at 10, requests highlights, distinguishes 401/403 login failures, applies a 25-second timeout and decodes/formats the Exa response. V2's Tavily provider has different endpoint, credentials, request/result shape and error semantics; generic provider selection is not equivalent. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/ts-check.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | needs port | Kilo-owned diagnostics provider over v2 LSP/diagnostics tool seam in `packages/core` (no v2 LSP module found under core/src) | Whole file collects TS diagnostics by shelling to tsgo/tsc for edited files. V2 Core has no LSP or tsgo integration (grep tsgo/typescript-language-server: none), so neither the low-memory invariant nor diagnostics after edits exist. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/kilocode/ts-client.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | needs port | Same Kilo-owned diagnostics seam as ts-check.ts | Whole file: shell-out TypeScript diagnostic client (~50MB peak vs ~500MB LSP). Absent in v2 (no LSP/diagnostics module). Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/lsp/launch.ts`
    3,14,15 | shared-upstream patch marker | Package behavior indicated by path | not needed | None yet; v2 has no LSP runtime (`packages/core/src/tool/plugin/edit.ts:107` TODO) | Markers sanitize LSP server env. V2 has no LSP launch path at all; when LSP lands the env scrubbing invariant should be re-evaluated with format/index.ts (needs port). Evidence: L3: import { model as modelEnv } from "@/kilocode/process/env" // kilocode_change / L14: env: modelEnv(cfg?.env), // kilocode_change - language servers must not inherit backend credentials | +| `packages/opencode/src/lsp/lsp.ts`
    14,262,275 | shared-upstream patch marker | Package behavior indicated by path | needs port | Future v2 LSP/diagnostics seam (none today) | Markers route TypeScript diagnostics to the Kilo tsgo client (ts-client.ts row needs port). V2 lacks any LSP/diagnostics runtime. Evidence: L14: import { TsClient } from "../kilocode/ts-client" // kilocode_change / L262: // kilocode_change start - use lightweight tsgo-based client when persistent LSP is not enabled | +| `packages/opencode/src/lsp/server.ts`
    5,17,20,119,144,989,993,1027,1029,1257,1462,1464 | shared-upstream patch marker | Server/API integration | needs port | Future v2 LSP runtime (none today) | Markers add tsgo support, reject remote release metadata as local executable paths (security), fixed local archive names and a jdtls option. No v2 LSP server module exists, so both the feature and its hardening are absent. Evidence: L5: import * as Log from "@opencode-ai/core/util/log" // kilocode_change / L17: import { TsCheck } from "../kilocode/ts-check" // kilocode_change | +| `packages/opencode/src/mcp/catalog.ts`
    85 | shared-upstream patch marker | Package behavior indicated by path | not needed | None (marker is a comment on `collect` used by sandbox network accounting) | Single marker comment; the sandbox network authority it references is a v1 design (v2 sandbox confines processes rather than classifying fetches). Evidence: L85: export function collect( // kilocode_change - distinguish collection from direct network fetch | +| `packages/opencode/src/mcp/index.ts`
    1,8,42,46,53,55,57,59,64,76,102,334,362,365,369,371,376,378,382,711,714,735,837,839,854,858,912,933,959 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/core/src/mcp/{index,stdio,oauth}.ts` (stderr drain stdio.ts:164-172, env stdio.ts:85) and `packages/kilo-cli/src/sandbox-mcp.ts` (connection-scoped fail-closed spawn hook) | PRESERVED — piped stderr consumed to avoid blocking (stdio.ts:164), local MCP confinement via Kilo sandbox spawn hook (sandbox-mcp.ts + Core McpClient.connect hook), OAuth flow exists (mcp/oauth.ts). MISSING/unverified — `--rm` injection for docker-run MCP servers (grep docker in core/mcp: none), client name `kilo` in MCP handshake (branding), scrubbing backend credentials from local MCP env (v2 passes options.environment; scrub not verified), routing remote MCP calls through the sandbox network authority (remote MCP explicitly not confined per sandbox acceptance note), deferred callback-port binding until redirect. Evidence: L1: // kilocode_change start / L8: // kilocode_change end | +| `packages/opencode/src/mcp/oauth-callback.ts`
    4,6,8,17,19,30,32,41,43,155,170 | shared-upstream patch marker | Authentication/account integration | partial | `packages/core/src/mcp/oauth.ts` and `core/src/oauth/page.ts` | PRESERVED — OAuth callback page and flow exist natively (oauth/page.ts renders offline-capable page). MISSING — Kilo branding blocks in the callback HTML and the Kilo-specific deferred callback binding delegate (see mcp/index row). Evidence: L4: import * as Log from "@opencode-ai/core/util/log" // kilocode_change / L6: import * as KiloOAuthCallback from "../kilocode/mcp-oauth-callback" // kilocode_change | +| `packages/opencode/src/permission/index.ts`
    2,4,9,10,12,14,15,20,22,25,50,52,64,66,73,76,79,83,88,92,99,114,125,126,148,154,162,163,169,170,188,192,194,196,213,215,217,219,223,225,228,233,234,239,243,245,249,257,265,266,272,273,274,282,290,300,333,335,338,350,362,370,400,459,461,480,482,490,512,514,539,544 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/core/src/permission.ts` (`evaluate` :87-99, `AskResult` :56, ask/assert :104-105) and `permission/saved.ts`; Kilo: `packages/kilo-cli/src/{skill-shell,agent-policy,tool-authorization}.ts` | 72 markers. PRESERVED — ask returns a typed result (AskResult) rather than void, saved rules persisted natively, delegated agents cannot answer prompts (agent-policy explore comment; subagent tool asserts parent permission), hard Ask/Plan denials are enforced by Kilo policy transforms (plan-policy/agent-policy add deny rules; whether saved approvals can override them is not proven). MISSING — deciding-rule provenance returned to clients for auto-approval explanation, session-scoped rulesets, skill-shell batches forcing a human reply and ignoring machine approvals (skill-shell.ts gating exists but the permission-service `skillShell` metadata contract is absent), protected config path 'allow→ask' override and persistence downgrade except one exact global skill, headless subagent asks failing instead of hanging (#11903; v2 run.ts auto-rejects only root), allowEverything global toggle, Agent Manager permission hardening, null delete sentinels, toConfig inverse. Evidence: L2: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change / L4: import * as Config from "@/config/config" // kilocode_change | +| `packages/opencode/src/plugin/azure.ts`
    5,41 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/core/src/plugin/provider/azure.ts:43-76` (resourceName prompt; baseURL accepted when configured) | Marker lets the auth form accept either a resource name or full endpoint. V2 Azure plugin prompts for resourceName and skips when settings.baseURL is configured (azure.ts:71), but the form itself does not offer an endpoint URL option; partial. Evidence: L5: // kilocode_change start - allow configuring either Azure resource name or full endpoint URL from the UI / L41: // kilocode_change end | +| `packages/opencode/src/plugin/digitalocean.ts`
    61,120,128 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/core/src/oauth/page.ts` shared branded page (`OauthCallbackPage`, WORDMARK :83) used by provider plugins | Markers retained a Kilo-branded callback page because the shared page was OpenCode-branded. V2 has one shared OauthCallbackPage with a single brand wordmark kept in sync with the app brand (page.ts:12 comment); provider plugins use it uniformly, so the per-provider branded HTML copies are unnecessary once the shared brand is Kilo (branding string itself is part of the identity/app-name hook, not this file). Evidence: L61: // kilocode_change start - retain Kilo-branded OAuth callback until the shared page supports Kilo branding / L120: // kilocode_change end | +| `packages/opencode/src/plugin/github-copilot/copilot.ts`
    315 | shared-upstream patch marker | Package behavior indicated by path | not needed | `packages/core/src/plugin/provider/github-copilot.ts` | Single marker is a comment on device flow; no behavior. Evidence: L315: // (See https://www.rfc-editor.org/rfc/rfc8628.html#section-3.5) // kilocode_change - avoid unstable redirect | +| `packages/opencode/src/plugin/github-copilot/models.ts`
    164,171 | shared-upstream patch marker | Provider/model routing or UI | needs port | `packages/core/src/plugin/provider/github-copilot.ts` model handling (grep opus/fable/sonnet: none) | Marker extends Copilot's model-family handling to newer Anthropic models (opus-4.8, fable, sonnet-5). V2 github-copilot plugin has no such family mapping, so those models would miss the opus-4.7 treatment. Evidence: L164: // kilocode_change start - treat opus-4.8, fable, and sonnet-5 like opus-4.7 / L171: // kilocode_change end | +| `packages/opencode/src/plugin/index.ts`
    29,30,31,70,71,72,79,80,83,112,113 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/src/interactive-server.ts`/`runtime.ts` registering `packages/kilo-gateway` plus Kilo policy plugins via v2 `define` | PRESERVED — Kilo Gateway auth plugin is bundled (kilo-gateway package registered by host), external plugins consume the v2 @opencode-ai/plugin API (no type bridge needed). MISSING — AtomicChat plugin and Anaconda Desktop provider plugin (grep atomic/anaconda in kilo-cli/gateway: none). Evidence: L29: import { KiloAuthPlugin } from "@kilocode/kilo-gateway" // kilocode_change / L30: import { AtomicChatPlugin } from "@kilocode/plugin-atomic-chat" // kilocode_change | +| `packages/opencode/src/plugin/install.ts`
    337 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/core/src/plugin/install.ts` (installs under .opencode) and Kilo layout `packages/kilo-cli/src/paths.ts` | Marker points plugin installation at `.kilo`. V2 Core plugin install uses the upstream directory; Kilo's kilo2 layout in paths.ts isolates state/config but plugin install roots were not rebranded (grep \.kilo in core/plugin: none). Evidence: L337: return path.join(root, ".kilo") // kilocode_change | +| `packages/opencode/src/plugin/loader.ts`
    14,15,164,165 | shared-upstream patch marker | Package behavior indicated by path | not needed | None; v2 Kilo indexing is a host plugin (`packages/kilo-cli/src/indexing.ts` + `packages/kilo-indexing`), not an installed npm plugin | Markers prevent install retries for the Kilo indexing and Atomic Chat plugin specs when loading. In v2 Kilo indexing is composed in-process by the host and Atomic Chat is absent, so the loader special-case has no subject. Evidence: L14: import { isIndexingPlugin } from "@kilocode/kilo-indexing/detect" // kilocode_change / L15: import { isAtomicChatPlugin } from "@/kilocode/atomic-chat-feature" // kilocode_change | +| `packages/opencode/src/plugin/openai/codex.ts`
    2,9,12,24,29,31,33,106,142,148,161,165,167,198,200,211,213,257,318,402,471,486 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/core/src/plugin/provider/openai.ts` (codexAllowed :23, codexDisallowed :24, originator :265, shared OauthCallbackPage :76-84) | PRESERVED — Codex allow/disallow model sets, OAuth device/callback flow, shared callback page. MISSING — Kilo's additional Codex model list (v1 markers add models beyond upstream), `originator: kilo` identity (v2 sends opencode), Kilo codex-refresh handling of expired auth (CodexAuthExpiredError). Paired test/plugin/codex.test.ts pending. Evidence: L2: import * as Log from "@opencode-ai/core/util/log" // kilocode_change / L9: import { refreshCodexAuth } from "@/kilocode/provider/codex-refresh" // kilocode_change | +| `packages/opencode/src/plugin/openai/ws-pool.ts`
    2,9,145,221,227 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/core/src/plugin/provider/openai.ts` (v2 Responses transport; ws pool not found) | Markers add Kilo logging and discard of leaked responses in the OpenAI websocket pool. V2 openai plugin has no websocket pool module (grep ws-pool: none); if v2 does not use the WS transport the fix is moot, but I could not confirm the transport choice, so partial. Evidence: L2: import * as Log from "@opencode-ai/core/util/log" // kilocode_change / L9: const log = Log.create({ service: "plugin.openai.ws" }) // kilocode_change | +| `packages/opencode/src/plugin/snowflake-cortex.ts`
    4 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/core/src/oauth/page.ts` shared page | Single marker imports the Kilo-branded OauthCallbackPage; v2 uses one shared branded page across providers (see digitalocean row). Evidence: L4: import { KiloOauthCallbackPage as OauthCallbackPage } from "@opencode-ai/core/kilocode/oauth/page" // kilocode_change - Kilo-branded callback page | +| `packages/opencode/src/plugin/tui/internal.ts`
    3,7,17 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-cli/src/tui-plugin/tui.tsx` (`kilo.preview` plugin) loaded through `packages/kilo-cli/src/tui.ts` packages.prepare allowlist | Marker wraps upstream internal TUI plugins with Kilo's. V2 Kilo host loads its own approved preview UI package (tui.ts:59 prepare guard) which registers logo/footer/sidebar/dialog claims. Evidence: L3: import { withKiloTuiPlugins } from "@/kilocode/plugins/internal" // kilocode_change / L7: // kilocode_change start | +| `packages/opencode/src/plugin/tui/runtime.ts`
    255,261,820 | shared-upstream patch marker | User-interface behavior | partial | `packages/tui/src/plugin/*` runtime and `packages/kilo-cli/src/paths.ts` (kilo2 XDG layout) | PRESERVED — Kilo v2 uses an isolated config/data layout (paths.ts) so TUI plugin state lands under the Kilo identity. MISSING — installing local themes into `.kilo`/`.kilocode` project directories and treating `.kilo/tui.json` as the config source (v2 TUI config path is native; theme install dir not verified as Kilo-scoped). Evidence: L255: // kilocode_change start - install local themes into supported Kilo config directories / L261: // kilocode_change end | +| `packages/opencode/src/plugin/xai.ts`
    5,91,126,128,141,290,380,431,440,449,461,492,494,639 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/core/src/plugin/provider/xai.ts:230` (`App.useragent(app)`), `oauth/page.ts` error rendering; app name set by `packages/kilo-cli/src/host.ts:21` | Markers set a kilocode UA/referrer and use branded, escaped error pages. V2 derives the UA from App metadata (host.ts sets the Kilo identity name) and renders errors through the shared OauthCallbackPage.error with escaping; referrer string remains `opencode` (branding only). Evidence: L5: import { escapeHtml } from "@/util/html" // kilocode_change - retained OAuth error page uses escaped diagnostics / L91: "User-Agent": `kilocode/${InstallationVersion}`, // kilocode_change | +| `packages/opencode/src/project/bootstrap.ts`
    9,12,31,34,40,45,49,62 | shared-upstream patch marker | Package behavior indicated by path | not needed | `packages/kilo-cli/src/interactive-server.ts` host registration | Markers add KilocodeBootstrap to the v1 instance bootstrap and quiet startup logs. V2 Kilo initializes its services as host plugins; sharing is a separate unaccepted row. Evidence: L9: // kilocode_change start - ShareNext init is handled by KilocodeBootstrap; upstream dropped File/FileWatcher bootstrap init / L12: // kilocode_change end | +| `packages/opencode/src/project/instance-runtime.ts`
    13,16 | shared-upstream patch marker | Package behavior indicated by path | not needed | None; v2 fixtures use Location-scoped hosts (`packages/kilo-cli/test/fixture.ts`) | Marker adds a fixture-friendly dispose ordering to the v1 instance runtime; v2 has no Instance runtime of that shape. Evidence: L13: // kilocode_change start - test fixtures dispose a directory's instance before deleting the directory / L16: // kilocode_change end | +| `packages/opencode/src/project/instance-store.ts`
    10,61,66,77,90,109,115,120,151,158,189,213 | shared-upstream patch marker | Package behavior indicated by path | not needed | None; v2 `packages/core/src/instance/*` and Location services replaced the v1 instance store | 12 markers adjust v1 InstanceStore lifecycle around legacy ALS contexts and disposers. V2 Kilo does not use that store. Evidence: L10: import { context as instanceContext, type InstanceContext } from "./instance-context" // kilocode_change / L61: // kilocode_change start - run bootstrap inside the Instance ALS so KilocodeBootstrap | +| `packages/opencode/src/provider/auth.ts`
    10,12,15,120,186,191,234,245,252 | shared-upstream patch marker | Provider/model routing or UI | partial | `packages/kilo-gateway/src/plugin.ts` (integration/credential handling, startup-cache.ts snapshot) and `packages/kilo-cli/src/telemetry.ts` | PRESERVED — Kilo credential lifecycle drives catalog reload and cached account snapshots (gateway plugin account()/startup-cache). MISSING/unverified — telemetry identity update on Kilo auth (grep identity in telemetry.ts not verified; telemetry-v2-overlap baseline lists gaps), the shared ModelCache service semantics (v2 catalog is Core-owned; kilo-gateway maintains its own snapshot). Evidence: L10: import { errorMessage } from "@/util/error" // kilocode_change / L12: // kilocode_change start | +| `packages/opencode/src/provider/error.ts`
    4,35,36,37,41,69,112,118,128,139,170 | shared-upstream patch marker | Provider/model routing or UI | partial | `packages/core/src/session/to-session-error.ts` and `runner/retry.ts` classification; Kilo hints would live in `packages/kilo-gateway/src/plugin.ts` or a Kilo error plugin | PRESERVED — v2 classifies provider errors (AIError with http headers/status) for retry and session errors natively. MISSING — Kilo-branded hints (`kilo auth login`), expired Copilot token reauth hint, unwrapping response.failed frames/bare error objects before envelope check, empty rate-limit stream normalization, KiloError.fallback clean rendering (grep KiloError/hint in core: none). Paired TUI error guidance rows were assessed needs port. Evidence: L4: import * as KiloError from "@/kilocode/provider/error" // kilocode_change / L35: const hint = KiloError.hint(providerID, e) // kilocode_change | +| `packages/opencode/src/provider/model-cache.ts`
    1,10,11,330 | shared-upstream patch marker | Provider/model routing or UI | native equivalent | `packages/kilo-gateway/src/startup-cache.ts` + `models.ts` (scoped catalog snapshots) and Core `catalog.ts`/`models-dev.ts` caching; baseline model-loading-v2-parity | Whole file caches Kilo/OpenRouter model lists across instances. V2 Gateway plugin persists per-scope startup snapshots (readSnapshot/writeSnapshot, retained on failed reads per Gateway regression fix) and Core caches models.dev; the v1 model-cache-effect test row was already assessed native equivalent. Evidence: L1: // kilocode_change - new file / L10: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change | +| `packages/opencode/src/provider/models.ts`
    1,10,103 | shared-upstream patch marker | Provider/model routing or UI | native equivalent | `packages/kilo-gateway/src/models.ts` (`catalogModels` :188, catalogEntries) feeding Core catalog via `ctx.catalog.transform` | Whole file assembles dynamic Kilo models around the upstream core catalog. V2 Gateway plugin fetches the scoped catalog and maps it into v2 catalog models/variants (models.ts:243-246 overlays→variants), replacing this service. Evidence: L1: // kilocode_change - new file / L10: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change | +| `packages/opencode/src/provider/provider.ts`
    13,27,33,49,151,250,268,271,276,288,480,481,491,501,502,512,618,639,866,876,903,1051,1057,1073,1080,1084,1096,1124,1278,1279,1283,1315,1445,1457,1527,1528,1530,1535,1537,1545,1552,1560,1601,1603,1605,1613,1626,1628,1633,1638,1642,1651,1691,1713,1791,1792,1796,1804,1805,1810,1818,1823,1828,1882,1883,1892,1893,1973,1981,2017,2020 | shared-upstream patch marker | Provider/model routing or UI | partial | `packages/kilo-gateway/src/plugin.ts:348-420` (kilo catalog/settings/headers/variants), `packages/core/src/plugin/provider/{kilo,azure,openrouter,...}.ts`, `packages/kilo-cli/src/routed-model*.ts` | 71 markers. PRESERVED — Kilo provider baseURL/org header/variants (gateway plugin), Kilo Auto routed model resolution and small-model fallback (routed-model rows; baseline model-loading), model description/metadata (KiloModels.Entry description/reasoning/family per board 60s), OAuth plugin providers (core plugin/provider/*). MISSING/unverified — 'Kilo Code' X-Title/X-Source/X-BILLING-INVOKE-ORIGIN/Cerebras/GitLab UA attribution headers for third-party providers (core kilo.ts sets opencode titles only; gateway headers() only adds org id), explicit Azure endpoint preference (core azure.ts not verified), null delete sentinels for provider/model config (config-mapping gap), OAuth-over-env credential precedence and Codex/config coexistence, keeping discovery failures visible (`failed` list; provider-list-failed-state test row native — so failed-state exists), first-byte timeout extension (header-timeout test row pending), `modelsEmpty` hint, per-provider Kilo privacy patch (prompt-training filter exists). Evidence: L13: import * as ModelsDev from "./models" // kilocode_change - assemble dynamic Kilo models around upstream core catalog / L27: import { optional, optionalOmitUndefined } from "@opencode-ai/core/schema" // kilocode_change | +| `packages/opencode/src/provider/transform.ts`
    8,9,56,167,170,331,348,373,384,394,422,489,507,559,569,580,675,682,686,690,702,728,746,754,797,798,799,810,821,825,828,829,836,841,845,849,856,857,861,1016,1027,1034,1038,1046,1138,1145,1148,1214,1322,1324,1339,1340,1434,1468,1495,1504,1560,1561,1564,1572,1579,1624,1629,1630,1688,1692,1921,1929 | shared-upstream patch marker | Provider/model routing or UI | partial | `packages/core/src/aisdk-native.ts` (reasoning/effort mapping :99-237) and `packages/kilo-gateway/src/models.ts` variants (:243-246 from catalog overlays) | 68 markers. PRESERVED — variants/reasoning efforts derived from catalog data (gateway models.ts maps source overlays to v2 variants; aisdk-native maps effort/thinking/budget per provider family), OpenRouter-shaped reasoning for the Kilo Gateway (v2 Kilo provider is @ai-sdk/openai-compatible at api.kilo.ai gateway). MISSING/unverified — explicit prompt-cache breakpoints (Anthropic/GPT-5.6+/alibaba) placed before trailing environment details (grep cache_control in aisdk: none), Ling model temperature/top_p/top_k defaults, Claude 5 alias adaptive-thinking detection, MiniMax thinking toggle, Bedrock Claude-only filtering, Gemini required-property schema fix, gpt-5 textVerbosity gating, reasoningSummary/encrypted reasoning include. Paired transform.test row pending. Evidence: L8: import { isLing } from "@/kilocode/model-match" // kilocode_change / L9: import { reasoningSummary } from "@/kilocode/provider/reasoning-summary" // kilocode_change | +| `packages/opencode/src/question/index.ts`
    6,53,62,93,105,109,111,118,124,169,176,178,182 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/core/src/form.ts` (form cancel/dismiss, terminal outcomes) and protocol `form` group | Markers add blocking questions, dismissAll for superseded prompts and guaranteed terminal events. V2 forms provide cancellation with terminal outcomes and the runner dismisses superseded prompts at delivery boundaries (prompt-dismiss-contract test row already assessed partial for remote specifics); paired question tests pending. Evidence: L6: import { KiloQuestion } from "@/kilocode/question" // kilocode_change / L53: blocking?: boolean // kilocode_change | +| `packages/opencode/src/server/auth.ts`
    19,40 | shared-upstream patch marker | Server/API integration | native equivalent | `packages/server/src/auth.ts:17,32` (username fixed to `opencode`) consumed by `packages/kilo-cli/src/daemon.ts:174` and `acp.ts:38` | Markers only rename the default/env-configurable basic-auth username from opencode to kilo (KILO_SERVER_USERNAME). V2 Kilo uses the native server auth with username `opencode` and its own daemon-managed password; clients (daemon.ts, acp.ts) are Kilo-owned and agree on that username, so the rename carries no behavior. Branding-only difference accepted by Kilo v2 daemon design. Evidence: L19: username: EffectConfig.string("KILO_SERVER_USERNAME").pipe(EffectConfig.withDefault("kilo")), // kilocode_change / L40: const username = credentials?.username ?? Flag.KILO_SERVER_USERNAME ?? "kilo" // kilocode_change | +| `packages/opencode/src/server/mdns.ts`
    11,12 | shared-upstream patch marker | Server/API integration | not needed | None; v2 server has no mDNS advertisement (grep mdns in packages/server/src: none) | Two markers rebrand the mDNS domain and service name. V2 removed mDNS discovery entirely (daemon registration file replaces it per kilo2 service contract), so no branding hook exists or is required. Evidence: L11: const host = domain ?? "kilo.local" // kilocode_change / L12: const name = `kilo-${port}` // kilocode_change | +| `packages/opencode/src/server/routes/instance/httpapi/api.ts`
    10,29,47,48,66,99,118 | shared-upstream patch marker | Server/API integration | partial | Kilo-owned RPC definitions `packages/kilo-cli/src/{memory,settings,privacy,indexing,session-usage,remote,cloud,model-state}-rpc.ts` via plugin `ctx.rpc` plus `packages/schema/src/kilocode/*` | Markers add Kilo groups (memory, indexing, presence, review, agent-builder, anaconda, branch-name, background-process, etc.) and legacy BusEvent payload schemas into the OpenAPI event union. V2 exposes Kilo APIs as plugin RPCs (memory-rpc, settings-rpc, indexing-rpc, session-usage-rpc, remote-rpc, model-state; baseline docs) without forking the upstream API, and Kilo events use native protocol events. PRESERVED: memory/indexing/settings/usage/remote/model-state/PR/processes surfaces; MISSING: agent-builder, anaconda-desktop, branch-name, review/worktree-diff, background-process, session-import/export, next-edit and console groups (no v2 RPC or route found). Evidence: L10: import { BusEvent } from "@/bus/bus-event" // kilocode_change - include legacy Kilo events until they migrate to EventV2 / L29: // kilocode_change start - Kilo HttpApi groups | +| `packages/opencode/src/server/routes/instance/httpapi/groups/config.ts`
    4,13,19,32,44,47,58 | shared-upstream patch marker | Configuration/settings | native equivalent | `packages/protocol/src/groups/config.ts` plus Kilo settings RPC (`packages/kilo-cli/src/settings-rpc.ts`) | Markers replace 'OpenCode' with 'Kilo' in endpoint descriptions and import Kilo config schema pieces. V2 does not fork the protocol wording; Kilo-only settings ride the settings RPC. Description branding is not a behavior. Evidence: L4: import { Schema } from "effect" // kilocode_change / L13: // kilocode_change start | +| `packages/opencode/src/server/routes/instance/httpapi/groups/experimental.ts`
    2,6,65,69,88,92,100,110,120,121,122,203,209,249,283 | shared-upstream patch marker | Server/API integration | partial | `packages/protocol/src/groups/worktree.ts` (list/create/remove/refresh) and `groups/vcs.ts:83` (vcs.diff); worktree diff/summary/file for review missing | Markers add WorktreeList description, worktree diff/diff-file/diff-summary routes backed by WorktreeDiff. V2 has native worktree list/create/remove and a vcs.diff endpoint, but no worktree diff summary/single-file detail endpoints tailored for the Agent Manager or /review (worktree-diff.ts row needs port). Evidence: L2: import { Snapshot } from "@/snapshot" // kilocode_change / L6: import { WorktreeDiff } from "@/kilocode/review/worktree-diff" // kilocode_change | +| `packages/opencode/src/server/routes/instance/httpapi/groups/global.ts`
    5,8,42,85,94,103,114,123,133,134 | shared-upstream patch marker | Server/API integration | partial | `packages/protocol/src/groups/{event,health,config}.ts`; indexing status in `packages/kilo-cli/src/indexing-rpc.ts` and sidebar-indexing (baseline indexing-v2-parity) | Markers are wording changes plus registering indexing.status and legacy BusEvent payloads on the global event schema. V2 keeps upstream wording; indexing status is delivered through Kilo RPC/sidebar rather than a global SSE event type. Preserved: indexing status surface; missing: legacy Kilo bus event payloads on the public event schema (intentionally not carried to v2 protocol). Evidence: L5: import { BusEvent } from "@/bus/bus-event" // kilocode_change - include legacy Kilo events until they migrate to EventV2 / L8: import "@/kilocode/indexing-event" // kilocode_change - register indexing.status before HttpApi event schemas | +| `packages/opencode/src/server/routes/instance/httpapi/groups/instance.ts`
    69,79,145,155,165 | shared-upstream patch marker | Server/API integration | not needed | None (v2 protocol wording unforked) | Five markers replace OpenCode with Kilo in descriptions only; no behavior. Evidence: L69: description: "Clean up and dispose the current Kilo instance, releasing all resources.", // kilocode_change / L79: description: "Retrieve the current working directory and related path information for the Kilo instance.", // kilocode_change | +| `packages/opencode/src/server/routes/instance/httpapi/groups/permission.ts`
    15,18,29,58,84 | shared-upstream patch marker | Server/API integration | partial | `packages/protocol/src/groups/permission.ts` reply payload and `packages/kilo-cli/src/skill-shell.ts` trust gating | Markers add an `interactive` (human-answered) boolean to permission replies that gates skill-shell approvals and import Kilo permission schemas. V2 permission.reply payload has no interactive flag (protocol permission group: reply once/always/reject only) and skill-shell.ts gates by trust/config; the human-answered distinction is not represented. Evidence: L15: interactive: Schema.optional(Schema.Boolean), // kilocode_change - human-answered flag; gates skill-shell approvals / L18: // kilocode_change start | +| `packages/opencode/src/server/routes/instance/httpapi/groups/question.ts`
    70 | shared-upstream patch marker | Server/API integration | not needed | None | Single marker sets the OpenAPI title to 'Kilo HttpApi'; wording only. Evidence: L70: title: "Kilo HttpApi", // kilocode_change | +| `packages/opencode/src/server/routes/instance/httpapi/groups/session.ts`
    41,42,78,90,119,133,156,227,258,260,460,473 | shared-upstream patch marker | Session lifecycle/history/UI | partial | `packages/protocol/src/groups/session.ts` (`session.fork` :260 with boundary payload; viewed :753) and vcs.diff | PRESERVED natively — viewed endpoint, fork endpoint (typed boundary payload; v1's bodyless-fork compatibility shim is obsolete). MISSING — `full`/single-file query on session diff for editor detail, Kilo wording (not a behavior). Fork-to-directory is tracked in the workspace-routing middleware row. Evidence: L41: ...Struct.omit(SessionSummary.DiffInput.fields, ["sessionID", "full"]), // kilocode_change - full is a query boolean / L42: full: Schema.optional(QueryBoolean), // kilocode_change - request full-content detail | +| `packages/opencode/src/server/routes/instance/httpapi/handlers/config.ts`
    2,8,12,31,35,38,45,47,50,56,68 | shared-upstream patch marker | Configuration/settings | needs port | `packages/kilo-gateway/src/plugin.ts` catalog/config transform (no default-model fetch today; grep defaultModel in kilo-gateway/kilo-cli: none) | Markers fetch the default model from the Kilo API when the kilo provider is authenticated and overlay it onto config responses. V2 Gateway plugin loads catalog/profile/balance and a routed 'Auto' model but does not read or apply a server-provided default model; consumers (TUI model picker, extension) pick from settings instead. Evidence: L2: // kilocode_change start - preserve Kilo API default model overlay / L8: // kilocode_change end | +| `packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts`
    2,7,13,24,27,34,38 | shared-upstream patch marker | Server/API integration | partial | `packages/kilo-gateway/src/plugin.ts` integration/credential handling and `packages/kilo-cli/src/presence-service.ts` | Markers route auth removal through Kilo's remove helper and drop the presence socket before instance disposal on Kilo auth changes. V2 Gateway plugin withdraws the provider on sign-out (reliability checkpoint: signed-out Gateway provider withdrawal) and presence-service is a separate Kilo service; explicit presence teardown ordering on auth change is not evidenced (presence scope fix was still in flight per latest session digest). Evidence: L2: // kilocode_change start / L7: // kilocode_change end | +| `packages/opencode/src/server/routes/instance/httpapi/handlers/event.ts`
    30,45 | shared-upstream patch marker | Server/API integration | not needed | None; v2 event stream is EventV2-native (`/api/event` server-wide) | Marker documents that GlobalBus mixes encoded EventV2 events, sync envelopes and Kilo legacy bus events. V2 has only native events; nothing to port. Evidence: L30: // kilocode_change start - GlobalBus includes encoded EventV2 events, sync envelopes, and Kilo's legacy / L45: // kilocode_change end | +| `packages/opencode/src/server/routes/instance/httpapi/handlers/experimental.ts`
    5,10,11,16,17,18,19,24,25,49,112,115,118,119,133,146,170,216,220,227,228,230,231,232,233,241,249,252,288,292 | shared-upstream patch marker | Server/API integration | partial | `packages/protocol/src/groups/worktree.ts` + Core worktree service; session list filters in Core `session/session.ts` (Location scoped); Agent Manager owned by `packages/kilo-vscode` | PRESERVED — worktree listing including whether managed (worktree.list/refresh natively), model info on requests. MISSING — worktree diff endpoints (WorktreeDiff), Review integration, session list with `worktrees`/`directories` roots and per-session worktree folder resolution across a project family (v2 lists by Location only; experimental-session-list test row exists separately), model.family in responses. Evidence: L5: import { EffectBridge } from "@/effect/bridge" // kilocode_change / L10: import { Provider } from "@/provider/provider" // kilocode_change | +| `packages/opencode/src/server/routes/instance/httpapi/handlers/file.ts`
    28,42 | shared-upstream patch marker | Server/API integration | not needed | `packages/protocol/src/groups/fs.ts` (v2 owns its response shape) | Marker keeps a released HTTP response shape while Core retained extra search metadata — a v1 compatibility constraint for released clients. V2 clients are generated from the v2 protocol; no compatibility shim needed. Evidence: L28: // kilocode_change start - preserve the released HTTP response shape while Core retains search metadata. / L42: // kilocode_change end | +| `packages/opencode/src/server/routes/instance/httpapi/handlers/global.ts`
    6,34,37,57,61,87,88,97,103 | shared-upstream patch marker | Server/API integration | partial | `packages/server/src` event route (`/api/event`) and Core event subscription lifetimes | Markers use a Kilo SSE helper to disconnect stale clients so full diff payloads are not retained. V2 event streaming is server-wide via Effect streams with scoped subscriptions; whether disconnected clients release buffered payloads was not verified in server source, so equivalence is not claimed. Evidence: L6: import { disconnect } from "@/kilocode/server/sse" // kilocode_change / L34: // kilocode_change start | +| `packages/opencode/src/server/routes/instance/httpapi/handlers/permission.ts`
    1,4,7,11,13,29,33,48,87 | shared-upstream patch marker | Server/API integration | partial | Kilo-owned permission policy over `packages/core/src/permission/saved.ts`; TUI auto-approve command in `packages/tui/src/app.tsx` | Markers implement AllowEverythingPermission (global saved rule toggled server-side) and pass `interactive` through replies. V2 has TUI-side auto-approve but no server RPC that persists an allow-everything global rule for all clients, and no interactive flag (see groups/permission row and kilocode tui app.tsx row). Evidence: L1: import { AllowEverythingPermission } from "@/kilocode/permission/allow-everything" // kilocode_change / L4: // kilocode_change start | +| `packages/opencode/src/server/routes/instance/httpapi/handlers/provider.ts`
    6,7,11,12,13,14,37,47,51,59,67,68,81,86,132,134,135 | shared-upstream patch marker | Provider/model routing or UI | partial | `packages/kilo-gateway/src/plugin.ts` catalog/integration transforms, `packages/kilo-cli/src/request-policy.ts` (`hidePromptTrainingModels`), provider-auth-lifecycle row (partial) | PRESERVED — hiding prompt-training models (request-policy.ts + settings-rpc hidePromptTrainingModels), Kilo catalog/model metadata via Gateway plugin, provider auth lifecycle partially (own row). MISSING — Anaconda Desktop provider overlay (grep anaconda: none), presence drop before instance disposal on OAuth callback, Kilo providerMetadata map for non-Gateway providers. Evidence: L6: import { mapValues, pickBy } from "remeda" // kilocode_change / L7: import { ModelCache } from "@/provider/model-cache" // kilocode_change | +| `packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts`
    1,2,3,4,5,7,43,68,109,116,231,302,304,315,331,334,340,345,346,347,409,419,448,453,467,483 | shared-upstream patch marker | Session lifecycle/history/UI | partial | `packages/protocol/src/groups/session.ts` (fork/viewed native), Core inbox (`session_inbox` cancel/change before delivery per AGENTS Session Core), `packages/kilo-cli/src/presence-service.ts` | PRESERVED — viewed transition and fork natively; deleting/cancelling queued prompts behind the active turn (v2 durable inbox items may be cancelled before delivery); interrupts not treated as errors (v2 outcome interrupted). MISSING — presence viewers integration on session routes (Kilo presence is a separate service; wiring to viewed unverified), typed user-image validation errors as 4xx, SQLite busy message mapping, full-content diff query, agent requirement errors. Evidence: L1: import { Image } from "@/image/image" // kilocode_change - classify user image validation defects / L2: import { busyMessage, isBusy } from "@/kilocode/database/sqlite-error" // kilocode_change | +| `packages/opencode/src/server/routes/instance/httpapi/middleware/authorization.ts`
    15,17,47,50,61,65,135,136,138 | shared-upstream patch marker | Server/API integration | native equivalent | `packages/server/src/auth.ts` (basic auth always required on v2 daemon; `packages/kilo-cli/src/daemon.ts:174` issues credentials to every client) | Markers fail closed on high-risk endpoints (e.g. allow-everything permission toggle) even when server auth is optional. V2 server auth is mandatory for all API routes via the daemon password, so the endpoint-specific escalation is subsumed by the stricter native policy. Evidence: L15: // kilocode_change start - require auth for high-risk permission toggles even when global auth is optional / L17: // kilocode_change end | +| `packages/opencode/src/server/routes/instance/httpapi/middleware/error.ts`
    3,20,32 | shared-upstream patch marker | Server/API integration | partial | `packages/core/src/database/database.ts:40` (PRAGMA busy_timeout = 5000) and server error middleware | Marker maps SQLITE_BUSY into a friendly busy message because multiple local clients contend. V2 mitigates contention with a 5 s busy_timeout at the connection level, but no server error middleware translates residual lock errors into a busy message (grep busyMessage/isBusy: none). Preserved: mitigation; missing: user-facing mapping. Evidence: L3: import { busyMessage, isBusy } from "@/kilocode/database/sqlite-error" // kilocode_change / L20: // kilocode_change start - SQLite lock contention is expected with multiple local clients | +| `packages/opencode/src/server/routes/instance/httpapi/middleware/workspace-routing.ts`
    9,182,188 | shared-upstream patch marker | Server/API integration | needs port | `packages/protocol/src/groups/session.ts:260` session.fork payload (`boundary` only) and Core `session/move.ts` — a Location-targeted fork would need protocol extension or fork+move composition in the Agent Manager backend (`packages/kilo-vscode/src/backend/session.ts`) | Marker lets a fork target another directory instead of inheriting the source session's directory (Agent Manager worktree flows). V2 fork payload only carries a boundary and the child inherits the parent Location; session move exists separately (core/session/move.ts) but the combined fork-into-worktree behavior consumed by the transplanted extension is not present. Evidence: L9: import { forkTargetDirectory } from "@/kilocode/server/routes/fork-routing" // kilocode_change - fork honors explicit target directory / L182: // kilocode_change start - a fork targeting an explicit directory (e.g. a worktree) must not inherit the source session's directory | +| `packages/opencode/src/server/routes/instance/httpapi/public.ts`
    2,5,67,71,85,93,97,119,197,298,302,523,535,557,570,582,586,588 | shared-upstream patch marker | Server/API integration | partial | `packages/client` generation (`bun run generate`) from v2 Protocol; Kilo RPC schemas in `packages/schema/src/kilocode/*` typed directly | Markers fix generated SDK schemas for Kilo endpoints (nullable Agent Manager move targets, indexing model reset sentinels, ServerConfig description, doc title/description, legacy spec matching). V2 client generation runs from the Protocol HttpApi and Kilo RPCs are consumed through typed Schema contracts (schema/src/kilocode), so most fixups are structurally unnecessary; the Agent Manager move-target and indexing-sentinel cases only matter once those Kilo endpoints exist (agent manager: IDE-owned; indexing config patch: indexing-rpc.ts present — sentinel handling unverified). Evidence: L2: // kilocode_change start / L5: // kilocode_change end | +| `packages/opencode/src/server/routes/instance/httpapi/server.ts`
    6,30,33,37,51,56,60,115,122,127,191,198,200,238,249,315,316,321,323,344,380 | shared-upstream patch marker | Server/API integration | partial | `packages/kilo-cli/src/interactive-server.ts` and `runtime.ts` host composition (memory-plugin, gateway plugin, settings) over native Core services | Markers wire Credential, ModelCache, MemoryService, Kilo HttpApi handlers and a reference-reconciler readiness gate into the upstream server layer. V2 composes Kilo services as plugins/hosts in kilo-cli (interactive-server.ts registers memory/gateway/settings/generation plugins; Core owns credential and catalog). PRESERVED: memory, credential, model catalog composition; MISSING: reference reconciler readiness gate (no v2 reconciler) and Kilo handler groups listed as missing in the api.ts row. Evidence: L6: import { EffectFlock } from "@opencode-ai/core/util/effect-flock" // kilocode_change / L30: import { ModelCache } from "@/provider/model-cache" // kilocode_change | +| `packages/opencode/src/server/server.ts`
    4,17,26,32,88,105,112,137 | shared-upstream patch marker | Server/API integration | partial | `packages/server/src/server-info.ts` (network URLs) and `packages/kilo-cli/src/server.ts`/`daemon.ts` listener ownership | PRESERVED — advertised URLs including network interfaces (server-info.ts uses networkInterfaces) and Kilo owning listener lifecycle in kilo-cli server/daemon. MISSING/unverified — the KiloListener.build layer semantics (Kilo-specific listener routes composition) have no direct counterpart; v2 Kilo composes routes through interactive-server.ts rather than wrapping upstream listeners, so remaining v1 listener extensions (if any beyond URL reporting) are not mapped. Evidence: L4: import { serverUrls } from "@/kilocode/cli/server-urls" // kilocode_change / L17: import * as KiloListener from "@/kilocode/server/listener" // kilocode_change | +| `packages/opencode/src/server/shared/ui.ts`
    5,64,73,77 | shared-upstream patch marker | Server/API integration | needs port | Kilo-owned static route in `packages/kilo-cli/src/interactive-server.ts`/`server.ts` (v2 packages/console is upstream's cloud console, not the CLI-embedded Kilo Console) | Markers mount ConsoleAssets under /console and remove the upstream proxy fallback to app.opencode.ai. V2 kilo-cli serves no embedded console assets (grep console in kilo-cli/src: none) and v2 server ui routing is upstream's; the Kilo Console command (v1 KiloConsoleCommand) is untracked here. Fallback removal is moot because v2 Kilo does not use the upstream web UI proxy. Evidence: L5: import { ConsoleAssets } from "@/kilocode/console/assets" // kilocode_change / L64: // kilocode_change start - serve Kilo Console under /console | +| `packages/opencode/src/server/shared/workspace-routing.ts`
    22 | shared-upstream patch marker | Server/API integration | native equivalent | `packages/protocol/src/groups/session.ts:753` (`session viewed` endpoint natively defined; Location-based routing replaces directory workspace routing) | Marker keeps the Kilo static viewed route out of session-ID parsing. V2 defines the viewed transition natively in the session group and routes by Location instead of path-based workspace parsing, so the special case is unnecessary. Evidence: L22: if (url.pathname === "/session/viewed") return null // kilocode_change - Kilo static route is not a session ID | +| `packages/opencode/src/session/compaction.ts`
    2,17,21,27,30,31,34,64,66,96,103,153,185,214,220,268,319,342,360,389,435,449,469,472,490,492,494,510,511,524,527,538,572,598,618,621,650,655,681,683,697,703,717 | shared-upstream patch marker | Session lifecycle/history/UI | partial | `packages/core/src/session/compaction.ts` (`estimateTokens` :128-164, one overflow-compaction rebuild per AGENTS Session Core) and `runner/step.ts`; queue retarget covered by v2 durable inbox | 43 markers. PRESERVED — token estimation over model messages (core compaction.ts:162-164 Token.estimate), compaction of oversized turns instead of replay loops (v2 single compaction rebuild then continuation), queued prompts surviving compaction (v2 inbox stops steers before compaction control items), compaction markers visible during queued turns (v2 durable compaction records). MISSING — opt-in `prune` with default payload/compaction cleanup and safe pruning at cache-invalidating boundaries (core/config/normalize.ts:323 marks compaction.prune unsupported), preflight threshold compaction replay with media preserved-then-stripped after provider overflow (overflow row partial: proactive economic threshold unproven), self-contained compaction export capture (no export module), `include: recent` field for released Core V2 readers (v1-only compatibility). Evidence: L2: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change / L17: import * as DateTime from "effect/DateTime" // kilocode_change | +| `packages/opencode/src/session/instruction.ts`
    14,15,63,65,91,93,96,111,128,186,190,191,196,200,204 | shared-upstream patch marker | Session lifecycle/history/UI | partial | `packages/core/src/instruction-discovery.ts` (global/project ordering :30, `global` option :42-100) and `core/src/instructions/*`; Kilo layout in `packages/kilo-cli/src/paths.ts` | PRESERVED — global-then-nearest project instruction ordering and 'Instructions from:' attribution (v2 InstructionDiscovery renders sources with paths). MISSING — preferring the KILO_CONFIG_DIR profile directory over global.config (v2 discovery uses Core Global paths; kilo2 layout is handled in paths.ts for state, not instruction roots — unverified linkage), refusing env/file reads outside the project root for project-declared instruction globs (v2 confinement of `{file:}`/glob expansion not found in instruction-discovery.ts), declaration provenance through path expansion. Evidence: L14: import { KilocodeInstruction } from "@/kilocode/session/instruction" // kilocode_change / L15: import type { KilocodeMarkdown } from "@/kilocode/config/markdown" // kilocode_change | +| `packages/opencode/src/session/llm.ts`
    7,19,27,36,43,60,61,100,129,159,167,263,299,372,374,395,398,419,422,423,439,472,474 | shared-upstream patch marker | Session lifecycle/history/UI | partial | `packages/core/src/session/runner/llm.ts` and `model-request.ts`; Kilo timeout/header policy would live in `packages/kilo-gateway/src/plugin.ts` http.request hook | PRESERVED — one explicit llm.stream per attempt with overflow compaction (v2 runner), tool-call name repair by lowercasing (unverified; v2 uses schema-validated tool names — treat as not proven), allowSystemInMessages (v2 request assembly renders system natively). MISSING — proactive preflight threshold compaction (overflow row partial), provider-reported context tokens as output-cap source, KiloLLM per-model timeout options, session-export request start/completion capture and stream observer (no export module), disabling AI SDK span recording (v2 uses aisdk-native.ts; telemetry overlap doc does not cover spans). Evidence: L7: import { Log } from "@opencode-ai/core/util/log" // kilocode_change / L19: import { usable } from "./overflow" // kilocode_change | +| `packages/opencode/src/session/llm/ai-sdk.ts`
    5,6,56,65,110,115 | shared-upstream patch marker | Session lifecycle/history/UI | partial | `packages/kilo-cli/src/routed-model.ts`/`routed-model-plugin.ts` (routed model native), `packages/core/src/aisdk-native.ts` | PRESERVED — Kilo routed-model resolution (routed-model rows, baseline model-loading-v2-parity). MISSING — preserving raw provider billing details (`providerMetadata.aiSdk`) on assistant parts for Kilo cost display (grep raw/billing in core/aisdk-native: none). Evidence: L5: import { KiloRoutedModel } from "@/kilocode/session/routed-model" // kilocode_change / L6: import { KiloResponseMetadata } from "@/kilocode/session/response-metadata" // kilocode_change | +| `packages/opencode/src/session/llm/request.ts`
    12,17,18,31,71,74,76,108,112,121,123,154,161,180,189,190,192,223,234,247,249,256 | shared-upstream patch marker | Session lifecycle/history/UI | partial | `packages/kilo-gateway/src/plugin.ts:310-345` http.request hook (auth/org header, body rewrite) and `packages/kilo-cli/src/model-prompt-policy.ts` | PRESERVED — Kilo model-specific system prompt selection (prompt-policy selectors incl. gpt55/ling/beast), Kilo Gateway auth and X-KILOCODE-ORGANIZATIONID (plugin.ts:40, account.ts:11). MISSING — soul/persona prepend policy (grep soul/persona: none), stripping id/displayName/source agent metadata (options.ts row), gpt-5 via openai-compatible proxy handling, project ID + machine ID + root product session attribution headers and DEFAULT_HEADERS for non-anthropic providers (gateway hook sets only authorization/org). Evidence: L12: import { USER_AGENT } from "@/installation" // kilocode_change / L17: import { DEFAULT_HEADERS } from "@/kilocode/const" // kilocode_change | +| `packages/opencode/src/session/message-v2.ts`
    19,38,39,40,41,42,55,82,86,114,128,183,199,201,216,401,420,426,632,675,756,763,764,767 | shared-upstream patch marker | Session lifecycle/history/UI | partial | `packages/core/src/session/{message,history,to-llm-message}.ts`, `runner/to-llm-message.ts`; Kilo error mapping in `packages/kilo-gateway` and codex provider plugin | PRESERVED — chronology-based ordering before projection (v2 ordered aggregate history), ignored parts excluded from prompts (v2 history projection). MISSING/unproven — stripping bloated part metadata on all read paths to prevent multi-MB payloads (grep stripPartMetadata: none), avoiding malformed Unicode in compacted tool output, not replaying send_file attachments (send-file needs port), CodexAuthExpiredError and SessionNetwork.disconnected mapping to session errors (network needs port; codex refresh in v2 core/plugin/provider/openai.ts handles OAuth natively but Kilo-specific expiry error class absent), EditorContext re-export. Paired test row (test/session/message-v2.test.ts) already partial. Evidence: L19: export { EditorContext } from "@/kilocode/editor-context" // kilocode_change / L38: import { Snapshot } from "@/snapshot" // kilocode_change | +| `packages/opencode/src/session/network.ts`
    1,158,180,352,379 | shared-upstream patch marker | Offline detection and reconnect wait | needs port | Kilo-owned Core session execution status plus TUI/Protocol event seam | Pinned v1 classifies nested socket/browser/TimeoutError failures, publishes an offline wait, probes restoration, permits manual reply/reject and cancels safely. V2 generic retry and simulation networking do not implement this user-visible lifecycle. Evidence: L1: // kilocode_change - new file / L158: // kilocode_change - recognize AbortSignal.timeout() errors | +| `packages/opencode/src/session/overflow.ts`
    7,32,33,35 | shared-upstream patch marker | Context overflow accounting and recovery | partial | `packages/core/src/session/runner/step.ts:100-152` and `test/session-runner.test.ts:2395-2615` | V2 detects provider context-overflow, performs one compaction rebuild, respects auto-compaction disablement and tests failure/interrupt boundaries. The pinned Kilo token counter and proactive economic threshold invariant are not proven. Evidence: L7: import { KiloSessionOverflow } from "@/kilocode/session/overflow" // kilocode_change / L32: const count = KiloSessionOverflow.count(input.tokens) // kilocode_change | +| `packages/opencode/src/session/processor.ts`
    16,24,26,31,47,52,63,70,73,89,94,97,101,124,130,136,142,147,150,155,158,159,161,173,178,192,198,211,229,257,270,273,280,286,290,303,307,313,392,412,434,436,440,446,452,455,480,520,537,549,553,566,576,583,588,601,602,608,615,623,624,643,645,647,657,658,659,660,661,665,685,709,716,719,723,744,769,771,842,843,848,850,852,857,862,870,873,895,899,906,912,914,918,955,1031,1044,1046,1071 | shared-upstream patch marker | Session lifecycle/history/UI | partial | Core `packages/core/src/session/runner/step.ts`/`llm.ts`/`retry.ts` natively; Kilo: `packages/kilo-cli/src/remote-suggestions.ts`, `session-usage-plugin.ts`, `telemetry.ts` | 98 markers. PRESERVED — retry only terminally incomplete attempts / consistent retry state (v2 runner retry.ts + generic pre-output retries per AGENTS Session Core), deleted-session FK tolerance (v2 durable projections), finish-step without start-step guard (v2 step model), cost reconciliation from subagents (cost-propagation native), step timing/model on step-finish (v2 durable step records), suggestion dismissal ending the turn (remote-suggestions.ts accept/dismiss flow), compactError as internal preflight signal (v2 compaction rebuild). MISSING — configured output-token ceiling via RuntimeFlags and stronger reasoning-only output-limit message (grep output limit in core: only v1-migration text), per-step metrics/generationID/vercelID capture (metrics row native covers usage only; no generationID/vercelID fields in v2 schema), send_file delivery attachments (send-file row needs port), offline handler abort (network row needs port), permission approval provenance merged into tool metadata (no PermissionProvenance in v2), buffered tool metadata before tool-call registration (grep toolmeta: none; v2 step model may not need it — unverified), accepted-suggest review telemetry tagging. Evidence: L16: import { RuntimeFlags } from "@/effect/runtime-flags" // kilocode_change - configured output token ceiling / L24: // kilocode_change start | +| `packages/opencode/src/session/prompt.ts`
    2,5,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,69,70,71,77,84,90,91,117,119,121,141,145,148,152,171,189,201,204,225,237,241,247,253,256,315,325,342,359,375,383,430,441,445,450,458,462,464,474,487,504,509,542,547,590,656,664,672,682,691,698,700,709,743,757,782,786,790,828,831,844,851,860,890,910,913,919,936,942,1034,1044,1046,1053,1057,1059,1063,1090,1093,1216,1295,1304,1331,1348,1366,1403,1406,1407,1415,1422,1425,1446,1450,1456,1464,1468,1471,1476,1477,1479,1480,1481,1491,1495,1496,1497,1499,1502,1509,1514,1515,1520,1530,1545,1551,1552,1594,1600,1609,1624,1675,1676,1693,1697,1698,1702,1706,1723,1728,1730,1733,1734,1759,1762,1770,1774,1785,1794,1797,1800,1817,1828,1835,1838,1843,1845,1858,1864,1865,1868,1878,1879,1893,1910,1920,1933,1948,1961,1964,2233,2244,2245,2252,2257,2258,2323,2351,2359,2362,2367,2383,2393,2395,2403,2406,2422,2447,2448,2470,2490,2494,2495,2497,2507,2522,2526,2548,2552,2607,2639,2640 | shared-upstream patch marker | Session lifecycle/history/UI | partial | Core `packages/core/src/session/{prompt,inbox,runner/*,compaction}.ts` for native equivalents; Kilo plugins `packages/kilo-cli/src/{plan-policy,memory-plugin,skill-shell,sandbox-mcp,resume-external,review-policy,routed-model}.ts` for ported Kilo behavior | 212 markers; per-invariant: PRESERVED natively — queued follow-ups/hide later prompts (v2 durable inbox + steer boundaries), trimBeforeLastSummary/compaction cap (v2 one overflow-compaction rebuild per step, compaction epoch), plan_exit hard stop (kilo-cli plan-policy.ts), memory injection + recall marker (memory-plugin.ts:662-673 system push), skill `!cmd` trusted/kill-switch (skill-shell.ts), sandbox MCP denial (sandbox-mcp.ts fail-closed connect hook), external resume commands (resume-external.ts/external-sessions.ts), deprecated review aliases static notice (review-policy.ts), user image normalization (core/session/prompt.ts:123-140), subagent cost propagation (cost-propagation row native), interrupted-cause handling (Effect interrupt), title-request isolation (core/session/title.ts). MISSING / unproven — configured @reference mention expansion into prompt parts and reference-root attachment authorization (v2 Core has `core/src/reference.ts` + protocol `reference.list`, but `core/src/session/prompt.ts` performs no reference mention expansion), prompt-attachment authorization through read/external_directory permissions (core external_directory is agent-config only), editorContext carry-through (grep editorContext core/kilo-cli: none), ephemeralTools remote tool restrictions (grep: none), workflow explicit model/variant for subtasks (workflow-variant row needs port), REQUEST_PRUNE_BYTES persistent tool-output pruning and SWE-Pruner (grep prune/Pruner: none), provider-reported context tokens feeding output cap, review-command LLM telemetry marker, BUILTIN_COMMANDS error hint, snapshotInitialization wait policy (slow-snapshot guard partial), 'No models are currently available' hint, v2-shell dual-write correlation (v2 native), guardRequirements before turn (no RequirementBlockedError in v2). Each missing item is a Kilo-specific behavior with no v2 consumer today. Evidence: L2: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change / L5: import fs from "node:fs" // kilocode_change | +| `packages/opencode/src/session/reminders.ts`
    3,12,25,35,40,46,64,65,72,74 | shared-upstream patch marker | Session lifecycle/history/UI | native equivalent | `packages/kilo-cli/src/plan-policy.ts` + `plan-prompt.txt` (plan-v2-parity baseline) and Kilo agent naming in `agent-policy.ts` | Markers rename BUILD_SWITCH to CODE_SWITCH and route Kilo's shared planning reminder/plan-file execution hint. V2 plan-policy owns the plan hand-off text and follow-up choices (newSession/continue/refine) with plan file paths, replacing the reminder mechanism; no Kilo agent is named build in v2 policy. Evidence: L3: import { KiloSessionPrompt } from "@/kilocode/session/prompt" // kilocode_change / L12: import CODE_SWITCH from "./prompt/code-switch.txt" // kilocode_change | +| `packages/opencode/src/session/retry.ts`
    5,6,12,68,74,76,82,86,134,137,141,145,151,163 | shared-upstream patch marker | Session lifecycle/history/UI | partial | `packages/core/src/session/runner/retry.ts` (`isRetryable` :29-59, bounded delay) | PRESERVED — bounded retry delays and classification (retry.ts:59 caps retry-after), no OpenCode Go upsell reasons (v2 Kilo does not expose Go). MISSING — treating Kilo auth/signup-required errors and FreeUsageLimitError as non-retryable (grep isKiloError/Kilo in runner/retry.ts: none), network-disconnect offline handler (network row needs port). Retry limit exists natively via generic pre-output retry accounting. Evidence: L5: import { isKiloError } from "@/kilocode/kilo-errors" // kilocode_change / L6: import { SessionNetwork } from "./network" // kilocode_change | +| `packages/opencode/src/session/revert.ts`
    5,13,39,73,83,85,123,132,150,185,190,210 | shared-upstream patch marker | Session lifecycle/history/UI | partial | `packages/core/src/session/revert.ts` and `snapshot.ts` restore | PRESERVED — v2 revert is durable and committed revert clears instruction epoch (AGENTS Session Core), snapshot restore exists. MISSING/unproven — keeping the whole workspace transition atomic and preserving the reverted workspace when redo cannot complete, clearing a reverted provider error from the retained assistant message, honoring Config snapshot toggle in revert (no evidence in core/session/revert.ts; not source-verified as equivalent). Evidence: L5: import { Config } from "@/config/config" // kilocode_change / L13: import { KiloSessionRevert } from "@/kilocode/session/revert" // kilocode_change | +| `packages/opencode/src/session/run-state.ts`
    2,112 | shared-upstream patch marker | Session run coordination layer | native equivalent | `packages/core/src/session/execution/` and SessionRunCoordinator | V2 replaces legacy defaultLayer composition with its process-global Session-ID coordinator and execution claims; same-session joins, different-session concurrency, cancellation and restart recovery are native architecture. Evidence: L2: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change / L112: export const defaultLayer: Layer.Layer = Layer.suspend(() => AppNodeBuilder.build(node)) // kilocode_change - build from the LayerNode graph | +| `packages/opencode/src/session/session.ts`
    2,31,36,45,47,50,79,89,157,187,222,266,278,279,282,328,333,402,406,409,414,423,467,475,513,522,526,601,613,614,616,641,643,657,659,663,685,689,691,709,727,742,744,745,746,750,758,763,769,778,804,812,820,821,831,832,833,836,844,846,847,863,870,871,872,886,891,899,907,909,915,1123,1149,1156,1175,1179,1206,1221,1223 | shared-upstream patch marker | Session lifecycle/history/UI | partial | `packages/schema/src/session.ts` Info + `packages/core/src/session/{session,transfer}.ts` for forks; Kilo: `packages/kilo-cli/src/sandbox.ts`, `import-v1-session.ts`; Agent Manager filters belong to `packages/kilo-vscode/src/agent-manager/` (IDE-owned) | 79 markers. PRESERVED — fork copies model context and history (v2 SessionFork boundary in schema/session-fork.ts; transfer.ts carries info.cost), revert state brand alignment (v2 Revert schema shared), FK-safe deletes (v2 projections), plans directory `.kilo/plans` (plan-policy.ts:194). MISSING — per-session `platform` telemetry attribution field (v2 Session.Info has no platform), worktreeName and cross-project worktree-family/directories[] list filters for Agent Manager (v2 list is Location/project scoped; Agent Manager stays with VS Code per plan), server-issued sandbox inheritance grant/sourceID/sandboxFallback across forks (v2 sandbox.ts confines processes but has no per-session inherited policy grant), fork zeroing pre-fork cost on copied assistant/step-finish parts (transfer.ts copies cost unchanged — v2 counts fork cost differently; not proven equivalent), lightweight `diffs` summary and workspace restore status on messages, provider-reported cost preference for OpenRouter/Kilo (unverified in v2 usage), SessionExport close hooks (no export module), requirement failures over session.error. Evidence: L2: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change / L31: import { SessionMessage } from "@opencode-ai/core/session/message" // kilocode_change - shared Revert.State brand | +| `packages/opencode/src/session/status.ts`
    54 | shared-upstream patch marker | Legacy status layer composition | native equivalent | Core durable execution/status projection and Server event delivery | The marker only preserved a legacy default Layer for old callers. V2 owns status through native Core/Server services and projected execution events; the indexing status filename is unrelated. Evidence: L54: // kilocode_change - preserve legacy layer composition for Kilo callers | +| `packages/opencode/src/session/summary.ts`
    8,9,70,83,111,113,129,138,142,147,148,166,167,183,204,205,212 | shared-upstream patch marker | Session lifecycle/history/UI | partial | `packages/core/src/snapshot.ts` diff (`DiffInput` :29, structured per-file diffs :58) and session stats; extension consumer `packages/kilo-vscode/src/agent-manager/worktree-diff-controller.ts` | PRESERVED — per-file structured diffs between snapshots (snapshot.ts diff). MISSING — respecting `snapshot:false` in summary (v2 snapshot enabled() gate exists at capture, equivalent in effect), cumulative/imported diff retention for cloud-forked sessions (no import of session_diff storage), `full`/`file` request for authoritative single-file full-content detail used by editor diff tabs (extension port relies on adapter; not found in v2 Core API). Evidence: L8: import { appendSessionDiffs, readSessionDiffBase } from "@/kilocode/session-portability/cumulative-diff" // kilocode_change / L9: import { Storage } from "@/storage/storage" // kilocode_change | +| `packages/opencode/src/session/system.ts`
    11,13,27,29,36,38,46,49,74,88,93,106,109,149,195 | shared-upstream patch marker | Session lifecycle/history/UI | native equivalent | `packages/kilo-cli/src/model-prompt-policy.ts` (ling/gpt55/beast selectors :18-23) and `packages/core/src/plugin/system-prompt.ts` environment builtins | Markers select PROMPT_LING/PROMPT_GPT55 by model, wait for plugin readiness and pass editorContext into environment. V2 prompt policy adapts all eight v1 catalog selectors from source and Core builtins render platform/environment (instructions/builtins.ts:35); editorContext injection is tracked under kilocode/system-prompt.ts (partial) rather than here. Evidence: L11: import PROMPT_GPT55 from "./prompt/kilocode-gpt-5.5.txt" // kilocode_change / L13: import PROMPT_LING from "./prompt/ling.txt" // kilocode_change | +| `packages/opencode/src/session/todo.ts`
    10,73 | shared-upstream patch marker | Legacy todo layer composition | native equivalent | Native v2 session projection and TUI tool rendering | The marked invariant is only dependency-layer compatibility in the old graph, which v2's native services do not require. This does not claim parity for unrelated legacy Todo workflow policy. Evidence: L10: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change / L73: // kilocode_change - preserve legacy layer composition for Kilo callers | +| `packages/opencode/src/session/tools.ts`
    2,3,23,26,30,51,55,61,64,68,73,83,133,138,141,145,158,164,174,175,191,444,446,462,471 | shared-upstream patch marker | Session lifecycle/history/UI | partial | `packages/core/src/session/runner/*` tool resolution, `packages/kilo-cli/src/memory-plugin.ts` (recall), `sandbox-mcp.ts` | PRESERVED — memory recall tool and injected marker (memory-plugin.ts recall :221-334), MCP tools withheld under sandbox network restriction (sandbox-mcp fail-closed hook prevents connections). MISSING — SWE-Pruner focus parameter advertisement (experimental; grep: none), model.family passed to tool definitions, Kilo ask-time permission resolution hooks via KiloSessionPrompt (v2 permission is native and resolves at request time). Evidence: L2: import { KiloSessionPrompt } from "@/kilocode/session/prompt" // kilocode_change / L3: import { MemoryMarker } from "@/kilocode/memory/marker" // kilocode_change | +| `packages/opencode/src/share/session.ts`
    7,28,34,54 | shared-upstream patch marker | Kilo public session sharing | partial | Kilo Gateway session share/fork/unshare plugin and `packages/kilo-cli/test/gateway-integration.test.ts:255-276` | A local fixture proves authenticated share URL creation, public retrieval/fork and unshare. Deployed viewer compatibility, organization/team policy and production service behavior remain unverified, so this is not blanket end-to-end parity. Evidence: L7: import { KiloSession } from "@/kilocode/session" // kilocode_change / L28: const result = yield* KiloSession.shareSession(sessionID) // kilocode_change - use Kilo public share URLs | +| `packages/opencode/src/share/share-next.ts`
    94,107 | shared-upstream patch marker | Legacy share transport normalization | partial | Kilo Gateway share serializer and v1 session import boundary | V2 normalizes imported legacy rows and uses a different Gateway-backed path, but no current test proves the pinned missing-file diff normalization at the deployed incremental-sync boundary; architecture change alone is insufficient parity evidence. Evidence: L94: // kilocode_change start - preserve the share transport contract when stored legacy summary diffs omit file details / L107: // kilocode_change end | +| `packages/opencode/src/skill/discovery.ts`
    9,52,55,70,104,106,157 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/core/src/skill/discovery.ts:16-40` (`isSafeSegment`, `isSafeRelativePath`) with origin-resolved downloads | Markers validate remote index-controlled names/paths and pin downloads to the index origin. V2 discovery.ts contains the same safe-segment/relative-path validators natively (upstream adopted the hardening). Evidence: L9: import { isSafeSegment, isSafeRelativePath } from "@/kilocode/skill/discovery-validate" // kilocode_change / L52: // kilocode_change start - resolve the index origin so file downloads can be pinned to it | +| `packages/opencode/src/skill/index.ts`
    17,18,19,21,23,27,29,39,85,102,112,115,117,124,129,132,146,150,154,156,164,186,195,210,211,221,224,228,230,233,234,241,246,248,260,272,280,286,291,301,308,311,313,329,340,341,414 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/core/src/skill.ts` + `skill/discovery.ts`; Kilo `packages/kilo-cli/src/skill-policy.ts` (builtin) and `skill-shell.ts` (trust gating) | PRESERVED — builtin skill seeded before discovery with user override handling (skill-policy.ts BUILTIN_LOCATION; collision reporting), shell injection gated by trust in skill-shell.ts. MISSING — `trusted` provenance field on skill records propagated from the declaring config source (grep trust in core/skill: none), project skills' frontmatter substitution confined to project root, primary-worktree fallback scanning for worktrees (.kilocode/.kilo primary paths), KILO_CONFIG_DIR skill roots, duplicate/location diagnostics. Evidence: L17: import { BUILTIN_SKILLS } from "../kilocode/skills/builtin" // kilocode_change / L18: import { primaryPaths } from "../kilocode/primary-worktree" // kilocode_change | +| `packages/opencode/src/snapshot/index.ts`
    2,4,12,16,25,30,33,37,42,45,56,63,69,75,80,83,90,183,188,190,202,212,214,215,219,223,250,252,317,334,341,343,357,359,364,374,383,386,420,432,437,480,487,495,502,526,539,845,865,889,898,912,914,918,922,931,958,969,988,990,995,1000,1005 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/core/src/snapshot.ts` (git-backed capture/compare/restore, `maximumUntrackedFileBytes`, enabled() gate) and `git.ts` tree operations | 63 markers. PRESERVED — git-based diff generation instead of JS diffs, structured per-file diffs, restore through git trees, snapshot disabled when Location has no git VCS. MISSING/unverified — SummaryFileDiff lightweight diffs and MAX_DIFF_SIZE cap, 7-day retention/pruning, EffectFlock serialization across CLI and extension processes, worktree-index seeding and object pinning with background materialization/resume, per-checkpoint validation before mutating files and never reporting success for unrestored files, `diffFile` authoritative full-content detail for editor tabs, ACP client opt-out, prompt-context slow-snapshot policy (track.ts row). Evidence: L2: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change / L4: import { Struct } from "effect" // kilocode_change | +| `packages/opencode/src/storage/db.ts`
    8,18,35,37,38,39,40,107 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/kilo-cli/src/paths.ts:26` (`kilo2.db` under isolated data dir; preflight()/layout()) and Core `database/path.ts` | Markers name the DB kilo.db (with opencode-*.db fallback) and assert writability with self-heal. V2 Kilo owns an isolated kilo2.db path with layout/preflight checks in paths.ts; fallback to old opencode DB names is intentionally replaced by explicit import. Evidence: L8: import { DbPreflight } from "@opencode-ai/core/kilocode/db-preflight" // kilocode_change / L18: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change | +| `packages/opencode/src/suggestion/index.ts`
    1 | shared-upstream patch marker | Suggestion lifecycle API | native equivalent | `packages/kilo-cli/src/remote-suggestions.ts:43-208` | The owned v2 state machine preserves pending requests, eligibility, single settlement, dismissal and lifecycle cleanup; the old shared re-export is unnecessary. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/sync/index.ts`
    10,15,20,21,40,52,57,117,130,219,239,242,243,246,335,351,355,367,369,372,382,394,427,428,433,436 | shared-upstream patch marker | Package behavior indicated by path | not needed | None; v2 durable events are native (`packages/core/src/event/*`) | 26 markers make the v1 legacy sync layer carry encoded EventV2 rows. V2 has no legacy sync layer (context/sync TUI rows assessed separately). Evidence: L10: import { EventSequenceTable, EventTable } from "@opencode-ai/core/event/sql" // kilocode_change - upstream moved the event tables to core / L15: import { EventManifest } from "@/event-manifest" // kilocode_change | +| `packages/opencode/src/tool/apply_patch.ts`
    15,16,17,20,72,79,106,122,135,139,176,184,196,210,251,256,263,310,314,325,330,337 | shared-upstream patch marker | Tool behavior/UI | partial | `packages/core/src/tool/plugin/patch.ts` (BOM-aware via @opencode-ai/util/bom) and `edit.ts:107` (LSP diagnostics TODO) | PRESERVED — BOM preservation on patched files (core patch.ts imports Bom; edit.ts:207-214 syncTextBom). MISSING — non-UTF-8 encoding detection/round-trip (EncodedIO; core reads utf-8 only), Kilo config validation warnings appended after edits (grep ConfigValidation: none), assertMutablePath protection of Agent Manager-managed paths (grep: none), diagnostics filtering to changed paths (v2 has no LSP diagnostics yet: edit.ts:107 TODO). Evidence: L15: import { filterDiagnostics } from "./diagnostics" // kilocode_change / L16: import { ConfigValidation } from "../kilocode/config-validation" // kilocode_change | +| `packages/opencode/src/tool/diagnostics.ts`
    1 | shared-upstream patch marker | Tool behavior/UI | needs port | Core tool diagnostics once LSP runtime exists (`packages/core/src/tool/plugin/edit.ts:107` TODO); test row test/tool/diagnostics-filter.test.ts pairs | Whole new file filters diagnostics to the edited paths so unrelated errors are not echoed. V2 has no LSP/diagnostics pipeline at all, so the filter and its motivating behavior are absent. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/tool/edit.ts`
    21,22,23,24,25,27,29,47,110,116,131,139,142,157,161,180,188,192,212,217,228,232,234 | shared-upstream patch marker | Tool behavior/UI | partial | `packages/core/src/tool/plugin/edit.ts` and `file-diff.ts` | PRESERVED — BOM-aware replace and structured file diff on results (edit.ts:207-214, file-diff.ts). MISSING — Encoding.read/EncodedIO non-UTF-8 handling, MAX_DIFF_CONTENT 500k cap on diff content (not found in file-diff.ts), ConfigValidation warnings for edited config files, assertMutablePath protection, diagnostics filtering (no LSP). Evidence: L21: import { filterDiagnostics } from "./diagnostics" // kilocode_change / L22: import { ConfigValidation } from "../kilocode/config-validation" // kilocode_change | +| `packages/opencode/src/tool/external-directory.ts`
    14,22,35,37 | shared-upstream patch marker | Tool behavior/UI | native equivalent | `packages/core/src/location-mutation.ts` (externalDirectory resolution) + `tool/plugin/shell.ts:125-140` permission.assert on each external directory | Markers stop root workspace boundaries from auto-allowing external_directory while keeping root-workspace behavior outside prompts. V2 resolves every path through LocationMutation and asserts external_directory permission per resource (shell.ts:134-140; read/edit via mutation.resolve), with agent config allows only via explicit rules (core/config/plugin/agent.ts), matching the fail-closed invariant. Evidence: L14: // kilocode_change start - root boundaries must not auto-allow external_directory / L22: // kilocode_change end | +| `packages/opencode/src/tool/glob.ts`
    10,26,46,57,60,71,74,76,80,92 | shared-upstream patch marker | Tool behavior/UI | partial | `packages/core/src/tool/plugin/glob.ts` (absolute target resolution :89-98, timeout) | PRESERVED — absolute paths resolved via Environment/LocationMutation and bounded search with timeout. MISSING/unverified — splitting absolute glob patterns like `~/.config/kilo/command/*.md` into cwd+relative glob (v2 resolves `path` param, not absolute `pattern` — not confirmed), passing ctx.abort to ripgrep and the '(Some discovered files could not be read.)' partial notice (grep partial in glob.ts: none). Evidence: L10: // kilocode_change start — support absolute glob patterns (e.g. ~/.config/kilo/command/*.md) / L26: // kilocode_change end | +| `packages/opencode/src/tool/grep.ts`
    6,12,19,28,32,33,51,67,71,74,75,77,80,83,87,88,91,95,96,106,111,113,114 | shared-upstream patch marker | Tool behavior/UI | partial | `packages/core/src/tool/plugin/grep.ts` (limit :35, truncation notice :55-58) over Core FileSystem.GrepInput | PRESERVED — limit parameter and truncation notice, path-scoped searches. MISSING/unverified — context lines, literal and ignoreCase fields (KiloGrep.fields; core GrepInput exposes limit; context/literal not found in grep.ts), abort signal to ripgrep, exact-file basename constraint, '(Some paths were inaccessible.)' partial notice, per-row textTruncated markers. Evidence: L6: import * as KiloGrep from "@/kilocode/tool/grep-signal-controls" // kilocode_change / L12: pattern: Schema.String.annotate({ description: "Pattern to search for in file contents (regex by default)" }), // kilocode_change | +| `packages/opencode/src/tool/plan.ts`
    1 | shared-upstream patch marker | Plan completion tool hook | native equivalent | Kilo CLI plan policy/tool registration and `packages/kilo-cli/test/plan-policy.test.ts` | V2 registers and authorizes native `plan_exit`, requires a saved plan path, carries configured denials, and tests completion/dismissal/fail-closed behavior. The memory capture filename is a consumer, not the implementation seam. Evidence: L1: export { Parameters, PlanExitTool } from "@/kilocode/tool/plan" // kilocode_change | +| `packages/opencode/src/tool/question.ts`
    5,13,26,37 | shared-upstream patch marker | Tool behavior/UI | native equivalent | `packages/core/src/tool/plugin/question.ts` + Core `form.ts` (form.cancel produces cancelled outcome; kilo-cli run.ts:132 cancelForm) | Markers surface Question.dismissAll rejection as a normal `dismissed` result instead of an error. V2 forms model cancellation as a first-class outcome (client.form.cancel; runner treats cancelled forms as tool results), covering the invariant; the prompt-dismiss-contract test row is separately partial for remote specifics. Evidence: L5: import { KiloQuestionTool } from "@/kilocode/tool/question" // kilocode_change / L13: dismissed?: boolean // kilocode_change | +| `packages/opencode/src/tool/read.ts`
    1,4,11,15,22,26,28,87,101,124,144,182,185,212,222,223,233,235,252,263,265,298,391,403,405,421,424 | shared-upstream patch marker | Tool behavior/UI | partial | `packages/core/src/tool/plugin/read.ts` and `tool/read-filesystem.ts` | PRESERVED — path resolution and permission before content read via LocationMutation (read.ts absolute resolution), missing-target failure before authorization details. MISSING/unverified — binding every content read to the same reopened authorized object and rejecting canonical path changes after approval (TOCTOU guard; not found), UTF-16/32 BOM NUL heuristic skip and Unicode-safe slice reporting, V1 configured reference resolution, bounded directory listing without child bodies, zero-limit guard. Evidence: L1: import { Effect, Schema, Scope } from "effect" // kilocode_change - stable object reads do not use Option / L4: import { Readable } from "stream" // kilocode_change | +| `packages/opencode/src/tool/recall.ts`
    1,8,9,10,11,12,13,14,36,72,84,131,132,139 | shared-upstream patch marker | Historical session transcript recall | partial | `packages/kilo-memory/src/tool.ts` and `recall/`, with a missing session-transcript search seam | V2 has permissioned Kilo memory catalog/search/digest recall with injection-safe output, but it does not search arbitrary historical session titles/transcripts across sibling worktrees or read a selected transcript with active queued-boundary exclusion. Project memory is not full transcript-recall parity. Evidence: L1: // kilocode_change - new file / L8: import { Filesystem } from "../util/filesystem" // kilocode_change | +| `packages/opencode/src/tool/registry.ts`
    2,7,10,33,34,35,36,37,38,39,46,48,63,64,65,68,69,72,73,74,80,97,104,115,128,129,138,143,230,234,235,249,250,256,259,265,269,284,294,303,325,337,340,342,344,348,350,354,357,369,385,400,478,518 | shared-upstream patch marker | Tool behavior/UI | partial | `packages/kilo-cli/src/{agent-policy,skill-policy,tool-authorization,remote-suggestions,memory-plugin,sandbox-mcp}.ts` registering via `ctx.tool.transform`; Core `tool/plugin/opencode.ts` | PRESERVED — suggest tool (remote-suggestions.ts), memory tools (memory-plugin), skill inventory in skill tool (skill-policy), question tool availability by client (native forms), sandbox-aware tool network (sandbox-mcp/sandbox.ts). MISSING — notebook, agent-manager, repo-overview/repo-clone (experimental Scout), notify_user, KiloToolRegistry.usePatch edit/patch switching and per-agent availability rules, `web_search: true` config forcing websearch on, ToolNetwork.builtin wrapping (no v2 tool-level network policy), warpgrep/codebase_search. Evidence: L2: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change / L7: // kilocode_change start | +| `packages/opencode/src/tool/repo_clone.ts`
    4,5,6 | shared-upstream patch marker | Tool behavior/UI | not needed | None; v2 has no Scout/repo_clone tool and Core `repository-cache` remained upstream | Three markers only import Global/Repository/RepositoryCache for the experimental Scout clone tool. The tool itself is unmarked v1 experimental code not present in v2; the marker is import glue. Evidence: L4: import { Global } from "@opencode-ai/core/global" // kilocode_change / L5: import { Repository } from "@opencode-ai/core/repository" // kilocode_change | +| `packages/opencode/src/tool/shell.ts`
    1,15,21,22,23,24,56,58,81,83,89,136,142,288,289,291,302,311,320,324,409,415,427,432,437,456,458,460,464,466,517,526,536,589,643,660,664,672,677,698,702,731,732,741 | shared-upstream patch marker | Tool behavior/UI | partial | `packages/core/src/tool/plugin/shell.ts` (ShellParse.scan :124, external_directory assert :134-140, DEFAULT_TIMEOUT_MS :22) and `core/src/shell/{parse,scan,select}.ts`; Kilo skill batching in `packages/kilo-cli/src/skill-shell.ts` | PRESERVED — tree-sitter/portable scan feeding permissions, PowerShell/pwsh handling (shell/select.ts, scan.ts), heredoc handling in scan.ts, timeout parameter, decomposition reused by skill-shell (skill-shell.ts imports ShellParse/ShellSelect), description shown as title (v2 shell tool description field). MISSING/unverified — explicit fail-closed on grammar parse failure (#12326) not confirmed in shell.ts, CommandTimeout clamp bounds, `modelEnv` stripping backend credentials from model shells (v2 sandbox.ts confines but env scrubbing unverified), normalizeUrls on command metadata, trailing-output drain fiber semantics. Evidence: L1: import { Effect, Fiber, Stream } from "effect" // kilocode_change - Fiber / L15: import { model as modelEnv } from "@/kilocode/process/env" // kilocode_change | +| `packages/opencode/src/tool/shell/prompt.ts`
    10,17,30,87,131,274,277 | shared-upstream patch marker | Tool behavior/UI | native equivalent | `packages/core/src/tool/plugin/shell.ts:47-56` (description field optional) | Markers make the shell description optional with guidance text. V2 shell tool schema already has optional description with its own guidance; equivalent. Evidence: L10: // kilocode_change start - description is optional, prefixed with "Recommended:" so the model knows it can omit it / L17: // kilocode_change end | +| `packages/opencode/src/tool/skill.ts`
    7,14,25,26,27,45,57,59,67,76,95 | shared-upstream patch marker | Tool behavior/UI | already ported | `packages/kilo-cli/src/skill-shell.ts` + `skill-shell-render.ts`, `skill-policy.ts` (BUILTIN_LOCATION); baseline skill-shell-parity | Markers gate and render shell injection in skill bodies and handle built-in skills lacking a filesystem dir. V2 Kilo skill-shell plugin decomposes commands like the shell tool, enforces trust and kill-switch, and skill-policy supplies the builtin skill from a bundled path; baseline doc records parity. Evidence: L7: // kilocode_change start - gate + run shell injection in skill bodies / L14: // kilocode_change end | +| `packages/opencode/src/tool/suggest.ts`
    1 | shared-upstream patch marker | Suggest tool registration | native equivalent | `packages/kilo-cli/src/remote-suggestions.ts:212-331` and `remote-plugin.ts:56-63` | V2 ports the model-facing suggest tool, gates it on viewer eligibility, awaits accept/dismiss, and admits an accepted prompt exactly once through the native session API. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/src/tool/task.ts`
    13,14,15,16,17,18,19,20,24,80,83,88,101,132,134,136,145,148,163,164,172,173,174,181,182,184,185,190,198,208,213,218,233,241,244,245,252,257,261,263,292,294,320,325,347,360,395,400,416,440 | shared-upstream patch marker | Tool behavior/UI | partial | `packages/core/src/tool/plugin/subagent.ts` (parent chain :110-125, permission :138-143, background :17-21) and `packages/kilo-cli/src/agent-policy.ts`; kilocode/tool/task.ts row partial | PRESERVED — background subagent results returned to parent (subagent.ts backgroundResult + subagent-completion.ts), parent-chain walk limiting nesting (subagent.ts:117-125 with configurable depth per master plan), question/interactive tools unavailable to delegated agents (agent-policy.ts:107 comment), cross-session resume prevention (existing.parentID check :164), cost accounting (cost-propagation row native). MISSING — inheriting edit/bash/MCP restriction ceilings from the calling agent (task.ts kilocode row: 'inherited denial ceilings not proven'), resumable task_id hint on failure, workflow/variant override precedence, review telemetry into child sessions, per-session platform attribution, background-process transfer on child end. Evidence: L13: import { Provider } from "@/provider/provider" // kilocode_change / L14: import { KiloTask } from "../kilocode/tool/task" // kilocode_change | +| `packages/opencode/src/tool/todo.ts`
    5,7,15,17,37,40,52,54 | shared-upstream patch marker | Tool behavior/UI | native equivalent | `packages/core/src/tool/plugin/opencode.ts` todo handling and session todo state (todo.ts row native) | Four small marker blocks adjust todo tool description/validation for Kilo. V2 todo state and tool are native (session todo row already native equivalent); wording differences carry no behavior. Evidence: L5: // kilocode_change start / L7: // kilocode_change end | +| `packages/opencode/src/tool/tool.ts`
    10,122,135 | shared-upstream patch marker | Tool behavior/UI | native equivalent | `packages/core/src/tool/runtime.ts` (formatInputIssues :86, formatEffectIssues) | Markers route tool output/error formatting through a Kilo helper. V2 runtime formats tool input issues and outputs natively; no Kilo-specific format contract remains. Evidence: L10: import { format } from "@/kilocode/tool/tool" // kilocode_change / L122: // kilocode_change start | +| `packages/opencode/src/tool/warpgrep.ts`
    3,4,5,6,7,17,24,36,60,64,73,83 | shared-upstream patch marker | Tool behavior/UI | needs port | Kilo-owned tool plugin in `packages/kilo-cli` using `ctx.tool.transform` and Kilo telemetry (`telemetry.ts`); indexing/semantic search is separate (`indexing.ts`) | Markers wire the morph warp-grep client, telemetry trackToolUsed, and a Kilo toast on quota errors. V2 has no warp-grep/morph dependency (grep warp/morph: none); Kilo indexing (semantic_search) is a different feature. Evidence: L3: import { WarpGrepClient } from "@morphllm/morphsdk/tools/warp-grep/client" // kilocode_change / L4: import { Telemetry } from "@kilocode/kilo-telemetry" // kilocode_change | +| `packages/opencode/src/tool/webfetch.ts`
    7,8,40,44,47,79,91,92,112,113 | shared-upstream patch marker | Tool behavior/UI | partial | `packages/core/src/tool/plugin/webfetch.ts` (User-Agent :53, image rejection :85-141) | PRESERVED — User-Agent header (v2 uses its own userAgent constant) and rejecting image/icon content types (isImageAttachment; SVG excluded). MISSING — IDN/homograph URL normalization to punycode before permission patterns and fetch (url.test row needs port), Kilo-specific UA string. Evidence: L7: import { isIconMimeType, isImageAttachment } from "@/util/media" // kilocode_change / L8: import { normalizeUrls } from "@/kilocode/util/url" // kilocode_change | +| `packages/opencode/src/tool/websearch.ts`
    1,5,10,12,17,31,36,45,95,108,122,145,146,151,167,181,186 | shared-upstream patch marker | Tool behavior/UI | partial | `packages/core/src/tool/plugin/websearch.ts` (provider selection :13-63 via WebSearch providers) and Kilo Exa row (needs port) | PRESERVED — provider abstraction with runtime selection (v2 WebSearch providers + ProviderRequiredError flow). MISSING — Kilo-REST Exa transport authenticated with the Kilo bearer (websearch-kilo-exa row needs port), env override for kilo-exa, numResults cap at 10 with metadata.transport. Evidence: L1: import { Effect, Option, Schema } from "effect" // kilocode_change - Option added for kilo-exa transport dispatch / L5: import * as KiloExa from "@/kilocode/tool/websearch-kilo-exa" // kilocode_change - Kilo-REST Exa transport | +| `packages/opencode/src/tool/write.ts`
    14,16,17,18,19,48,52,56,63,71,75,102,107,110,111 | shared-upstream patch marker | Tool behavior/UI | partial | `packages/core/src/tool/plugin/write.ts` + `file-diff.ts` | PRESERVED — BOM handling and structured file diff on write results. MISSING — non-UTF-8 encoding round-trip, ConfigValidation warnings, assertMutablePath protection, LSP diagnostics filtering (no LSP in v2). Evidence: L14: import { trimDiff, buildFileDiff } from "./edit" // kilocode_change / L16: import { filterDiagnostics } from "./diagnostics" // kilocode_change | +| `packages/opencode/src/util/archive.ts`
    9,18 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/kilo-cli/src/updater.ts` staging (no PowerShell extraction) and Core archive handling | Marker passes archive paths as arguments instead of embedding them in PowerShell programs. V2 updater stages directories via cp/verbatimSymlinks without PowerShell scripts; the injection surface does not exist. Evidence: L9: // kilocode_change start - keep paths out of the PowerShell program / L18: // kilocode_change end | +| `packages/opencode/src/util/filesystem.ts`
    1,4,6,27,33,69,82,85,115 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/util/src/fs-util.ts` and `packages/core/src/file-mutation.ts`; Kilo settings atomic writes in `packages/kilo-cli/src/settings.ts` | PRESERVED — Kilo settings store performs atomic writes (settings.ts), LocationMutation containment for tools. MISSING/unverified — generic atomic write helper with Windows transient-lock retries in the shared FS util (grep rename/atomic in fs-util.ts: none), ENOTDIR/EACCES-as-absent semantics. Evidence: L1: import { chmod, mkdir, readFile, rename, stat as statFile, writeFile } from "fs/promises" // kilocode_change / L4: // kilocode_change start - harden containment checks | +| `packages/opencode/src/util/media.ts`
    2,12,22 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/core/src/tool/plugin/webfetch.ts:85-141` (image attachment rejection) | Markers define icon MIME types rejected by webfetch. V2 webfetch rejects unsupported fetched image content types (all image/* except svg), a superset of the icon set. Evidence: L2: const icons = new Set(["image/vnd.microsoft.icon", "image/x-icon", "image/x-ico", "image/ico", "image/icon"]) // kilocode_change / L12: // kilocode_change start | +| `packages/opencode/src/util/process.ts`
    13,67,119 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/core/src/process.ts`/`child-process` utilities | Markers allow spawning with a complete sanitized environment instead of extending process.env. V2 process utilities pass `env` but a no-inherit option used by formatter/LSP/MCP callers is not evidenced (format row needs port). Evidence: L13: extendEnv?: boolean // kilocode_change - allow a complete sanitized environment / L67: env: opts.env === null ? {} : opts.env ? (opts.extendEnv === false ? opts.env : { ...process.env, ...opts.env }) : undefined, // kilocode_change | +| `packages/opencode/src/worktree/index.ts`
    19,369,377,414 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/core/src/worktree.ts:277-288` (Windows-aware removal commands) | PRESERVED — v2 worktree removal has Windows-specific command execution. MISSING/unverified — retry/wait helper for slow Windows handle release before deleting worktree directories. Evidence: L19: import { WorktreeCleanup } from "@/kilocode/worktree-cleanup" // kilocode_change / L369: // kilocode_change start - use Kilo cleanup helper for slow Windows handle release | +| `packages/opencode/test/account/service.test.ts`
    39 | shared-upstream patch marker | Package behavior indicated by path | not needed | `packages/kilo-cli/test/gateway-*.test.ts` (Gateway account via real loopback fixtures) | Marker compiles the v1 Account node with a mocked HttpClient layer; v2 Kilo account tests use real loopback Gateway fixtures, no layer-graph shim. Evidence: L39: LayerNode.compile(Account.node, [[httpClient, Layer.succeed(HttpClient.HttpClient, client)]]) // kilocode_change | +| `packages/opencode/test/acp/permission.test.ts`
    183,187,336,367 | shared-upstream patch marker | Package behavior indicated by path | needs port | `packages/kilo-cli/test/acp.test.ts`/`acp-cli.test.ts` once the interactive flag and skill-shell batch prompt exist | Marked tests assert human selections are marked interactive and skill-shell batches list commands and cannot be persisted. V2 acp tests cover initialize/session basics but the interactive flag and skill-shell batch semantics are absent (acp/permission.ts row partial). Evidence: L183: // kilocode_change start - human selections are marked interactive / L187: // kilocode_change end | +| `packages/opencode/test/agent/agent.test.ts`
    18,25,34,64,81,91,134,150,182,197,637 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/test/agent-policy.test.ts`, `packages/core/test/agent.test.ts` | PRESERVED — agent permission matrix tests for plan/explore denials exist (agent-policy tests). MISSING — Scout opt-in/exclusion assertions, interactive_terminal allow/deny matrix (tool absent), `.kilo/skill` directory usage (discovery scans .opencode). Evidence: L18: import { MCP } from "../../src/mcp" // kilocode_change / L25: [MCP.node, Layer.mock(MCP.Service)({})], // kilocode_change | +| `packages/opencode/test/agent/plan-mode-subagent-bypass.test.ts`
    8,163,179 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/test/plan-policy.test.ts`, `agent-policy.test.ts` and Core `test/tool/subagent` coverage (inherited denial ceilings 'not proven' per task.ts row) | Marked block asserts Plan-mode mutation denials survive delegation through the Kilo task tool. V2 plan-policy tests cover Plan restrictions for the agent, but inheritance of ceilings by delegated child sessions is not proven (kilocode/tool/task.ts row); regression absent. Evidence: L8: import { KiloTask } from "../../src/kilocode/tool/task" // kilocode_change / L163: // kilocode_change start - preserve Plan mutation ceilings across Kilo task delegation | +| `packages/opencode/test/agent/plugin-agent-regression.test.ts`
    13,15,50 | shared-upstream patch marker | Package behavior indicated by path | not needed | `packages/core/test/agent.test.ts` (native fixtures) | Markers add Git/MCP layer mocks required by the v1 agent node graph; test-plumbing only. Evidence: L13: import { Git } from "../../src/git" // kilocode_change / L15: import { MCP } from "../../src/mcp" // kilocode_change | +| `packages/opencode/test/bun/registry.test.ts`
    4,44 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/core/test/plugin*.test.ts` / models-dev retry & parsing (upstream v2 hardened plugin spec parsing) | Marked tests guard against crashes on invalid semver in the Bun registry helper. V2 plugin install/loader parse specs with typed schemas and retryTransient; the v1 registry helper does not exist. Not verified as a dedicated test, but the failure mode is structurally different. Evidence: L4: // kilocode_change — test the semver guard logic that prevents crashes on invalid version strings / L44: // kilocode_change — invalid semver guard tests | +| `packages/opencode/test/cli/acp/acp-test-client.ts`
    47 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/kilo-cli/test/acp-fixture.ts` | Marker adds a 30s receive timeout; v2 Kilo has its own ACP fixture with bounded waits. Evidence: L47: const received = yield* acp.receive.pipe(Effect.timeout(Duration.seconds(30))) // kilocode_change | +| `packages/opencode/test/cli/acp/initialize-auth.test.ts`
    24,36,39,57 | shared-upstream patch marker | Authentication/account integration | partial | `packages/kilo-cli/test/acp.test.ts:15-16`, `acp-cli.test.ts:52` (agentInfo name/title Kilo) | PRESERVED — agentInfo.name/title `Kilo` asserted in v2 acp tests. MISSING — `kilo-login` auth method id assertion (acp/service.ts row partial; v2 uses daemon credentials). Evidence: L24: expect(initialized.agentInfo?.name).toBe("Kilo") // kilocode_change / L36: expect(initialized.authMethods?.[0]?.id).toBe("kilo-login") // kilocode_change | +| `packages/opencode/test/cli/acp/lifecycle.test.ts`
    21 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/kilo-cli/test/acp-cli.test.ts` lifecycle coverage | Marker bounds exit wait; v2 acp-cli tests cover process lifecycle with their own bounds. Evidence: L21: const code = yield* Effect.promise(() => acp.exited).pipe(Effect.timeout(Duration.seconds(15))) // kilocode_change | +| `packages/opencode/test/cli/auto-mode.test.ts`
    1 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/test/run.test.ts` (auto approval once for root permissions) | Whole-file v1 auto-mode test. V2 run tests cover --auto root approval; Task-child auto approval and headless auto-reject failure are not covered (run-auto row). Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/cli/bin-kilo.test.ts`
    1 | shared-upstream patch marker | Package behavior indicated by path | deferred | npm distribution (deferred); portable launcher covered by `packages/kilo-cli/test/portable.test.ts` | Whole-file test of the npm bin launcher; distribution deferred, portable launcher has its own smoke tests. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/cli/cmd/tui/attention.test.ts`
    92,164,179,188,193,242,387 | shared-upstream patch marker | User-interface behavior | partial | `packages/tui/test/cli/cmd/tui/notifications.test.ts` (native notifications; attention row partial: Kilo title/pack absent) | Markers assert `Kilo` notification title and `kilo.default` soundboard. V2 notification tests exist but Kilo title and default pack are absent per the TUI attention row. Evidence: L92: sound_pack: "kilo.default", // kilocode_change / L164: expect(renderer.notifications).toEqual([{ title: "Kilo", message: "focused" }]) // kilocode_change | +| `packages/opencode/test/cli/error.test.ts`
    77 | shared-upstream patch marker | Package behavior indicated by path | needs port | Paired with cli/error.ts (partial) — Kilo-branded hints absent | Marker asserts the kilo.json hint; the hint does not exist in v2 CLI error output. Evidence: L77: "Or check your kilo.json provider/model names", // kilocode_change | +| `packages/opencode/test/cli/github-remote.test.ts`
    4,34 | shared-upstream patch marker | Package behavior indicated by path | deferred | Deferred with github.handler.ts | Rebranded fixtures for the GitHub agent command, which is deferred. Evidence: L4: // kilocode_change start: rebrand fixtures off upstream repo path / L34: // kilocode_change end | +| `packages/opencode/test/cli/help/help-snapshots.test.ts`
    11,14,32,38,39,41,52,57,105,107,135,146 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/kilo-cli/test/commands.test.ts` (Kilo command tree/help coverage) | Markers snapshot Kilo-branded help for the v1 command list. V2 Kilo owns its command tree and tests it in commands.test.ts; the v1 snapshot set does not apply. Evidence: L11: // kilocode_change start - describe Kilo's branded CLI / L14: // kilocode_change end | +| `packages/opencode/test/cli/mcp-add.test.ts`
    4,26,62,76,97 | shared-upstream patch marker | Package behavior indicated by path | needs port | Paired with cli/cmd/mcp.ts (needs port) | Asserts MCP add persists into the Kilo global config file; command absent in v2. Evidence: L4: import { mkdir } from "node:fs/promises" // kilocode_change / L26: Bun.file(path.join(home, ".config", "kilo", "kilo.json")).json(), // kilocode_change | +| `packages/opencode/test/cli/pr.test.ts`
    1 | shared-upstream patch marker | Package behavior indicated by path | needs port | Paired with cli/cmd/pr.ts (needs port) | Whole-file test for the absent pr command. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/cli/run/footer.view.test.tsx`
    838,905,971,972,973 | shared-upstream patch marker | Package behavior indicated by path | needs port | None (direct interactive run mode absent) | Markers test Kilo skill picker transitions and terminal callback props in `kilo run -i` footer; mode absent in v2. Evidence: L838: // kilocode_change start - cover direct-mode Kilo skill picker transitions / L905: // kilocode_change end | +| `packages/opencode/test/cli/run/permission.shared.test.ts`
    9,33,46,63,137,142 | shared-upstream patch marker | Interactive permission footer tests | partial | `packages/tui/test/mini/permission.shared.test.ts` | Current tests cover the real v2 footer state machine and presentation, but omit the pinned skill-shell prohibition on persistent approval because that input is absent from the current shared implementation. Evidence: L9: permissionOptions, // kilocode_change / L33: interactive: true, // kilocode_change | +| `packages/opencode/test/cli/run/run-process.test.ts`
    8,98,111,169,219,299,307,312,316,317,328,334,336,407,412,414,417,471 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/test/run.test.ts` (auto-reject blocker + interrupt; exit codes) | PRESERVED — headless runs surface auto-rejected permissions as failures (run.ts sets blocker and interrupts; run tests cover). MISSING/unverified — exact exit-code 1 on denial and the Kilo double-record/settlement ordering assertions (v2 event model differs; not asserted). Evidence: L8: import { createKiloClient } from "@kilocode/sdk/v2" // kilocode_change / L98: // kilocode_change - settle bash up front so the run exercises the stream finish rather than | +| `packages/opencode/test/cli/run/stream.transport.test.ts`
    1 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/kilo-cli/test/run.test.ts` real-host event stream coverage | Whole-file v1 transport tests; v2 run consumes the native event stream and is tested against a real host. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/cli/tui/markdown.test.ts`
    1 | shared-upstream patch marker | User-interface behavior | partial | `packages/tui/test` markdown coverage; Kilo table normalization row needs port | Whole-file Kilo markdown tests (incl. table normalization). V2 TUI has native markdown tests but the Kilo table-normalization behavior is missing per the TUI batch. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/cli/tui/plugin-lifecycle.test.ts`
    182 | shared-upstream patch marker | User-interface behavior | not needed | None | CI skip marker only. Evidence: L182: // kilocode_change - skipped flaky test on Windows #9496 | +| `packages/opencode/test/cli/tui/plugin-loader.test.ts`
    171,173,552,1234 | shared-upstream patch marker | User-interface behavior | partial | `packages/tui/test` plugin loader tests (theme install path native) | Markers relocate theme install expectations to `.kilo/themes`; v2 theme install directory is not Kilo-scoped (plugin/tui/runtime row partial). Evidence: L171: const localDest = path.join(dir, ".kilo", "themes", localThemeFile) // kilocode_change / L173: const preloadedDest = path.join(dir, ".kilo", "themes", preloadedThemeFile) // kilocode_change | +| `packages/opencode/test/cli/tui/thread.test.ts`
    40,44 | shared-upstream patch marker | User-interface behavior | partial | `packages/kilo-cli/script/dev.ts` (caller cwd preserved) and `tui.ts` directory handling | Marker tests stale PWD handling for `bun --cwd` wrappers. V2 dev launcher preserves caller cwd (board 229) but no unit test for stale PWD resolution exists. Evidence: L40: test("ignores stale PWD when resolving a relative mini project", async () => { // kilocode_change / L44: expect(resolveThreadDirectory(".", pwd.path, cwd.path)).toBe(cwd.path) // kilocode_change | +| `packages/opencode/test/config/config.test.ts`
    20,45,46,116,153,155,157,164,213,324,325,349,356,363,388,402,407,417,494,502,516,520,523,532,549,551,717,762,776,778,788,794,818,842,864,877,916,921,938,944,959,964,973,998,1000,1005,1013,1033,1035,1040,1048,1068,1070,1096,1106,1226,1309,1313,1327,1343,1359,1369,1375,1392,1427,1440,1453,1471,1488,1494,1497,1500,1507,1521,1565,1612,1617,1620,1638,1647,1667,1668,1670,1682,1686,1695,1710,1715,1725,1727,1736,1743,1945,1973,1992,2035,2043,2047,2064,2077,2107,2142,2147 | shared-upstream patch marker | Configuration/settings | partial | `packages/core/test/config/config.test.ts` (Kilo-touched per git status: refresh regression) and `packages/kilo-cli/test/settings.test.ts`, `project-config.test.ts`, `config-refresh.test.ts` | 103 markers. PRESERVED — Kilo settings/project config tests cover $schema, scoped writes, refresh (settings 27 pass). MISSING — Core-level discovery of .kilo/kilo.json, managed directory loading, trusted/untrusted substitution, Kilo plugin detection assertions (features absent per config rows). Evidence: L20: import { Git } from "../../src/git" // kilocode_change / L45: import { isIndexingPlugin } from "@kilocode/kilo-indexing/detect" // kilocode_change | +| `packages/opencode/test/config/markdown.test.ts`
    94,175,185,195,215 | shared-upstream patch marker | Configuration/settings | partial | `packages/core/test/config` markdown tests | Markers pass `trusted: true` to parse; v2 parse has no trust parameter (markdown row partial), tests otherwise native. Evidence: L94: const parsed = await ConfigMarkdown.parse(import.meta.dir + "/fixtures/frontmatter.md", { trusted: true }) // kilocode_change / L175: const result = await ConfigMarkdown.parse(import.meta.dir + "/fixtures/empty-frontmatter.md", { trusted: true }) // kilocode_change | +| `packages/opencode/test/config/tui.test.ts`
    101,143,165,694,771,778,782,932,946 | shared-upstream patch marker | Configuration/settings | partial | `packages/kilo-cli/test/tui-config.test.ts`, `packages/tui/test` config tests | PRESERVED — Kilo TUI config tests exist (tui-config.test.ts). MISSING — .kilo/tui.json discovery, kilo.default pack, trust-gated substitution assertions (config/tui.ts row partial). Evidence: L101: const local = path.join(test.directory, ".kilo") // kilocode_change / L143: path.join(test.directory, ".kilo", "tui.json"), // kilocode_change | +| `packages/opencode/test/event-manifest.test.ts`
    12,18 | shared-upstream patch marker | Package behavior indicated by path | not needed | None; v2 event manifest is native (`packages/core/src/event`) | Markers adjust the v1 manifest size/entries for Kilo events; v2 has no legacy manifest. Evidence: L12: expect(EventManifest.Latest.size).toBe(89) // kilocode_change - include global.config.updated / L18: expect(EventManifest.Latest.has("global.config.updated")).toBe(true) // kilocode_change | +| `packages/opencode/test/fixture/db.ts`
    6,10 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/kilo-cli/test/fixture.ts` + `fixture-guard.test.ts` (isolated kilo2 store guard) | Marker refuses to reset a disk-backed DB. V2 Kilo fixtures enforce isolated storage via fixture guards (cli-fixture-storage-incident baseline). Evidence: L6: // kilocode_change start / L10: // kilocode_change end | +| `packages/opencode/test/fixture/fixture.ts`
    11,12,13,14,21,25,52,54,58,64,75,82,97,138,163,186,222,246 | shared-upstream patch marker | Package behavior indicated by path | not needed | `packages/kilo-cli/test/fixture.ts` (Location-scoped real hosts) | Markers provide legacy ALS instance context and disposal ordering for v1 fixtures; v2 fixtures are host-based. Evidence: L11: import { Database } from "@opencode-ai/core/database/database" // kilocode_change / L12: import { ProjectV2 } from "@opencode-ai/core/project" // kilocode_change | +| `packages/opencode/test/fixture/log-init-worker.ts`
    1 | shared-upstream patch marker | Package behavior indicated by path | not needed | None | Whole-file v1 fixture for Kilo log initialization in workers; v2 logs use kilo2 layout without this worker. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/fixture/tui-plugin.ts`
    100,201,307,311,326 | shared-upstream patch marker | User-interface behavior | partial | `packages/kilo-cli/test/tui-fixture.tsx` and sidebar-*-ui fixtures | PRESERVED — Kilo TUI plugin fixtures exist (processes sidebar fixture). MISSING — kilo.default soundboard default (attention row). Evidence: L100: globalConfig?: HostPluginApi["state"]["globalConfig"] // kilocode_change / L201: current: () => opts.attention?.soundboard?.current?.() ?? "kilo.default", // kilocode_change | +| `packages/opencode/test/fixture/tui-runtime.ts`
    31 | shared-upstream patch marker | User-interface behavior | partial | `packages/kilo-cli/test/tui-fixture.tsx` | Default pack marker; Kilo pack absent in v2 (attention row partial). Evidence: L31: sound_pack: "kilo.default", // kilocode_change | +| `packages/opencode/test/fixture/tui-sdk.ts`
    51,55,56,60,72,75,81,82,85 | shared-upstream patch marker | User-interface behavior | not needed | Real loopback hosts in `packages/kilo-cli/test/*fixture*.ts` (no fake SDK per remote_v2 decision) | Markers stub Kilo endpoints in a fake SDK; v2 Kilo tests use real hosts, and those endpoints are either native or absent per their rows. Evidence: L51: // case "/experimental/workspace": // kilocode_change / L55: case "/network": // kilocode_change | +| `packages/opencode/test/image/image.test.ts`
    58,96 | shared-upstream patch marker | Package behavior indicated by path | needs port | `packages/core/test/image*.test.ts` paired with image.ts row | Marked block covers Kilo's Photon fallback; behavior unverified/absent in v2 image.ts. Evidence: L58: // kilocode_change start - cover Kilo's Photon-unavailable fallback / L96: // kilocode_change end | +| `packages/opencode/test/installation/installation.test.ts`
    71,95,107,121,135,160,161,179 | shared-upstream patch marker | Package behavior indicated by path | deferred | Deferred with package-manager distribution; `packages/kilo-cli/test/updater.test.ts` covers local updater | Markers assert Kilo npm/brew identities in upgrade resolution; distribution deferred. Evidence: L71: // kilocode_change start - curl/unknown fallback now resolves from the public npm / L95: // kilocode_change end | +| `packages/opencode/test/kilocode/agent-global-config-dirs.test.ts`
    1 | Kilo-owned inherited marker | Configuration/settings | partial | `packages/kilo-cli/test/agent-policy.test.ts` and native Core permission tests | Test asserts the built-in code agent allows external_directory reads under Global.Path.config by default. V2 agent-policy.ts appends Kilo skill/semantic_search allows and the exploreBash ceiling (:235-252) but I found no rule granting the global config directory as an external_directory allow, and no v2 test for it. Preserved: agent permission transform seam and tests; missing: this specific default and its regression. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/agent-remove.test.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | needs port | Kilo agent removal RPC/command in `packages/kilo-cli` (master plan lists agent/skill removal among missing backend consumers) with a paired test | Test covers 'removes config-backed imported agents' for the v1 Kilo agent remove path. V2 has no agent-remove producer (grep remove in agent-policy.ts: none) and the transplanted extension still lacks that backend consumer per the master plan, so both behavior and test are absent. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/agent-skill-permissions.test.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | partial | `packages/kilo-cli/test/agent-policy.test.ts`, `test/skill-policy.test.ts` | Test asserts non-system native agents can use skills while system agents get skill denied/disabled. V2 agent-policy adds `skill: * allow` for supervised agents (agent-policy.ts:252) and skill-policy tests exist, but no v2 rule or test denies the skill tool for system agents (grep deny/system in skill-policy.ts: none). Preserved: allow half; missing: system-agent denial regression. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/cli-run-auto-helper.test.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | needs port | `packages/kilo-cli/test/run.test.ts` once run.ts tracks Task-child sessions | Unit tests for create/allowed/track of the --auto child-session tracker. V2 run.ts has no child tracking (source row partial), so these regressions have no target yet. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/cli/cmd/remote.test.ts`
    1 | Kilo-owned inherited marker | Standalone remote command contract | partial | `packages/kilo-cli/test/remote-ui.test.tsx`, `remote-rpc.test.ts`, and `remote-session.test.ts` | Current tests cover consent, preview limitations, device authentication, relay protocol, commands and detach, but the pinned instance-advertisement helper belongs to the unsupported standalone headless-host surface and has no exact v2 test. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/config-gitignore.test.ts`
    1 | Kilo-owned inherited marker | Configuration/settings | needs port | Kilo project-config writer `packages/kilo-cli/src/project-config.ts` / paths.ts plus a paired test | Test asserts the .kilo config directory .gitignore lists pnpm and yarn lockfile patterns because Kilo's Npm.Service may write them. V2 kilo-cli has no .gitignore writer for the project config directory (grep gitignore in kilo-cli/src: none) and no plugin dependency installer writing lockfiles there; if v2 gains plugin install into .kilo, this invariant and test need porting. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/config-validation.test.ts`
    1 | Kilo-owned inherited marker | Configuration/settings | native equivalent | `packages/kilo-cli/test/settings.test.ts` | Replacement settings suite covers schema refusal, queue recovery, boundary refusal, no-secret diagnostics, and native verification pipeline. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/config/opentelemetry-default.test.ts`
    1 | Kilo-owned inherited marker | Configuration/settings | deferred | `packages/kilo-cli/src/telemetry-settings.ts` owner-only opt-in | Pinned default-true telemetry behavior is deliberately not mapped; v2 consent is separate opt-in. Product/consent decision required, so refusal is not parity. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/custom-provider-delete.test.ts`
    1 | Kilo-owned inherited marker | Provider/model routing or UI | native equivalent | SettingsRpc provider collection unset + CLI settings tests | V2 collection translation implements provider null-removal as leaf removal through verified queued writes; replacement tests cover the original caller semantics. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/diff-full.test.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | native equivalent | Core VCS/git diff tests under `packages/core/test` covering `git diff --unified` batching | Tests the v1 Kilo diffFull replacement for JS Myers. V2 Core already produces diffs via git (plugin/vcs/git.ts:383-401) and its tests exercise the native path; the Kilo-specific module does not exist to test. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/encoding.test.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | needs port | Kilo encoding module port over Core read/write tools (`packages/core/src/tool/read.ts`/`write.ts`) with paired tests | Tests Kilo's Encoding namespace: BOM/UTF-16 detection, round-trip write preserving encoding. V2 Core tools read files as UTF-8 only (grep utf-16/BOM in core/src/tool and filesystem: none), so neither behavior nor tests exist. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/help.test.ts`
    10 | Kilo-owned inherited marker | Package behavior indicated by path | not needed | `packages/kilo-cli/test/commands.test.ts` help coverage | The single marked line adapts the import after upstream renamed auth→providers; it is merge glue, not a Kilo invariant. Kilo v2 owns its own command tree and tests it in kilo-cli/test/commands.test.ts. Evidence: L10: import { ProvidersCommand } from "../../src/cli/cmd/providers" // kilocode_change — upstream renamed auth → providers | +| `packages/opencode/test/kilocode/instruction.test.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | needs port | Core `packages/core/src/instruction-discovery.ts` global path resolution via Kilo `paths.ts` (KILO_CONFIG_DIR handled in kilo-cli/src/paths.ts) plus a test | Test verifies instruction discovery falls back across KILO_CONFIG_DIR profile directories. V2 paths.ts knows KILO_CONFIG_DIR for the kilo2 layout, but Core instruction discovery uses its own Global paths and no test covers a Kilo profile fallback; invariant unproven. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/kilo-loader-auth.test.ts`
    1 | Kilo-owned inherited marker | Authentication/account integration | native equivalent | `packages/kilo-gateway/src/models.ts:111-188` (anonymous scope returns only isFree models) with `packages/kilo-cli/test/gateway-anonymous.test.ts` | V1 tests assert paid models are listed anonymously with apiKey 'anonymous' and free status is not inferred from prices. V2 deliberately changed the contract: signed-out catalog exposes only isFree records (models.ts:150-153 documents this as stricter than v1) and gateway-anonymous.test.ts covers anonymous startup. Behavior is intentionally different and tested; the v1 assertions must not be ported verbatim. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/kilo-models-401-fallback.test.ts`
    1 | Kilo-owned inherited marker | Provider/model routing or UI | native equivalent | `packages/kilo-gateway/test/{gateway,plugin}.test.ts` | Current Gateway tests cover 401/403 auth failures, signed-out public/free catalog behavior, failed public reads, and withdrawal without cross-account fallback. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/kilo-sessions.test.ts`
    1,280,331,528 | Kilo-owned inherited marker | Session lifecycle/history/UI | partial | `packages/kilo-cli/test/remote-session.test.ts`, `remote-status.test.ts`, `remote-rpc.test.ts` | Marked blocks test setInstanceAdvertisement default/replace semantics and out-of-band heartbeat plus per-session platform. V2 remote tests cover heartbeat, attach/ownership and capabilities but the advertisement feature is deferred (instance-advertisement.ts row), so those assertions have no target; the rest of the file (new-file marker) maps to existing remote tests. Evidence: L1: // kilocode_change - new file / L280: // kilocode_change start - K1 W1 / DEF-1: instance advertisement + per-session platform. | +| `packages/opencode/test/kilocode/local-model.test.ts`
    1 | Kilo-owned inherited marker | Provider/model routing or UI | native equivalent | Native provider config/model catalog plus current Core provider tests | Local/custom providers are represented by native v2 provider configuration and provider plugin tests; no Kilo-only runtime fork is required for the tested local-model path. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/model-cache-effect.test.ts`
    1 | Kilo-owned inherited marker | Provider/model routing or UI | native equivalent | `packages/kilo-gateway/src/startup-cache.ts` and `packages/kilo-gateway/test/{startup-cache,plugin}.test.ts` | V2 owns account/location-scoped startup catalog snapshots with explicit lifetime, credential identity isolation, stale-response discard, and failure isolation tests. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/patch-jsonc.test.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | native equivalent | `packages/kilo-cli/src/settings.ts:378,622` (jsonc-parser modify/applyEdits) with `packages/kilo-cli/test/settings.test.ts` | Tests jsonc-parser modify semantics when a permission rule changes from string to object. V2 settings writes use the same jsonc-parser modify/applyEdits path and settings tests cover field set/unset on jsonc sources; the specific string→object permission case is a library behavior now exercised through settings collection writes. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/permission/config-paths.test.ts`
    1 | Kilo-owned inherited marker | Configuration/settings | native equivalent | Core config source/variable pipeline + CLI settings scope tests | V2 has explicit source provenance and project-confined file substitution; settings tests exercise profile/project folding and missing file references. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/permission/permission-origins.test.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | needs port | Kilo permission provenance over `packages/kilo-cli/src/project-config.ts` scopes (config provenance exists; permission-rule provenance does not) | Tests that each permission key is attributed to global vs project scope to drive auto-approval provenance. V2 tracks settings provenance per scope but has no permission_origins map or provenance in approvals (see kilocode/session/prompt.ts row); needs port with the provenance feature. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/project-config-update.test.ts`
    1 | Kilo-owned inherited marker | Configuration/settings | native equivalent | SettingsRpc collectionSet/Unset and `packages/kilo-cli/src/settings.ts` | Project-scoped writes use expected revisions, atomic update, verification, provenance and refresh; replacement settings/config-refresh coverage exercises the consumer path. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/prompt-dismiss-contract.test.ts`
    6,39 | Kilo-owned inherited marker | Queued-prompt interactive dismissal | partial | `packages/kilo-cli/src/remote-session.ts:187-195`, `remote-suggestions.ts:177`, and `test/remote-suggestions.test.ts:524-580` | V2 explicitly dismisses pending remote suggestions when session input is enqueued and tests replay suppression, but the pinned contract also dismisses pending questions without cancelling the active stream; that combined question-and-suggestion boundary is not proven. Evidence: L6: * stream" behaviour lives inside a kilocode_change block. An upstream merge / L39: /kilocode_change start[^\n]*register the queued follow-up[\s\S]*?Suggestion\.dismissAll[\s\S]*?question\.dismissAll[\s\S]*?KiloSessionPromptQueue\.enq | +| `packages/opencode/test/kilocode/provider-list-failed-state.test.ts`
    1 | Kilo-owned inherited marker | Provider/model routing or UI | native equivalent | `packages/kilo-gateway/src/plugin.ts` catalog editor plus plugin tests | Failed or empty account/public catalog reads withdraw only the affected scoped offer and never reuse another account snapshot; current tests exercise failed team/public/cold refresh states. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/question-option-schema.test.ts`
    55,57,59,61,64,66 | Kilo-owned inherited marker | Package behavior indicated by path | needs port | `packages/schema/src/form.ts`/question schema extension via Kilo-owned schema, consumed by `packages/kilo-vscode` localization | Tests (incl. static source guards on the marker block) that Question options carry optional mode, labelKey, descriptionKey for localization and mode hints. V2 schema has no such fields (grep labelKey/descriptionKey in schema/core: none); the transplanted extension's localized option rendering would need them. Evidence: L55: // Static source checks — guard the kilocode_change markers so a conflict / L57: test("source declares mode as an optional field inside a kilocode_change block", () => { | +| `packages/opencode/test/kilocode/sandbox/session-tools.test.ts`
    115 | Kilo-owned inherited marker | Sandboxed tool confinement | partial | `packages/kilo-cli/test/sandbox.test.ts` and `sandbox-mcp.test.ts` | Current tests prove native shell and local-MCP confinement, protected paths, fail-closed configuration and cleanup. V2 intentionally does not reproduce v1's per-session filesystem decoration across every direct file tool, so this is narrower than exact parity. Evidence: L115: clients: () => Effect.succeed({}), // kilocode_change - upstream's MCP resource tools probe the clients | +| `packages/opencode/test/kilocode/sandbox/session.test.ts`
    41 | Kilo-owned inherited marker | Per-session sandbox lifecycle | partial | `packages/kilo-cli/src/sandbox.ts`, `host.ts`, `sandbox-pty.ts` and their tests | V2 provides opt-in OS confinement at host launch, wraps shell/local MCP, refuses PTYs, and cleans launchers, but it does not port v1's persisted per-session toggle, fork inheritance, family activation or background-job guards. Evidence: L41: Layer.provide(AppNodeBuilder.build(SessionV2.node, [[SessionExecution.node, SessionExecution.noopLayer]])), // kilocode_change | +| `packages/opencode/test/kilocode/server/httpapi-kilo-edit.test.ts`
    33 | Kilo-owned inherited marker | Server/API integration | needs port | Kilo Gateway route in `packages/kilo-gateway` (FIM/next-edit is listed as a remaining feature-specific service in the master plan) | Marked line only keeps the filewatcher-disable flag visible in the test layer, but the whole test covers the Kilo next-edit proxy (gateway auth, autocomplete headers, BYOK passthrough, 504 mapping). V2 has no next-edit route (grep next-edit/autocomplete in kilo-gateway/kilo-cli: none). Evidence: L33: // kilocode_change - keep the filewatcher-disable flag visible (see httpapi-instance-route-auth.test.ts) | +| `packages/opencode/test/kilocode/server/httpapi-memory.test.ts`
    17 | Kilo-owned inherited marker | Memory API integration | native equivalent | `packages/schema/src/kilocode/memory.ts:108-145`, `packages/kilo-cli/src/memory-rpc.ts`, and `packages/kilo-cli/test/memory.test.ts:365-403,655-778` | The HTTP group is replaced by location-scoped Memory RPC. Current integration tests cover enable/configure/remember/recall/correct/disable/purge, disabled failures, and location isolation; engine tests cover safe purge ownership. Evidence: L17: // kilocode_change - keep the filewatcher-disable flag visible (see httpapi-instance-route-auth.test.ts) | +| `packages/opencode/test/kilocode/session-export/source-contract.test.ts`
    8 | Kilo-owned inherited marker | Session lifecycle/history/UI | needs port | Kilo session export feature port with paired tests | Static-source tests guard the export compaction block, absence of rawParts retention and windowsHide on git spawns in the v1 session-export module. V2 has no session export/ingest module (grep session-export: none), so these contracts have no subject. Evidence: L8: text.indexOf("// kilocode_change start - export self-contained compaction capture"), | +| `packages/opencode/test/kilocode/session-metrics.test.ts`
    1 | Kilo-owned inherited marker | Throughput computation tests | native equivalent | `packages/tui/test/cli/tui/session-rows.test.ts:29-52` | Current focused tests exercise real turn-output throughput across multiple model steps, exclude tool time, and omit throughput without a valid stream boundary, superseding the narrower helper-only v1 tests. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/session-processor-review-telemetry.test.ts`
    1 | Kilo-owned inherited marker | Session lifecycle/history/UI | needs port | `packages/kilo-cli/src/telemetry.ts` hook in review-policy with test | Tests KiloSessionProcessor emitting ReviewCommand telemetry. V2 review-policy has no telemetry emission (grep ReviewCommand in kilo-cli: none). Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/sessions/remote-command.test.ts`
    119,162 | Kilo-owned inherited marker | Remote command exit capability | native equivalent | `packages/kilo-cli/src/remote-session.ts:863-873,1183-1210` and `test/remote-session.test.ts:189-248,924-1139` | The v2 relay always advertises `canExitSession: true` independently of synthetic command discovery, and `exit_cli` detaches without ending the durable session; focused tests cover both invariants. Evidence: L119: // kilocode_change - K1 W1: canExitSession is always true, independent of / L162: // kilocode_change - K1 W1: canExitSession is true even when the synthetic | +| `packages/opencode/test/kilocode/sessions/remote-protocol.test.ts`
    269 | Kilo-owned inherited marker | Remote presence and instance advertisement | partial | `packages/kilo-cli/src/remote-session.ts:386-406` and current remote protocol/presence schemas | V2 carries heartbeat capabilities and per-session presence/platform behavior, but its preview relay intentionally omits the pinned spawn-capable instance advertisement and cloud-clone metadata surface. Evidence: L269: // kilocode_change - K1 W1: instance advertisement + per-session platform | +| `packages/opencode/test/kilocode/sessions/remote-sender.test.ts`
    118,122,1215,1425,2266,2676,2797,2849,2906,4070 | Kilo-owned inherited marker | Session lifecycle/history/UI | partial | `packages/kilo-cli/test/remote-rpc.test.ts`, `remote-session.test.ts`, `remote-suggestions.test.ts` | Marked blocks add mock.restore and tests for send_message model-string normalization to kilo/ and ephemeralTools mapping. V2 remote tests use real loopback hosts (no mock.restore need) and cover send_message delivery, but I found no assertion normalizing an unprefixed model string to the Kilo provider (remote-sender.ts source row is partial). Preserved: delivery tests; missing: model-normalization case. Evidence: L118: // kilocode_change start / L122: // kilocode_change end | +| `packages/opencode/test/kilocode/sessions/remote-ws.test.ts`
    420,1996 | Kilo-owned inherited marker | Remote WebSocket heartbeat and advertisement | partial | `packages/kilo-cli/src/remote-session.ts:386-406` and `test/remote-session.test.ts:75-91,924-1139` | Current relay tests cover immediate heartbeat capabilities, reconnect and detach behavior, but no current consumer or test carries the pinned instance-advertisement propagation invariant. Evidence: L420: // kilocode_change - K1 W1: with the immediate heartbeat on FIRST open, / L1996: // kilocode_change - K1 W1: instance advertisement flows through the | +| `packages/opencode/test/kilocode/snapshot-freeze-repro.test.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | native equivalent | Core git diff batching/cap in `packages/core/src/plugin/vcs/git.ts` and Snapshot capture bounds (`maximumUntrackedFileBytes`) | Regression for the JS Myers freeze; v2 never uses the JS diff path for snapshots (git-based, capped), so the freeze mode is structurally absent. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/snapshot-track-timeout.test.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | needs port | Paired with the missing interactive guard (kilocode/snapshot/track.ts partial) in Core snapshot or a Kilo snapshot plugin | 969-line unit suite for the timeout/continue/disable/progress-part guard. V2 has only size bounds, so the guard's tests have no subject. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/telemetry/feedback.test.ts`
    1 | Kilo-owned inherited marker | Feedback telemetry event contract | needs port | Test accompanying the proposed v2 thumbs-feedback telemetry event | The pinned test freezes the externally consumed `Feedback Submitted` event name. V2 has no feedback event/action consumer, so neither this wire label nor its emission is currently covered. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/tool/generate-image.test.ts`
    1 | Kilo-owned inherited marker | Image-tool parsing/provider/path regressions | needs port | Tests accompanying the proposed owned v2 image tool | No current v2 tests cover Kilo-vs-OpenRouter provider selection, image data-URL parsing/MIME normalization, model fallback catalog, or output-extension correction. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/tool/send-file.test.ts`
    182,221,223,262,512,542 | Kilo-owned inherited marker | Remote file-delivery authorization regressions | needs port | Tests accompanying the proposed typed outbound file-delivery seam | Current attachment tests cover inbound user files. They do not cover pre-inspection authorization for missing/directory/symlink targets, remote-connect gating, file caps/sniffing, or outbound delivery attachment semantics. Evidence: L182: // kilocode_change start — send_file now authorizes missing files with external_directory + read / L221: // kilocode_change end | +| `packages/opencode/test/kilocode/tool/websearch-kilo-exa.test.ts`
    1 | Kilo-owned inherited marker | Tool behavior/UI | needs port | Paired with websearch-kilo-exa.ts (needs port) in a Kilo-owned tool plugin | Tests the authenticated Kilo Exa route, response and error contract; source row already classified needs port with no v2 consumer. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/kilocode/tui/sync-v2.test.tsx`
    19,27 | Kilo-owned inherited marker | V2 TUI projection and project-filter regressions | partial | `packages/tui/src/context/data.tsx` and `packages/tui/test/cli/tui/data.test.tsx:810-1478` | Current TUI data tests extensively cover durable execution/status projection, stale refreshes and live-event convergence. The pinned suite additionally proves resolved-project filtering and duplicate-event behavior for tool failure and prompt admission/promotion; exact project-filter parity was not located. Evidence: L19: // kilocode_change start - live events are filtered by the resolved project ID / L27: // kilocode_change end | +| `packages/opencode/test/kilocode/util/url.test.ts`
    1 | Kilo-owned inherited marker | Package behavior indicated by path | needs port | Kilo-owned URL normalization used by webfetch/websearch tools in a kilo-cli tool policy | Tests normalizeUrls converting look-alike/unicode hostnames to punycode. V2 has no equivalent (grep punycode/toASCII in core and kilo-cli: none). Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/lib/cli-process.ts`
    32,36,40,215,225,294,340,413,540,546 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/kilo-cli/test/fixture.ts`, `daemon-fixture.ts`, `interactive-fixture.ts` | Markers tune the v1 CLI subprocess helper. V2 Kilo has its own real-host fixtures with timeouts and isolation. Evidence: L32: import { TestCli } from "../../script/kilocode/test-cli" // kilocode_change / L36: // kilocode_change start - reuse the runner's once-built CLI graph instead of transpiling it in every child | +| `packages/opencode/test/lib/effect.ts`
    139,144 | shared-upstream patch marker | Package behavior indicated by path | not needed | `packages/core/test/lib` Effect test helpers | Marker exports thin helper aliases for v1 layer tests; v2 core has its own. Evidence: L139: // kilocode_change start / L144: // kilocode_change end | +| `packages/opencode/test/lib/llm-server.ts`
    512,520 | shared-upstream patch marker | Server/API integration | native equivalent | `packages/core/test/lib` LLM test server / `packages/kilo-cli/test/*` scripted model fixtures | Marker adds a finish-reason control to the fake LLM server; v2 test LLM servers support scripted finishes. Evidence: L512: // kilocode_change start / L520: // kilocode_change end | +| `packages/opencode/test/lsp/index.test.ts`
    12,14,17,18,20,83,170,172,213 | shared-upstream patch marker | Package behavior indicated by path | needs port | Future v2 LSP tests (no LSP runtime) | Markers gate typescript spawn behind KILO_EXPERIMENTAL_LSP_TOOL and TsClient; v2 has no LSP. Evidence: L12: import * as launch from "../../src/lsp/launch" // kilocode_change - spy on spawn / L14: import { provideTestInstance, provideTmpdirInstance, TestInstance, tmpdir } from "../fixture/fixture" // kilocode_change | +| `packages/opencode/test/lsp/launch.test.ts`
    8,37 | shared-upstream patch marker | Package behavior indicated by path | needs port | Future v2 LSP launch (see lsp/launch.ts) and formatter env scrub (format row needs port) | Marked test asserts KILO_SERVER_PASSWORD/USERNAME/CONFIG* are stripped from language server env; no v2 LSP, and env scrubbing for spawned helpers is not implemented. Evidence: L8: // kilocode_change start / L37: // kilocode_change end | +| `packages/opencode/test/mcp/lifecycle.test.ts`
    9,10,283,291,298,352 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/test/sandbox-mcp.test.ts` (native MCP add/disconnect/reconnect under sandbox; 20 pass) | PRESERVED — MCP lifecycle under Kilo sandbox confinement is tested. MISSING — sandbox network-relay authority semantics (v2 confines processes, no relay). Evidence: L9: import * as SandboxNetwork from "../../src/kilocode/sandbox/network" // kilocode_change / L10: import { run as runSandbox, type Profile } from "@kilocode/sandbox" // kilocode_change | +| `packages/opencode/test/mcp/oauth-browser.test.ts`
    21,30 | shared-upstream patch marker | Authentication/account integration | native equivalent | `packages/core/test` MCP oauth tests | Marker fixes a test race; not a product invariant. Evidence: L21: // kilocode_change start - buffer the error until the consumer attaches / L30: // kilocode_change end | +| `packages/opencode/test/permission/next.test.ts`
    24,575,807,1122 | shared-upstream patch marker | Package behavior indicated by path | needs port | `packages/core/test/permission.test.ts` (AskResult without rule provenance) | Marked assertions expect `{manual:false, rule:{...}}` from ask; v2 AskResult does not carry the deciding rule (permission row partial). Evidence: L24: AppNodeBuilder.build(InstanceStore.node, [[InstanceStore.bootstrapNode, noopBootstrap]]), // kilocode_change / L575: expect(result).toEqual({ manual: false, rule: { permission: "bash", pattern: "*", action: "allow" } }) // kilocode_change - ask returns the auto-appro | +| `packages/opencode/test/plugin/auth-override.test.ts`
    48 | shared-upstream patch marker | Authentication/account integration | partial | `packages/core/test/plugin*.test.ts` (.opencode/plugin) | Path-convention marker; Core plugin dirs are not Kilo-scoped (plugin/install row partial). Evidence: L48: const pluginDir = path.join(tmp.directory, ".kilo", "plugin") // kilocode_change | +| `packages/opencode/test/plugin/codex.test.ts`
    134,204 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/core/src/plugin/provider/openai.ts:23-24` (codexAllowed/codexDisallowed) with core provider tests | Marked tests assert disallowed models are filtered for OAuth users and kept for API-key users. V2 openai plugin encodes allowed/disallowed sets natively. Evidence: L134: // kilocode_change start / L204: // kilocode_change end | +| `packages/opencode/test/plugin/install-concurrency.test.ts`
    85,108,111,119,140 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/core/test/plugin*.test.ts` | Path-convention markers (.kilo); concurrency behavior native, directory not Kilo-scoped. Evidence: L85: const cfg = await read(path.join(tmp.path, ".kilo", "opencode.jsonc")) // kilocode_change / L108: // kilocode_change start | +| `packages/opencode/test/plugin/install.test.ts`
    125,128,149,152,160,219,264,271,291,311,331,374,393,396,413,430,445,448,465,468,470,486,489,495,498,522,551,554,570,584 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/core/test/plugin*.test.ts` | 30 path markers; same as install-concurrency. Evidence: L125: // kilocode_change start / L128: // kilocode_change end | +| `packages/opencode/test/plugin/meta.test.ts`
    53,57 | shared-upstream patch marker | Package behavior indicated by path | not needed | None | Test workaround for a Bun 1.3.11 bug; v2 pins Bun 1.4.0. Evidence: L53: // kilocode_change start / L57: // kilocode_change end | +| `packages/opencode/test/plugin/openai-ws.test.ts`
    520,571 | shared-upstream patch marker | Package behavior indicated by path | not needed | None (ws pool absent) | Test timing tweak for a module absent in v2. Evidence: L520: idleTimeout: 100, // kilocode_change - leave enough time for WebSocket callbacks on loaded CI runners / L571: idleTimeout: 100, // kilocode_change - leave enough time for WebSocket callbacks on loaded CI runners | +| `packages/opencode/test/plugin/trigger.test.ts`
    13 | shared-upstream patch marker | Package behavior indicated by path | not needed | None | Layer-graph plumbing marker. Evidence: L13: import { Git } from "../../src/git" // kilocode_change | +| `packages/opencode/test/plugin/workspace-adapter.test.ts`
    16 | shared-upstream patch marker | Package behavior indicated by path | not needed | None | Layer-graph plumbing marker. Evidence: L16: import { Git } from "../../src/git" // kilocode_change | +| `packages/opencode/test/plugin/xai.test.ts`
    96,140,180,466 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/core/test` xai tests + App identity (`host.ts:21`) | Markers assert kilocode UA/referrer; v2 derives UA from App identity (xai row native). Evidence: L96: expect(params.get("referrer")).toBe("kilocode") // kilocode_change / L140: expect(captured[0].get("user-agent")).toMatch(/^kilocode\//) // kilocode_change | +| `packages/opencode/test/preload.ts`
    8,33,43,94,95,98 | shared-upstream patch marker | Package behavior indicated by path | not needed | `packages/kilo-cli/test/fixture.ts` and `packages/kilo-memory` tests | Markers wire v1 test environment; v2 Kilo tests have their own fixtures and memory package tests. Evidence: L8: import { remove as cleanup } from "./kilocode/cleanup" // kilocode_change / L33: await cleanup(dir) // kilocode_change | +| `packages/opencode/test/project/instance.test.ts`
    9,52,55,73,176,194,276 | shared-upstream patch marker | Package behavior indicated by path | not needed | None | Tests for v1 instance ALS bridging; not applicable. Evidence: L9: import { capture } from "../../src/kilocode/instance" // kilocode_change / L52: // kilocode_change start - capture prefers legacy ALS, then falls back to the Effect fiber reference | +| `packages/opencode/test/provider/header-timeout.test.ts`
    28,44,200,215 | shared-upstream patch marker | Provider/model routing or UI | needs port | Paired with provider.ts first-byte timeout (partial/unverified) in `packages/core/src/plugin/provider` fetch | Marked tests assert a delayed-body server trips the header timeout; v2 fetch timeout extension to first byte is unverified/absent. Evidence: L28: Effect.promise(() => delayedBodyServer(1000, ":\n\n")), // kilocode_change / L44: { config: providerConfig(server.url, { headerTimeout: 500 }) }, // kilocode_change | +| `packages/opencode/test/provider/provider.test.ts`
    81,730,736,1242,1243,1244,1255,1256,1257,1539,1545,1775,1832,1836 | shared-upstream patch marker | Provider/model routing or UI | partial | `packages/kilo-cli/test/routed-model*.test.ts`, `gateway-protocol.test.ts`, `model-picker.test.ts` | PRESERVED — Kilo auto/small-model fallback and gateway variants tested (routed-model, gateway tests). MISSING — Kilo attribution headers (X-Title/X-BILLING-INVOKE-ORIGIN) assertions, opencode-provider drop rule. Evidence: L81: if (!item) return 0 // kilocode_change - Kilo drops opencode provider without apiKey/auth / L730: // kilocode_change start - Kilo always has an auto-routed small-model fallback | +| `packages/opencode/test/provider/transform.test.ts`
    76,140,1270,1315,3033,3197,3296,3330,3709,3725,3758,3866,3887,3889,4250,4272,4341,4358,4395,4411,4480,4481,5171,5255,5460,5547 | shared-upstream patch marker | Provider/model routing or UI | partial | `packages/core/test/aisdk-native.test.ts` | PRESERVED — reasoning/effort mapping tests exist natively. MISSING — MiniMax thinking toggle, Azure helper exclusions and reasoningSummary guard cases (transform row partial). Evidence: L76: // kilocode_change start / L140: // kilocode_change end | +| `packages/opencode/test/question/question.test.ts`
    21,123,150,231,235,309,313 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/core/test/form.test.ts` | PRESERVED — form request/cancel/unknown-request tests native. MISSING — non-blocking question mode used by review follow-up. Evidence: L21: blocking?: boolean // kilocode_change / L123: // kilocode_change start - review follow-up uses non-blocking question prompts | +| `packages/opencode/test/server/auth.test.ts`
    26,31,58 | shared-upstream patch marker | Server/API integration | native equivalent | `packages/kilo-cli/test/http-boundary.test.ts`, `daemon.test.ts` (username opencode) | Branding assertion; v2 uses opencode username by design (auth row). Evidence: L26: // kilocode_change / L31: Authorization: `Basic ${Buffer.from("kilo:secret").toString("base64")}`, // kilocode_change | +| `packages/opencode/test/server/experimental-session-list.test.ts`
    1 | shared-upstream patch marker | Session lifecycle/history/UI | partial | `packages/kilo-vscode/test/backend-session.test.ts` (experimental.session.list adapter over v2 pagination/registry) | Whole-file v1 test for worktree-family session listing. V2 has an extension-backend adapter with real-host tests, but no server endpoint with worktrees/directories filters (handlers/experimental row partial). Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/server/httpapi-compression.test.ts`
    98 | shared-upstream patch marker | Server/API integration | not needed | None | Comment marker about /config payload size. Evidence: L98: // kilocode_change - /config now serialises enough defaults to clear the threshold on its own, | +| `packages/opencode/test/server/httpapi-config.test.ts`
    59,61 | shared-upstream patch marker | Configuration/settings | native equivalent | `packages/kilo-cli/test/settings.test.ts` / core config tests | Marker adjusts expected file path; native config write tests exist. Evidence: L59: // kilocode_change start / L61: // kilocode_change end | +| `packages/opencode/test/server/httpapi-cors.test.ts`
    67,76 | shared-upstream patch marker | Server/API integration | not needed | None | Test plumbing marker. Evidence: L67: // kilocode_change start - keep the filewatcher-disable flag visible (see httpapi-instance-route-auth.test.ts) / L76: // kilocode_change end | +| `packages/opencode/test/server/httpapi-event.test.ts`
    4,6,20,32,41,43,78,80,123,158,179,185,220,256,265 | shared-upstream patch marker | Server/API integration | not needed | None; v2 `/api/event` native stream tested in server tests | 15 markers verify legacy SSE encoding of EventV2/Kilo bus events; legacy wire removed in v2. Evidence: L4: import * as Sse from "effect/unstable/encoding/Sse" // kilocode_change - decode the legacy SSE wire format / L6: // kilocode_change start - verify transformed EventV2 values at the legacy SSE boundary | +| `packages/opencode/test/server/httpapi-exercise/backend.ts`
    48,66,78,86,119 | shared-upstream patch marker | Server/API integration | not needed | None (v1 route exerciser) | Test harness plumbing for the v1 HttpApi exerciser. Evidence: L48: // kilocode_change start - an in-flight SSE fiber can leave the in-process router scope unable / L66: // kilocode_change end | +| `packages/opencode/test/server/httpapi-exercise/dsl.ts`
    30,58,62,168 | shared-upstream patch marker | Server/API integration | not needed | None | v1 exerciser harness. Evidence: L30: validAuthProbe: true, // kilocode_change / L58: // kilocode_change start - blocking routes only prove they reject missing credentials so no valid request leaks into app disposal | +| `packages/opencode/test/server/httpapi-exercise/environment.ts`
    14,15,16,18 | shared-upstream patch marker | Server/API integration | not needed | None | v1 exerciser harness. Evidence: L14: process.env.KILO_DISABLE_SESSION_INGEST = "true" // kilocode_change - isolate the exerciser from async Kilo session sync / L15: process.env.KILO_DISABLE_PRESENCE = "1" // kilocode_change - presence now has a default Event Service URL; never open real sockets from the exerciser | +| `packages/opencode/test/server/httpapi-exercise/index.ts`
    20,39,301,344,1716,1722,1768,1787,1799,1808,1850,1863 | shared-upstream patch marker | Server/API integration | not needed | None | v1 exerciser harness; Kilo endpoints tracked by their rows. Evidence: L20: import { Effect, Layer } from "effect" // kilocode_change / L39: import { kiloScenarios } from "../../kilocode/server/httpapi-exercise-scenarios" // kilocode_change | +| `packages/opencode/test/server/httpapi-exercise/runner.ts`
    52,267 | shared-upstream patch marker | Server/API integration | not needed | None | v1 exerciser harness. Evidence: L52: if (!scenario.validAuthProbe) return // kilocode_change - blocking routes skip the valid probe; a leaked valid request hangs final app disposal / L267: // kilocode_change - each exerciser process already owns an isolated DB; unlinking it between scenarios races async Kilo callbacks | +| `packages/opencode/test/server/httpapi-exercise/types.ts`
    91,108 | shared-upstream patch marker | Server/API integration | not needed | None | v1 exerciser harness. Evidence: L91: validAuthProbe: boolean // kilocode_change / L108: validAuthProbe: boolean // kilocode_change | +| `packages/opencode/test/server/httpapi-experimental.test.ts`
    2,280,281 | shared-upstream patch marker | Server/API integration | native equivalent | `packages/core/test` worktree tests / protocol `worktree.list` | Marker asserts managed worktree listing with realpath; v2 worktree.list is native. Evidence: L2: import { realpath } from "node:fs/promises" // kilocode_change / L280: const directory = yield* Effect.promise(() => realpath(info.directory)) // kilocode_change | +| `packages/opencode/test/server/httpapi-file.test.ts`
    55 | shared-upstream patch marker | Server/API integration | not needed | None | CI skip marker. Evidence: L55: // kilocode_change - skip on Windows: Kilo file search returns [] for hello.txt. | +| `packages/opencode/test/server/httpapi-instance-route-auth.test.ts`
    17,25,64,81 | shared-upstream patch marker | Server/API integration | native equivalent | `packages/kilo-cli/test/http-boundary.test.ts` | Branding in auth header; v2 route auth tested with opencode username. Evidence: L17: // kilocode_change start - keep the filewatcher-disable flag visible so the / L25: // kilocode_change end | +| `packages/opencode/test/server/httpapi-listen.test.ts`
    296,304 | shared-upstream patch marker | Server/API integration | not needed | None | Test adaptation to absent proxy. Evidence: L296: // kilocode_change start - use an authenticated local route instead of proxy-dependent status / L304: // kilocode_change end | +| `packages/opencode/test/server/httpapi-mdns.test.ts`
    62 | shared-upstream patch marker | Server/API integration | not needed | None (mDNS removed) | Branding on removed feature. Evidence: L62: expect(published[0]!.name).toBe(`kilo-${listener.port}`) // kilocode_change | +| `packages/opencode/test/server/httpapi-provider.test.ts`
    9,109,112,144,147,186,189,218,221 | shared-upstream patch marker | Provider/model routing or UI | partial | `packages/core/test/plugin*.test.ts` | Path markers (.kilo/plugin) and dependency preparation helper; plugin dir not Kilo-scoped. Evidence: L9: import { preparePluginDependencies } from "../kilocode/plugin-dependencies" // kilocode_change / L109: yield* Effect.promise(() => preparePluginDependencies(dir)) // kilocode_change | +| `packages/opencode/test/server/httpapi-pty.test.ts`
    84,95 | shared-upstream patch marker | Server/API integration | native equivalent | `packages/kilo-cli/test/native-pty*.test.ts`, `sandbox-pty.test.ts` | Marked block tests initial PTY dimensions; v2 native PTY tests cover spawn parameters. Evidence: L84: // kilocode_change start - test initial spawn dimensions / L95: // kilocode_change end | +| `packages/opencode/test/server/httpapi-query-schema-drift.test.ts`
    87,96 | shared-upstream patch marker | Server/API integration | not needed | None (v2 protocol schema generated) | Test list of v1 path-parameter patterns; not applicable to v2 protocol. Evidence: L87: // kilocode_change start / L96: // kilocode_change end | +| `packages/opencode/test/server/httpapi-reference.test.ts`
    64,98,100,140 | shared-upstream patch marker | Server/API integration | needs port | Core `reference.ts` + protocol `reference.list` tests once Kilo reference config is consumed | Marked blocks assert Kilo reference reconciliation semantics; v2 has native references but no Kilo reconciler (reference-reconciler row), so these assertions have no subject. Evidence: L64: // kilocode_change start - reference reads must reconcile config changes after instance disposal. / L98: // kilocode_change end | +| `packages/opencode/test/server/httpapi-sdk.test.ts`
    27,182,189,297,371,373,378,385,388,389,429,436,528,539,782,804,809,873,909,963 | shared-upstream patch marker | Server/API integration | partial | `packages/kilo-cli/test/http-boundary.test.ts`, `packages/client` generated client tests | PRESERVED — v2 session create returns Location (native). MISSING — .kilo skills path, legacy directory header routing (not needed), invalid-image request classification (handlers/session row). Evidence: L27: import { awaitWithTimeout, pollWithTimeout, testEffect } from "../lib/effect" // kilocode_change / L182: // kilocode_change start | +| `packages/opencode/test/server/httpapi-session.test.ts`
    38,157,194,562,590,1016,1071,1073 | shared-upstream patch marker | Session lifecycle/history/UI | partial | `packages/core/test/session*.test.ts` (inbox cancel semantics) | PRESERVED — cancelling/deleting inbox items (v2 inbox tests). MISSING — released-client media content compatibility and mixed-version DB compatibility (v1-only). Evidence: L38: import { pollWithTimeout, testEffect } from "../lib/effect" // kilocode_change / L157: // kilocode_change start - released V2 clients persisted media-shaped tool content | +| `packages/opencode/test/server/httpapi-ui.test.ts`
    64,72,115,121,199,215,287,298,301,303,307,310,314,320,323,325,329,334,338,352,366,378 | shared-upstream patch marker | Server/API integration | needs port | Paired with shared/ui.ts (needs port: Kilo Console) | 22 markers assert no proxy fallback and Kilo auth; v2 Kilo serves no embedded console, proxy tests not applicable but console absence stands. Evidence: L64: // kilocode_change start - keep the filewatcher-disable flag visible (see httpapi-instance-route-auth.test.ts) / L72: // kilocode_change end | +| `packages/opencode/test/server/httpapi-v2-location.test.ts`
    81,91,132,147 | shared-upstream patch marker | Server/API integration | not needed | None (v2 Location routing native) | v1 compatibility assertions for Kilo location headers and legacy manifest encoding. Evidence: L81: // kilocode_change start - malformed Kilo location headers are client errors / L91: // kilocode_change end | +| `packages/opencode/test/server/httpapi-v2-pty.test.ts`
    17,20,187,283,287 | shared-upstream patch marker | Server/API integration | native equivalent | `packages/kilo-cli/test/sandbox-pty.test.ts`, `native-lifecycle.test.ts` | Marked block verifies child env precedence and credential stripping via the PTY route. V2 Kilo PTY tests cover native PTY spawn and sandbox refusal; credential stripping is exercised through the sandbox path (mcp/shell env scrub for non-sandbox remains unverified — see format row). Evidence: L17: // kilocode_change start - PTY route tests do not need an indexing worker per temp project; / L20: // kilocode_change end | +| `packages/opencode/test/server/project-init-git.test.ts`
    74 | shared-upstream patch marker | Server/API integration | not needed | None | Asserts a v1 path is not created; v2 snapshot storage lives under global data (snapshot.ts:96). Evidence: L74: expect(yield* fs.exists(path.join(tmp.directory, ".git", "kilo"))).toBe(false) // kilocode_change | +| `packages/opencode/test/server/session-diff-missing-patch.test.ts`
    7,8,11,44,63 | shared-upstream patch marker | Session lifecycle/history/UI | partial | Core snapshot diff (`snapshot.ts:58`) via vcs.diff/session stats | Whole-file test of cumulative diff endpoint; v2 has native structured diffs but not the cumulative/imported session_diff row semantics (summary.ts row partial). Evidence: L7: * session never loaded. // kilocode_change / L8: * Kilo still surfaces cumulative session diffs to its TUI and VS Code clients. // kilocode_change | +| `packages/opencode/test/server/workspace-routing.test.ts`
    54,59 | shared-upstream patch marker | Server/API integration | not needed | None (viewed endpoint native in protocol) | v1 routing special-case test; v2 route is native. Evidence: L54: // kilocode_change start / L59: // kilocode_change end | +| `packages/opencode/test/session/compaction.test.ts`
    14,207,255,256,266,268,286,301,972,1004,1301,1303,1325,1335,1341,1343,1812,2008,2009 | shared-upstream patch marker | Session lifecycle/history/UI | partial | `packages/core/test/session-runner.test.ts:2395-2615` (overflow/compaction) | PRESERVED — compaction/overflow tests native. MISSING — configured output ceiling controlling tail budgeting (processor/prompt rows). Evidence: L14: import { Snapshot } from "@/snapshot" // kilocode_change / L207: metadata: Effect.fn("TestSessionProcessor.metadata")(() => Effect.void), // kilocode_change | +| `packages/opencode/test/session/llm-native-recorded.test.ts`
    30,32,63,66,164,226,230,280,285,297 | shared-upstream patch marker | Session lifecycle/history/UI | not needed | `packages/kilo-cli/test/fixtures` + `packages/http-recorder` | Fixture path/branding markers for v1 recorded tests. Evidence: L30: const KILO_FIXTURES_DIR = path.join(FIXTURES_DIR, "kilocode") // kilocode_change / L32: const zenURL = (connection: string) => `https://console.opencode.ai/proxy/connections/${connection}/v1` // kilocode_change | +| `packages/opencode/test/session/llm.test.ts`
    25,32,283,296,452,479,506,511,845,1836,1839,1952,1956,1973,2067 | shared-upstream patch marker | Session lifecycle/history/UI | partial | `packages/core/test/aisdk-native.test.ts`, `packages/kilo-cli/test/gateway-protocol.test.ts` | PRESERVED — Kilo Gateway request wire tests (gateway-protocol). MISSING — AI SDK raw usage retention for billing (ai-sdk row), Kilo-Code UA assertion (branding via App). Evidence: L25: import { USER_AGENT } from "../../src/installation" // kilocode_change / L32: type ConfigModel = NonNullable[string]>["models"]>[string] // kilocode_change | +| `packages/opencode/test/session/message-v2.test.ts`
    497,555 | shared-upstream patch marker | Tool-delivery attachment model visibility | partial | `packages/core/src/session/runner/to-llm-message.ts`, `model-request.ts`, and `packages/kilo-cli/test/remote-session.test.ts:647-722` | V2 has a different typed attachment pipeline with admission and model-request normalization, but current coverage proves user multipart attachments, not the pinned `send_file` tool-delivery rule that delivery-only attachments stay out of later model history. Evidence: L497: // kilocode_change start — send_file delivery attachments must not be replayed to the model / L555: // kilocode_change end | +| `packages/opencode/test/session/network.test.ts`
    1 | shared-upstream patch marker | Offline reconnect lifecycle tests | needs port | Core execution tests plus proposed Kilo offline-status integration tests | No current test maps to pinned code/message classification, restored-delay race, reply/reject, already-aborted signal and cleanup behavior; the simulation network test is unrelated. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/session/processor-effect.test.ts`
    20,21,190,200,225,251,462,502,558,562,600,1103,1105,1145,1199,1291 | shared-upstream patch marker | Session lifecycle/history/UI | partial | `packages/core/test/session-runner.test.ts` | MISSING — configured output ceiling, offline reconnection prompt, send_file attachments (rows needs port/partial); PRESERVED — step/finish accounting natively tested. Evidence: L20: import { SessionNetwork } from "../../src/session/network" // kilocode_change / L21: import { Bus } from "../../src/bus" // kilocode_change | +| `packages/opencode/test/session/prompt.test.ts`
    43,48,51,175,294,806,815,1275,1507,1791,1904,1909,2094,2103 | shared-upstream patch marker | Session lifecycle/history/UI | partial | `packages/core/test/session*.test.ts` (inbox/steer/interrupt coverage per AGENTS Session Core) | PRESERVED — cancel then reprompt settles to idle promptly (v2 interruption + inbox tests). MISSING — environment details appended to queued prompts (Kilo-specific prompt caching detail). Evidence: L43: import { SessionProjector } from "@opencode-ai/core/session/projector" // kilocode_change / L48: // kilocode_change start - Item 14 cancel→reprompt proof helpers | +| `packages/opencode/test/session/retry.test.ts`
    257,273,328,343 | shared-upstream patch marker | Session lifecycle/history/UI | native equivalent | `packages/core/test/session-runner.test.ts` retry cases | Marked block removes Go upsell expectations; v2 Kilo has no Go upsell. Evidence: L257: // kilocode_change start - Kilo does not support OpenCode Go upsells / L273: // kilocode_change end | +| `packages/opencode/test/session/schema-decoding.test.ts`
    62,241,243 | shared-upstream patch marker | Session lifecycle/history/UI | not needed | None | v1 schema fixture adjustments. Evidence: L62: diffs: [{ additions: 1, deletions: 0, file: "a.ts" }], // kilocode_change / L241: message: "Subscribe to OpenCode Go.", // kilocode_change | +| `packages/opencode/test/session/session.test.ts`
    9,255,281,283,337 | shared-upstream patch marker | Session lifecycle/history/UI | not needed | None | Type-only marker. Evidence: L9: type SessionModel = NonNullable // kilocode_change / L255: // kilocode_change start | +| `packages/opencode/test/session/shell-v2.test.ts`
    1 | shared-upstream patch marker | Session lifecycle/history/UI | native equivalent | `packages/core/test/session*.test.ts` shell events (v2 native shell record) | Whole-file v1 test of the v2 shell dual-write; native in v2. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/session/system.test.ts`
    11 | shared-upstream patch marker | Session lifecycle/history/UI | not needed | None | Import marker. Evidence: L11: import { Config } from "../../src/config/config" // kilocode_change | +| `packages/opencode/test/share/share-next.test.ts`
    256,279,314,317 | shared-upstream patch marker | Share transport compatibility | partial | `packages/kilo-cli/test/gateway-integration.test.ts:255-276` and v1 import fixtures | Coverage is local-fixture acceptance for create/retrieve/fork/unshare plus legacy decoding. It does not exercise a deployed viewer, team/org gates, or incremental sync with omitted legacy diff filenames, so the exact marked regression proof remains open. Evidence: L256: // kilocode_change start / L279: // kilocode_change end | +| `packages/opencode/test/skill/discovery.test.ts`
    40,51,143,164 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/core/src/skill/discovery.ts:16-40` validators (core tests) | Marked tests assert `../` names cannot escape the cache; v2 discovery has the validators natively. Evidence: L40: // kilocode_change start - serve a crafted index whose skill name escapes the cache via `../` / L51: // kilocode_change end | +| `packages/opencode/test/skill/skill.test.ts`
    9,27,61,91,93,113,132,145,153,155,185,200,209,219,243,246,276,299,315,370,409,450,489,522,533,538,571,592 | shared-upstream patch marker | Package behavior indicated by path | partial | `packages/kilo-cli/test/skill-policy.test.ts`, `packages/core/test` skill tests | PRESERVED — builtin skill exclusion/collision tests (skill-policy). MISSING — `.kilo/skill` primary directory discovery. Evidence: L9: import { Git } from "../../src/git" // kilocode_change / L27: const itWithoutClaudeCodeSkills = testEffect(Layer.mergeAll(skills(false, true), node, testInstanceStoreLayer)) // kilocode_change | +| `packages/opencode/test/storage/db.test.ts`
    16 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | `packages/kilo-cli/test/project-config*.test.ts`/paths (kilo2.db) | Path-naming assertion; v2 has isolated kilo2.db. Evidence: L16: : path.join(Global.Path.data, `kilo-${InstallationChannel.replace(/[^a-zA-Z0-9._-]/g, "-")}.db`) // kilocode_change | +| `packages/opencode/test/suggestion/suggestion.test.ts`
    1 | shared-upstream patch marker | Suggestion lifecycle tests | native equivalent | `packages/kilo-cli/test/remote-suggestions.test.ts:210-308,312-614` | Current tests cover singleton/lifecycle isolation, eligibility, accept, dismiss, reconnect replay and unsubscribed viewers against the actual v2 adapter. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/tool/apply_patch.test.ts`
    27 | shared-upstream patch marker | Tool behavior/UI | not needed | None (v2 keeps build) | Fixture rename only. Evidence: L27: agent: "code", // kilocode_change | +| `packages/opencode/test/tool/diagnostics-filter.test.ts`
    1 | shared-upstream patch marker | Tool behavior/UI | needs port | Paired with tool/diagnostics.ts (needs port) | No LSP diagnostics in v2. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/tool/external-directory.test.ts`
    21 | shared-upstream patch marker | Tool behavior/UI | not needed | None | Fixture rename only. Evidence: L21: agent: "code", // kilocode_change | +| `packages/opencode/test/tool/glob.test.ts`
    4,7,46,48,51,53,96,119 | shared-upstream patch marker | Tool behavior/UI | partial | `packages/core/test/tool` glob tests | MISSING — absolute glob pattern test (glob row partial). Evidence: L4: // kilocode_change start / L7: // kilocode_change end | +| `packages/opencode/test/tool/grep.test.ts`
    37,148,173,192 | shared-upstream patch marker | Tool behavior/UI | partial | `packages/core/test/tool` grep tests | MISSING — Kilo limit notice wording and exact-file constraint (grep row partial). Evidence: L37: agent: "code", // kilocode_change / L148: expect(result.output).toContain("100 matches limit reached. Use limit=200 for more, or refine pattern.") // kilocode_change | +| `packages/opencode/test/tool/parameters.test.ts`
    117,121,166,180 | shared-upstream patch marker | Tool behavior/UI | partial | `packages/core/test/tool` parameter tests | PRESERVED — optional shell description native. MISSING — grep signal controls. Evidence: L117: // kilocode_change start - description is optional in kilo (see bash.ts Parameters) / L121: // kilocode_change end | +| `packages/opencode/test/tool/read.test.ts`
    40,273,275 | shared-upstream patch marker | Tool behavior/UI | not needed | None | Fixture rename only. Evidence: L40: agent: "code", // kilocode_change / L273: // kilocode_change start - renamed from "build" to "code" | +| `packages/opencode/test/tool/recall.test.ts`
    1,66,184 | shared-upstream patch marker | Historical transcript recall tests | needs port | Proposed v2 session-history recall integration tests; memory tests cover only the replacement subset | Current memory tests prove memory indexing/recall and safe rendering, but not pinned current-worktree-family search, exclusion of active/future queued text, cross-project rejection, sibling worktree allowance under project-ID drift, or full transcript formatting. Evidence: L1: // kilocode_change - new file / L66: await Bun.write(path.join(first.path, ".git", "kilo"), "stale-project-id") // kilocode_change | +| `packages/opencode/test/tool/registry.test.ts`
    5,20,21,26,32,62,70,72,85,91,99,102,163,211,214,238,241,274,316,319,346,399,401,413,433,464,509,541 | shared-upstream patch marker | Tool behavior/UI | partial | `packages/kilo-cli/test/sandbox*.test.ts`, core tool tests | PRESERVED — sandbox-confined tool behavior tested. MISSING — .kilo/tool loading, Scout tools. Evidence: L5: import { Effect, Exit, Layer, Result, Schema } from "effect" // kilocode_change / L20: import * as SandboxNetwork from "@/kilocode/sandbox/network" // kilocode_change | +| `packages/opencode/test/tool/shell.test.ts`
    78,1002,1005 | shared-upstream patch marker | Tool behavior/UI | partial | `packages/core/test/shell*.test.ts` | MISSING — BashHierarchy always-pattern generation (bash-hierarchy row needs port). Evidence: L78: agent: "code", // kilocode_change / L1002: // kilocode_change start — arity prefix produces "ls *" with space before wildcard | +| `packages/opencode/test/tool/skill.test.ts`
    12,32,39,73,74,138,184 | shared-upstream patch marker | Tool behavior/UI | partial | `packages/kilo-cli/test/skill-policy.test.ts` | PRESERVED — skill inventory in tool description (skill-policy). MISSING — .kilo/skill directory. Evidence: L12: import { disposeAllInstances, provideTmpdirInstance, TestInstance } from "../fixture/fixture" // kilocode_change / L32: // kilocode_change - skip on windows: address windows ci failures #9496 | +| `packages/opencode/test/tool/suggest.test.ts`
    1 | shared-upstream patch marker | Suggest tool tests | native equivalent | `packages/kilo-cli/test/remote-suggestions.test.ts:312-614` | The v2 tests drive the real tool/relay path and verify accepted prompt admission once, dismissal, cleanup, ownership, reconnection and viewer eligibility. Evidence: L1: // kilocode_change - new file | +| `packages/opencode/test/tool/task.test.ts`
    6,14,16,19,20,53,102,123,272,333,335,369,536,561,563,564,566,583,589,646,648,703,913,946,948,1016,1257,1466 | shared-upstream patch marker | Tool behavior/UI | partial | `packages/core/test/tool` subagent tests + cost-propagation native row | PRESERVED — child cost accounting native. MISSING — resume hint on failure, platform attribution rebuild (task row partial). Evidence: L6: import { Cause, Deferred, Effect, Exit, Fiber, Layer } from "effect" // kilocode_change - Cause for resume-hint coverage / L14: import { MessageV2 } from "@/session/message-v2" // kilocode_change | +| `packages/opencode/test/tool/tool-define.test.ts`
    41,52,152,160,215 | shared-upstream patch marker | Tool behavior/UI | not needed | None | Test helper marker. Evidence: L41: // kilocode_change start / L52: // kilocode_change end | +| `packages/opencode/test/tool/websearch.test.ts`
    41,42,43 | shared-upstream patch marker | Tool behavior/UI | needs port | Paired with websearch.ts/kilo-exa rows (needs port) | Asserts websearch enabled for Kilo provider without exa/parallel keys; Kilo Exa transport absent in v2. Evidence: L41: // kilocode_change / L42: expect(webSearchEnabled(ProviderV2.ID.kilo, { exa: false, parallel: false })).toBe(true) // kilocode_change | +| `packages/opencode/test/util/process.test.ts`
    80,97 | shared-upstream patch marker | Package behavior indicated by path | needs port | Paired with util/process.ts (partial) / format env scrub (needs port) | Marked test verifies inherited env is excluded; v2 has no extendEnv option consumer. Evidence: L80: // kilocode_change start / L97: // kilocode_change end | +| `packages/opencode/tsconfig.json`
    13 | shared-upstream patch marker | Configuration/settings | not needed | None; v2 Kilo TUI plugin imports `@opencode-ai/plugin`/`@opencode-ai/tui` packages directly | Marker adds a path alias for Kilo components importing the extracted TUI; v2 uses workspace packages. Evidence: L13: "@tui/*": ["../tui/src/*"], // kilocode_change - extracted TUI imports used by Kilo-owned components | +| `packages/plugin/script/publish.ts`
    34 | shared-upstream patch marker | Package behavior indicated by path | partial | packages/plugin/script/publish.ts:34 (add `--provenance`) | V1 published the plugin package with `--provenance` (L34). V2 publishes with `--tag --access public` only. Preserved: channel-tagged publish; missing: npm provenance attestation — a release-policy choice for Kilo's v2 plugin package. Evidence: L34: await $`npm publish *.tgz --tag ${Script.channel} --access public --provenance` // kilocode_change | +| `packages/plugin/tsconfig.json`
    11 | shared-upstream patch marker | Configuration/settings | not needed | packages/plugin/tsconfig.json (upstream, 12 lines) | V1 added `types: ["node"]` (L11). V2 plugin tsconfig omits it and typechecks; no Kilo-owned plugin source depends on Node globals. Evidence: L11: "types": ["node"] // kilocode_change | +| `packages/protocol/src/api.ts`
    41,58 | shared-upstream patch marker | Package behavior indicated by path | partial | packages/kilo-cli/src/run.ts:56-60 (client supplies `location` on session.create); OpenAPI title stays upstream | V1 markers pass the configured-location middleware into session creation so SDK callers omitting `location` get the configured directory (L41) and brand the OpenAPI title 'Kilo HttpApi' (L58). V2 api.ts:159 wires `makeSessionGroup(sessionLocationMiddleware)` without the location fallback and titles the API 'opencode HttpApi' (L188); Kilo callers compensate by always passing `location` explicitly (run.ts:56). Preserved: sessions land in the intended directory for owned callers. Missing: server-side default for third-party SDK callers and Kilo branding of the generated OpenAPI document. Evidence: L41: .add(makeSessionGroup(sessionLocationMiddleware, locationMiddleware)) // kilocode_change - provide configured location to session creation / L58: title: "Kilo HttpApi", // kilocode_change - public API grouping is Kilo-branded | +| `packages/protocol/src/groups/event.ts`
    58 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | packages/protocol/src/groups/event.ts:62-68 (`EventManifest.ServerDefinitions` encoded stream) | V1 exported a `KiloEvent` schema encoding the full Kilo event bus (L58). V2 encodes the server event manifest natively (`make(EventManifest.ServerDefinitions)`), and Kilo-owned events are defined via schema/src/kilocode RPC/event definitions that ride the same manifest. No separate Kilo event schema export is needed. Evidence: L58: export const KiloEvent = schema(EventManifest.Definitions) // kilocode_change - encode the full Kilo event bus | +| `packages/protocol/src/groups/fs.ts`
    13,18,30 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/protocol/src/groups/fs.ts:24-27 (`fs.read` query `path` for the wildcard resource) and regenerate packages/client | V1 exposes an optional `path` in the fs.read query so generated SDK callers can address the wildcard resource without URL-path encoding (L13-18,30). V2 fs.read takes only `LocationQuery` (L25) — the wildcard is path-only — so SDK callers must encode the path into the route. Port only if an owned client needs the query form; regenerate the client after changing the HttpApi. Evidence: L13: // kilocode_change start - expose the wildcard resource to generated SDK callers / L18: // kilocode_change end | +| `packages/protocol/src/groups/session.ts`
    24,114,154,322,343 | shared-upstream patch marker | Session lifecycle/history/UI | partial | packages/protocol/src/groups/session.ts:170-186 (session.create without location fallback), :646-653 (native durable log) | V1 markers: accept the configured-location middleware so session.create defaults `location` from the SDK directory (L114,154), and type history/event streams as the released `SessionDurable` union (L24,322,343). V2 session.create requires callers to pass `location` (Kilo callers do, run.ts:56) and serves native `SessionEvent.Durable` items — the released-key widening is obsolete (isolated store). Missing: the server-side configured-directory default for session.create; everything else is native or not needed. Evidence: L24: import { SessionDurable } from "@opencode-ai/schema/durable-event-manifest" // kilocode_change - released history keys / L114: locationMiddleware: Context.Key, // kilocode_change - supply the configured location for session creation | +| `packages/schema/src/command.ts`
    14 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/schema/src/command.ts (optional `variant` on CommandV2.Info) paired with packages/core/src/config/plugin/command.ts override pass | V1 adds an optional `variant` so kilo.json can override only the model variant of a command (L14). V2 schema/src/command.ts has no `variant` field (grep empty) and the config command schema (config/command.ts) has none either, so variant-only command overrides cannot be expressed. Same feature as the core config/command.ts and config/plugin/command.ts rows. Evidence: L14: variant: Model.VariantID.pipe(optional), // kilocode_change - support variant-only command overrides | +| `packages/schema/src/durable-event-manifest.ts`
    4,7,9,15,18,19,24 | shared-upstream patch marker | Package behavior indicated by path | not needed | none — released-storage replay is obsolete (packages/core/src/session/projector.ts row); v2 manifest is packages/schema/src/durable-event-manifest.ts (7 lines, upstream) | V1 markers add the released `PromptPromoted` event to the durable inventory and expose a `SessionDurable` schema/definitions so history/replay could decode rows written by released Kilo clients (L4-25). V2 reduced the manifest to upstream's 7-line inventory and kilo2's isolated store never contains released Kilo rows; session history serves native `SessionEvent.Durable` (protocol/groups/session.ts:653). Evidence: L4: import { Schema } from "effect" // kilocode_change / L7: import { PromptPromoted } from "./kilocode/durable-event" // kilocode_change - released storage key | +| `packages/schema/src/file-diff.ts`
    9,10 | shared-upstream patch marker | Package behavior indicated by path | native equivalent | packages/kilo-ide-ui/src/components/session-diff.ts:30-36,53-66 (client-side before/after reconstruction from the patch) | V1 adds optional full-content `before`/`after` sides so editor diff tabs could show whole files (L9-10). V2 schema has no such fields, but the owned IDE UI accepts optional `before`/`after` on its DiffSource and otherwise reconstructs both sides from a complete unified patch (`completePatchContents`), so editor diff tabs render full content without a server-side schema change. Remainder: diffs whose patch is incomplete fall back to hunk parsing rather than full sides. Evidence: L9: before: optional(Schema.String), // kilocode_change - full-content sides for editor diff tabs / L10: after: optional(Schema.String), // kilocode_change - full-content sides for editor diff tabs | +| `packages/schema/src/llm.ts`
    30,85 | shared-upstream patch marker | Package behavior indicated by path | not needed | none — see packages/core/src/kilocode/event-storage.ts and packages/core/src/event.ts rows | V1 defines `StoredToolContent`, a codec between current `ToolContent` and the released `{type:"file",source}`/`{type:"media"}` persisted shapes (L30-85). V2 schema/src/llm.ts is a 6-line upstream re-export; no v2 path reads released Kilo tool rows in place, so the codec has no consumer. Evidence: L30: // kilocode_change start - durable events must keep decoding released tool content shapes / L85: // kilocode_change end | +| `packages/schema/src/pty.ts`
    7,32,48,55 | shared-upstream patch marker | Package behavior indicated by path | partial | packages/schema/src/pty.ts:51-55 (native `size`); `sessionID` association absent | V1 adds `sessionID` to Pty.Info so nested Kilo TUI terminals associate with the viewed session (L7,32) and an initial `size{rows,cols}` on CreateInput (L48-55). V2 natively includes `size` on CreateInput (L51-55, though core pty.ts:176 does not yet pass it to spawn) but has no `sessionID` field and no Kilo consumer writes one. Follows the packages/core/src/pty.ts row. Evidence: L7: import { SessionID } from "./session-id" // kilocode_change / L32: sessionID: optional(Schema.NullOr(SessionID)), // kilocode_change - Kilo associates PTYs with sessions | +| `packages/schema/src/server-event.ts`
    7,9,11 | shared-upstream patch marker | Server/API integration | native equivalent | packages/schema/src/config.ts:135 (`config.updated` event) consumed by packages/kilo-cli/src/project-config.ts:80 and telemetry.ts:26 | V1 adds a `global.config.updated` server event emitted via GlobalBus when config updates without a full dispose (L7-11). V2 has a native `config.updated` bus event (schema/src/config.ts:135) that the Kilo ProjectConfig reload publishes after refresh and that Kilo telemetry observes, so the notification invariant exists without a separate server-event definition. Evidence: L7: // kilocode_change start - emitted (via GlobalBus) when config updates without a full dispose / L9: // kilocode_change end | +| `packages/schema/src/session-event.ts`
    429 | shared-upstream patch marker | Session lifecycle/history/UI | not needed | none — released-reader compatibility (packages/core/src/session/compaction.ts row) | V1 adds an optional `include` field on the compaction-ended durable event for released v1 readers (L429). V2 session-event.ts (690 lines) has no such field and no shared-store reader exists. Evidence: L429: include: Schema.String.pipe(Schema.optional), // kilocode_change - released v1 readers recognize this compatibility field | +| `packages/schema/src/session-status-event.ts`
    9,34,40 | shared-upstream patch marker | Session lifecycle/history/UI | needs port | packages/schema/src/session-status-event.ts (or a Kilo-owned status extension) plus the offline-question feature it represents | V1 adds an `offline` session status carrying a QuestionID and message for a session paused on an offline Kilo question (L9,34-40). V2 SessionStatus has only idle/retry/busy variants (L11-30) and no Kilo schema extension declares `offline`. This status is the wire representation of the v1 offline/network-wait feature already recorded as needing a port in the 'Network and overflow session slice'. Evidence: L9: const QuestionID = Schema.String.check(Schema.isStartsWith("que")).pipe(Schema.brand("QuestionID")) // kilocode_change / L34: // kilocode_change start - represent a session paused for an offline Kilo question | +| `packages/schema/src/v1/permission.ts`
    44 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/schema/src/permission.ts reply schema (`interactive` flag) consumed by packages/kilo-cli/src/skill-shell.ts | V1 adds an `interactive` flag on permission replies, set by clients when a human answered, so the server can refuse machine approvals of skill-shell batches (L44-45). V2 permission schemas have no `interactive` field and skill-shell.ts asserts permission without distinguishing human from machine replies (skill-shell.ts:198,212). Consistent with the earlier partial permission assessment (skill-shell requests no longer remove persistent approval). Evidence: L44: // kilocode_change - set by clients when a human answered the prompt; the server refuses machine approvals of skill-shell batches | +| `packages/schema/src/v1/question.ts`
    18,26,34,41,58 | shared-upstream patch marker | Package behavior indicated by path | needs port | Kilo-owned question schema extension (see canonical row for packages/opencode/test/kilocode/question-option-schema.test.ts) | V1 adds `labelKey`/`descriptionKey`/`mode` on options, `questionKey`/`headerKey` on questions and a `blocking` flag (L18-26,34-41,56-58) for Kilo client localization and mode pre-selection. V2 schema (v1/question.ts 66 lines, question.ts) has none of these fields (grep empty) and no packages/schema/src/kilocode extension declares them; the canonical test row already records the gap. Evidence: L18: // kilocode_change start - Kilo client localization and mode selection hints / L26: // kilocode_change end | +| `packages/schema/src/v1/session.ts`
    216,246,274,362,372,396,398,494 | shared-upstream patch marker | Session lifecycle/history/UI | partial | packages/schema/src/v1/session.ts (legacy import schema, `variant` native at L350,483,540); editorContext now Session metadata (packages/kilo-vscode/src/backend/session.ts:160); generation metrics absent | V1 markers add `variant` on subtask parts (L216,494), assistant `model`/`generationID`/`vercelID`/`metrics`/`time{start,end,elapsed}` for weighted throughput (L246-274), and a user-message `editorContext` (L362-398). In v2 this file is only the legacy schema decoded by the v1 import migration (database/v1-migration.bun.ts:218-219, `decodeUnknownOption` ignores unknown fields). Preserved: `variant` is native; editor context is carried in v2 as Session metadata by the owned VS Code backend rather than on user messages. Missing: per-assistant generation metrics/elapsed time are not imported from v1 rows and have no v2 message field (usage aggregation is durable root/descendant totals, schema/src/kilocode/session-usage.ts), so v1 throughput history is dropped on import. Evidence: L216: variant: Schema.optional(Schema.String), // kilocode_change - preserve workflow subtask variant / L246: // kilocode_change start | +| `packages/script/src/index.ts`
    19,27,29,30,32,33,34,35,39,90,95,100,103,104,107,146,165 | shared-upstream patch marker | Package behavior indicated by path | needs port | packages/script/src/index.ts (v2, 85 lines, reads OPENCODE_* env) — Kilo release scripts need KILO_* env, `rc` pre-release channel, GH_REPO highest-release version resolution, `@kilocode/cli` latest lookup and the Kilo team roster | V1 markers: KILO_CHANNEL/BUMP/VERSION/RELEASE/PRE_RELEASE env (L19-35), `rc` channel for pre-releases, plain-semver rc versions bumped from the highest GitHub release or `@kilocode/cli@latest` (L39-104), the Kilo contributor team list used to strip changelog thanks (L107-146), and a `kilo script` log line (L165). V2 packages/script reads OPENCODE_* variables only (L21-29), has no rc/pre-release channel, resolves versions from upstream's npm package, and carries upstream's team list. Every Kilo release/publish script depends on this module, so the Kilo variant must accompany the publish lane. Evidence: L19: // kilocode_change start / L27: // kilocode_change end | +| `packages/script/tests/check-opencode-annotations.test.ts`
    45,56,66,72,225,226,227,228,229,230,231,232,235,236,237,238,239,240,241,242,245,246,247,248,251,252,253,254,255,256,257,266,267,268,269,270,315,396,401,406,411,416,421,428,431,441,444,453,454,456,457,464,470,476,482,488,494,500,507,509,517,519,521,523,533,535,543,544,546,548,561,586,602,609,613,625,629,641,643,645,647,655,657,665,673,685,738,745,747,759,768,774,784,788,792,803,815,817,842,844,869,873,877,881,885,889,892,896,900,901,902,903,904,923,925,932,938,940,948,955,957,958,960,967,994,996,998,1000,1010,1018,1025,1027 | shared-upstream patch marker | Package behavior indicated by path | deferred | packages/script/tests/check-opencode-annotations.test.ts with script/check-opencode-annotations.ts when marker enforcement is added to the v2 line | V1 1032-line test suite (132 marker-string occurrences, mostly fixture lines) verifies the annotation checker's coverage rules (inline/block/new-file markers, JSX/YAML/shell comment styles). V2 has neither the checker nor its tests; kilocode/v2-fork-conventions.md documents the marker rules but nothing enforces them. Deferred with the Kilo CI lane; note the scope list would change (no packages/opencode, Kilo-owned paths exempt). Evidence: L45: const MARKER_PREFIX = /(?:\/\/|\{?\s*\/\*|#)\s*kilocode_change\b/ / L56: if (first?.match(/(?:\/\/|\{?\s*\/\*|#)\s*kilocode_change\s*-\s*new\s*file\b/)) { | +| `packages/sdk-next/test/embedded.test.ts`
    9,21,117,156,199,224 | shared-upstream patch marker | Package behavior indicated by path | not needed | packages/sdk/test/embedded.test.ts (upstream; no temp-directory database cleanup loop) | V1 markers add an EBUSY retry loop around temp-dir removal because Windows SQLite handles are released by GC finalizers (L9-21,117,156,199,224). V2's embedded test (packages/sdk/test/embedded.test.ts) does not create per-test database directories that need this cleanup (no rm/cleanup calls), so the Windows workaround has no target; revisit only if Kilo CI runs the sdk suite on Windows and hits EBUSY. Evidence: L9: // kilocode_change start - retry Windows SQLite locks until GC finalizers release them / L21: // kilocode_change end | +| `packages/sdk/js/script/publish.ts`
    41 | shared-upstream patch marker | Package behavior indicated by path | partial | packages/sdk/script/publish.ts (v2 sdk publish) — add `--provenance` if Kilo publishes the v2 SDK with npm provenance | V1 published the SDK tarball with `--provenance` (L41). The v2 SDK lives at packages/sdk (no js/ subtree); its publish script is upstream and the sibling packages/plugin/script/publish.ts:34 shows v2 publishes without `--provenance`. Preserved: channel-tagged public publish; missing: provenance attestation. Whether Kilo publishes the v2 SDK at all is a release decision, so this is a low-priority partial rather than a hard gap. Evidence: L41: await $`npm publish *.tgz --tag ${Script.channel} --access public --provenance` // kilocode_change | +| `packages/sdk/js/src/client.ts`
    28 | shared-upstream patch marker | Package behavior indicated by path | not needed | none — v2 SDK (packages/sdk/src) uses `location[directory]` query / `x-opencode-directory` header natively (packages/server/src/location.ts:45-46) | V1 rewrote outgoing requests to move the `x-kilo-directory` header into a `directory` query parameter (L28-30). V2's server accepts `location[directory]` in the query and the directory header, and the v2 client (packages/client, packages/sdk) is generated against that contract; there is no Kilo header rename in v2, so no rewrite is required. Evidence: L28: const next = new Request(url.href, request) // kilocode_change | +| `packages/sdk/js/src/error-interceptor.ts`
    40 | shared-upstream patch marker | Package behavior indicated by path | not needed | none — v2 SDK error handling is upstream (packages/sdk/src); no `kilo server` branded message consumer | V1 branded SDK network/empty-body errors as `kilo server ...` (L40-41). V2 removed the v1 SDK error interceptor entirely; the owned Kilo clients (kilo-cli, kilo-vscode backend) format their own errors. Cosmetic branding with no v2 seam. Evidence: L40: // kilocode_change | +| `packages/sdk/js/src/server.ts`
    5,36,69,72,90,92,155,160 | shared-upstream patch marker | Server/API integration | needs port | packages/kilo-cli/src/host.ts:30 (`content` option on ProjectConfig) plus a Kilo-owned config-content merge when kilo-cli spawns nested hosts | V1 merged an existing KILO_CONFIG_CONTENT with the caller's config (agents/commands/mcp/mode/plugin/instructions) so nested CLI instances kept Kilo-injected modes (L5-36,68-73,160-161) and launched the `kilo` binary, waiting for 'kilo server listening' (L91). V2 has no packages/sdk/js/src/server.ts; the Kilo host accepts a single `content` string (ProjectConfig.configured({ content }), host.ts:30) with no merge of an inherited KILO_CONFIG_CONTENT (grep in kilo-cli/kilo-vscode returns only the unrelated skill-policy constant), and nested spawns are done by the owned CLI/VS Code backend rather than an SDK helper. The merge-preserving-injected-modes invariant is absent. Evidence: L5: // kilocode_change start - Merge existing KILO_CONFIG_CONTENT with new config / L36: // kilocode_change end | +| `packages/sdk/js/src/v2/server.ts`
    5,36,68,70,73,91,93,155,157,162 | shared-upstream patch marker | Server/API integration | needs port | same as packages/sdk/js/src/server.ts | Duplicate of the v1 SDK server helper for the v2 client surface: KILO_CONFIG_CONTENT merge, `kilo` binary launch, 'kilo server listening' detection (L5-36,68-73,91-93,155-163). No v2 counterpart exists; see the server.ts row. Evidence: L5: // kilocode_change start - Merge existing KILO_CONFIG_CONTENT with new config / L36: // kilocode_change end | +| `packages/sdk/js/test/server.test.ts`
    1,111 | shared-upstream patch marker | Server/API integration | needs port | Kilo-owned test for the config-content merge once the server.ts behavior is ported | V1 test suite covers buildConfigEnv merging semantics (empty, incoming-only, preserve existing, override same keys, plugin concatenation, L1-111). V2 has no buildConfigEnv and no equivalent test. Follows the sdk server rows. Evidence: L1: // kilocode_change start - Tests for KILO_CONFIG_CONTENT merging / L111: // kilocode_change end | +| `packages/server/src/cors.ts`
    2,20 | shared-upstream patch marker | Server/API integration | needs port | packages/server/src/cors.ts:12-20 (CorsOptions) — supply `https://*.kilo.ai` via the Kilo host's `cors` option or a Kilo-owned origin matcher | V1 allowed any `https://*.kilo.ai` origin through a Kilo-owned matcher (L2,20; v1 server/src/kilocode/cors.ts). V2 cors.ts allows localhost, oc://renderer, tauri and `*.opencode.ai` plus explicit `opts.cors` entries; no Kilo host code passes kilo.ai origins (grep for kilo.ai/cors in kilo-cli interactive-server.ts and daemon.ts is empty). Browser clients served from kilo.ai cannot reach a v2 local server without a port. Evidence: L2: import { corsOrigin } from "./kilocode/cors" // kilocode_change / L20: if (corsOrigin(input)) return true // kilocode_change | +| `packages/server/src/handlers/event.ts`
    2,16 | shared-upstream patch marker | Server/API integration | native equivalent | packages/server/src/handlers/event.ts:24 with packages/protocol/src/groups/event.ts:62 (server manifest encoding) | V1 encoded SSE payloads with the Kilo-wide `KiloEvent` schema so Kilo bus events serialized correctly (L2,16). V2 encodes via the shared server event manifest, which Kilo-owned events join through the schema/kilocode definitions; see packages/protocol/src/groups/event.ts row. Evidence: L2: import { KiloEvent } from "@opencode-ai/protocol/groups/event" // kilocode_change - encode the full Kilo event bus / L16: data: JSON.stringify(Schema.encodeUnknownSync(KiloEvent)(data)), // kilocode_change | +| `packages/server/src/handlers/fs.ts`
    16 | shared-upstream patch marker | Server/API integration | needs port | packages/server/src/handlers/fs.ts:16-17 (`ctx.query.path ?? wildcard`) paired with packages/protocol/src/groups/fs.ts | V1 preferred the typed `path` query when present and fell back to the raw wildcard (L16-17). V2 fs.ts only decodes the wildcard path. Follows the protocol fs.ts row; both must change together. Evidence: L16: // kilocode_change - generated clients use the typed query; retain raw wildcard compatibility | +| `packages/server/src/handlers/pty.ts`
    186 | shared-upstream patch marker | Server/API integration | needs port | packages/server/src/handlers/pty.ts attach (replay-exited query flag) following the core pty `allowExited` port | V1 passes `allowExited` from a `PTY_REPLAY_EXITED_QUERY` websocket query parameter so canonical clients can replay retained output of an exited terminal (L186). V2 pty.ts has no such flag (grep empty) because core Pty.attach has no allowExited option. Follows packages/core/src/pty.ts. Evidence: L186: allowExited: url.searchParams.get(PTY_REPLAY_EXITED_QUERY) === "1", // kilocode_change | +| `packages/server/src/handlers/reference.ts`
    5,9 | shared-upstream patch marker | Server/API integration | native equivalent | packages/server/src/handlers/reference.ts (8 lines, upstream) fed by packages/kilo-cli/src/project-config.ts config entries | V1 wrapped reference.list in a Kilo request-scoped reconciler so kilo.json references were applied before listing (L5,9). V2 has no reconciler because Kilo config entries flow through Config.Service and the native reference plugin (see packages/core/src/reference.ts row). Evidence: L5: import { reconcile } from "../kilocode/reference-reconciler" // kilocode_change / L9: response(reconcile(Reference.Service.use((reference) => reference.list()))), // kilocode_change | +| `packages/server/src/handlers/session.ts`
    14,70,76,80 | shared-upstream patch marker | Session lifecycle/history/UI | needs port | packages/server/src/handlers/session.ts:123,136 (`location ?? process.cwd()` default) — default from the request Location instead of process.cwd() | V1 defaulted a missing `payload.location` on session.create to the configured request Location (directory + workspaceID) so `createKiloClient` callers landed in their configured directory (L14,70-80). V2 defaults to `process.cwd()` of the server process (L123,136) — wrong for a shared daemon serving several directories. Owned callers avoid it by always passing `location` (kilo-cli run.ts:56), so the gap affects third-party SDK callers only. Paired with protocol api.ts/session.ts rows. Evidence: L14: import { Location } from "@opencode-ai/core/location" // kilocode_change / L70: const location = yield* Location.Service // kilocode_change | +| `packages/server/src/location.ts`
    8,14,35,47,57 | shared-upstream patch marker | Server/API integration | needs port | packages/server/src/location.ts:53-58 (`decode` swallows URIError) — return InvalidRequestError 400 for malformed encoded directory headers | V1 rejected malformed percent-encoded directory headers with a 400 InvalidRequestError via the middleware error channel (L8,14,35-47,57). V2 `decode` catches the URIError and silently uses the raw header value as the directory (L53-58), so a malformed header becomes a bogus location instead of a client error. Evidence: L8: import { InvalidRequestError } from "@opencode-ai/protocol/errors" // kilocode_change / L14: { error: InvalidRequestError }, // kilocode_change - surface malformed headers as 400s | +| `packages/server/src/routes.ts`
    21,58,63 | shared-upstream patch marker | Server/API integration | native equivalent | packages/server/src/routes.ts (188 lines, upstream); Kilo host overrides via packages/kilo-cli/src/host.ts:26-47 | V1 provided the plain location layer and a no-op reference reconciler for the standalone server because only the CLI had a Kilo config reconciler (L21,58,63). V2 has no reconciler concept; Kilo behavior is injected as node replacements/overrides through `ServerFetch.make(..., { overrides })` (host.ts), so the standalone/CLI split the markers worked around no longer exists. Evidence: L21: import * as ReferenceReconciler from "./kilocode/reference-reconciler" // kilocode_change / L58: Layer.provide(locationLayer), // kilocode_change - standalone server has no Kilo config reconciler | +| `packages/server/tsconfig.json`
    5 | shared-upstream patch marker | Configuration/settings | not needed | packages/server/tsconfig.json (upstream) | V1 added `types: ["bun"]` because typechecking imported core sources needed Bun globals (L5). V2 server tsconfig has no marker and `bun typecheck` is the authoritative check per AGENTS.md; no Kilo-owned server source requires the addition. Evidence: L5: "types": ["bun"], // kilocode_change - required when typechecking imported core sources | +| `packages/session-ui/src/components/markdown-worker.ts`
    2,120 | shared-upstream patch marker | Session lifecycle/history/UI | not needed | packages/session-ui/src/components/markdown-worker.ts (228 lines, upstream; theme no longer posted at init) | V1 imported the renamed `KiloTheme` from `@opencode-ai/ui/context/marked` and posted it in the worker init message (L2,120). V2's worker protocol has no `init`/theme message (only parse/dispose, L68,91,117) and session-ui theming is handled through CSS color-scheme (markdown-mermaid.ts:15); Kilo theming lives in packages/kilo-ui (theme/default-themes.ts). The rename marker has no v2 consumer. Evidence: L2: import { KiloTheme } from "@opencode-ai/ui/context/marked" // kilocode_change - Kilo renamed the shared theme / L120: worker.postMessage({ type: "init", theme: KiloTheme } satisfies MarkdownWorkerRequest) // kilocode_change | +| `packages/storybook/.storybook/mocks/app/context/platform.ts`
    1,12 | shared-upstream patch marker | Package behavior indicated by path | not needed | packages/storybook/.storybook/mocks/app/context/platform.ts (imports the real `Platform` type) | V1 inlined a local `Platform` type for the storybook mock (L1-12). V2 imports the type from `app/src/runtime/platform/platform` and mocks the current surface (openExternal/restart/notify/fetch). The inline type was a v1 typing workaround with no behavior. Evidence: L1: // kilocode_change start / L12: // kilocode_change end | +| `packages/tui/src/app.tsx`
    9,26,59,62,90,91,92,153,193,202,233,249,256,260,303,321,338,371,380,461,466,469,475,489,827,833,957,968,1004,1043,1095,1141,1145 | shared-upstream patch marker | User-interface behavior | partial | packages/tui/src/app.tsx; packages/tui/src/context/runtime.tsx; packages/kilo-cli/src/tui.ts; packages/kilo-cli/src/tui-plugin/tui.tsx | V1 markers combine renderer exit bridging, terminal/Kitty cleanup, headless-link and Nudge providers, display detection, and Kilo plugin installation. V2 preserves host-driven app identity/plugins and renderer exit/lifecycle through TuiApp/runtime plus kilo-cli, but no concrete v2 Nudge provider or v1 headless-link dialog is present; terminal handling is reorganized and exact cleanup parity is not proven. Evidence: L9: import type { Exit } from "./context/exit" // kilocode_change / L26: untrack, // kilocode_change | +| `packages/tui/src/attention.ts`
    41,42,48 | shared-upstream patch marker | Kilo-branded attention defaults | partial | `packages/tui/src/attention.ts` plus a Kilo-owned branding override/plugin seam | V2 has a substantially stronger focus-aware notification and configurable sound implementation with tests, but its fixed default title is `OpenCode` and it no longer exposes the marked `kilo.default` / `Kilo Default` pack identity. Evidence: L41: const DEFAULT_TITLE = "Kilo" // kilocode_change / L42: const DEFAULT_PACK_ID = "kilo.default" // kilocode_change | +| `packages/tui/src/component/dialog-agent.tsx`
    14,24 | shared-upstream patch marker | Agent display-name selection | native equivalent | `packages/tui/src/component/dialog-agent.tsx:9-27` | The current dialog displays `item.name` while retaining canonical `item.id` as option value/current selection. This preserves the marked invariant despite the v2 agent schema renaming `displayName/name` to `name/id`. Evidence: L14: title: item.displayName ?? item.name, // kilocode_change / L24: current={local.agent.current()?.name ?? ""} // kilocode_change | +| `packages/tui/src/component/dialog-model.tsx`
    1,2,5,10,13,14,21,25,33,37,74,78,83,97,99,133,139,141,143,147,162,165,181,194,224,257,268,276,281,283,286,290,296,298 | shared-upstream patch marker | Provider/model routing or UI | partial | packages/tui/src/component/dialog-model.tsx; packages/tui/src/context/runtime.tsx; packages/kilo-cli/src/model-picker.ts; packages/kilo-cli/src/tui-plugin/model-info-dialog.tsx | Ported core picker policy through the v2 host-owned TuiApp.modelPicker seam: Kilo catalog groups/ranking, hidden models (including favorite/recent removal), preferred provider ordering, disclosures, native favorites/recents and a separate model-info dialog are concrete consumers. Partial because the v1 inline wide selection preview/ModelInfoPanel and its exact per-section fuzzy-search/header behavior are not reproduced in the native v2 dialog; the owned info dialog is a separate command flow, not selection-preserving inline parity. Evidence: Ported core picker policy through the v2 host-owned TuiApp.modelPicker seam: Kilo catalog groups/ranking, hidden models (including favorite/recent removal), preferred provider ordering, disclosures, native favorites/recents and a separate model-info dialog are concrete consumers. Partial because the v1 inline wide selection preview/ModelInfoPanel and its exact per-section fuzzy-search/header behavior are not reproduced in the native v2 dialog; the owned info dialog is a separate command flow, not selection-preserving inline parity. | +| `packages/tui/src/component/dialog-provider.tsx`
    15,18,21,56,89,127,132,135,137,140,143,190,201,214,223,382,387,388,390,391,396,405 | shared-upstream patch marker | Provider/model routing or UI | partial | packages/tui/src/component/dialog-integration.tsx; packages/server/src/integration; packages/kilo-cli/src/integration.ts | V2 replaces the provider-auth dialog with the Integration workflow and concretely preserves Kilo-first/recommended placement, method selection, key/form/OAuth/command flows, connection refresh, and provider-to-model return routing. Partial because the v1 KiloProvider adapter's per-provider titles/descriptions, failed-provider gutters/details, provider-specific automatic method rendering, local-provider optional-key placeholder/description, and custom-provider kilo.json credential guidance are not all evidenced in the v2 integration UI; adjacent generic connection support is not parity for those remainders. Evidence: V2 replaces the provider-auth dialog with the Integration workflow and concretely preserves Kilo-first/recommended placement, method selection, key/form/OAuth/command flows, connection refresh, and provider-to-model return routing. Partial because the v1 KiloProvider adapter's per-provider titles/descriptions, failed-provider gutters/details, provider-specific automatic method rendering, local-provider optional-key placeholder/description, and custom-provider kilo.json credential guidance are not all evidenced in the v2 integration UI; adjacent generic connection support is not parity for those remainders. | +| `packages/tui/src/component/dialog-retry-action.tsx`
    10,43 | shared-upstream patch marker | Kilo pricing retry/upgrade dialog | needs port | Owned v2 retry error action component or plugin-provided dialog | The marked behavior recognizes the Kilo pricing URL and applies a distinct upgrade treatment. The component and URL-specific rendering branch are absent from current v2; generic integration/retry errors do not preserve this Kilo conversion path. Evidence: L10: const KILO_PRICING_URL = "https://kilo.ai/pricing" // kilocode_change / L43: const showGoTreatment = () => props.link === KILO_PRICING_URL // kilocode_change | +| `packages/tui/src/component/dialog-session-list.tsx`
    58,63,86,198,220,258,286,362,386,388,390 | shared-upstream patch marker | Session lifecycle/history/UI | needs port | packages/tui/src/component/dialog-session-list.tsx with location/worktree-aware session queries | V1 adds current-worktree-by-default listing, Ctrl+A all-worktrees scope, retained worktree labels, selection/deletion/rename behavior across scopes. Current v2 dialog has no scope toggle or worktreeName presentation; location-keyed data is adjacent infrastructure, not this user-facing cross-worktree behavior. Evidence: L58: const [global, setGlobal] = createSignal(false) // kilocode_change - show current worktree by default / L63: // kilocode_change start - experimental sessions retain worktree labels and scope | +| `packages/tui/src/component/dialog-session-rename.tsx`
    9,10,22,29 | shared-upstream patch marker | Rename dialog prefill and successful completion | native equivalent | `packages/tui/src/component/dialog-session-rename.tsx` and `packages/tui/src/routes/session/index.tsx:862-875` | V2 accepts `currentTitle`, trims/refuses empty titles, calls the typed rename API, closes only after success, and reports failures. The old optional callback let session-list callers return after rename; v2's durable rename event updates shared data while post-success clear provides the completion boundary. Evidence: L9: title?: string // kilocode_change / L10: onConfirm?: () => void // kilocode_change | +| `packages/tui/src/component/dialog-status.tsx`
    9,27,57,59,60,74,105 | shared-upstream patch marker | Kilo-branded status, paths, and MCP guidance | partial | Kilo-owned status contribution/dialog; generic MCP status remains in `packages/tui/src/component/dialog-status.tsx` | V2 retains typed MCP status and no longer parses plugin file URLs, eliminating the marked Windows separator bug. It omits Kilo version/config/project path rows and `kilo mcp auth` guidance; generic authentication text and footer branding do not preserve those dialog details. Evidence: L9: import { Global } from "@opencode-ai/core/global" // kilocode_change / L27: const parts = path.split(/[/\\]/) // kilocode_change: fix Windows backslash paths | +| `packages/tui/src/component/error-component.tsx`
    10,19,22,26,76,114,127,213,227,231 | shared-upstream patch marker | User-interface behavior | partial | packages/tui/src/component/error-component.tsx plus a Kilo-owned recovery/help action seam | V1 guards ErrorBoundary rendering when renderer/theme/keyboard contexts are unavailable and adds Kilo-specific recovery/help actions. V2 has a native error boundary and generic error rendering, but the marked context-safe fallbacks and full Kilo recovery guidance are not evidenced together. Evidence: L10: // kilocode_change start — guard against missing renderer context in ErrorBoundary fallback / L19: // kilocode_change end | +| `packages/tui/src/component/kilo-logo.tsx`
    1 | Kilo-owned inherited marker | Kilo TUI home logo | native equivalent | `packages/kilo-cli/src/tui-plugin/logo.tsx`, `logo-data.ts`, and `test/logo.test.tsx` | The inherited marker disappears because the implementation moved into the Kilo-owned CLI package. V2 retains the branded responsive logo, shadow tinting, compact fallback, Unicode capability selection, and focused renderer tests; its `home.logo` slot supplies the component without a shared TUI fork. Evidence: L1: // kilocode_change - new file | +| `packages/tui/src/component/logo.tsx`
    1,4 | shared-upstream patch marker | Kilo TUI home logo | native equivalent | `packages/kilo-cli/src/tui-plugin/tui.tsx:31-35` and `logo.tsx` | The shared replacement is superseded by the Kilo preview plugin's explicit `replace: "home.logo"` registration. The production KiloLogo is owned by `packages/kilo-cli`, so no marker is required in v2's generic responsive OpenCode logo component. Evidence: L1: import { KiloLogo } from "./kilo-logo" // kilocode_change / L4: return // kilocode_change | +| `packages/tui/src/component/prompt/autocomplete.tsx`
    20,21,22,23,65,68,374,393,480,493,522,672,680,691,697,725,729,763,776 | shared-upstream patch marker | User-interface behavior | partial | packages/tui/src/component/prompt/autocomplete.tsx; packages/tui/src/context/runtime.tsx; Kilo-owned session-mention plugin/dialog | V1 markers preserve slash suffix insertion and cursor-safe overlay closing, add Past Chats with searchable session mentions, and render Kilo command labels. V2 retains native autocomplete and host command presentation, but no searchable Past Chats/session-part insertion flow is present; exact overlay/cursor regressions are unproven. Evidence: L20: import { slashDisplay } from "@/kilocode/cli/cmd/command-display" // kilocode_change / L21: import { createSessionPart, sessionMentionText } from "../../kilocode/session-mentions" // kilocode_change | +| `packages/tui/src/component/prompt/index.tsx`
    27,55,61,64,66,73,173,225,234,310,321,334,359,449,462,555,563,624,625,659,686,692,812,828,846,878,897,969,1006,1046,1048,1056,1081,1088,1109,1200,1210,1224,1259,1324,1332,1355,1433,1436,1449,1456,1490,1547,1551,1556,1562,1621,1626,1628,1639,1677,1838,1844 | shared-upstream patch marker | User-interface behavior | partial | packages/tui/src/component/prompt/index.tsx; packages/kilo-cli/src/tui-plugin; packages/core/src/session/inbox.ts | V1's 58 markers span vim editing, nudge delivery, agent/model synchronization, double-Ctrl+C exit, directory-aware submission, attachments/references, queued prompts and Kilo commands. V2 preserves durable queueing, native prompt editing and several Kilo plugin commands, but Nudge/vim/reference and exact lifecycle behaviors are not all present, so the large block is mixed. Evidence: L27: import { useNudge } from "@/kilocode/cli/cmd/tui/context/nudge" // kilocode_change / L55: // kilocode_change start | +| `packages/tui/src/component/use-connected.tsx`
    4,22 | shared-upstream patch marker | Authenticated-provider connectivity detection | native equivalent | `packages/tui/src/util/connected-provider.ts` consumed by `packages/tui/src/component/use-connected.tsx` | V2 no longer infers authentication from provider identity or paid-model cost. It reads typed integration connections and returns connected only when a credential or environment connection exists, so anonymous built-in catalog availability cannot produce a false positive; this is a stronger boundary-equivalent implementation. Evidence: L4: // kilocode_change start - anonymous Kilo and OpenCode providers do not prove authentication / L22: // kilocode_change end | +| `packages/tui/src/config/index.tsx`
    7,62,68,110 | shared-upstream patch marker | Kilo TUI title, prompt mode, and attention defaults | partial | `packages/tui/src/config/index.tsx` plus Kilo-owned config/plugin contributions for branding; prompt editing mode requires an explicit v2 design | V2 retains and expands the typed TUI config/resolve boundary and attention sound-pack override, but defaults to `opencode.default`; the marked `title_icon` and `vim` options have no current schema or consumers. Kilo runtime disables the generic terminal title rather than implementing the old icon selector, so this is not full parity. Evidence: L7: import { KiloTitleIcon } from "@/kilocode/cli/cmd/tui/title-icon" // kilocode_change / L62: title_icon: Schema.optional(KiloTitleIcon.Value), // kilocode_change | +| `packages/tui/src/config/keybind.ts`
    148,151,223,230,358,361,415 | shared-upstream patch marker | Kilo feedback, Vim-mode, and news commands | needs port | Kilo-owned TUI command registrations and bindings; add shared binding definitions only for commands implemented in the generic TUI | The marked feedback up/down, `prompt.vim.toggle`, and `news.toggle` bindings are absent from v2's binding catalogue, and no corresponding TUI command consumers exist. Generic feedback-colored UI, input editing, and home footer content do not implement these commands. Evidence: L148: // kilocode_change start - message feedback / L151: // kilocode_change end | +| `packages/tui/src/context/data.tsx`
    22,27,60,65,67,82,102,408,422,443,463,475,595,600,601,614 | shared-upstream patch marker | User-interface behavior | partial | packages/tui/src/context/data.tsx and packages/tui/src/context/event.ts | V1 adds global-event routing, disposal-safe refresh error classification, legacy promotion compatibility and hydration that reconciles live events received during snapshot load. V2 has location-keyed data/event projection and current sync tests, but earlier event assessment found server-wide project isolation and legacy/hydration race remainders; generic data loading is not full parity. Evidence: L22: import { createStore, produce, reconcile } from "solid-js/store" // kilocode_change / L27: import { hydrate } from "../kilocode/hydration" // kilocode_change | +| `packages/tui/src/context/event.ts`
    3,61,65,73 | shared-upstream patch marker | Event envelope normalization and project routing | partial | `packages/tui/src/context/event.ts` plus location-scoped client/event filtering before generic subscribers | V2 events arrive in the native `OpenCodeEvent` shape, so the legacy `syncEvent` envelope normalization is obsolete and metadata is preserved from `event.location`. However, `useEvent.subscribe` forwards every non-connection event and does not enforce the marked current-project/global filter; raw `/api/event` subscription is server-wide, so that isolation invariant is not proven by the renamed architecture. Evidence: L3: // kilocode_change start - normalize the runtime SyncEvent wire envelope to the legacy consumer shape / L61: // kilocode_change end | +| `packages/tui/src/context/local.tsx`
    10,57,99,105,124,149,166,179,188,191,210,218,226,238,287,288,297,305,309,310,318,355,356,384,400,405,592,603 | shared-upstream patch marker | User-interface behavior | partial | packages/tui/src/context/local.tsx; packages/tui/src/context/runtime.tsx; packages/kilo-cli/src/model-picker.ts | V1 serializes preference writes, separates process-local configured-agent overrides, falls back after agent removal and persists model/agent choices per project. V2 retains local model preferences and host picker policy, including some serialized state markers, but the full configured-agent/org-switch fallback and persistence contract is not evidenced. Evidence: L10: import { useProject } from "./project" // kilocode_change / L57: const project = useProject() // kilocode_change | +| `packages/tui/src/context/route.tsx`
    1,17,21,29,39,41,48,51,58 | shared-upstream patch marker | User-interface behavior | partial | packages/tui/src/context/route.tsx plus a Kilo-owned KiloClaw route/plugin if Claw returns | V1 adds KiloClawRoute and clone-safe navigation/history state. V2 native routing preserves ordinary home/session/plugin navigation, but KiloClaw is absent and the old specialized route cannot be called native; only the generic history portion remains. Evidence: L1: import { createStore, reconcile, unwrap } from "solid-js/store" // kilocode_change / L17: // kilocode_change start | +| `packages/tui/src/context/sync.tsx`
    12,13,24,25,26,36,39,40,41,90,93,95,106,109,125,139,144,145,153,154,162,168,170,198,201,202,203,204,233,238,325,357,377,416,495,617,622,635,639,660,757,765,774,777,792,799,810,820,829,840,858,882,893,908,931,942,958,1002 | shared-upstream patch marker | Kilo TUI projection, compatibility events, and workspace rebootstrap | partial | `packages/client/src/solid/data.ts` and TUI `DataProvider`, plus owned Kilo remote/sidebar services and explicit ports for network/tool-terminal surfaces | V2 replaces the monolithic sync store with location-keyed client projections and native durable events. Session eviction, shell/background state, VCS-by-location, configuration refresh, indexing/account/sidebar consumers, and remote suggestions are covered by current native or owned seams. The v1 network-wait prompt and tool-emitted interactive-terminal projection remain unported, legacy released-v1 Sync event folding is intentionally absent, and direct generic event subscribers still lack project filtering; the mixed aggregate is therefore partial. Evidence: L12: SuggestionRequest, // kilocode_change / L13: SessionNetworkWait, // kilocode_change | +| `packages/tui/src/context/theme.tsx`
    41,43,59,67,106,131,132,148,154,173,188,277,317,322 | shared-upstream patch marker | User-interface behavior | partial | packages/tui/src/context/theme.tsx; packages/kilo-cli/src/tui-plugin/theme.ts; packages/kilo-ui/src/theme | V1 discovers Kilo theme roots, isolates malformed custom themes, defaults/falls back to the built-in kilo theme and validates required colors. V2 has native semantic theme resolution and owned Kilo theme packs, but prior theme audit found the released built-in pack/fallback surface incomplete; malformed-file isolation alone is not full parity. Evidence: L41: // kilocode_change start - discover Kilo config roots, not OpenCode roots / L43: // kilocode_change end | +| `packages/tui/src/feature-plugins/home/tips-view.tsx`
    4,136,143,168,224,227,236,238,255 | shared-upstream patch marker | Kilo-branded contextual home tips | needs port | Kilo-owned TUI plugin rendering an appended `home.footer` or a new narrow shared `home.tips` slot | V1 replaces the upstream tip catalogue with Kilo-specific commands, config paths, branding, provider copy, and shortcut-aware tips. V2 has no tips component or catalogue; the existing generic and Kilo footer slots are static status/footer content, not rotating contextual guidance, so adjacent home chrome is not parity. Evidence: L4: import { KILO_TIPS } from "@/kilocode/cli/cmd/tui/feature-plugins/home/tips" // kilocode_change / L136: // kilocode_change start | +| `packages/tui/src/feature-plugins/home/tips.tsx`
    47 | shared-upstream patch marker | Persistent home-tip visibility and toggle | needs port | Kilo-owned TUI home contribution plus plugin KV/command binding; add a narrow slot only if the current home footer cannot host it | V1 deliberately shows tips beyond first-run state while retaining the `tips.toggle` hidden preference. V2 has no tip registration, toggle command, or `tips_hidden` state; `packages/tui/src/routes/home.tsx` exposes logo and footer slots, but no current consumer implements this invariant. Evidence: L47: const show = createMemo(() => !hidden()) // kilocode_change - always show tips regardless of first-time status | +| `packages/tui/src/feature-plugins/sidebar/context.tsx`
    4,14,16,20,28,50,68 | shared-upstream patch marker | User-interface behavior | partial | packages/tui/src/feature-plugins/sidebar/context.tsx; packages/tui/src/plugin/slots.tsx; packages/kilo-cli/src/tui-plugin/sidebar-* | V1 markers support plugin sidebar sections, ordering/visibility and reactive content. V2 has a concrete sidebar.content slot with many Kilo consumers, but exact v1 ordering/collapse/context behavior is not fully retained; consumers prove the seam, not every marked invariant. Evidence: L4: import { createMemo, createSignal, Show } from "solid-js" // kilocode_change / L14: // kilocode_change start | +| `packages/tui/src/feature-plugins/sidebar/footer.tsx`
    56,58,73,77 | shared-upstream patch marker | Kilo getting-started and sidebar branding | partial | `packages/kilo-cli/src/tui-plugin/tui.tsx` sidebar footer slot plus an owned onboarding contribution | V2's owned Kilo plugin preserves the branded version line through `sidebar.footer`, while the generic footer preserves the working-directory consumer. The marked free-model onboarding sentence and its getting-started card are absent; Kilo's static home footer/account status is not the same onboarding behavior. Evidence: L56: {/* kilocode_change start */} / L58: {/* kilocode_change end */} | +| `packages/tui/src/feature-plugins/system/notifications.ts`
    59,75,82 | shared-upstream patch marker | Completion/error attention semantics | partial | `packages/tui/src/feature-plugins/system/notifications.ts` with a Kilo-owned notification policy seam or narrowly configurable terminal-event policy | V2 natively deduplicates forms/permissions and terminal executions, suppresses a duplicate done after failure, and uses focus-aware attention. It does not preserve the marked Kilo invariant: `session.execution.interrupted` calls the same completion path as success, subagents receive `subagent_done`, errors expose raw messages, and no manual-abort/timeout normalization matches v1's silent abort and `Model stopped responding` behavior. Evidence: L59: // kilocode_change start - only completed turns need completion attention / L75: // kilocode_change end | +| `packages/tui/src/keymap.tsx`
    108 | shared-upstream patch marker | Kilo-branded internal error text | not needed | No v2 destination; retain generic keymap diagnostics in the shared TUI | The marker changes only an unreachable-in-normal-use error string from OpenCode to Kilo. V2 no longer exposes this mode-stack helper, and product behavior does not depend on branded internal exception wording; porting it would require an unnecessary shared-file fork. Evidence: L108: if (!value) throw new Error("Kilo mode stack is not registered for this keymap") // kilocode_change | +| `packages/tui/src/plugin/adapters.tsx`
    69,71,110,114,143,151 | shared-upstream patch marker | User-interface behavior | native equivalent | packages/tui/src/plugin/adapters.tsx and packages/tui/src/context/runtime.tsx | V1 markers adapt plugin dialog/toast/clipboard services to TUI contexts. V2 exposes the same host UI capabilities through current runtime adapters and Kilo plugins actively consume ctx.ui.dialog/select/toast; the adapter mechanism was reorganized without losing the service boundary. Evidence: L69: // kilocode_change start / L71: // kilocode_change end | +| `packages/tui/src/plugin/slots.tsx`
    3,28,38 | shared-upstream patch marker | User-interface behavior | already ported | packages/tui/src/plugin/slots.tsx | The current v2 file contains the marked stateful-fallback behavior: fallback children stay mounted while reactive slot props update. This is the same invariant and same shared seam, with an active Kilo sidebar/home consumer. Evidence: L3: import { children, createSignal, mergeProps } from "solid-js" // kilocode_change / L28: // kilocode_change start - keep stateful fallback children mounted while preserving reactive slot props | +| `packages/tui/src/routes/session/footer.tsx`
    6,9,10,25,26,61,68,85,99 | shared-upstream patch marker | Remote presence and connected-service footer | partial | Kilo-owned TUI remote-status contribution plus native sidebar/home status consumers | V2 replaces the old footer layout with tabs, home/footer, sidebar, and status surfaces; MCP/location information remains available through native consumers. The marked `RemoteIndicator` live presence surface has no equivalent registration in the Kilo TUI plugin, so the mixed row is not fully ported. Evidence: L6: import { useSDK } from "../../context/sdk" // kilocode_change / L9: import { useEvent } from "../../context/event" // kilocode_change | +| `packages/tui/src/routes/session/index.tsx`
    6,39,66,71,76,92,99,154,157,255,302,344,346,382,433,977,981,1021,1034,1040,1046,1052,1055,1096,1102,1106,1322,1328,1425,1479,1622,1623,1642,1660,1685,1706,1723,1727,1742,1749,1753,1776,1816,1818,1845,1883,1885,1895,1897,1905,1962,1972,2053,2157,2197,2223,2224,2255,2256,2309,2331,2337,2367,2371,2628,2691,2697,2730,2757,2793,2838,2865,2917,2992,2996 | shared-upstream patch marker | Session lifecycle/history/UI | partial | packages/tui/src/routes/session/index.tsx; packages/kilo-cli/src/tui-plugin/{memory,remote,sidebar-*}.tsx | V1's 75 markers mix approval badges, feedback, memory attachment, session-scoped processes, routed-model/status/sidebar actions and older session behavior. V2 visibly carries several Kilo markers and owned plugin consumers for memory, usage, PR, processes, account and routed models, but feedback and all marked session lifecycle subfeatures are not uniformly present. Evidence: L6: onCleanup, // kilocode_change / L39: StepFinishPart, // kilocode_change | +| `packages/tui/src/routes/session/network.tsx`
    1 | shared-upstream patch marker | Offline wait/reconnect prompt | needs port | Kilo-owned TUI session contribution backed by a v2 network-wait protocol/control seam | V1 renders durable disconnect/restored state, a retry countdown, resume-now, stop-turn, and app-exit rejection wired to network reply/reject APIs. V2 has retry/reconnecting presentation but no `SessionNetworkWait`, reply/reject control contract, or equivalent session prompt; generic transport retry is not this user-controlled offline state machine. Evidence: L1: // kilocode_change - new file | +| `packages/tui/src/routes/session/permission.tsx`
    17,23,53,68,99,165,170,196,296,309,310,323,354,423,424,451,461,465,471,478,504 | shared-upstream patch marker | Session lifecycle/history/UI | partial | packages/tui/src/routes/session/permission.tsx; packages/core/src/permission.ts; packages/kilo-cli/src/skill-shell.ts | V1 renders diff hunks, sends interactive:true for human replies, normalizes URL permissions, and supplies Kilo skill-shell/memory custom renderers. V2 has native permission UI and policy services, but schema audit shows interactive is absent and skill-shell human-vs-machine approval plus specialized renderers are incomplete. Evidence: L17: // kilocode_change start / L23: // kilocode_change end | +| `packages/tui/src/routes/session/question.tsx`
    14,17,219,221 | shared-upstream patch marker | Session lifecycle/history/UI | partial | packages/tui/src/routes/session/question.tsx and packages/schema/src/v1/question.ts Kilo extension | V1 avoids intrusive key capture for non-blocking review questions and consumes localized/mode-aware question fields. V2 has native form/question UI, but the schema fields and non-blocking review interaction contract are not fully ported. Evidence: L14: // kilocode_change start / L17: // kilocode_change end | +| `packages/tui/src/routes/session/sidebar.tsx`
    91,95 | shared-upstream patch marker | Kilo version in session sidebar | native equivalent | `packages/kilo-cli/src/tui-plugin/tui.tsx:91-99` through `packages/tui/src/routes/session/sidebar.tsx:77-79` | V2 replaces the shared fallback with an additive `sidebar.footer` slot. The Kilo-owned plugin registers `Kilo {ctx.app.version}` into that production slot, preserving the branded version line without forking the generic sidebar; other owned sidebar contributions coexist. Evidence: L91: {/* kilocode_change start */} / L95: {/* kilocode_change end */} | +| `packages/tui/src/routes/session/subagent-footer.tsx`
    6,7,16,20,32,105,109 | shared-upstream patch marker | Running subagent identity and navigation footer | partial | `packages/tui/src/component/session-tabs.tsx` and child-session navigation in `packages/tui/src/routes/session/index.tsx`; owned agent-colored running affordance if still required | V2 natively tracks child sessions, running/unread status, parent/child navigation, and agent identity across tabs and session routes. The exact marked footer spinner derived from unfinished assistant messages and colored by the selected agent is absent, so generic tab activity is only a partial presentation equivalent. Evidence: L6: import { Spinner } from "@tui/component/spinner" // kilocode_change / L7: import { useLocal } from "@tui/context/local" // kilocode_change | +| `packages/tui/src/routes/session/suggest.tsx`
    1 | shared-upstream patch marker | Suggestion route hook | native equivalent | Owned Kilo CLI remote suggestion adapter and Kilo VS Code UI | The shared TUI forwarding hook is removed because suggestion presentation is hosted by Kilo-owned remote and VS Code surfaces; behavior remains without editing the upstream TUI route. Evidence: L1: // kilocode_change - new file | +| `packages/tui/src/routes/session/terminal.tsx`
    1 | shared-upstream patch marker | Embedded interactive tool terminal | partial | `packages/tui/src/component/terminal-pane.tsx` and session terminal contexts; an owned tool-terminal bridge remains required | V2 has a richer native PTY pane with selection, resize, input, tabs, persistence, and session commands. The v1 row specifically consumes tool-emitted `InteractiveTerminalSnapshot` in the transcript; no v2 consumer bridges tool-created snapshots into the pane, and Kilo runtime currently disables the generic session terminal. Evidence: L1: // kilocode_change - new file | +| `packages/tui/src/runtime.tsx`
    8 | shared-upstream patch marker | Portable home-path abbreviation | native equivalent | `packages/tui/src/util/path-format.ts:3-10`, re-exported by `packages/tui/src/runtime.tsx` | V2 preserves the invariant and strengthens it: it selects `path.win32` or `path.posix` from the supplied path instead of the host platform, then joins relative segments with `/`. Existing consumers import the runtime re-export or the owned formatter directly. Evidence: L8: return "~/" + relative.replaceAll(path.sep, "/") // kilocode_change - keep displayed paths portable | +| `packages/tui/src/theme/index.ts`
    5,17,18,147,148,169 | shared-upstream patch marker | Bundled Kilo and accessibility theme catalogue | needs port | Kilo-owned theme documents registered through the v2 plugin theme API, or explicit additions to `packages/tui/src/theme/v1.ts` when they must be generic built-ins | V1 adds `kilo`, `kilo-v1`, and `colorblind` assets to the built-in theme map. V2's `DEFAULT_THEMES` contains none of those names or assets; the new `packages/theme` logo semantic token preserves branded logo coloring but is not a substitute for the three complete selectable themes. Evidence: L5: import colorblind from "./assets/colorblind.json" with { type: "json" } // kilocode_change / L17: import kilo from "./assets/kilo.json" with { type: "json" } // kilocode_change | +| `packages/tui/src/ui/dialog-alert.tsx`
    5,40,46 | shared-upstream patch marker | Multiline alert rendering | needs port | `packages/tui/src/ui/dialog-alert.tsx` or a Kilo-owned alert renderer supplied through the plugin API | V1 normalizes escaped `\\n` and literal newlines, then renders each line in its own row. V2 passes the message to one text node unchanged; no alert test or helper proves escaped-newline normalization, so the marked behavior is absent. Evidence: L5: import { For } from "solid-js" // kilocode_change / L40: {/* kilocode_change start - render escaped and literal newlines on separate rows */} | +| `packages/tui/src/ui/dialog-export-options.tsx`
    129,132,147,150,165,168,183,186 | shared-upstream patch marker | User-interface behavior | needs port | packages/tui/src/ui/dialog-export-options.tsx or a Kilo-owned export dialog plugin | V1 markers add Kilo export actions/options and guarded per-action completion. Current v2 has no matching marked branches or owned Kilo export-options consumer; generic session export support does not establish parity for those choices. Evidence: L129: // kilocode_change start / L132: } /* kilocode_change end */ | +| `packages/tui/src/ui/dialog-select.tsx`
    39,49,69,459,464 | shared-upstream patch marker | User-interface behavior | needs port | packages/tui/src/ui/dialog-select.tsx list-level action API | V1 explicitly permits DialogSelect actions when no option is selected, enabling empty-list scope toggles such as the session-list Ctrl+A action. Current v2 select lacks this action surface, and the missing session-scope behavior is its concrete consumer. Evidence: L39: actions?: DialogSelectAction[] // kilocode_change - supports actions without a selected option / L49: // kilocode_change start - support list-level actions when no option is selected | +| `packages/tui/src/ui/dialog.tsx`
    43 | shared-upstream patch marker | Vertically centered dialogs | partial | `packages/tui/src/ui/dialog.tsx` through its current `centered` state/API | V2 preserves centering as a supported dialog and plugin option, and consumers such as the diff viewer request it. The default is now quarter-height rather than centered, so the v1 invariant for every dialog is not global; this is a caller-selectable equivalent rather than an absent capability. Evidence: L43: justifyContent="center" // kilocode_change | +| `packages/tui/src/ui/toast.tsx`
    65,79 | shared-upstream patch marker | User-interface behavior | partial | packages/tui/src/ui/toast.tsx and packages/tui/src/plugin/adapters.tsx | V1 markers adjust toast lifecycle/rendering for Kilo flows. V2 has a native toast service heavily used by Kilo plugins, but the exact marked timing/cleanup behavior is not evidenced by current source or a paired regression. Evidence: L65: // kilocode_change start / L79: // kilocode_change end | +| `packages/tui/src/util/error.ts`
    27,28,73 | shared-upstream patch marker | Kilo-branded model and MCP error guidance | needs port | Kilo-owned CLI error presentation hook or app-aware command/config labels in `packages/tui/src/util/error.ts` | The v2 formatter retains structured model suggestions but emits `opencode models` and `opencode.json`; its MCP failure message also claims authentication is unsupported even though v2 has integration authentication. No Kilo runtime override rewrites these strings, so the marked Kilo commands and capability-accurate guidance are absent. Evidence: L27: "Try: `kilo models` to list available models", // kilocode_change / L28: "Or check your config (kilo.json) provider/model names", // kilocode_change | +| `packages/tui/src/util/markdown.ts`
    1 | shared-upstream patch marker | Fixed-width Markdown table normalization | needs port | Kilo-owned Markdown preprocessing/render hook before `packages/tui/src/routes/session/index.tsx` transcript rendering | V1 parses pipe tables, preserves alignment, measures wide Unicode, pads columns, and preprocesses assistant text before rendering, with a dedicated test suite. V2 renders Markdown directly and has no `formatMarkdownTables` implementation or call site; generic rendering is not evidence of this alignment invariant. Evidence: L1: // kilocode_change - new file | +| `packages/tui/src/util/presentation.ts`
    1 | shared-upstream patch marker | Kilo exit banner and resume command | native equivalent | `packages/kilo-cli/src/tui-plugin/epilogue.ts` injected by `packages/kilo-cli/src/tui.ts` | V2 keeps generic `opencode2` presentation in the shared TUI and injects Kilo's owned `sessionEpilogue` through the app contract. The owned renderer emits responsive Kilo artwork, session title, and `kilo2 -s `, so removal of the shared-file marker reflects relocation rather than behavior loss. Evidence: L1: // kilocode_change - Kilo exit banner instead of the opencode wordmark | +| `packages/tui/test/app-lifecycle.test.tsx`
    122 | shared-upstream patch marker | Kilo exit epilogue regression | already ported | `packages/kilo-cli/test/epilogue.test.ts` plus generic lifecycle coverage in `packages/tui/test/app-lifecycle.test.tsx:261` | The shared v2 lifecycle test verifies cleanup, renamed session title, and generic `opencode2 -s` output. Kilo's owned epilogue test separately asserts artwork, title, `kilo2 -s ses_123`, absence of OpenCode branding, and parsing of the displayed resume command; this is stronger architectural separation than changing the generic assertion. Evidence: L122: expect(stdout).toContain("kilo -s dummy") // kilocode_change | +| `packages/tui/test/cli/cmd/tui/notifications.test.ts`
    139,298 | shared-upstream patch marker | Completion/error attention regression coverage | needs port | `packages/tui/test/cli/cmd/tui/notifications.test.ts` or Kilo-owned plugin tests accompanying a notification-policy override | Current v2 tests intentionally expect completion notifications for terminal lifecycle events and `subagent_done`, and cover failure deduplication. They do not test v1's completed-only rule, interrupted silence, suppressed subagent completion, silent manual abort, or friendly SSE-timeout message; those missing invariants require explicit tests with any policy port. Evidence: L139: // kilocode_change start / L298: // kilocode_change end | +| `packages/tui/test/cli/cmd/tui/provider-options.test.ts`
    26 | shared-upstream patch marker | Kilo provider ordering regression | needs port | Kilo-owned integration/model-picker ordering policy and focused test around the v2 dialog consumer | V1 pins Anthropic/OpenAI ahead of alphabetically sorted providers and the custom-provider sentinel. V2's integration dialog no longer exposes the old `providerOptions` helper, and no source-backed priority table or test preserves this exact Kilo ordering; unrelated model sorting and account-popularity UI are not equivalent. Evidence: L26: ).toEqual(["anthropic", "openai", "aws", "mistral", "custom-z", "__opencode_custom_provider__"]) // kilocode_change - preserve Kilo provider priority | +| `packages/tui/test/cli/cmd/tui/sync-fixture.tsx`
    9,10,52,54,58,64,68,70,77 | shared-upstream patch marker | Legacy monolithic SyncProvider fixture wiring | not needed | No v2 destination; current tests compose `ClientProvider`, `ProjectProvider`, and `DataProvider` through v2 fixture contexts | The marked Toast/Exit providers and explicit `project.sync()` were dependencies of v1's monolithic `SyncProvider` bootstrap and project-filtered compatibility stream. V2's data projection and client lifecycle own those boundaries directly; restoring the obsolete fixture nesting would test removed architecture rather than product behavior. Evidence: L9: import { ToastProvider } from "../../../../src/ui/toast" // kilocode_change / L10: import { ExitProvider } from "../../../../src/context/exit" // kilocode_change | +| `packages/tui/test/cli/cmd/tui/sync.test.tsx`
    49 | shared-upstream patch marker | Workspace-isolated VCS projection regression | native equivalent | `packages/tui/test/cli/tui/data.test.tsx:104-151` and location-keyed handling in `packages/client/src/solid/data.ts:1129-1160` | V2's data test bootstraps VCS for the active location and verifies a branch event updates that location's keyed projection. The production handler indexes by `event.location`, while `data.location.vcs.info()` reads the selected/default location, preventing another workspace key from replacing active VCS state; the old fixture-specific rebootstrap comment is no longer required. Evidence: L49: ) // kilocode_change - workspace re-bootstrap retains the selected test workspace | +| `packages/tui/test/cli/tui/data.test.tsx`
    5,6,28,38,196,202,463,513,568,1094 | shared-upstream patch marker | User-interface behavior | partial | packages/tui/test/data.test.tsx and packages/tui/test/context/event.test.ts | V1 regressions cover project-filter initialization, promoted-prompt compatibility and hydration/live-event races. V2 tests location-keyed data and event projection, but earlier source assessment retained project-isolation and hydration/legacy gaps, so only part of this suite has a native target. Evidence: L5: import { createSignal, onMount, Show, type ParentProps } from "solid-js" // kilocode_change / L6: import { ProjectProvider, useProject } from "../../../src/context/project" // kilocode_change | +| `packages/tui/test/cli/tui/inline-tool-wrap-snapshot.test.tsx`
    228,232 | shared-upstream patch marker | Tool behavior/UI | partial | packages/tui test snapshots plus packages/kilo-cli TUI plugin renderer tests | V1 snapshot keeps dedicated Kilo tool renderers when inline output wraps. V2 has Kilo plugin renderers and native wrapping, but no equivalent snapshot proving their combined layout; implementation adjacency is not regression parity. Evidence: L228: // kilocode_change start - Kilo tools keep their dedicated renderers / L232: // kilocode_change end | +| `packages/tui/test/cli/tui/use-event.test.tsx`
    119,134 | shared-upstream patch marker | Cross-project event isolation regression | needs port | `packages/tui/test/cli/tui/use-event.test.tsx` alongside the v2 event filtering seam | The v2 suite covers metadata, global/current events, durable logging, reconnect, backoff, and cleanup, but removed the marked assertion that a foreign-project event is ignored while a current-project event is delivered. Those adjacent tests do not prove project isolation, so the focused regression remains required. Evidence: L119: // kilocode_change start - the compatibility stream contains events from every loaded project / L134: // kilocode_change end | +| `packages/tui/test/config.test.tsx`
    52 | shared-upstream patch marker | Kilo attention default regression | needs port | Kilo-owned TUI config/attention test accompanying a branding default override | Current v2 config tests assert `opencode.default`, while Kilo's runtime merely disables attention and does not supply `kilo.default`. The old default-pack assertion is therefore not ported; it should accompany any Kilo attention branding policy rather than alter the generic host-neutral test. Evidence: L52: sound_pack: "kilo.default", // kilocode_change | +| `packages/tui/test/fixture/tui-sdk.ts`
    90,95 | shared-upstream patch marker | User-interface behavior | partial | packages/tui/test/fixture/tui-runtime.ts and packages/kilo-cli/test/fixture.ts | V1 fixture adds Kilo bootstrap endpoints used by TUI tests. V2 test fixtures provide native runtime/plugin clients, but not every legacy Kilo endpoint; the fixture is obsolete in shape while feature-specific coverage remains split. Evidence: L90: // kilocode_change start - Kilo bootstrap endpoints / L95: // kilocode_change end | +| `packages/tui/test/use-connected.test.ts`
    1 | shared-upstream patch marker | Authenticated-provider connectivity regression | already ported | `packages/tui/test/util/connected-provider.test.ts` | The renamed v2 test proves empty integration lists and integrations without connections are disconnected, while credential and environment connections are connected. That directly tests the new typed boundary and prevents anonymous catalog entries from counting as authentication without retaining a Kilo marker. Evidence: L1: // kilocode_change - new file | +| `packages/tui/test/util/error.test.ts`
    50,64 | shared-upstream patch marker | Kilo error-guidance regression | needs port | Kilo-owned error-presentation tests accompanying the model/MCP guidance port | V2's error tests cover generic formatting and opaque throwables but do not call `cliErrorMessage` for model-not-found or MCP failures. The old assertions for `kilo models`, `kilo.json`, absence of OpenCode branding, and capability-accurate MCP text are missing and should be restored with the presentation seam. Evidence: L50: // kilocode_change start - preserve Kilo-branded CLI guidance / L64: // kilocode_change end | +| `packages/tui/test/util/presentation.test.ts`
    7 | shared-upstream patch marker | User-interface behavior | needs port | packages/tui/test/util/presentation.test.ts with Kilo CLI epilogue override | V1 asserts the CLI epilogue uses `kilo -s `. Current v2 presentation is upstream-oriented and no owned epilogue override/test proves this exact Kilo resume command; nearby branding does not preserve it. Evidence: L7: expect(epilogue).toContain("kilo -s ses_123") // kilocode_change | +| `packages/tui/test/util/transcript.test.ts`
    70,83,88,99,265,280,292,310,321,347,399,417 | shared-upstream patch marker | User-interface behavior | partial | packages/tui/test/util/transcript.test.ts and Kilo agent/model presentation policy | V1 tests Code agent naming, friendly model names, duration display and suppression rules across transcript variants. V2 retains native transcript formatting and host model presentation, but the build-to-Code rename is explicitly missing in the agent audit and these exact Kilo expectations are not all covered. Evidence: L70: agent: "code", // kilocode_change / L83: expect(result).toBe("## Assistant (Code · claude-sonnet-4-20250514 · 5.4s)\n\n") // kilocode_change | +| `packages/tui/tsconfig.json`
    9,15 | shared-upstream patch marker | Legacy cross-package Kilo source resolution | not needed | No v2 destination; Kilo integrations compile from their owned packages and public package exports | The v1 aliases reached back into the removed `packages/opencode` tree so retained TUI Kilo imports and ambient declarations compiled. V2's Kilo logo/sidebar integrations live in `packages/kilo-cli` and consume public TUI/plugin/theme APIs; the TUI tsconfig has no such cross-tree imports, so restoring these aliases would violate the v2 package boundary rather than preserve behavior. Evidence: L9: // kilocode_change - resolve Kilo integrations retained during upstream's standalone TUI extraction / L15: // kilocode_change - include ambient declarations used by retained Kilo imports from opencode | +| `packages/ui/src/components/basic-tool.tsx`
    6,36,37,40,95,137,180,181,184,187,193,202,206,265,276,317,323 | shared-upstream patch marker | Tool behavior/UI | already ported | `packages/kilo-ide-ui/src/components/basic-tool.tsx` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L6: import { Icon, type IconProps } from "./icon" // kilocode_change: added Icon / L36: retainDetails?: boolean // kilocode_change | +| `packages/ui/src/components/button.css`
    1 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/components/button.css` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L1: /* kilocode_change - reverted to main */ | +| `packages/ui/src/components/card.css`
    10,16,88,121 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/components/card.css` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L10: /* kilocode_change start — preserve Kilo's bordered card surface */ / L16: /* kilocode_change end */ | +| `packages/ui/src/components/dock-surface.tsx`
    28 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/components/dock-surface.tsx` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L28: data-component="prompt-input-form" // kilocode_change | +| `packages/ui/src/components/icon.tsx`
    89,113 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/components/icon.tsx` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L89: lock: ``, // | +| `packages/ui/src/components/list.tsx`
    184,187 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/components/list.tsx` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L184: // kilocode_change start - fall back to first result when no item is active (noInitialSelection) / L187: // kilocode_change end | +| `packages/ui/src/components/markdown-shiki.worker.ts`
    23,32 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/components/markdown-shiki.worker.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L23: let theme = "Kilo" // kilocode_change - use the Kilo theme supplied during worker initialization / L32: theme = event.data.theme.name // kilocode_change | +| `packages/ui/src/components/markdown-worker.ts`
    2,120 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/components/markdown-worker.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L2: import { KiloTheme } from "../context/marked" // kilocode_change / L120: worker.postMessage({ type: "init", theme: KiloTheme } satisfies MarkdownWorkerRequest) // kilocode_change | +| `packages/ui/src/components/markdown.css`
    69,70,80,106,110,115,117,235,248,266 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/components/markdown.css` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L69: margin-inline-start: 0; /* kilocode_change */ / L70: padding-inline-start: 32px; /* kilocode_change */ | +| `packages/ui/src/components/markdown.tsx`
    2,29,32,46,380,389,398,406,452,455,457,459,470,473,487,498,519,522,543,545,560,563,567,570,579,609,629,664,666,670,673,674,693,713,723,732,768 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/components/markdown.tsx` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L2: import { deferredHighlight, fnv1a } from "../context/marked" // kilocode_change / L29: // kilocode_change start: Mermaid rendering and morphdom guards for highlighted blocks | +| `packages/ui/src/components/message-part.css`
    24,46,185,252,485,495,513,523,1290,1300,1345,1354 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/components/message-part.css` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L24: gap: 8px; /* kilocode_change */ / L46: cursor: pointer; /* kilocode_change */ | +| `packages/ui/src/components/message-part.tsx`
    163,859,1065,1155,1181,1184,1190,1555,1567,1570,1575,1593 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/components/message-part.tsx` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L163: queued?: boolean // kilocode_change / L859: queued={props.queued} // kilocode_change | +| `packages/ui/src/components/popover.tsx`
    23,38,57,141,163 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/components/popover.tsx` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L23: contentLabel?: string // kilocode_change / L38: "contentLabel", // kilocode_change | +| `packages/ui/src/components/resize-handle.tsx`
    35,37,40,60 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/components/resize-handle.tsx` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L35: // kilocode_change start - set resize cursor on body during drag / L37: // kilocode_change end | +| `packages/ui/src/components/select.css`
    172 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/components/select.css` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L172: max-width: min(23rem, var(--kb-popper-content-available-width)); /* kilocode_change */ | +| `packages/ui/src/components/select.test.ts`
    1 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/components/select.test.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L1: // kilocode_change - new file | +| `packages/ui/src/components/select.tsx`
    92,93,94 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/components/select.tsx` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L92: overlap={local.triggerVariant === "settings"} // kilocode_change / L93: fitViewport={local.triggerVariant === "settings"} // kilocode_change | +| `packages/ui/src/components/session-turn.tsx`
    162,209,234,244,386,415,417 | shared-upstream patch marker | Session lifecycle/history/UI | already ported | `packages/kilo-ide-ui/src/components/session-turn.tsx` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L162: queued?: boolean // kilocode_change / L209: if (typeof props.active === "boolean" && typeof props.queued === "boolean") return // kilocode_change | +| `packages/ui/src/components/switch.tsx`
    8,12,14,17 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/components/switch.tsx` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L8: inputProps?: ComponentProps // kilocode_change / L12: // kilocode_change start | +| `packages/ui/src/components/text-shimmer.css`
    24,59 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/components/text-shimmer.css` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L24: white-space: pre; /* kilocode_change - preserve inactive fallback whitespace */ / L59: /* kilocode_change — gate animation on data-active directly (was data-run | +| `packages/ui/src/components/text-shimmer.tsx`
    1,51 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/components/text-shimmer.tsx` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L1: // kilocode_change start — the previous implementation used a createEffect that / L51: // kilocode_change end | +| `packages/ui/src/components/todo-panel-motion.stories.tsx`
    2,13,16,142,184,191,254,280 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/components/todo-panel-motion.stories.tsx` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L2: import { createMemo, onCleanup } from "solid-js" // kilocode_change / L13: // kilocode_change start | +| `packages/ui/src/components/tool-error-card.css`
    3,4 | shared-upstream patch marker | Tool behavior/UI | already ported | `packages/kilo-ide-ui/src/components/tool-error-card.css` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L3: background-color: transparent; /* kilocode_change — override restored card background */ / L4: border: none; /* kilocode_change — override restored card border */ | +| `packages/ui/src/context/data.tsx`
    44,49,50,51,58,60,61,70,71,72,73,74,85,86,87,88,89 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/context/data.tsx` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L44: // kilocode_change start / L49: before?: string // kilocode_change - optional, kilo uses `patch` | +| `packages/ui/src/context/index.ts`
    2 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/context/index.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L2: export * from "./data" // kilocode_change - Kilo typechecks packages/ui, whose components import useData from here | +| `packages/ui/src/context/marked.tsx`
    2,9,11,13,15,16,18,25,27,401,403,406,408,413,422,427,433,499,501,541,542,652,654,656,667,698,700,710,712,760,761,766,786 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/context/marked.tsx` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L2: // kilocode_change: marked-shiki highlighted code blocks synchronously during / L9: // kilocode_change start: import types for double-dollar math extension | +| `packages/ui/src/file-path.test.ts`
    1 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/file-path.test.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L1: // kilocode_change - new file | +| `packages/ui/src/file-path.ts`
    1 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/file-path.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L1: // kilocode_change - new file | +| `packages/ui/src/i18n/ar.ts`
    108,119,124,160,176,178 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/i18n/ar.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L108: // kilocode_change start / L119: // kilocode_change end | +| `packages/ui/src/i18n/br.ts`
    108,119,124,160,176,178 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/i18n/br.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L108: // kilocode_change start / L119: // kilocode_change end | +| `packages/ui/src/i18n/bs.ts`
    112,123,128,164,180,182 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/i18n/bs.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L112: // kilocode_change start / L123: // kilocode_change end | +| `packages/ui/src/i18n/da.ts`
    107,118,123,159,175,177 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/i18n/da.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L107: // kilocode_change start / L118: // kilocode_change end | +| `packages/ui/src/i18n/de.ts`
    113,124,129,165,181,183 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/i18n/de.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L113: // kilocode_change start / L124: // kilocode_change end | +| `packages/ui/src/i18n/en.ts`
    114,125,130,172,189,191 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/i18n/en.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L114: // kilocode_change start / L125: // kilocode_change end | +| `packages/ui/src/i18n/es.ts`
    108,119,124,160,176,178 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/i18n/es.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L108: // kilocode_change start / L119: // kilocode_change end | +| `packages/ui/src/i18n/fr.ts`
    108,119,124,160,176,178 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/i18n/fr.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L108: // kilocode_change start / L119: // kilocode_change end | +| `packages/ui/src/i18n/it.ts`
    1,134,173,191,193 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/i18n/it.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L1: // kilocode_change - new file / L134: "ui.tool.swePruned": "SWE-Pruner · {{kept}} di {{total}} righe mantenute", // kilocode_change | +| `packages/ui/src/i18n/ja.ts`
    107,118,123,159,175,177 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/i18n/ja.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L107: // kilocode_change start / L118: // kilocode_change end | +| `packages/ui/src/i18n/ko.ts`
    108,119,124,160,176,178 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/i18n/ko.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L108: // kilocode_change start / L119: // kilocode_change end | +| `packages/ui/src/i18n/nl.ts`
    56,65,111,122,137,176,194,196 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/i18n/nl.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L56: // kilocode_change start - complete upstream usage-exceeded translations / L65: // kilocode_change end | +| `packages/ui/src/i18n/no.ts`
    111,122,127,163,179,181 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/i18n/no.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L111: // kilocode_change start / L122: // kilocode_change end | +| `packages/ui/src/i18n/pl.ts`
    107,118,123,159,175,177 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/i18n/pl.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L107: // kilocode_change start / L118: // kilocode_change end | +| `packages/ui/src/i18n/ru.ts`
    107,118,123,159,175,177 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/i18n/ru.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L107: // kilocode_change start / L118: // kilocode_change end | +| `packages/ui/src/i18n/th.ts`
    109,120,125,161,177,179 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/i18n/th.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L109: // kilocode_change start / L120: // kilocode_change end | +| `packages/ui/src/i18n/tr.ts`
    114,125,130,166,182,184 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/i18n/tr.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L114: // kilocode_change start / L125: // kilocode_change end | +| `packages/ui/src/i18n/uk.ts`
    15,18,29,31,34,52,53,56,60,69,72,75,89,92,94,96,99,102,126,137,138,142,147,149,185,199,202,204 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/i18n/uk.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L15: // kilocode_change start / L18: // kilocode_change end | +| `packages/ui/src/i18n/zh.ts`
    111,122,127,163,179,181 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/i18n/zh.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L111: // kilocode_change start / L122: // kilocode_change end | +| `packages/ui/src/i18n/zht.ts`
    111,122,127,163,179,181 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/i18n/zht.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L111: // kilocode_change start / L122: // kilocode_change end | +| `packages/ui/src/pierre/index.ts`
    4,156 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/pierre/index.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L4: import { KILO_DIFF_THEME } from "./kilo-diff-theme" // kilocode_change / L156: theme: KILO_DIFF_THEME, // kilocode_change | +| `packages/ui/src/pierre/kilo-diff-theme.ts`
    1 | Kilo-owned inherited marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/pierre/kilo-diff-theme.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L1: // kilocode_change - new file | +| `packages/ui/src/pierre/worker.ts`
    3,5,11,31 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/pierre/worker.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L3: import { ensureKiloDiffTheme, KILO_DIFF_THEME } from "./kilo-diff-theme" // kilocode_change / L5: // kilocode_change start: register the "Kilo" theme as a precondition of creating | +| `packages/ui/src/styles/index.css`
    37 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/styles/index.css` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L37: @import "../kilocode/markdown-mermaid.css" layer(components); /* kilocode_change */ | +| `packages/ui/src/styles/theme.css`
    135,395 | shared-upstream patch marker | User-interface behavior | already ported | `packages/kilo-ide-ui/src/styles/theme.css` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L135: --surface-critical-base: #feefeb; /* kilocode_change */ / L395: --surface-critical-base: #42120b; /* kilocode_change */ | +| `packages/ui/vite.config.ts`
    39,43 | shared-upstream patch marker | Configuration/settings | already ported | `packages/kilo-ide-ui/vite.config.ts` (Kilo-owned; marker removed by design) | Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. Evidence: L39: if (!process.env.KILO_FETCH_PROVIDER_ICONS) return // kilocode_change / L43: if (!process.env.KILO_FETCH_PROVIDER_ICONS) return // kilocode_change | +| `script/changelog-github.cjs`
    1 | shared-upstream patch marker | CI/repository tooling | deferred | script/changelog-github.cjs with the Kilo release/changelog lane (this repo does not use Changesets per AGENTS.md) | V1 Kilo-owned changelog generator wraps @changesets/changelog-github and strips 'Thanks @user!' for team members. V2 has no changesets (AGENTS.md: 'This repository does not use Changesets') and generates release notes through script/changelog.ts + raw-changelog.ts instead, so the Changesets wrapper has no consumer unless Kilo re-adopts Changesets for the v2 line. Evidence: L1: // kilocode_change - new file | +| `script/changelog.ts`
    51 | shared-upstream patch marker | CI/repository tooling | needs port | script/changelog.ts:51 (`["opencode", "run"]` → Kilo CLI) with the Kilo release lane | V1 runs the changelog command through the `kilo` CLI (L51). V2 changelog.ts invokes `opencode run`; in a Kilo v2 release the notes generator would need the kilo2 binary/command name. Evidence: L51: const cmd = ["kilo", "run"] // kilocode_change - opencode -> kilo | +| `script/check-architecture.ts`
    2 | shared-upstream patch marker | CI/repository tooling | deferred | script/check-architecture.ts with the Kilo CI lane; the v2 dependency-direction rules it would encode are documented in AGENTS.md ('Keep runtime dependencies directed from Schema to Core and Protocol…') and partly tested by packages/client/test/import-boundaries.test.ts | V1 Kilo-owned CI script enforces domain architecture boundaries and ratchets. V2 has no such script; the dependency-direction invariant is stated in AGENTS.md and enforced for the client package by its import-boundaries test only. Repository-wide ratchets would need a v2-specific rule set. Evidence: L2: // kilocode_change - new file | +| `script/check-forbidden-strings.ts`
    2 | shared-upstream patch marker | CI/repository tooling | deferred | script/check-forbidden-strings.ts with the Kilo CI lane | V1 Kilo-owned guard fails CI when forbidden strings (competitor branding, leaked identifiers) appear in Kilo-facing sources. V2 has no equivalent; the fork intentionally keeps upstream branding in upstream files (kilocode/v2-fork-conventions.md), so the rule set would need to be scoped to Kilo-owned paths. Evidence: L2: // kilocode_change - new file | +| `script/check-kilo-generated-artifacts.ts`
    2 | authored tooling/check | CI/repository tooling | deferred | script/check-kilo-generated-artifacts.ts with the Kilo CI lane; upstream `check:generated` covers client/www only | V1 Kilo-owned script verifies committed Kilo generated artifacts. V2 relies on manual `bun run check:generated` in packages/client and packages/www (pinned-v2-baseline.md:97-98); Kilo-owned generated outputs (kilo-vscode/kilo-ui) have no check. Evidence: L2: // kilocode_change - new file | +| `script/check-model-tool-network.ts`
    2,109 | shared-upstream patch marker | Provider/model routing or UI | deferred | script/check-model-tool-network.ts with the Kilo CI lane, rewritten against the v2 tool registry (packages/core/src/tool/runtime.ts) and sandbox (packages/kilo-cli/src/sandbox*.ts) | V1 Kilo-owned CI-only architecture test asserts model tools route network access through the sandbox network layer and that the registry wires ToolNetwork.httpLayer (L107-111 marker tracks the v1.17.13 LayerNode change). V2 has a different tool registry and a launch-scoped sandbox (MCP spawn/PTY only), so the assertions would have to be rewritten; the invariant belongs to the sandbox slice already marked partial. Evidence: L2: // kilocode_change - new file / L109: // kilocode_change - v1.17.13 moved registry wiring from Layer.provide onto the LayerNode graph | +| `script/check-opencode-annotations.ts`
    5,13,14,16,137,142,192,203,205,206,216,218,228,234,270,272,285,301,304,306,309,310,312,315,316,318,321 | shared-upstream patch marker | CI/repository tooling | deferred | script/check-opencode-annotations.ts (marker coverage checker) with the Kilo CI lane; scopes must be updated for v2 (no packages/opencode; Kilo-owned paths per kilocode/v2-fork-conventions.md are exempt) | V1 Kilo-owned checker verifies every Kilo change in shared upstream files carries a `kilocode_change` marker (27 marker-string occurrences are its own rules/exempt list, L23-61). V2 documents the same marker convention (v2-fork-conventions.md '`kilocode_change` markers') but has no automated enforcement. Deferred with CI; scope/exempt lists need a v2 rewrite. Evidence: L5: * is annotated with a kilocode_change marker. / L13: * - contains a kilocode_change marker comment (inline annotation) | +| `script/check-opencode-promise-facades.ts`
    2 | shared-upstream patch marker | CI/repository tooling | deferred | script/check-opencode-promise-facades.ts with the Kilo CI lane (v2 sdk exposes promise facades in packages/sdk/src/promise.ts) | V1 Kilo-owned allowlist check for Effect→Promise facades. V2 has no such check; whether the facade allowlist is still meaningful depends on the v2 SDK design (promise.ts is upstream's facade layer). Evidence: L2: // kilocode_change - new file | +| `script/check-workflows.ts`
    2 | shared-upstream patch marker | CI/repository tooling | deferred | script/check-workflows.ts with the Kilo CI lane | V1 Kilo-owned workflow allowlist check (which GitHub workflows may exist/run). V2 has none; relevant only when the Kilo v2 repository curates its workflow set (see .github rows). Evidence: L2: // kilocode_change - new file | +| `script/extract-source-links.ts`
    47,52,89,96,104,110,114,127,139 | shared-upstream patch marker | CI/repository tooling | deferred | script/extract-source-links.ts with the docs/link-check lane (.github/workflows/source-check-links.yml row) | V1 markers add Kilo API/Azure exclusion patterns, skip the forbidden-strings data file, the unregistered v1 account command and non-English i18n files, and normalize source paths (L47,52,89,96,104,110-139). V2 has no link extractor; deferred with the docs lane. Evidence: L47: /^https?:\/\/supermassive-black-hole\.kiloapps\.io\/v1\/session-export\//, // kilocode_change / L52: /^https?:\/\/[^/]+\.openai\.azure\.com\/openai/, // kilocode_change | +| `script/jetbrains-release-pr.ts`
    2 | shared-upstream patch marker | CI/repository tooling | deferred | script/jetbrains-release-pr.ts with the JetBrains lane | V1 Kilo-owned script opens JetBrains release PRs. No v2 JetBrains package. Evidence: L2: // kilocode_change - new file | +| `script/jetbrains-release-validate.ts`
    2 | shared-upstream patch marker | CI/repository tooling | deferred | script/jetbrains-release-validate.ts with the JetBrains lane | V1 Kilo-owned JetBrains release validation. No v2 JetBrains package. Evidence: L2: // kilocode_change - new file | +| `script/publish-start.ts`
    40,41,92,95 | shared-upstream patch marker | CI/repository tooling | needs port | script/publish.ts / script/release (v2 publish lane) — KILO_SKIP_NOTES handling and draft-flag control for Kilo stable publishes | V1 markers honor `KILO_SKIP_NOTES=1` to skip changelog generation and drop the draft flag for the stable publish workflow that lacks a publish-complete step (L40-43,92-95). V2 has no publish-start.ts (publish orchestration lives in script/publish.ts and script/release with upstream semantics). Kilo's stable-release workflow relied on this switch; it must be re-expressed in the Kilo v2 publish lane. Evidence: L40: const skipNotes = process.env["KILO_SKIP_NOTES"] === "1" // kilocode_change / L41: if (skipNotes) console.log("changelog skipped: KILO_SKIP_NOTES=1") // kilocode_change | +| `script/publish.ts`
    9,12,14,41,50,56,74,101,103,111,117,125,127,130,132,137,142,144,146,174 | shared-upstream patch marker | CI/repository tooling | needs port | script/publish.ts (v2 upstream publish orchestrator: versions all packages, pushes to upstream branches, publishes @opencode-ai packages, desktop finalize) — a Kilo v2 publish must replace it with Kilo package set (kilo-cli/kilo-vscode/…), rebase+retry push to the Kilo default branch, channel-aware release notes, and skip upstream preview/ui/desktop steps | V1 markers (20): preserve the JetBrains CLI pin across version bumps and open a follow-up pin-bump PR (L9-12,50-56,142-143), consume changesets on the publish runner and fix changelog headings (L14-41), commit/tag/push with rebase+retry against `main` (L74-101), publish channel-aware GitHub release notes via kilocode/release-notes (L103-111), skip upstream preview CLI and @opencode-ai/ui publishing (L117,125), publish the Kilo VS Code extension (L127-130) and skip desktop finalize (L132-137). V2 script/publish.ts is upstream's orchestrator (publishes opencode/desktop/ui packages); none of the Kilo release invariants exist and running it from a Kilo fork would attempt upstream publishes. The changesets step is not applicable (v2 line has no Changesets) and JetBrains steps follow that lane; the remaining Kilo publish behavior belongs to the needs-port release lane together with .github/workflows/publish.yml and packages/script/src/index.ts. Evidence: L9: // kilocode_change start - keep JetBrains CLI pin reviewable outside CLI release commits / L12: // kilocode_change end | +| `script/raw-changelog.ts`
    34,75,121 | shared-upstream patch marker | CI/repository tooling | needs port | script/raw-changelog.ts:34,77,123 (section order, priority, scanned paths) for the Kilo release lane | V1 markers set the changelog sections to Core/TUI/SDK/Extensions, prioritize core/tui/sdk/plugin/extensions/vscode/github, and scan packages/opencode, sdk, plugin, extensions, github (L34,75,121). V2 raw-changelog.ts adds Desktop/app/tauri sections and scans upstream paths (packages/desktop, packages/app, sdks/vscode). A Kilo v2 changelog must scan Kilo-owned packages (kilo-cli, kilo-vscode, kilo-ui, kilo-gateway, …) and drop upstream desktop sections; the current script would produce an OpenCode-shaped changelog. Evidence: L34: const order = ["Core", "TUI", "SDK", "Extensions"] as const // kilocode_change / L75: const priority = ["core", "tui", "sdk", "plugin", "extensions/vscode", "github"] // kilocode_change | +| `script/setup-git.ts`
    2 | shared-upstream patch marker | CI/repository tooling | deferred | script/setup-git.ts with the Kilo CI lane (bot git identity; see .github/workflows/test.yml row) | V1 Kilo-owned helper configures the kilo-maintainer bot git identity for CI. V2 workflows configure identity inline or via setup-git-committer; no Kilo identity exists in v2 CI. Evidence: L2: // kilocode_change - new file | +| `script/sync-versions.ts`
    2 | shared-upstream patch marker | CI/repository tooling | deferred | script/sync-versions.ts with the Kilo release lane | V1 Kilo-owned script syncs every Kilo version string across the monorepo (CLI, VS Code, JetBrains, docs). V2 has no version-sync script; upstream versions packages through packages/script + publish.yml. Needed once Kilo v2 ships multiple versioned Kilo packages together. Evidence: L2: // kilocode_change - new file | +| `script/upstream/README.md`
    38,105,192,208,261,305,370 | documentation/reference | Documentation/agent guidance | deferred | kilocode/v2-fork-conventions.md ('Upstream snapshot tracking', 'Branches') documents the v2 manual process; tooling for the v2 line is not built | V1 Kilo-owned upstream-merge guide (branding transforms, reset candidates, marker rebuild, conflict recommendations; 7 marker-string mentions). V2 replaces the process with pinned-SHA forward merges and Kilo-owned paths, documented in v2-fork-conventions.md, and has no script/upstream tooling or kilocode/script merge helpers (kilocode/script contains only v2-baseline.ts). Tooling for the v2 process is deferred. Evidence: L38: | `fix-kilocode-markers.ts` | Rebuild `kilocode_change` markers for one file against the last merged upstream | / L105: - Remaining conflicts are files with actual code differences (kilocode_change markers) | +| `script/upstream/find-reset-candidates.ts`
    10 | shared-upstream patch marker | CI/repository tooling | deferred | kilocode/script (marker/drift classifier for the v2 merge process) | V1 Kilo-owned classifier finds files whose only local drift is markers/cosmetics against transformed upstream. V2 has no merge tooling; a v2 variant would compare against pinned `upstream/v2` without branding transforms. Useful but unbuilt. Evidence: L10: * - markers-only : only diff is kilocode_change markers wrapping | +| `script/upstream/fix-kilocode-markers.ts`
    3,25,29,65,88 | Kilo-owned inherited marker | CI/repository tooling | deferred | kilocode/script (marker normalizer for the v2 merge process) | V1 Kilo-owned helper rebuilds `kilocode_change` markers for a file against the last merged upstream version. The v2 conventions require preserving/reapplying markers on every upstream merge (v2-fork-conventions.md) but provide no tooling; deferred. Evidence: L3: * Rebuild kilocode_change markers for one file by comparing it with the last / L25: Rebuilds kilocode_change markers by: | +| `script/upstream/merge.ts`
    594,614,623,652,670,690,710,730,750,790,794,804 | shared-upstream patch marker | CI/repository tooling | not needed | kilocode/v2-fork-conventions.md merge-forward process; no branding-transform merge driver for v2 | V1 Kilo-owned merge driver applies branding transforms pre-merge and flags marker-bearing files for manual resolution (12 marker-string mentions, L594-805). The v2 line explicitly does not rebrand upstream files or fork packages ('Prefer new Kilo-owned files over edits to upstream files'; 'Do not fork or rename an upstream package'), so the transform-driven merge is the wrong model for v2; the manual merge-forward process replaces it. Evidence: L594: // These are likely files with kilocode_change markers or actual logic differences. / L614: // Files with kilocode_change markers are flagged for manual resolution instead. | +| `script/upstream/transforms/transform-extensions.ts`
    168,170 | shared-upstream patch marker | CI/repository tooling | not needed | none — v2 does not rebrand upstream extension files | V1 branding transform for extension files, skipping marker-bearing files (L168-171). Obsolete under the v2 no-rebrand convention. Evidence: L168: // If our version has kilocode_change markers, flag for manual resolution / L170: warn(`${file} has kilocode_change markers — skipping auto-transform, needs manual resolution`) | +| `script/upstream/transforms/transform-i18n.ts`
    8,21,139,142,146,158,300,302 | shared-upstream patch marker | CI/repository tooling | not needed | none — v2 keeps upstream i18n; Kilo strings live in packages/kilo-i18n | V1 branding transform for i18n files preserving marked lines (L8-21,139-159,300-303). Obsolete under the v2 no-rebrand convention; Kilo-owned i18n is a separate package. Evidence: L8: * 3. Preserving lines marked with `// kilocode_change` / L21: * - Lines with `// kilocode_change` | +| `script/upstream/transforms/transform-package-json.ts`
    470,472,905,915 | shared-upstream patch marker | CI/repository tooling | not needed | none — v2 does not rename upstream packages (v2-fork-conventions.md 'Do not fork or rename an upstream package') | V1 branding/reconcile transform for package.json files (L470-473,904-916). Obsolete for v2. Evidence: L470: // If our version has kilocode_change markers, flag for manual resolution / L472: warn(`${file} has kilocode_change markers — skipping auto-transform, needs manual resolution`) | +| `script/upstream/transforms/transform-scripts.ts`
    141,143 | shared-upstream patch marker | CI/repository tooling | not needed | none — v2 keeps upstream scripts unbranded | V1 branding transform for shell/TS scripts (L141-144). Obsolete under v2 conventions. Evidence: L141: // If our version has kilocode_change markers, flag for manual resolution / L143: warn(`${file} has kilocode_change markers — skipping auto-transform, needs manual resolution`) | +| `script/upstream/transforms/transform-take-theirs.ts`
    11,149,150,190,192 | shared-upstream patch marker | CI/repository tooling | not needed | none — v2 merges upstream forward without take-theirs rebranding | V1 transform that takes upstream content for predictable files while preserving marked lines (L11,148-151,190-193). Obsolete for v2. Evidence: L11: * - Files without kilocode_change logic blocks / L149: // Skip lines with kilocode_change marker (already customized) | +| `script/upstream/transforms/transform-web.ts`
    177,179 | shared-upstream patch marker | CI/repository tooling | not needed | none — v2 web/docs are upstream-owned; Kilo docs live in packages/kilo-docs | V1 branding transform for web/docs files (L177-180). Obsolete under v2 conventions. Evidence: L177: // If our version has kilocode_change markers, flag for manual resolution / L179: warn(`${file} has kilocode_change markers — skipping auto-transform, needs manual resolution`) | +| `script/upstream/utils/git.ts`
    439,446 | shared-upstream patch marker | CI/repository tooling | not needed | none — helper for the v1 transform merge (`oursHasKilocodeChanges` on stage :2:) | V1 git helpers for the transform-driven merge (L439-447). Obsolete with merge.ts; a v2 marker-detection helper would be part of the deferred kilocode/script tooling if built. Evidence: L439: * Check if the "ours" version of a conflicted file contains kilocode_change markers. / L446: return result.stdout.toString().includes("kilocode_change") | +| `script/upstream/utils/markers.ts`
    3,50,51,52,54,55,56,105,106,239,240,241,242,246,247,248,249,364 | shared-upstream patch marker | CI/repository tooling | deferred | kilocode/script (shared marker regexes/normalizer for the v2 merge process) | V1 Kilo-owned marker helper library (standalone/start/end/new-file regexes, comment-style aware wrapping, L49-57,104-107,239-249,363-365) used by the marker fixer and reset classifier. The v2 convention keeps the same marker grammar but has no tooling; deferred with fix-kilocode-markers/find-reset-candidates. Evidence: L3: * Shared kilocode_change marker helpers used by both the marker fixer and the / L50: /^\s*\/\/\s*kilocode_change\b.*$/, | +| `script/upstream/utils/report.ts`
    107,149,150,153,164,165,168,176,179,187,190,198,201,214,217,227,269 | shared-upstream patch marker | CI/repository tooling | not needed | none — conflict recommendation report for the v1 transform merge | V1 report classifies conflicted files and recommends manual review when markers are present (L107-227,269-270). Obsolete with merge.ts under the v2 merge-forward process. Evidence: L107: * Pass currentContent (our version of the file) to detect kilocode_change markers / L149: // If our version has kilocode_change markers, flag for manual review | +| `script/version.ts`
    9,12,16,23 | shared-upstream patch marker | CI/repository tooling | needs port | script/version.ts:18-23 (v2 creates draft releases with `--target`/notes-file and handles `beta` only) — add the `rc` pre-release channel and Kilo draft/notes split | V1 markers create a draft release with empty notes (notes generated later by publish.ts on the committing runner) and handle both `beta` and `rc` preview channels with `--prerelease` (L9-23). V2 version.ts creates the draft with `--notes-file` and handles only `beta` (L18-23). The `rc` channel is Kilo's VS Code Marketplace pre-release lane (packages/script row) and is absent. Evidence: L9: // kilocode_change start - create draft release; changelog generation and / L12: // kilocode_change end | diff --git a/migration-tracking/marker-audit/v1-kilo-marker-port-assessment.tsv b/migration-tracking/marker-audit/v1-kilo-marker-port-assessment.tsv new file mode 100644 index 000000000000..975e3a327dd2 --- /dev/null +++ b/migration-tracking/marker-audit/v1-kilo-marker-port-assessment.tsv @@ -0,0 +1,817 @@ +v1_file marker_lines marker_occurrences marker_ownership feature_purpose disposition v2_destination_or_proposed_seam reason representative_marker_snippets +.github/actions/setup-bun/action.yml 36,37,39,48,74,82,86,90,92,99,101,105,107 13 shared-upstream patch marker CI/repository tooling partial .github/actions/setup-bun/action.yml (v2, 80 lines): Windows hoisted-linker install native; cache skip, node-gyp headers, frozen-lockfile and Windows retry absent V1 markers: skip the ~1 GB Bun cache on Windows for restore and save (L36-39,105-107), set up Node 24 and node-gyp headers for native dependency builds on non-Windows (L48-74,82-86), install with `--frozen-lockfile` (L93,101), and retry a failed Windows install with streaming disabled (L92-99). V2 action keeps the upstream Windows `--linker hoisted` install (L68-72) and caches on every OS without frozen-lockfile, Node header setup or retry. No Kilo v2 CI lane exists yet (workflows target upstream `dev`/`v2` and the Kilo fork runs no Kilo-specific CI, kilocode/baseline/pinned-v2-baseline.md:92-120), so the Windows-cache/native-build hardening is only needed once Kilo CI runs these actions with Kilo's native dependencies. L36: # Restoring and extracting the ~1 GB cache took 2m23s on Windows, while a # kilocode_change / L37: # fresh install took 1m27s. Keep Windows off this cache until that reverses. # kilocode_change +.github/actions/setup-git-committer/action.yml 4,11,26,29,42,50 6 shared-upstream patch marker CI/repository tooling needs port .github/actions/setup-git-committer/action.yml:3-25 (app-id/private-key inputs) plus the includeIf credential cleanup V1 renamed the GitHub App inputs to `kilo-maintainer-app-id`/`kilo-maintainer-app-secret` (L4-11,26-29) and removed stale `includeIf.gitdir` git-credentials includes before committing (L42-50). V2 action uses `opencode-app-id`/`opencode-app-secret` and has no includeIf cleanup. Any Kilo v2 workflow that commits as the Kilo maintainer bot (release/version/nix-hash bumps) needs the Kilo App inputs; the cleanup guards against leaked credential includes on shared runners. L4: # kilocode_change start / L11: # kilocode_change end +.github/actions/setup-kilo/action.yml 1 1 shared-upstream patch marker CI/repository tooling deferred .github/actions/setup-kilo/action.yml when Kilo v2 CI needs a released Kilo CLI on runners V1 new-file action installs the Kilo CLI on runners for workflows that drive Kilo (docs automation, review). V2 has no Kilo CI lane and the kilo2 CLI is not yet published as an installable release, so there is no consumer; keep with the deferred docs/automation lane. L1: # kilocode_change - new file +.github/actions/setup-linux-sandbox/action.yml 1 1 shared-upstream patch marker Linux sandbox CI setup partial `packages/kilo-cli/script/sandbox-linux-smoke.ts` and Kilo CLI sandbox tests V2 owns Linux Bubblewrap support and a dedicated smoke script, but the pinned reusable GitHub setup action is absent; runtime parity does not by itself port the CI bootstrap seam. L1: # kilocode_change - new file +.github/codeql/codeql-config.yml 1 1 shared-upstream patch marker Configuration/settings deferred .github/codeql/codeql-config.yml with .github/workflows/codeql.yml when Kilo security scanning is enabled for the v2 line V1 new-file CodeQL 'critical-only' configuration used by codeql.yml/codeql-kotlin.yml. V2 has no CodeQL workflow or config; security scanning for the Kilo v2 repository is a CI-lane decision, not a runtime behavior. L1: # kilocode_change - new file +.github/docs-sync/collect.mjs 1 1 shared-upstream patch marker CI/repository tooling deferred Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. L1: // kilocode_change - new file +.github/docs-sync/edit.mjs 1 1 shared-upstream patch marker CI/repository tooling deferred Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. L1: // kilocode_change - new file +.github/docs-sync/extract-json.mjs 1 1 shared-upstream patch marker CI/repository tooling deferred Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. L1: // kilocode_change - new file +.github/docs-sync/filter-worthy.mjs 1 1 shared-upstream patch marker CI/repository tooling deferred Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. L1: // kilocode_change - new file +.github/docs-sync/learn.mjs 1 1 shared-upstream patch marker CI/repository tooling deferred Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. L1: // kilocode_change - new file +.github/docs-sync/lib.mjs 1 1 shared-upstream patch marker CI/repository tooling deferred Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. L1: // kilocode_change - new file +.github/docs-sync/prepare-branch.mjs 1 1 shared-upstream patch marker CI/repository tooling deferred Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. L1: // kilocode_change - new file +.github/docs-sync/redact-stream.mjs 1 1 shared-upstream patch marker CI/repository tooling deferred Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. L1: // kilocode_change - new file +.github/docs-sync/reverts.mjs 1 1 shared-upstream patch marker CI/repository tooling deferred Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. L1: // kilocode_change - new file +.github/docs-sync/selftest.mjs 1 1 shared-upstream patch marker CI/repository tooling deferred Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. L1: // kilocode_change - new file +.github/docs-sync/triage.mjs 1 1 shared-upstream patch marker CI/repository tooling deferred Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. L1: // kilocode_change - new file +.github/docs-sync/upsert-pr.mjs 1 1 shared-upstream patch marker CI/repository tooling deferred Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. L1: // kilocode_change - new file +.github/docs-sync/watermark.mjs 1 1 shared-upstream patch marker CI/repository tooling deferred Restore the same .github/docs-sync/ module plus .github/workflows/docs-sync.yml when Kilo repository automation is enabled for v2 Pinned v1 docs-sync.yml directly invokes this module in the daily Kilo-Org/cloud + Kilo-Org/kilocode docs bot; the automation module, workflow, and packages/kilo-docs tree are all absent from v2. This is repository automation, not a runtime consumer, so no adjacent v2 feature is an equivalent. L1: // kilocode_change - new file +.github/workflows/auto-docs.yml 29,40 2 shared-upstream patch marker CI/repository tooling deferred .github/workflows/auto-docs.yml or the chosen v2 documentation-intake automation Pinned v1 gates a pull_request_target webhook to merged feat PRs in Kilo-Org/kilocode and forwards metadata only; v2 has no workflow or consumer for that intake. It is Kilo repository automation and remains deferred rather than a runtime gap. L29: # kilocode_change start / L40: # kilocode_change end +.github/workflows/beta.yml 17,19,29,32 4 shared-upstream patch marker CI/repository tooling deferred .github/workflows/beta.yml (Kilo beta release trigger) once the Kilo v2 release lane exists V1 markers pin checkout@v6 with `persist-credentials: false` and pass the Kilo maintainer App secrets to setup-git-committer (L17-19,29-32) for the beta release trigger. V2 has no beta.yml; the v2 publish lane is upstream's (`opencode2` artifacts, publish.yml:171). Kilo v2 release automation is not established. L17: uses: actions/checkout@v6 # kilocode_change / L19: persist-credentials: false # kilocode_change +.github/workflows/check-forbidden-strings.yml 1,14 2 shared-upstream patch marker CI/repository tooling deferred .github/workflows/check-forbidden-strings.yml + script/check-forbidden-strings.ts when Kilo v2 CI is set up V1 new-file workflow runs the Kilo forbidden-strings guard on PRs. V2 has neither the workflow nor the script (see script/check-forbidden-strings.ts row); the guard protects Kilo branding/leak invariants and belongs to the deferred Kilo CI lane. L1: # kilocode_change - new file / L14: - uses: actions/checkout@v6 # kilocode_change +.github/workflows/check-kilo-generated-artifacts.yml 1,20 2 authored tooling/check CI/repository tooling deferred .github/workflows/check-kilo-generated-artifacts.yml + script/check-kilo-generated-artifacts.ts; v2 equivalent gate is `bun run check:generated` in packages/client and packages/www (kilocode/baseline/pinned-v2-baseline.md:97-98) V1 Kilo-owned workflow verifies Kilo generated artifacts are committed. V2 has no Kilo CI lane; upstream's generated-client check (`check:generated`) is run manually per the baseline. Kilo-specific generated artifacts (kilo-vscode/kilo-ui outputs) have no v2 CI verification yet. L1: # kilocode_change - new file / L20: - uses: actions/checkout@v6 # kilocode_change +.github/workflows/check-md-table-padding.yml 17 1 shared-upstream patch marker CI/repository tooling deferred .github/workflows/check-md-table-padding.yml + script/check-md-table-padding.ts (kilocode/v2-fork-conventions.md notes the rule is currently upheld by review only) V1 marker pins checkout@v6 in the Kilo markdown-table-padding workflow. V2 has neither workflow nor script; the fork conventions explicitly record that no v2 workflow enforces compact tables yet. Deferred with the Kilo CI lane. L17: - uses: actions/checkout@v6 # kilocode_change +.github/workflows/check-opencode-annotations.yml 19,23,30,40,52 5 shared-upstream patch marker CI/repository tooling deferred .github/workflows/check-opencode-annotations.yml + script/check-opencode-annotations.ts, check-opencode-promise-facades.ts, check-architecture.ts, check-model-tool-network.ts, check-workflows.ts V1 workflow (Kilo-only via `github.repository == 'Kilo-Org/kilocode'`) checks `kilocode_change` annotations in shared upstream files and runs four additional Kilo architecture guards (L19-52). V2 has none of these scripts or the workflow, although the marker convention itself is documented in kilocode/v2-fork-conventions.md. The marker-enforcement invariant is a Kilo CI concern deferred with that lane; see the script/ rows for per-script status. L19: name: Check kilocode_change annotations / L23: - uses: actions/checkout@v6 # kilocode_change +.github/workflows/check-org-member.yml 21 1 shared-upstream patch marker CI/repository tooling not needed none — v2 has no check-org-member workflow; runner choice is a CI-lane detail V1 marker only switches the runner to `blacksmith-2vcpu-ubuntu-2404` (L21). V2 has no such workflow; if one is added for the Kilo v2 repo the runner label is an infrastructure choice, not a behavior to port. L21: runs-on: blacksmith-2vcpu-ubuntu-2404 # kilocode_change +.github/workflows/codeql-kotlin.yml 1 1 shared-upstream patch marker CI/repository tooling deferred .github/workflows/codeql-kotlin.yml when the JetBrains plugin lane (packages/kilo-jetbrains) is restored on v2 V1 new-file CodeQL workflow for the Kotlin JetBrains plugin. V2 has no packages/kilo-jetbrains and no CodeQL; deferred together with the JetBrains lane (see the containers/jetbrains deferral already recorded). L1: # kilocode_change - new file +.github/workflows/codeql.yml 1 1 shared-upstream patch marker CI/repository tooling deferred .github/workflows/codeql.yml when Kilo security scanning is enabled for the v2 line V1 new-file CodeQL workflow (critical-only config). V2 has no security scanning workflow; CI-lane decision. L1: # kilocode_change - new file +.github/workflows/containers.yml 6,19,22,28,30,50,53 7 shared-upstream patch marker CI/repository tooling needs port .github/workflows/containers.yml:5,21 (default branch trigger and lowercase registry owner) plus the Kilo repository guard V1 markers trigger on `main`, guard builds to `Kilo-Org/kilocode` or manual dispatch, lowercase the repository owner for the GHCR registry, and pin the Ubuntu 24.04 tag (L6,19,22-28,50-53). V2 containers.yml triggers on upstream branches and uses `ghcr.io/${{ github.repository_owner }}` unlowercased (L21) — `ghcr.io/Kilo-Org/...` is an invalid image reference, so a Kilo fork run would fail to push. The containers workflow does exist in v2 and would run in a Kilo fork, so this is a concrete gap rather than a deferred lane (consistent with the containers Dockerfile rows already marked needs port). L6: - main # kilocode_change / L19: if: github.event_name == 'workflow_dispatch' || github.repository == 'Kilo-Org/kilocode' # kilocode_change +.github/workflows/disabled/duplicate-issues.yml.disabled 143 1 shared-upstream patch marker CI/repository tooling not needed none — v2 ships an active upstream duplicate-issues.yml; Kilo disabled its own copy V1 kept a disabled copy with a `needs:compliance` recheck tweak (L143). V2 has upstream's live duplicate-issues.yml and no disabled directory; whether to disable issue automation for the Kilo v2 repo is a repository-settings decision, and the marked tweak has no consumer. L143: if: github.event.action == 'edited' && contains(github.event.issue.labels.*.name, 'needs:compliance') # kilocode_change +.github/workflows/disabled/notify-discord.yml.disabled 9,11 2 shared-upstream patch marker CI/repository tooling not needed none — Kilo disabled OpenCode Discord notifications; v2 fork should disable/remove upstream notify-discord.yml at CI setup V1 disabled upstream's Discord notification workflow via `if: false` (L9-11). V2 contains the live upstream notify-discord.yml; a Kilo v2 repository must not notify OpenCode's Discord, but that is achieved by not carrying the upstream workflow (or repository secrets) rather than porting a disabled file. L9: # kilocode_change start - disabled for kilo-cli fork (OpenCode Discord notifications) / L11: # kilocode_change end +.github/workflows/disabled/review.yml.disabled 9,11 2 shared-upstream patch marker CI/repository tooling not needed none — see notify-discord row; upstream review.yml requires OpenCode install/API keys V1 disabled upstream's AI review workflow (L9-11). V2 has upstream's live review.yml; for a Kilo v2 repo it would fail/no-op without OpenCode secrets. Disposition of upstream automation is a CI-setup decision; no Kilo behavior is encoded in the disabled file. L9: # kilocode_change start - disabled for kilo-cli fork (requires OpenCode install and API keys) / L11: # kilocode_change end +.github/workflows/docs-build.yml 16,19 2 shared-upstream patch marker CI/repository tooling deferred .github/workflows/docs-build.yml for packages/kilo-docs when the docs lane is restored (docs-sync/auto-docs rows already deferred) V1 markers only change runner and checkout pin (L16,19) in the Kilo docs build workflow. V2 has no docs-build workflow though packages/kilo-docs exists; belongs with the deferred docs automation lane. L16: runs-on: blacksmith-4vcpu-ubuntu-2404 # kilocode_change / L19: uses: actions/checkout@v6 # kilocode_change +.github/workflows/docs-check-links.yml 20 1 shared-upstream patch marker CI/repository tooling deferred .github/workflows/docs-check-links.yml with the docs lane V1 marker pins checkout@v6 (L20) in the Kilo docs link-check workflow. V2 has no such workflow; deferred with docs automation. L20: - uses: actions/checkout@v6 # kilocode_change +.github/workflows/docs-sync.yml 1 1 shared-upstream patch marker CI/repository tooling deferred .github/workflows/docs-sync.yml with its .github/docs-sync/* modules Pinned v1 owns the complete scheduled/manual docs bot (collect, learn, triage, edit, verify, rolling PR) and invokes every docs-sync module; v2 retains packages/kilo-docs but has neither this workflow nor .github/docs-sync. Deferred repository automation, not implicitly ported. L1: # kilocode_change - new file +.github/workflows/generate.yml 17,18,21,30,33 5 shared-upstream patch marker CI/repository tooling deferred .github/workflows/generate.yml (automated SDK regeneration commits) once the Kilo v2 release lane exists; manual equivalent is `bun run generate` in packages/client V1 markers pin checkout with `persist-credentials: false` and pass Kilo maintainer App secrets (L17-21,30-33). V2 has no generate workflow; regeneration is manual per AGENTS.md. Automated regeneration commits require the Kilo App identity (setup-git-committer row). L17: uses: actions/checkout@v6 # kilocode_change / L18: # kilocode_change start +.github/workflows/kilo-auto-close.yml 1 1 Kilo-owned inherited marker CI/repository tooling deferred .github/workflows/kilo-auto-close.yml when Kilo v2 issue automation is enabled V1 Kilo-owned 366-line workflow auto-closes issues/PRs per Kilo policy. V2 has upstream close-issues.yml/close-prs.yml/compliance-close.yml instead; Kilo's policy automation is a repository-lane decision. L1: # kilocode_change - new file +.github/workflows/nix-hashes.yml 86,99,109,112,119 5 shared-upstream patch marker CI/repository tooling needs port .github/workflows/nix-hashes.yml:105 (setup-git-committer inputs) — pass Kilo maintainer App secrets; action version pins are cosmetic V1 markers pin upload/download-artifact and checkout versions (L86,99,119) and pass the Kilo maintainer App secrets to setup-git-committer (L109-112). V2 nix-hashes.yml exists and calls setup-git-committer with upstream `opencode-app-*` inputs; in a Kilo fork the hash-bump commit would need the Kilo App identity. Follows the setup-git-committer row. L86: uses: actions/upload-artifact@v7 # kilocode_change / L99: uses: actions/checkout@v6 # kilocode_change +.github/workflows/prepare-jetbrains-release.yml 1 1 shared-upstream patch marker CI/repository tooling deferred .github/workflows/prepare-jetbrains-release.yml with the JetBrains lane V1 Kilo-owned JetBrains release-preparation workflow. V2 has no packages/kilo-jetbrains; deferred with that product lane. L1: # kilocode_change - new file +.github/workflows/publish-jetbrains-bundled.yml 1 1 shared-upstream patch marker CI/repository tooling deferred .github/workflows/publish-jetbrains-bundled.yml with the JetBrains lane V1 Kilo-owned bundled JetBrains publish workflow (341 lines). No v2 JetBrains package; deferred. L1: # kilocode_change - new file +.github/workflows/publish-jetbrains.yml 1 1 shared-upstream patch marker CI/repository tooling deferred .github/workflows/publish-jetbrains.yml with the JetBrains lane V1 Kilo-owned JetBrains publish workflow (222 lines). No v2 JetBrains package; deferred. L1: # kilocode_change - new file +.github/workflows/publish.yml 25,31,38,40,47,53,60,83,89,98,110,128,133,234,238,239,240,241,242,261,322,331,335,343,354,364,369,382,389,390,394,395,398,415,419,428,431,433,444,451 40 shared-upstream patch marker CI/repository tooling needs port .github/workflows/publish.yml (v2, 696 lines, publishes upstream `opencode`/`opencode2` artifacts from packages/opencode and packages/cli) — a Kilo v2 release workflow must re-add pre-release input, Kilo App secrets, Zig/bwrap sandbox helper build, tar.zst artifact packing, validate-cli matrices incl. Alpine libstdc++, VSIX build from the packed CLI, and the smoke-test gate V1 markers (40) add: `pre_release` dispatch input for VS Code marketplace + npm rc channel (L25-31), issues/pull-requests write permissions for JetBrains pin-bump PRs (L38,40), Kilo CLI install for version bumps (L53-60), Zig setup for Linux sandbox helpers (L89-98), tar.zst packing of the CLI dist (L110-128) with matching unpack in build-vscode/publish (L343-354,433-444), `validate-cli-unix`/`validate-cli-windows` matrices executing every published binary incl. bwrap license/version checks and Alpine libstdc++ install (L133-322), a pre-publish smoke-test gate (L369-382,389-391), Kilo App secrets (L428-431) and action pins. V2 publish.yml is upstream's release pipeline for `opencode`; none of the Kilo release invariants exist and the artifact names differ (`opencode2`, L171). Because the v2 workflow is live and would publish upstream packages from a Kilo fork, the Kilo release lane must replace it before any Kilo v2 release — hence needs port rather than deferred, although sequencing depends on the packaging plan (kilocode/baseline/packaging-correction-plan.md). L25: # kilocode_change start / L31: # kilocode_change end +.github/workflows/smoke-test.yml 1,50,143 3 shared-upstream patch marker CI/repository tooling deferred .github/workflows/smoke-test.yml (kilo-bench pre-publish smoke) with the Kilo release lane V1 Kilo-owned reusable workflow checks out kilo-bench and smoke-tests released CLI assets (L1,50,143). V2 has no smoke-test workflow and the kilo2 CLI has its own local `service-smoke.ts` (pinned-v2-baseline.md:101-102) but no bench-based release gate. Deferred with publish.yml. L1: # kilocode_change - new file / L50: uses: actions/checkout@v6 # kilocode_change +.github/workflows/source-check-links.yml 19 1 shared-upstream patch marker CI/repository tooling deferred .github/workflows/source-check-links.yml with the docs lane V1 marker pins checkout@v6 (L19) in the Kilo source link-check workflow. V2 has no such workflow. L19: - uses: actions/checkout@v6 # kilocode_change +.github/workflows/test-jetbrains.yml 1 1 shared-upstream patch marker CI/repository tooling deferred .github/workflows/test-jetbrains.yml with the JetBrains lane V1 Kilo-owned JetBrains test workflow. No v2 JetBrains package. L1: # kilocode_change - new file +.github/workflows/test-vscode.yml 24,30,53 3 shared-upstream patch marker CI/repository tooling deferred .github/workflows/test-vscode.yml for packages/kilo-vscode (unit tests + `kilocode_change` marker check) when Kilo v2 CI is set up V1 markers set the runner, pin checkout and run a `kilocode_change` marker check for the VS Code package (L24,30,53-54). V2 has packages/kilo-vscode but no workflow testing it (upstream publish-vscode.yml targets upstream's extension). Deferred with the Kilo CI lane; note the fork conventions now forbid markers in Kilo-owned packages, so the marker check semantics would invert. L24: runs-on: blacksmith-4vcpu-ubuntu-2404 # kilocode_change / L30: uses: actions/checkout@v6 # kilocode_change +.github/workflows/test.yml 6,11,13,24,68,70,73,77,79,84,88,91,92,96,104,106,112,115,118,122,124,125,129,132,133,135,136,137,142,144,149,150,152,157,159,164,167,169,189,191,233,235,237,245,247,259,260,271,283 49 shared-upstream patch marker CI/repository tooling needs port .github/workflows/test.yml (v2, 262 lines): default-branch concurrency (L14 keys on `dev`), unit matrix (L56-119) — a Kilo v2 CI must add Kilo default-branch handling, sandbox helper setup, Kilo bot git identity, junit publishing and required-check aggregation; JetBrains/CLI shard/httpapi jobs follow their lanes V1 markers (49) add: `main` trigger and run-id concurrency for the default branch (L6,11-13), a paths-filter `changes` job that skips general unit tests for isolated product/docs changes (L24-68,70-88), Node 24 setup with Windows retry (L96-112), Linux sandbox helper setup (L118-122), Kilo maintainer bot git identity (L123-129), Turbo cache keys (L131-142), split non-CLI vs sharded CLI test runs with KILO_EXPERIMENTAL_DISABLE_FILEWATCHER/KILO_TEST_PROFILE/KILO_TEST_SHARD (L144-167), junit report publishing/artifacts (L169-189), an HttpApi exerciser job with generated-client check (L191-235), a JetBrains job (L237-245) and `unit-required`/`required` aggregation jobs (L247-283). V2 test.yml is upstream's: keys concurrency on `dev`, runs `GITHUB_ACTIONS=false bun turbo test` (L116-119), has no sandbox setup, Kilo identity, junit publishing, sharding or required aggregation, and its e2e job is skipped on v2 (baseline:116). The v2 workflow exists and would run in a Kilo fork, so Kilo's CI invariants (sandbox helper for sandbox tests, default-branch concurrency, required checks) are concrete gaps; the CLI shard/profile/httpapi jobs depend on v1 test infrastructure that v2 does not have. L6: - main # kilocode_change / L11: # Keep every run on main so cancelled checks do not pollute the default branch # kilocode_change +.github/workflows/typecheck.yml 13,16,21,27,29,102 6 shared-upstream patch marker CI/repository tooling partial .github/workflows/typecheck.yml (v2, 21 lines): `bun typecheck` native; Kilo Console build and JetBrains typecheck/required jobs absent V1 markers set the runner/checkout, exclude the JetBrains package from `bun turbo typecheck` then build Kilo Console (L13-27), add a JetBrains path filter + Gradle typecheck (L29-79) and a `required` aggregation job (L80-102). V2 typecheck.yml runs `bun typecheck` on blacksmith (matching the AGENTS.md command). Preserved: TypeScript typecheck gate; missing: Kilo Console build (no packages/kilo-console in v2), JetBrains typecheck (no package) and the required-status aggregation. L13: runs-on: blacksmith-4vcpu-ubuntu-2404 # kilocode_change / L16: uses: actions/checkout@v6 # kilocode_change +.github/workflows/visual-regression.yml 1,15,61,94,223,258 6 shared-upstream patch marker CI/repository tooling deferred .github/workflows/visual-regression.yml for packages/kilo-ui and packages/kilo-vscode webview stories when Kilo v2 CI is set up V1 Kilo-owned 412-line workflow runs visual regression for kilo-ui and the kilo-vscode webview via setup-bun (L1,15,61,94,223,258). V2 has both packages and the vscode-visual-regression skill/tests but no workflow (upstream storybook.yml targets upstream storybook). Deferred with the Kilo CI lane. L1: # kilocode_change - new file / L15: runs-on: ubuntu-latest # kilocode_change +.github/workflows/watch-opencode-releases.yml 15 1 shared-upstream patch marker CI/repository tooling deferred .github/workflows/watch-opencode-releases.yml — upstream-release watcher for the Kilo fork; v2 line pins `upstream/v2` SHAs manually (kilocode/v2-fork-conventions.md 'Upstream snapshot tracking') V1 marker gates the release watcher to the Kilo repo or manual dispatch with an `OPENCODE_WATCH_ENABLED` variable (L15). V2 has no watcher; the fork conventions describe a manual pinned-SHA adoption process. Automating upstream-release detection for the v2 line is deferred. L15: if: vars.OPENCODE_WATCH_ENABLED != 'false' && (github.event_name == 'workflow_dispatch' || github.repository == 'Kilo-Org/kilocode') # kilocode_change +.husky/pre-push 21,50 2 shared-upstream patch marker Package behavior indicated by path partial .husky/pre-push (v2, 20 lines: bun-version guard + `bun typecheck`); JetBrains split not applicable V1 block (L21-50) splits the pre-push typecheck: always typecheck non-JetBrains packages, and typecheck `@kilocode/kilo-jetbrains` only when files outside .changeset/kilo-vscode/kilo-docs changed (computed from the pushed refs against `/main`). V2's hook keeps the bun-version guard and runs `bun typecheck` for every package. Preserved: typecheck gate on push (the AGENTS.md/baseline-authoritative command). Missing: change-scoped skipping, which only matters once a heavy Kilo package (JetBrains, absent in v2) is added. L21: # kilocode_change start / L50: # kilocode_change end +.kilo/agent/upstream-merge.md 75,125,145,146,149,175,181,185,201,303,307,352,379,380,414 15 documentation/reference Documentation/agent guidance native equivalent AGENTS.md pointer plus kilocode/v2-fork-conventions.md sections Upstream snapshot tracking, Narrow shared hooks, and kilocode_change markers Pinned v1 is merge-agent guidance: inspect marker intent, preserve Kilo behavior, minimize shared diffs, and justify removals. V2's AGENTS.md requires reading kilocode/v2-fork-conventions.md before merges; that document explicitly requires pinned-SHA merge accounting, owned seams, preserved markers, and an explanation for marker removal. The prose is reorganized for v2 rather than copied. L75: - does a `kilocode_change` marker in HEAD encode a bug fix, a feature, or a / L125: `kilocode_change` comments when possible). +.kilo/command/review-upstream-merge.md 15,37,53,81 4 documentation/reference Documentation/agent guidance native equivalent kilocode/v2-fork-conventions.md sections Dev-fix triage lane, Kilo-owned paths, Narrow shared hooks, and kilocode_change markers The four marker occurrences instruct reviewers to detect lost/redundant markers, trace producer-to-consumer behavior, and preserve Kilo tests. V2 conventions retain the behavioral classification, owned-seam trace, preservation/removal rules, and baseline validation; the obsolete v1 command file has no runtime consumer. L15: Review carefully, file by file, whether we accidentally removed any `kilocode_change` marker. / L37: Check whether any merged files now use `kilocode_change` markers without any actual difference to upstream. +.kilo/plans/1779980012659-happy-circuit.md 24 1 documentation/reference Documentation/agent guidance not needed No v2 destination; historical implementation plan only This pinned-v1 file is an authored historical plan, not executable configuration or a runtime/CI consumer. Its marker occurrence only states repository ownership/marker policy for that completed v1 work; v2 has its own active plan and ownership rules in plans/kilo-opencode-v2-issue-13750.md and kilocode/v2-fork-conventions.md. L24: - If shared upstream files must be edited, keep the change narrow and add `kilocode_change` markers where required. +.kilo/plans/1779980572375-proud-river.md 25 1 documentation/reference Documentation/agent guidance not needed No v2 destination; historical implementation plan only This pinned-v1 file is an authored historical plan, not executable configuration or a runtime/CI consumer. Its marker occurrence only states repository ownership/marker policy for that completed v1 work; v2 has its own active plan and ownership rules in plans/kilo-opencode-v2-issue-13750.md and kilocode/v2-fork-conventions.md. L25: - If shared upstream-owned files are involved, use the repository’s `kilocode_change` marker guidance. +.kilo/plans/1779987162764-misty-rocket.md 68 1 documentation/reference Documentation/agent guidance not needed No v2 destination; historical implementation plan only This pinned-v1 file is an authored historical plan, not executable configuration or a runtime/CI consumer. Its marker occurrence only states repository ownership/marker policy for that completed v1 work; v2 has its own active plan and ownership rules in plans/kilo-opencode-v2-issue-13750.md and kilocode/v2-fork-conventions.md. L68: - This change is in `packages/kilo-jetbrains/`, a Kilo-owned package, so `kilocode_change` markers are not needed. +.kilo/plans/1783518847053-prompt-rollback-redo-jetbrains-parity.md 109 1 documentation/reference Documentation/agent guidance not needed No v2 destination; historical implementation plan only This pinned-v1 file is an authored historical plan, not executable configuration or a runtime/CI consumer. Its marker occurrence only states repository ownership/marker policy for that completed v1 work; v2 has its own active plan and ownership rules in plans/kilo-opencode-v2-issue-13750.md and kilocode/v2-fork-conventions.md. L109: - Confirm no `kilocode_change` markers are needed for the VS Code i18n edits (Kilo-owned webview path — none required). +.kilo/plans/1783550396285-jetbrains-rollback-hardening.md 8 1 documentation/reference Documentation/agent guidance not needed No v2 destination; historical implementation plan only This pinned-v1 file is an authored historical plan, not executable configuration or a runtime/CI consumer. Its marker occurrence only states repository ownership/marker policy for that completed v1 work; v2 has its own active plan and ownership rules in plans/kilo-opencode-v2-issue-13750.md and kilocode/v2-fork-conventions.md. L8: so no `kilocode_change` markers are needed. +.kilo/plans/1784057744472-jetbrains-custom-provider-dialog.md 12 1 documentation/reference Documentation/agent guidance not needed No v2 destination; historical implementation plan only This pinned-v1 file is an authored historical plan, not executable configuration or a runtime/CI consumer. Its marker occurrence only states repository ownership/marker policy for that completed v1 work; v2 has its own active plan and ownership rules in plans/kilo-opencode-v2-issue-13750.md and kilocode/v2-fork-conventions.md. L12: All work is in `packages/kilo-jetbrains/frontend/`. No backend, RPC, or DTO changes. `saveCustom` already upserts, and `ProviderSettingsDto.config[id] +.kilo/plans/1784058434633-consolidate-jetbrains-model-pickers.md 19 1 documentation/reference Documentation/agent guidance not needed No v2 destination; historical implementation plan only This pinned-v1 file is an authored historical plan, not executable configuration or a runtime/CI consumer. Its marker occurrence only states repository ownership/marker policy for that completed v1 work; v2 has its own active plan and ownership rules in plans/kilo-opencode-v2-issue-13750.md and kilocode/v2-fork-conventions.md. L19: Kilo-owned (no `kilocode_change` markers). +.kilo/plans/agent-manager-multi-project-sidebar-density.md 10 1 documentation/reference Documentation/agent guidance not needed No v2 destination; historical implementation plan only This pinned-v1 file is an authored historical plan, not executable configuration or a runtime/CI consumer. Its marker occurrence only states repository ownership/marker policy for that completed v1 work; v2 has its own active plan and ownership rules in plans/kilo-opencode-v2-issue-13750.md and kilocode/v2-fork-conventions.md. L10: - Do **not** add `kilocode_change` markers. This package is Kilo-owned and CI fails if you do. +.kilo/plans/jetbrains-mdview-vscode-styling.md 68 1 documentation/reference Documentation/agent guidance not needed No v2 destination; historical implementation plan only This pinned-v1 file is an authored historical plan, not executable configuration or a runtime/CI consumer. Its marker occurrence only states repository ownership/marker policy for that completed v1 work; v2 has its own active plan and ownership rules in plans/kilo-opencode-v2-issue-13750.md and kilocode/v2-fork-conventions.md. L68: - No `kilocode_change` markers are needed for JetBrains or Kilo UI paths. +.kilo/skills/kilocode-merge-minimizer/SKILL.md 3,8,22,74,78,80,86 7 documentation/reference Documentation/agent guidance native equivalent AGENTS.md and kilocode/v2-fork-conventions.md sections Kilo-owned paths, Narrow shared hooks, and kilocode_change markers Pinned v1's skill requires Kilo logic in owned files, narrow hooks in shared files, inline/block markers only around the changed lines, and no markers in Kilo-owned paths. Those same operational invariants are explicit in the v2 conventions consumed through the root AGENTS.md pointer; the legacy repository-local skill itself is unnecessary. L3: description: Use when changing shared upstream-owned files to add Kilo-specific behavior, editing `kilocode_change` markers in shared code, or moving / L8: Use this skill whenever a normal development task touches shared upstream-owned code and includes Kilo-specific behavior, especially for marker cleanu +.opencode/agent/triage.md 4 1 documentation/reference Documentation/agent guidance needs port .opencode/agent/triage.md Kilo-owned model selection for repository triage Pinned v1 explicitly selects kilo/openai/gpt-5-nano for the hidden triage agent. Current v2 selects opencode/gpt-5.4-mini and the same agent invokes github-triage, so the Kilo provider/cost-routing invariant is absent at the actual consumer. L4: model: kilo/openai/gpt-5-nano # kilocode_change +.opencode/tool/github-triage.ts 4,13,52,55 4 documentation/reference Documentation/agent guidance needs port .opencode/tool/github-triage.ts Kilo repository owner/repo and current Kilo team roster Pinned v1 replaces the upstream team table and sends assignments to Kilo-Org/kilocode. Current v2 still executes this tool but targets anomalyco/opencode with the upstream roster, so neither Kilo repository routing nor assignee ownership is preserved; both marked subfeatures must be ported together. L4: // kilocode_change start / L13: // kilocode_change end +AGENTS.md 22,23,187,192,202,204,205,206,207 9 documentation/reference Documentation/agent guidance native equivalent AGENTS.md lines 1-4 and kilocode/v2-fork-conventions.md sections Kilo-owned paths, Narrow shared hooks, and kilocode_change markers The v1 occurrences combine marker-check commands, shared-file minimization, marker syntax, and a cloud-schema reminder tied to removed packages/opencode Config.Info. V2 replaces the shared guidance with a marked root pointer and explicit conventions covering ownership, minimal hooks, syntax, preservation, and removal evidence. The packages/opencode cloud-mirror path is not applicable to v2's Schema/Core layout; checker automation is assessed in its own script/workflow rows. L22: - **kilocode_change check**: `bun run check-kilocode-change` from `packages/kilo-vscode/`. CI runs this — `kilocode_change` is a marker for upstream m / L23: - **opencode annotation check**: `bun run script/check-opencode-annotations.ts --worktree` from repo root when verifying local agent changes. CI runs +CONTRIBUTING.md 110 1 documentation/reference Documentation/agent guidance native equivalent CONTRIBUTING.md instruction to follow AGENTS.md, then AGENTS.md pointer to kilocode/v2-fork-conventions.md Pinned v1's single occurrence says shared packages/opencode edits need narrow kilocode_change markers and Kilo-owned paths do not. V2 has no packages/opencode tree; current CONTRIBUTING.md delegates repository style to AGENTS.md, whose fork header routes contributors to the v2 ownership and marker rules. L110: - When editing shared `packages/opencode/` files, keep Kilo changes small and mark Kilo-only edits with `// kilocode_change` for a single line or `// +REVIEW.md 14,65 2 documentation/reference Documentation/agent guidance partial Add a v2 review checklist only if desired; invariant source is kilocode/v2-fork-conventions.md and baseline checks are in kilocode/baseline/pinned-v2-baseline.md The marker-review half has a native v2 equivalent: conventions require evidence for marker removal and forbid markers in owned paths, while the baseline document owns validation. The second v1 occurrence is a cloud Config.Info mirror reminder tied to removed packages/opencode and is not applicable as written. What is absent is only the standalone reviewer-oriented checklist document, not the ownership invariant or a runtime capability. L14: - `kilocode_change` marker rules — both directions: / L65: When `Config.Info` in `packages/opencode/src/config/config.ts` gains a new `kilocode_change` field, the matching JSON Schema entry must also be added +install 3,10,29,70,227,243,286,298,336,339,344,353,360,365,369,387,389,527,578 19 shared-upstream patch marker Package behavior indicated by path needs port `install` (v2 script hard-codes APP=opencode2, `$HOME/.opencode/bin`, opencode.ai URLs and `@opencode-ai/cli-*` packages; a Kilo v2 installer must re-apply the Kilo branding, `~/.kilo/bin`, `@kilocode/*` package resolution, tree-sitter payload move and logo) V1 markers (19) brand the installer as Kilo: APP=kilo, usage text with kilo.ai URLs (L10-29), `INSTALL_DIR=$HOME/.kilo/bin` (L70), `check_version` against the installed `kilo` binary (L227-243), Kilo temp paths and curl tracing (L286,298), Kilo-branded install messages, moving the `kilo` binary plus the `tree-sitter` directory into place (L336-369), `# kilo` PATH comment (L387-389), and a KILO_UNICODE_LOGO-aware logo/getting-started epilogue (L527-578). V2's `install` is upstream's opencode2 installer (L3,23-25,68,169-183) with none of the Kilo branding or the tree-sitter payload step. A Kilo v2 release needs its own installer; the packaging plan (kilocode/baseline/packaging-correction-plan.md) governs timing, but the shared file as-is would install OpenCode. L3: APP=kilo # kilocode_change / L10: # kilocode_change start - usage text +packages/client/test/import-boundaries.test.ts 15,23,68,72 4 shared-upstream patch marker Package behavior indicated by path native equivalent packages/client/test/import-boundaries.test.ts:7 (`realpathSync(resolve(.../node_modules/effect))`) V1 replaced the effect-path check with a separator-normalized `/node_modules/effect/` substring match to support hoisted dependencies on Windows (L15,23,68-72). V2 resolves the effect package through `realpathSync` of the package-local node_modules and compares with `within`, which handles hoisting/symlinks natively; the Windows separator normalization is not present but `within` operates on resolved absolute paths. L15: expect(dependency(root, "effect")).toEqual([]) // kilocode_change / L23: expect(dependency(network, "effect").length).toBeGreaterThan(0) // kilocode_change +packages/containers/README.md 11 1 documentation/reference Documentation/agent guidance partial packages/containers/README.md alongside a restored packages/containers/jetbrains/Dockerfile The v2 README still documents and is consumed with the same build script/images directory, but its JetBrains image entry is gone. Base/bun-node/rust/tauri/publish documentation remains natively equivalent; only the marked JetBrains image documentation is missing with that deferred image. L11: - `jetbrains`: `bun-node` plus Java 21, JBR font libraries, and pre-cached Gradle and Bun packages +packages/containers/bun-node/Dockerfile 1,3 2 shared-upstream patch marker Package behavior indicated by path needs port packages/containers/bun-node/Dockerfile ARG REGISTRY default Pinned v1 changes the parent-image registry default to ghcr.io/kilo-org. Current v2 builds the same bun-node image but defaults to ghcr.io/anomalyco, so a Kilo build without explicit REGISTRY pulls the wrong organization image; the executable consumer is packages/containers/script/build.ts. L1: # kilocode_change start / L3: # kilocode_change end +packages/containers/jetbrains/Dockerfile 1,52 2 shared-upstream patch marker Package behavior indicated by path deferred packages/containers/jetbrains/Dockerfile when the JetBrains client build lane is restored Pinned v1 defines the complete Java 21/JBR/Gradle/Bun-cache build image and its Kilo registry parent. V2 has no file and its build.ts image list omits jetbrains; this is a coherent deferred JetBrains build capability, not an adjacent container equivalent. L1: # kilocode_change start / L52: # kilocode_change end +packages/containers/publish/Dockerfile 1,3 2 shared-upstream patch marker Package behavior indicated by path needs port packages/containers/publish/Dockerfile ARG REGISTRY default The publish container still exists and is built by v2 build.ts, but its marked Kilo parent registry default reverted from ghcr.io/kilo-org to ghcr.io/anomalyco. Explicit REGISTRY can override it; default Kilo publishing behavior is not preserved. L1: # kilocode_change start / L3: # kilocode_change end +packages/containers/rust/Dockerfile 1,3 2 shared-upstream patch marker Package behavior indicated by path needs port packages/containers/rust/Dockerfile ARG REGISTRY default The rust container still exists and is built by v2 build.ts, but the marked Kilo parent registry default is now ghcr.io/anomalyco. The Rust toolchain body is native-equivalent; only registry ownership remains missing. L1: # kilocode_change start / L3: # kilocode_change end +packages/containers/script/build.ts 10,20,25,31 4 shared-upstream patch marker Package behavior indicated by path partial packages/containers/script/build.ts Kilo registry/builder defaults; JetBrains image remains deferred with its Dockerfile The same v2 build consumer retains the image build loop, but defaults to ghcr.io/anomalyco, names its buildx builder opencode, and omits jetbrains. Thus common image mechanics are native-equivalent while all three marked Kilo subfeatures are absent; JetBrains is deferred and registry/builder identity needs porting. L10: const reg = process.env.REGISTRY ?? "ghcr.io/kilo-org" // kilocode_change / L20: const images = ["base", "bun-node", "jetbrains", "rust", "tauri-linux", "publish"] // kilocode_change +packages/containers/tauri-linux/Dockerfile 1,3 2 shared-upstream patch marker Package behavior indicated by path needs port packages/containers/tauri-linux/Dockerfile ARG REGISTRY default The tauri-linux image still exists and is built by v2 build.ts, but its parent registry default reverted from the marked ghcr.io/kilo-org to ghcr.io/anomalyco. Tauri dependency installation is otherwise retained. L1: # kilocode_change start / L3: # kilocode_change end +packages/core/src/config.ts 9,143,178,182,190,196,198,205 8 shared-upstream patch marker Configuration/settings partial packages/kilo-cli/src/project-config.ts (ProjectConfig.configured) wired through packages/kilo-cli/src/host.ts:30; native packages/core/src/config/discovery.ts stays upstream V1 markers add kilo.json/kilo.jsonc/config.json to the discovered names, walk .kilo/.kilocode directories, honor Flag.KILO_DISABLE_PROJECT_CONFIG, and order global < project files < Kilo config-directory files. V2 leaves upstream discovery untouched (discovery.ts:11 still opencode.json/.opencode) and instead replaces Config.node with the owned ProjectConfig service (project-config.ts:31-32 files kilo.json/kilo.jsonc, directories .kilo/.kilocode; readProjectEntries walks project boundary lowest-to-highest priority incl. agent markdown and skills). Preserved: Kilo file/dir names, ancestor precedence, project-boundary confinement. Different/missing: native global+project discovery is disabled (`Config.configured({global:false, project:false})`), so opencode.json/.opencode compatibility and the native global config directory are not read; the disable flag became the host opt-in `projectConfig` option (commands.ts:199 `--project-config`, interactive-server.ts:140) rather than an env flag; project plugins are explicitly dropped. The three v2 markers in config.ts (L36,322,325) are a different Kilo hook (host-driven refresh), not this invariant. L9: import { Flag } from "./flag/flag" // kilocode_change / L143: const names = ["config.json", "kilo.json", "kilo.jsonc", "opencode.json", "opencode.jsonc"] // kilocode_change +packages/core/src/config/command.ts 6 1 shared-upstream patch marker Configuration/settings needs port packages/schema/src/config/command.ts (template optionality) plus an owned override step in the command plugin or a Kilo-owned config plugin V1 makes `template` optional so kilo.json can partially override an existing command (description/agent/model/variant/subtask) without restating its template. V2 schema packages/schema/src/config/command.ts:8 keeps `template: Schema.String` required, and no Kilo consumer (kilo-cli ProjectConfig decodes through the native Info schema at project-config.ts:113) relaxes it, so a partial command override document fails decoding. Paired with packages/core/src/config/plugin/command.ts. L6: template: Schema.String.pipe(Schema.optional), // kilocode_change - allow partial command overrides +packages/core/src/config/plugin/command.ts 30,55 2 shared-upstream patch marker Configuration/settings needs port packages/core/src/config/plugin/command.ts editor transform or a Kilo-owned plugin applying overrides after native registration V1 block L30-55 applies partial workflow overrides after every command template is registered: template-only updates first, then description/agent/model/variant/subtask onto existing drafts, skipping template-less commands that do not exist. V2 packages/core/src/config/plugin/command.ts:66-72 only `editor.add`s each document command wholesale with a required template; there is no post-registration override pass and no owned plugin calls `ctx.command.transform` for commands (only memory-plugin.ts registers its own commands). Kilo partial override semantics are absent. L30: // kilocode_change start - apply partial workflow overrides after every command template is registered / L55: // kilocode_change end +packages/core/src/config/plugin/reference.ts 25,28 2 shared-upstream patch marker Configuration/settings native equivalent packages/core/src/config/plugin/reference.ts:24 (per-document directory) — no Kilo seam needed V1 L25-28 resolved local reference paths against `location.project.directory` (falling back to `location.directory` only when the project is the filesystem root) so opening a subdirectory did not break repo-relative references. V2 resolves each reference relative to the directory of the config document that declared it (`doc.path ? path.dirname(doc.path) : location.directory`, reference.ts:24) — for repo-root kilo.json documents this yields the project directory, and for nested .kilo documents it is more precise than v1. The v1 behavior of resolving even inline `content` documents against the project directory is the only divergence (falls back to location.directory), which is not a Kilo-specific invariant. L25: // kilocode_change start / L28: // kilocode_change end +packages/core/src/credential.ts 3,4,6,12,20,41,86,109,193,224,226,244,311,333,337,349,354,367,378,383,403,408,423,430,433,440,442,450,451 29 shared-upstream patch marker Package behavior indicated by path partial packages/kilo-cli/src/import-v1-config.ts + packages/kilo-cli/src/credential-import.ts (`kilo2 import-v1 --auth --apply`, commands.ts:216-236) and packages/kilo-cli/src/paths.ts:87 preflight V1 markers: (a) legacy auth.json / account.json / auth-v2.json import with data-migration markers and startup reconciliation of the active imported account (legacyImportLayer L109-224); (b) write-back of credential changes into auth.json for released v1 clients (writeLegacy L289-333, L403); (c) KILO_AUTH_CONTENT process-local injected credentials that bypass storage (L244-283, L337-383); (d) retained `layer` export for Kilo test layers. V2 core credential.ts has no markers. Preserved through an owned seam: one-shot explicit import of v1 auth.json credentials (api keys, kilo device OAuth, openai/github-copilot/xai OAuth) via `kilo2 import-v1` writing through Credential.create; automatic import is deliberately refused when legacy files are found in preview storage (paths.ts:87-89). Missing: startup reconciliation of a released client's later auth.json edits, auth.json write-back for v1 clients (kilo2 uses its own kilo2.db, so shared-store compatibility is intentionally out of scope), and the KILO_AUTH_CONTENT isolated-workspace credential injection (no v2 reference in packages/*/src). L3: import { asc, desc, eq } from "drizzle-orm" // kilocode_change / L4: // kilocode_change start +packages/core/src/cross-spawn-spawner.ts 4,5,6,7,8,255,261,275,280,288,335,345,390,391,393,399,401,414,417,426,429,433,437,444,450,459,476 27 shared-upstream patch marker Package behavior indicated by path partial packages/util/src/cross-spawn-spawner.ts (upstream, unmarked) with Kilo confinement moved to packages/kilo-cli/src/sandbox.ts, sandbox-mcp.ts, sandbox-pty.ts and packages/kilo-cli/src/host.ts:36-47 V1 markers thread every StandardCommand through `@kilocode/sandbox` prepareCommand (L393-426), validate spawn targets (SpawnValidation, L390,402-414), opt selected commands into direct-exit settlement with descendant group cleanup (SpawnExit, L288,335-345,437-444), tap stdio at spawn time because Bun drops buffered stdio on close (L255,261,280), preserve prepared kill options (L450-459), and settle signal termination as 128+signum (L476). V2 util cross-spawn-spawner.ts carries none of these (0 markers; no tap/settle/direct). Preserved partially through owned seams: sandbox confinement for MCP local server spawns (sandbox-mcp.ts wraps argv with a launcher) and PTY refusal (sandbox-pty.ts), matching the earlier 'Sandbox intentionally partial' assessment. Missing: generic shell/tool-command sandbox preparation at the spawner, spawn-target validation, direct-exit settlement/descendant cleanup, stdio tap, kill-option propagation, and 128+signum exit settlement. L4: import { prepareCommand as prepareSandbox } from "@kilocode/sandbox" // kilocode_change / L5: import { tap as tapStdio, tapped } from "./kilocode/stdio-tap" // kilocode_change - Bun drops buffered stdio on close +packages/core/src/database/database.ts 9,10,11,37,44,59,65,68 8 shared-upstream patch marker Package behavior indicated by path partial packages/kilo-cli/src/paths.ts:26 (`kilo2.db` injected via Database options) and paths.ts preflight; no v2 DbPreflight/compat V1 markers: DbPreflight.assertWritable for an actionable read-only-db error with self-heal (L44), `compat(db)` to keep the shared DB usable by released CLIs (L37), kilo-branded `kilo-.db` with fallback to a pre-existing opencode-named DB (L59-65), and lazy path resolution so KILO_DB overrides apply (L68). V2 database.ts is unmarked but takes `Options.path` at layer build time (database.ts:66-79), and the Kilo host injects its own `kilo2.db` inside an isolated profile root (paths.ts:26, host.ts:22) — this natively satisfies branding and late path resolution. Released-CLI compat is not needed because kilo2 never shares v1's database. Missing: the read-only database preflight/self-heal (paths.ts preflight checks protected paths, symlinks and hard links but not writability). L9: import { existsSync } from "fs" // kilocode_change / L10: import { DbPreflight } from "../kilocode/db-preflight" // kilocode_change +packages/core/src/database/migration/20260603040000_session_message_projection_order.ts 11 1 shared-upstream patch marker Session lifecycle/history/UI native equivalent packages/core/src/database/migration/20260603040000_session_message_projection_order.ts:11 V1 marker relaxed `ADD COLUMN seq integer` (nullable) so released Kilo writers lacking `seq` could still insert into the shared table. V2 keeps upstream `seq integer NOT NULL` (L11) and adds a type+seq index. Because kilo2 owns an isolated database that no released v1 writer opens, the nullable relaxation has no consumer; the upstream projection order invariant itself is present natively. L11: yield* tx.run(`ALTER TABLE \`session_message\` ADD COLUMN \`seq\` integer;`) // kilocode_change +packages/core/src/database/migration/20260714141136_session-message-legacy-writer-compat.ts 1 1 shared-upstream patch marker Session lifecycle/history/UI not needed none — kilo2 isolated database (packages/kilo-cli/src/paths.ts:26) V1 new-file migration exists solely to keep `session_message` writable by legacy Kilo writers sharing one database (see v1 database.ts `compat`). V2 has no such migration and no shared-database scenario: the Kilo host uses its own `kilo2.db` under a profile root and refuses legacy stores in preview storage (paths.ts:87). Historical shared-DB compatibility is not a v2 behavior. L1: // kilocode_change - new file +packages/core/src/effect/layer-node.ts 333,361 2 shared-upstream patch marker Package behavior indicated by path not needed packages/util/src/effect/layer-node.ts (upstream) — no Kilo consumer V1 added `LayerNode.suspend` to break circular node dependency chains introduced by Kilo v1 modules (consumers at pinned SHA: packages/opencode/src/kilo-sessions/kilo-sessions.ts:611, packages/opencode/src/kilocode/bootstrap.ts:113, packages/opencode/src/tool/registry.ts:481). V2 util layer-node.ts has no `suspend` export and no Kilo v2 module needs it (Kilo nodes use makeLocationNode/replace in kilo-cli). The helper served v1-only composition; nothing to port unless a future owned node reintroduces a cycle. L333: // kilocode_change start - defer node construction to break circular dependency chains / L361: // kilocode_change end +packages/core/src/event.ts 9,60,103,252,458 5 shared-upstream patch marker Package behavior indicated by path not needed none — packages/core/src/event/* stores current shapes; v1 rows enter only through packages/kilo-cli/src/import-v1-session.ts V1 markers route durable tool `content` through kilocode/event-storage encode/decode (L9,60,103,252,458) so rows persisted by pre-1.17.13 released Kilo readers/writers stay readable in a shared database. V2 core event storage (packages/core/src/event/sql.ts, session/event.ts) has no such codec and no Kilo marker. The compat only matters when v2 reads v1-written rows in the same DB; kilo2 uses an isolated database and imports v1 sessions through an explicit conversion path instead. Paired Kilo-owned module below. L9: import * as EventStorage from "./kilocode/event-storage" // kilocode_change - released tool content shapes / L60: data: Schema.decodeUnknownSync(definition.data)(EventStorage.decode(definition.type, event.data)), // kilocode_change +packages/core/src/filesystem.ts 5,11,35,36,37,77,78,86,87,110,114,115,137 13 shared-upstream patch marker Package behavior indicated by path needs port packages/core/src/filesystem.ts read/list paths or an owned FileSystem replacement node in packages/kilo-cli V1 markers: bounded searches (`MAX_SEARCH_LIMIT = 100`, `SearchLimit` capped schema, L35-37), TOCTOU-hardened read that opens the validated descriptor and compares dev/ino before reading (L86-110), and list() that validates the directory target before and after enumeration via kilocode/search-target (L114-137). V2 filesystem.ts keeps `DEFAULT_SEARCH_LIMIT = 100` but its Glob/Grep inputs accept any PositiveInt limit (L41,50), read() does a stat-then-readFile by path (L96-103), and list() only stats once (L105-108). No Kilo-owned FileSystem override exists in packages/kilo-cli. The Kilo hardening invariants (limit cap, descriptor-bound read, swap detection) are absent. L5: import { Context, Effect, Layer, Option, Schema } from "effect" // kilocode_change / L11: import * as SearchTarget from "./kilocode/search-target" // kilocode_change +packages/core/src/filesystem/search.ts 14,17,34,45,68,71,77,82,95,98,103,106,111,150,168,169,216,219,223,225,232,234,243,246,250,252,261,264,284,288,289 31 shared-upstream patch marker Package behavior indicated by path partial packages/core/src/filesystem/search.ts (ripgrepLayer/fff layer); missing canonical target inspection, spawn-bound validate, realpath result filtering V1 markers confine every glob/grep to the canonical active Location via SearchTarget.inspect (L34-45,68-71,95-98,150-168), pass spawn-bound `validate` (L77,106), defer FFF index creation until first search with scoped release (L169-216), and post-filter FFF results by realpath containment (L235-236,265-266). V2 search.ts (202 lines) keeps a ripgrep layer and an FFF layer chosen by option/platform (L191) with location-scoped indexes, but has no SearchTarget/realpath post-filter or validate hook. Preserved: location-scoped search and FFF availability gating; missing: symlink-escape hardening and swap detection around search execution; FFF laziness was not compared line-by-line. L14: // kilocode_change start / L17: // kilocode_change end +packages/core/src/flag/flag.ts 2,9,30,42,52,56,60,66,70,72,74,80,85,87,89,91,93,95,97,99,101,103,109,113,115,117,123,125,127,129,155,159 32 shared-upstream patch marker Package behavior indicated by path partial Kilo-owned env reads in packages/kilo-cli/src (sandbox.ts, updater.ts, presence-service.ts, interactive-server.ts, daemon-entry.ts, skill-shell.ts) and host options in packages/kilo-cli/src/host.ts; no shared Flag module V1 markers add ~30 KILO_* flags (unstable-channel defaults, KILO_EXPERIMENTAL umbrella, Claude Code/skills opt-outs, EXA, plan mode, scout, parallel, markdown, embedded web UI, channel DB, skip migrations, strict config deps, session switching, FFF win32 default, project-config disable, session retry limit). V2 core has no flag.ts at all (upstream moved flags out) and the Kilo host reads a different, smaller env surface directly (KILO_DB/KILO_CONFIG_DIR as protected paths in paths.ts, KILO_DISABLE_SKILL_SHELL, KILO_SANDBOX_ROOT/KILO_BWRAP_PATH, KILO_TELEMETRY_*, KILO_UPDATE_MANIFEST_URL, KILO_EVENT_SERVICE_URL, KILO_PLATFORM). Preserved in spirit: env-driven Kilo host configuration and project-config gating (now the `--project-config` option). Missing: every v1 flag whose feature exists in v2 but has no env toggle (models fetch/autocompact remain host options `models.fetch`; retry limit, experimental models, LSP download, default plugins, question tool, FFF override have no Kilo toggle), and v1-only feature flags (Claude Code, EXA, scout, plan-mode, parallel, session switcher) are moot until those features are ported. A per-flag consumer inventory should accompany any port. L2: import { InstallationChannel } from "../installation/version" // kilocode_change / L9: // kilocode_change start +packages/core/src/fs-util.ts 2,56,108,123,205 5 shared-upstream patch marker Package behavior indicated by path needs port packages/util/src/fs-util.ts FSUtil layer or a Kilo-owned FSUtil.node replacement in packages/kilo-cli/src/host.ts overrides V1 markers decorate the Effect FileSystem with `@kilocode/sandbox` (decorateFileSystem L56; ensureDirectory for ensureDir/writeJson L108,123) so every file-tool write is sandbox-confined, and export a `defaultLayer` for legacy runtime compatibility (L205). V2 util fs-util.ts has no decoration, and the Kilo sandbox (packages/kilo-cli/src/sandbox*.ts) confines only MCP spawns and PTY; consistent with the earlier sandbox assessment ('all-file-tool decoration' missing). The legacy `defaultLayer` export is not needed (no v1 runtime in v2). L2: import { decorateFileSystem, ensureDirectory } from "@kilocode/sandbox" // kilocode_change / L56: const fs = decorateFileSystem(yield* FileSystem.FileSystem) // kilocode_change +packages/core/src/global.ts 7,8,12,13,26,31,48,49,50,51,52,53,54,57,59 15 shared-upstream patch marker Package behavior indicated by path partial packages/kilo-cli/src/paths.ts layout() + `Global.node.replace(Global.layerWith(input.paths))` in host.ts:27 V1 markers: app name `kilo` (L12), newline-sanitized XDG roots and trimmed home (L13-31 incl. KILO_TEST_HOME), ensureRealDir for every root (L48-54), and macOS Spotlight no-index marking of data/cache/state (L57-59). V2 util global.ts stays `opencode` (L12) and is replaced at runtime by the owned kilo2 layout (paths.ts:6-33, distinct `kilo2/` roots, canonical realpath resolution in preflight). Preserved: Kilo-branded roots and real-directory resolution (preflight `canonical`). Missing: newline sanitization of $HOME/$XDG_* values, the test-home override (v2 uses upstream OPENCODE_TEST_HOME), and Spotlight no-index marking (no `markNoIndex`/metadata_never_index anywhere in v2). L7: import { markNoIndex } from "./kilocode/spotlight" // kilocode_change / L8: import { ensureRealDir } from "./kilocode/global" // kilocode_change +packages/core/src/image.ts 21,33,55 3 shared-upstream patch marker Package behavior indicated by path needs port packages/core/src/image.ts + packages/core/src/image/photon.ts (pixel-limit pre-check) or Kilo-owned image size guard V1 adds `PixelLimitError` (L21-33) surfaced from the resizer contract (L55) so decompression-bomb images are rejected before native decode. V2 image.ts has no PixelLimitError and no dimension check in photon.ts (grep for dimension/MAX_/bomb empty). Paired with packages/core/src/image/photon.ts. L21: // kilocode_change start - report images rejected before native decode allocation / L33: // kilocode_change end +packages/core/src/image/photon.ts 7,8,31,45,48 5 shared-upstream patch marker Package behavior indicated by path needs port packages/core/src/image/photon.ts:31-48 pre-decode dimension check backed by a Kilo-owned image-size helper V1 block L31-45 parses image header dimensions and rejects images over MAX_DIMENSION/MAX_PIXELS before `PhotonImage.new_from_byteslice` allocates native pixels. V2 photon.ts decodes directly with no dimension guard; the v1 helper `kilocode/image-size` has no v2 counterpart. Decompression-bomb protection is absent. L7: import { DecodeError, PixelLimitError, ResizerUnavailableError, SizeError } from "../image" // kilocode_change / L8: import { allowed, dimensions, MAX_DIMENSION, MAX_PIXELS } from "../kilocode/image-size" // kilocode_change +packages/core/src/installation/version.ts 4,10,13 3 shared-upstream patch marker Package behavior indicated by path not needed packages/kilo-cli/src/host.ts:22 (`app.channel` from the kilo2 layout) covers release/preview identity V1 adds `KILO_BUILD_KIND` and `InstallationBuildKind` (source vs release) for v1 CLI build-time distinction. V2 core has no installation/version.ts and no BUILD_KIND reference; the Kilo host identifies itself via `identity`/`channel` in the layout (paths.ts:6-7) passed as `app.channel`. No v2 consumer needs a separate build-kind flag; revisit only if release packaging introduces one. L4: const KILO_BUILD_KIND: string // kilocode_change / L10: // kilocode_change start - distinguish release builds from source / local builds +packages/core/src/integration.ts 201,207,324,348,381,389,472,482,488,529,541,553,558 13 shared-upstream patch marker Package behavior indicated by path native equivalent packages/core/src/integration.ts:380-433 (`persisting` flag, uninterruptible settle) V1 markers add a `settling` flag, a 30s settlement timeout and `Effect.uninterruptibleMask` so OAuth credential persistence is atomic with cancel/expiry and an interrupted callback still settles (L201,207,324-381,389,472,482-488,529,541-553,558). Upstream v2 now implements the same invariant natively: `settle` is `Effect.uninterruptible`, marks the attempt `persisting` so cancel/expiry/claim cannot overtake persistence ('Persisting attempts cannot be cancelled, expired, or claimed again', integration.ts:427), and terminal state is written after createCredential resolves. The explicit 30s timeout is not present, but the atomicity and persist-before-expose invariants are. L201: const settlementTimeout = Duration.seconds(30) // kilocode_change - bound retained OAuth attempt secrets / L207: settling: boolean // kilocode_change - cancellation and expiry cannot overtake credential persistence +packages/core/src/kilocode/event-storage.ts 1,30 2 Kilo-owned inherited marker Package behavior indicated by path not needed none (see packages/core/src/event.ts) Kilo-owned v1 module implementing the released tool-content shape codec (`{type:"media"}`, `{type:"file",source}`) for shared-database compatibility. V2 has no packages/core/src/kilocode directory and no consumer reading released v1 durable rows in place; the isolated kilo2.db plus explicit `import-v1` flow make the in-place codec unnecessary. L1: // kilocode_change start - released readers persist tool content in the pre-1.17.13 shapes / L30: // kilocode_change end +packages/core/src/models-dev.ts 11,48,64,73,96,102,131,227,232,253 10 shared-upstream patch marker Provider/model routing or UI partial packages/core/src/models-dev.ts:42,192 (native reasoning_options); packages/schema/src/kilocode/models.ts + packages/kilo-gateway/src/models.ts (Kilo catalog metadata); no v2 cache re-check/refresh notify V1 markers: (a) models.dev `reasoning_options` schema snatched from upstream (L48-73); (b) Kilo catalog metadata fields recommendedIndex/prompt/isFree/mayTrainOnYourPrompts/ai_sdk_provider and provider `description` (L96-102,131); (c) re-read the disk cache under the lock before refetching to avoid duplicate network calls (L227-232); (d) ModelsRefresh.notify after refresh (L253). V2: (a) is native — `reasoning_options` is a first-class SourceModel field consumed at models-dev.ts:192/289. (b) is relocated: Kilo model metadata lives in the owned Gateway schema (schema/src/kilocode/models.ts:8-17 recommendedIndex, mayTrainOnYourPrompts, ...) consumed by packages/kilo-gateway, so the shared models.dev schema no longer needs those fields. Missing: (c) the under-lock cache re-check (v2 models-dev.ts has no rechecked/loadFromDisk path) and (d) the Kilo refresh notification hook (no ModelsRefresh in v2); these are minor robustness/notification invariants without a v2 consumer. L11: import * as ModelsRefresh from "./kilocode/models-refresh" // kilocode_change / L48: // kilocode_change start - models.dev reasoning_options (snatched from upstream +packages/core/src/plugin/internal.ts 112 1 shared-upstream patch marker Package behavior indicated by path partial packages/kilo-cli/src/skill-policy.ts (adds `kilo-config`, KILO_CONFIG_ID L10, transform L40-46); packages/core/src/plugin/skill.ts:28-36 still registers builtin `opencode` V1 marker skips registering upstream's redundant `opencode` skill because Kilo's registry supplies `kilo-config`. V2 preserves the Kilo half: the owned skill-policy plugin registers/updates the `kilo-config` builtin skill. Missing: the native `opencode.skill` plugin still adds the builtin `opencode` skill (skill.ts:28-36) and skill-policy.ts only removes project skills escaping the project realpath (L47-55), so agents see both `opencode` and `kilo-config` skills. L112: // kilocode_change - Kilo's CLI registry supplies `kilo-config`; do not register the redundant opencode skill. +packages/core/src/plugin/provider/kilo.ts 1,3,6,14,16,26,28,33,38,45 10 shared-upstream patch marker Provider/model routing or UI native equivalent `packages/kilo-gateway/src/plugin.ts`, `models.ts`, and Kilo routed-model host plugin V1 shared provider hook is superseded by location/account-scoped Gateway catalog transforms, request isolation, routed protocols, and credential-event refresh; current Core Kilo plugin retains only upstream-compatible header behavior. L1: import { createKilo, KILO_OPENROUTER_BASE } from "@kilocode/kilo-gateway" // kilocode_change / L3: import { ProviderV2 } from "../../provider" // kilocode_change +packages/core/src/plugin/provider/llmgateway.ts 4,17,21,24 4 shared-upstream patch marker Provider/model routing or UI needs port packages/kilo-gateway or a Kilo-owned catalog transform overriding attribution headers for llmgateway/nvidia/openrouter/vercel/zenmux V1 sets Kilo attribution headers (HTTP-Referer https://kilo.ai/, X-Title 'Kilo Code', X-Source 'kilo') and guards on the `llmgateway` provider ID. V2 llmgateway.ts:21-23 sends https://opencode.ai/ / 'opencode' / 'opencode' and no Kilo-owned transform rewrites them (grep for kilo.ai/X-Title in kilo-gateway and kilo-cli returns none; the Gateway plugin only manages the `kilo` provider's baseURL/organization header, plugin.ts:352-366). Kilo attribution is absent at the executing consumer. L4: import { ProviderV2 } from "../../provider" // kilocode_change / L17: if (item.provider.id !== ProviderV2.ID.make("llmgateway")) continue // kilocode_change +packages/core/src/plugin/provider/nvidia.ts 3,14,16,17,20 5 shared-upstream patch marker Provider/model routing or UI needs port same owned attribution transform as llmgateway.ts V1 sends Kilo attribution (HTTP-Referer kilo.ai, X-Title 'Kilo Code') and defaults X-BILLING-INVOKE-ORIGIN to 'KiloCode'. V2 nvidia.ts:16-18 sends opencode.ai / 'opencode' / 'OpenCode'. No Kilo consumer overrides these headers, so NVIDIA billing attribution reports OpenCode for Kilo users. L3: import { ProviderV2 } from "../../provider" // kilocode_change / L14: if (item.provider.id !== ProviderV2.ID.make("nvidia")) continue // kilocode_change +packages/core/src/plugin/provider/openai.ts 9,58,63,70,78,84,200,279 8 shared-upstream patch marker Provider/model routing or UI partial packages/core/src/plugin/provider/openai.ts:75-90 (native state check, callback page); missing Kilo-branded originator/User-Agent V1 markers: reject callback requests whose `state` mismatches without terminating the active attempt (L58-63), Kilo-branded OAuth callback pages (L70,78,84), `kilo/` User-Agent (L200), and `originator: "kilo"` in the ChatGPT authorize URL (L279). V2 natively validates state (openai.ts:79-84 'Invalid OAuth state') and returns 404 for unrelated paths (L67), using the upstream OauthCallbackPage. Missing: Kilo-branded callback page, `kilo/` User-Agent header, and `originator: kilo` — the ChatGPT flow identifies as opencode. L9: import { KiloOauthCallbackPage } from "../../kilocode/oauth/page" // kilocode_change / L58: // kilocode_change start - unrelated localhost requests must not terminate the active OAuth attempt +packages/core/src/plugin/provider/opencode.ts 5,44 2 shared-upstream patch marker Provider/model routing or UI needs port packages/kilo-gateway/src/plugin.ts (Zen suppression, L136-147,384) plus a Kilo decision on removing the OpenCode Console auth methods from the native opencode plugin V1 replaced the whole plugin with only the free-tier catalog gate: no OpenCode Console OAuth device flow (client id opencode-cli), no 'OpenCode Console account'/'service account' methods, no console-driven remote config; paid zen models disabled unless a key is present and the provider marked `public` otherwise (L5-44, with an explicit 'do not restore without product decision'). V2 opencode.ts (343 lines) is upstream's full plugin: clientID 'opencode-cli' (L14), OAuth method registration (L34-110), `public` apiKey and paid-model disabling (L194-201). The Kilo Gateway plugin suppresses the uncredentialed Zen catalog (plugin.ts:384 suppressUncredentialedZen) but does not remove the competitor sign-in methods, so the v1 product invariant (no OpenCode Console account system offered) is not met. L5: // kilocode_change start - Kilo keeps only the free-tier catalog gate from upstream's opencode plugin. / L44: // kilocode_change end +packages/core/src/plugin/provider/openrouter.ts 4,14,16,17 4 shared-upstream patch marker Provider/model routing or UI needs port same owned attribution transform as llmgateway.ts V1 sends HTTP-Referer https://kilo.ai/ and X-Title 'Kilo Code' for the `openrouter` provider. V2 openrouter.ts:14 sends https://opencode.ai/ and 'opencode'; no Kilo override exists. L4: import { ProviderV2 } from "../../provider" // kilocode_change / L14: if (item.provider.id !== ProviderV2.ID.openrouter) continue // kilocode_change +packages/core/src/plugin/provider/vercel.ts 3,13,15,16 4 shared-upstream patch marker Provider/model routing or UI needs port same owned attribution transform as llmgateway.ts V1 sets lowercase http-referer https://kilo.ai/ and x-title 'Kilo Code' for the `vercel` provider. V2 vercel.ts:13 sends opencode.ai/'opencode'; no Kilo override exists. L3: import { ProviderV2 } from "../../provider" // kilocode_change / L13: if (item.provider.id !== ProviderV2.ID.make("vercel")) continue // kilocode_change +packages/core/src/plugin/provider/zenmux.ts 3,14,16,17 4 shared-upstream patch marker Provider/model routing or UI needs port same owned attribution transform as llmgateway.ts V1 defaults HTTP-Referer to https://kilo.ai/ and X-Title to 'Kilo Code' (with `??=` so user headers win). V2 zenmux.ts:15-16 sends opencode.ai/'opencode'; no Kilo override exists. L3: import { ProviderV2 } from "../../provider" // kilocode_change / L14: if (item.provider.id !== ProviderV2.ID.make("zenmux")) continue // kilocode_change +packages/core/src/project.ts 66,125 2 shared-upstream patch marker Package behavior indicated by path needs port packages/core/src/project.ts:237 (cached id file name) — Kilo-owned project id file name or accept `opencode` file V1 stores and reads the cached project ID in `/kilo` (L66,125) so Kilo and OpenCode installs do not share/clobber each other's project identity file. V2 project.ts:237 reads `/opencode` and there is no Kilo override (the kilo2 host only replaces Global/Config/InstructionDiscovery/Mcp/Pty nodes, host.ts:27-47). Because the id file lives inside the location's git store, a v2 Kilo install writes/reads the OpenCode file name — a behavior difference (shared identity with an OpenCode install) rather than a crash; port only if separate identity is still required. L66: return yield* fs.readFileString(path.join(dir, "kilo")).pipe( // kilocode_change / L125: yield* fs.writeFileString(path.join(input.store, "kilo"), input.id).pipe(Effect.ignore) // kilocode_change +packages/core/src/provider.ts 6 1 shared-upstream patch marker Provider/model routing or UI native equivalent packages/kilo-gateway/src/plugin.ts:339-364 (string `kilo` provider ID) and packages/core/src/plugin/provider/kilo.ts V1 attached a well-known `Provider.ID.kilo` constant without forking the schema. V2 provider.ts has no such constant, but every Kilo consumer addresses the provider by the literal `"kilo"` ID through the owned Gateway plugin (editor.provider.get/update("kilo")), and upstream itself ships a `kilo` provider plugin keyed on the Gateway baseURL. The routing identity is present; only the convenience constant is gone. L6: // kilocode_change - preserve Kilo's well-known routing ID without forking the shared schema +packages/core/src/pty.ts 11,14,15,40,43,54,59,71,129,137,141,147,157,168,186,197,203,205,209,217,227,245,269,294,296,309,318 27 shared-upstream patch marker Package behavior indicated by path needs port packages/core/src/pty.ts create/teardown/attach or a Kilo-owned Pty.node replacement (packages/kilo-cli/src/sandbox-pty.ts shows the replacement pattern) V1 markers: terminate the full PTY process tree on teardown and await it during service teardown (L129-147), uninterruptible remove+deleted event (L157-168), resolve Kilo self-commands to the real binary/cwd (L186-197), KILO_PTY_ID env for nested processes (L203), tombstone KILO_SERVER_PASSWORD/USERNAME so terminals never inherit local server credentials (L205-209), spawn with initial cols/rows (L217-227), `stopping` guard against double exit (L40,245,269), `sessionID` association on update (L54,294-296), and `allowExited` attach with replay (L71,309,318). V2 pty.ts is upstream: teardown calls `process.kill()` only (L117-126), no uninterruptible removal, no self-command resolution, env sets OPENCODE_TERMINAL only (L163-169), spawn without initial size (L176), no sessionID/allowExited. The only Kilo Pty seam in v2 is sandbox-pty.ts (refusal). Credential-tombstone and tree-termination invariants are absent. L11: import { SessionSchema } from "./session/schema" // kilocode_change / L14: import { KiloPtySelfCommand } from "./kilocode/pty-self-command" // kilocode_change +packages/core/src/pty/driver.ts 1 1 shared-upstream patch marker Package behavior indicated by path not needed none — v2 core imports `#pty` directly (packages/core/src/pty.ts:4,17) V1 re-exported the conditional PTY driver for Kilo's legacy interactive terminal in packages/opencode. V2 has no legacy interactive terminal consumer; the kilo-cli TUI plugin uses the server PTY API and native-pty.ts only resolves the bundled binary path. No consumer for a driver re-export. L1: // kilocode_change - expose the conditional PTY driver to Kilo's legacy interactive terminal +packages/core/src/reference.ts 38,116,124 3 shared-upstream patch marker Package behavior indicated by path native equivalent packages/core/src/config/plugin/reference.ts (ConfigEntryObserver over Config.Service entries) fed by packages/kilo-cli/src/project-config.ts V1 added `Reference.replace(sources)` so Kilo's effective-config reconciliation (v1 packages/opencode/src/kilocode/reference.ts:190) could rewrite references without a request-scoped transform slot. V2 Kilo config enters through the owned ProjectConfig extension of `Config.Service.entries`, and the native reference plugin observes those entries and reloads via `ctx.reference.reload()`/transform (reference.ts:22-24), so kilo.json references are materialized without a bespoke replace API. L38: readonly replace: (sources: readonly (readonly [string, Source])[]) => Effect.Effect // kilocode_change / L116: // kilocode_change start - reconcile Kilo's effective config without a request-scoped transform slot. +packages/core/src/ripgrep.ts 7,8,9,26,42,71,75,82,87,88,91,97,120,121,126,131,134,136,142,143,148,158,162,163,179,185,195,213,223,260,266,286,294,301,326 35 shared-upstream patch marker Package behavior indicated by path needs port packages/core/src/ripgrep.ts run/glob/grep (SearchResult truncation state, context rows, spawn-bound validation, bounded stop) or a Kilo-owned Ripgrep.node replacement in packages/kilo-cli V1 markers: return `SearchResult{items,truncated,partial}` from glob/grep so model-facing tools see truncation (L87-97,213-223,301-326), retain ripgrep `context` records and decorate matches via kilocode/ripgrep-grep options/flags/select (L26,42,74,260-266,286-294), stop bounded grep at the overflow match with a 1s force-kill and direct-exit settlement (L120,131-134,142-163), bind approved search targets at spawn via SpawnValidation (L71,82,121,126-136,195), and surface the underlying failure reason (L179-185). V2 ripgrep.ts (273 lines, unmarked) returns plain arrays with `rows.length > input.limit` slicing (L140-141), has no context-record support, no stop/forceKill, no validate hook and no Kilo grep option module; no owned Ripgrep replacement exists in packages/kilo-cli. Paired with tool/glob.ts and tool/grep.ts rows. L7: import * as KiloGrep from "./kilocode/ripgrep-grep" // kilocode_change / L8: import * as SpawnExit from "./kilocode/spawn-exit" // kilocode_change +packages/core/src/ripgrep/binary.ts 94 1 shared-upstream patch marker Package behavior indicated by path needs port packages/core/src/ripgrep/binary.ts:98 (skip system `rg` on win32) V1 skipped the system `rg` lookup on Windows because Git for Windows exposes an incompatible MSYS rg.exe, always using the downloaded binary. V2 binary.ts:98 resolves `rg.exe` from PATH/global.bin on win32 and prefers it when present, so the incompatible MSYS binary can be selected again. Small, Windows-only regression. L94: // kilocode_change - Git for Windows may expose an incompatible MSYS rg.exe +packages/core/src/schema.ts 14,17 2 shared-upstream patch marker Package behavior indicated by path not needed none — no v2 consumer of `optionalOmitUndefined`/`withStatics` V1 exported compatibility aliases for Kilo-owned schemas written against older helper names. V2 core schema.ts lacks them and no Kilo v2 package references either alias (repo-wide grep of packages/*/src is empty); Kilo v2 schemas use the native `optional`/`statics` helpers directly. L14: // kilocode_change start - compatibility aliases for Kilo-owned schemas / L17: // kilocode_change end +packages/core/src/session.ts 6,35,40,137,142,196,199,319,335,359 10 shared-upstream patch marker Session lifecycle/history/UI not needed none — kilo2 uses an isolated database; imported v1 rows are rebuilt by packages/core/src/database/migration/20260804233008_loose_psylocke.ts (kilocode/baseline/v1-session-schema-audit.md:65,122) V1 markers exist only for released-data compatibility in a shared store: normalize legacy tool content on paginated reads (L35,197-203), filter `isNotNull(seq)` because Kilo kept `session_message.seq` nullable (L319,335), and widen the durable event stream/history to the released `SessionDurable` key manifest (L40,137,142,196,359). V2 session.ts is unmarked, `seq` is NOT NULL, and the explicit `import-v1` path copies the v1 store and rebuilds `session_message` from `message`/`part` (dropping nullable-seq rows) rather than reading released Kilo rows in place. No v2 consumer needs the compatibility filters. L6: import { and, asc, desc, eq, gt, isNotNull, like, lt, or, type SQL } from "drizzle-orm" // kilocode_change / L35: import { normalize } from "./kilocode/session-message" // kilocode_change +packages/core/src/session/compaction.ts 227 1 shared-upstream patch marker Session lifecycle/history/UI not needed none (released-reader compatibility; see packages/core/src/session.ts) V1 duplicated `recent` into a compatibility `include` field so released v1 readers could parse newer compaction rows in the shared database. V2 compaction.ts writes only the native `recent` field (L260) and no released reader shares kilo2's database. L227: include: selected.recent, // kilocode_change - released v1 readers recognize this compatibility field +packages/core/src/session/context-epoch.ts 13,19,30,40,50,58,90,94,133,135,152 11 shared-upstream patch marker Session lifecycle/history/UI not needed none — v2 removed context epochs (packages/core/src/database/migration/20260622142730_simplify_session_context_epoch.ts, 20260622170816_reset_v2_session_state.ts); Location guard lives in packages/core/src/session/context.ts:123 V1 markers thread the expected `Location.Ref` into epoch initialization and re-check Session placement inside the insert so a concurrent move cannot write an epoch for the source Location (L13-152). V2 has no context-epoch module or table: instruction state is persisted durably per the V2 Session Core rules and `SessionContext.select` interrupts when the Session's stored Location differs from the running Location (context.ts:123). The v1 race (epoch row written for the wrong Location) has no v2 write to protect; a v2-specific move/instruction-state race was not separately proven here. L13: import { Location } from "../location" // kilocode_change / L19: class LocationMismatch extends Error {} // kilocode_change - a concurrent Session move must abort the epoch +packages/core/src/session/history.ts 1,8,22,45,65,109 6 shared-upstream patch marker Session lifecycle/history/UI not needed none (released-data compatibility; see packages/core/src/session.ts) V1 markers add `isNotNull(seq)` filters, legacy tool-content normalization at the row boundary, and a die on legacy rows (L1,8,22,45,65-67,109). V2 history.ts is unmarked with NOT NULL `seq`; nullable/legacy rows never exist in kilo2's isolated database. L1: import { and, asc, desc, eq, gt, gte, isNotNull, ne, or } from "drizzle-orm" // kilocode_change / L8: import { normalize } from "../kilocode/session-message" // kilocode_change +packages/core/src/session/message-updater.ts 378,386 2 shared-upstream patch marker Session lifecycle/history/UI not needed none (released-data compatibility) V1 tolerated released compaction events lacking `messageID`/`reason`/`recent` (L378,386). V2 message-updater.ts consumes only native compaction events; released v1-line durable events are not replayed into kilo2 (import rebuilds projections from message/part, kilocode/baseline/v1-session-schema-audit.md:83-84). L378: if (event.data.messageID === undefined || event.data.reason === undefined) return Effect.void // kilocode_change / L386: recent: event.data.recent ?? "", // kilocode_change - current v1 writes include recent; released rows omit it +packages/core/src/session/projector.ts 3,13,19,24,27,62,120,146,152,173,188,196,397,449 14 shared-upstream patch marker Session lifecycle/history/UI not needed none — released-data compatibility is obsolete in kilo2's isolated store; sharing is a Gateway RPC (packages/kilo-gateway/src/session.ts:132-177), not a projected session column V1 markers: released-shape encode/normalize of stored messages (L13,24-27,120), `isNotNull(seq)` filters (L146-152,173,188-196), nullable-seq revert boundary guard (L449-451), replay of released two-step prompt promotions via PromptCompat (L19,397), and `share_url: info.share?.url ?? null` so full session updates clear removed shares (L62). The compatibility markers are not needed (isolated database, NOT NULL seq, projections rebuilt from message/part on import). The share-clearing marker also has no v2 consumer: `share_url` survives only as an unused column (session/sql.ts:39; no writer in packages/core/src) and Kilo sharing is served by the owned Gateway session RPC rather than a session-row projection. L3: import { and, desc, eq, isNotNull, gt, or, sql } from "drizzle-orm" // kilocode_change / L13: import * as StoredMessage from "../kilocode/session-message" // kilocode_change +packages/core/src/session/revert.ts 11,36,56 3 shared-upstream patch marker Session lifecycle/history/UI not needed none (released-data compatibility) V1 markers guard nullable `seq` boundaries and normalize legacy tool content when collecting snapshot files (L11,36-37,54-56). V2 revert.ts is unmarked over NOT NULL `seq` rows in kilo2's isolated store. L11: import * as StoredMessage from "../kilocode/session-message" // kilocode_change / L36: // kilocode_change - Kilo keeps session_message.seq nullable for released-data compatibility +packages/core/src/session/runner/llm.ts 178,193 2 shared-upstream patch marker Session lifecycle/history/UI not needed none — see packages/core/src/session/context-epoch.ts V1 passed the expected Location into SessionContextEpoch.initialize/prepare (L178,193-194). V2 runner/llm.ts has no context epoch calls; Location scoping is enforced by `SessionContext.select` (context.ts:123). L178: const initialized = yield* SessionContextEpoch.initialize(db, loadSystemContext(agent), session.id, location) // kilocode_change / L193: // kilocode_change - pass the expected Location so a concurrent move aborts the epoch +packages/core/src/session/runner/model.ts 141,150 2 shared-upstream patch marker Session lifecycle/history/UI native equivalent packages/kilo-gateway/src/plugin.ts:40,190-193,352-357 (X-KILOCODE-ORGANIZATIONID header from the stored organization selection) V1 injected the migrated OAuth account id into the request body as `kilocodeOrganizationId` for Kilo Gateway organization routing (L141-150). V2 routes organization identity through the owned Gateway plugin instead: the selected organizationID is read from credential metadata/stored selection and applied as the `X-KILOCODE-ORGANIZATIONID` header on provider, model and variant headers (plugin.ts:352-394). The routing invariant is preserved through a Kilo-owned seam; the body-field mechanism is intentionally different. L141: // kilocode_change start - Kilo Gateway consumes the migrated OAuth account as its organization route. / L150: // kilocode_change end +packages/core/src/session/sql.ts 16,49,100,132 4 shared-upstream patch marker Session lifecycle/history/UI partial packages/core/src/session/sql.ts (part index) paired with the unported historical-recall tool; revert workspace status and nullable seq not needed V1 markers: nullable `seq` for released clients (L132) — not needed in kilo2's isolated store; `revert.workspace` restore status persisted on the session revert record (L49-51) — v2 Session.Revert schema has no workspace field and no Kilo consumer writes one (grep empty), tied to the v1 workspace-restore feature; `RecallPartIndex` on `part` (L16,100) — a search index backing v1's historical session recall tool, which the canonical 'Plan and historical-recall tool slice' already records as needing a port. Preserved: nothing Kilo-specific is required today; missing: the recall index (only meaningful once the recall tool is ported) and revert workspace status. L16: import { RecallPartIndex } from "../kilocode/session/recall-part-index" // kilocode_change / L49: // kilocode_change - Kilo also persists a workspace restore status on the revert record +packages/core/src/session/store.ts 3,13,50,56 4 shared-upstream patch marker Session lifecycle/history/UI not needed none (released-data compatibility) V1 markers filter `isNotNull(seq)` and normalize legacy tool content when loading a message by id (L3,13,50,56). V2 store.ts is unmarked; kilo2 never reads released Kilo rows in place. L3: import { and, eq, isNotNull } from "drizzle-orm" // kilocode_change / L13: import { normalize } from "../kilocode/session-message" // kilocode_change +packages/core/src/shell.ts 11,180,194,199 4 shared-upstream patch marker Package behavior indicated by path partial packages/core/src/shell/select.ts:163-167 (PowerShell args) and packages/core/src/shell.ts invocation; missing UTF-8 prologue and `kilo` $0 V1 markers: pass `kilo` as `$0` for the eval-wrapped bash/zsh command (L180,194) and route PowerShell through kilocode/powershell.ts which injects a UTF-8 console-encoding setup after any `param(...)` prologue (L11,199). V2 ShellSelect.args natively uses `-NoLogo -NoProfile -NonInteractive -Command` for PowerShell (select.ts:166) but does not inject the UTF-8 InputEncoding/OutputEncoding prologue or preserve `param()` ordering, and the eval wrapper `$0` is upstream's. Preserved: PowerShell detection and non-interactive invocation; missing: UTF-8 encoding guarantee for PowerShell tool output and Kilo-branded `$0` (cosmetic). L11: import { PowerShell } from "./kilocode/powershell" // kilocode_change / L180: "kilo", // kilocode_change +packages/core/src/state.ts 59,68,136,142 4 shared-upstream patch marker Package behavior indicated by path native equivalent packages/kilo-cli/src/project-config.ts (Config.Service entries extension) replacing out-of-fold mutation V1 added `State.mutate` so Kilo could reconcile config-derived state (references) outside the replayable transform fold (L59-68,136-142); its only consumer was v1 Reference.replace. V2 state.ts has no `mutate`, and Kilo's effective config now flows through the native Config.Service entries (ProjectConfig) which every config plugin observes and reloads from, so no out-of-fold mutation is required. See packages/core/src/reference.ts row. L59: // kilocode_change start - Kilo reconciles config-derived state outside the transform fold / L68: // kilocode_change end +packages/core/src/tool/apply-patch.ts 13,38,51,91,92 5 shared-upstream patch marker Tool behavior/UI needs port packages/core/src/tool/plugin/patch.ts (`toStructuredOutput`/structured output compaction against the tool-output store limit) V1 adds a `compact` step that drops per-file diff previews only when the encoded output exceeds ToolOutputStore.MAX_BYTES, keeping durable/SSE tool records bounded (L13,38-51,91-92). V2 tool/plugin/patch.ts (and file-diff.ts) has no compact/toStructuredOutput/MAX_BYTES logic (grep empty), so oversized patch outputs are persisted/streamed unbounded. Paired with tool/edit.ts. L13: import { ToolOutputStore } from "../tool-output-store" // kilocode_change / L38: // kilocode_change start - keep durable/SSE tool records bounded without hiding normal-sized diff previews +packages/core/src/tool/edit.ts 8,20,45,58,124,125 6 shared-upstream patch marker Tool behavior/UI needs port packages/core/src/tool/plugin/edit.ts (`toStructuredOutput` compaction) — the named-reference doc marker is not needed V1 markers: the same bounded structured-output `compact` as apply-patch (L20,45-58,124-125) and a doc-comment marker preserving named-reference mutation-constraint documentation (L8). V2 tool/plugin/edit.ts has no compaction step. The named-reference constraint documentation is moot because v2 tools do not accept `reference` inputs (see glob/grep rows). L8: // kilocode_change - preserve named-reference mutation constraint documentation / L20: import { ToolOutputStore } from "../tool-output-store" // kilocode_change +packages/core/src/tool/glob.ts 8,11,13,28,33,38,54,60,63,76,81,93,100,113,116,118,121,124,139,153 20 shared-upstream patch marker Tool behavior/UI partial packages/core/src/tool/plugin/glob.ts:95-134 (native containment, truncation flag, timeout); missing `reference` input, `partial` status, capped limit schema, spawn-bound validation V1 markers: optional `reference` input to search a named project reference instead of the active Location (L28-31,93,100-105), `SearchLimit`-capped limit schema (L32), typed `Result{items,truncated,partial}` with model text noting truncation/partial reads (L38-54,76-81,124-139), canonical SearchTarget inspection of base and target with escape checks (L100-113), and spawn-bound `validate` (L118-121). V2 glob plugin natively resolves the target through LocationMutation, fails on escaped/non-directory paths (L95-98), requests `limit+1` to compute `truncated` and reports it in model content and metadata (L99-134), and adds a 30s timeout. Missing: named-reference search, `partial` (unreadable files) status, the 100-cap on model-supplied limits (v2 accepts any PositiveInt), and TOCTOU validation of the search root at spawn. L8: // kilocode_change start / L11: // kilocode_change end +packages/core/src/tool/grep.ts 9,12,14,31,35,39,44,69,76,79,92,100,112,120,137,140,142,144,147,150,171,183 22 shared-upstream patch marker Tool behavior/UI partial packages/core/src/tool/plugin/grep.ts:95-162 (native containment/truncation); missing `reference`, single-file targets, managed-data retention, `partial`, capped limit, spawn validation V1 markers mirror glob.ts plus: grep may target a single file (`file:` basename with cwd = dirname, L136-142), absolute paths under Kilo's global data directory are retained via SearchTarget.managed (L127-135), and `Global.Service` is a dependency (L76,183). V2 grep plugin natively handles containment via LocationMutation, `limit+1` truncation reporting (L116-162) and timeout. Missing: named-reference search, file-target grep, managed-data path retention, `partial` status, 100-cap limit schema, and spawn-bound validation. L9: // kilocode_change start / L12: // kilocode_change end +packages/core/src/tool/read-filesystem.ts 93,110,177,181,185,199,203,346 8 shared-upstream patch marker Tool behavior/UI needs port packages/core/src/tool/read-filesystem.ts inspect/read/list (identity-bound Target) consumed by packages/core/src/tool/plugin/read.ts V1 binds approved reads to filesystem identity: `inspect` returns a Target carrying dev/ino (L93-110,177-181), `read` opens the approved path and verifies the descriptor identity before reading (L185-203), and `list` re-verifies directory identity (L346-348). V2 read-filesystem.ts (389 lines, unmarked) has no dev/ino tracking or verify step (grep for ino/verify/open( empty) and tool/plugin/read.ts authorizes by resolved path only (read.ts:60-80). The permission-approved-target TOCTOU hardening is absent. L93: // kilocode_change start - bind approved reads to filesystem identity / L110: // kilocode_change end +packages/core/src/tool/read.ts 8,34,71,86 4 shared-upstream patch marker Tool behavior/UI needs port packages/core/src/tool/plugin/read.ts (thread the approved Target from read-filesystem inspect through permission.assert to reader.read) V1 markers inspect the resolved target before permission and pass the approved identity into the read (L8,34,71-86). V2 tool/plugin/read.ts resolves via LocationMutation, asserts permission on the resource string, then calls `reader.read(AbsolutePath.make(target.absolute), ...)` by path (read.ts:73-84); no identity carried. Paired with read-filesystem.ts. L8: import { LocationMutation } from "../location-mutation" // kilocode_change / L34: const mutation = yield* LocationMutation.Service // kilocode_change +packages/core/src/tool/write.ts 8 1 shared-upstream patch marker Tool behavior/UI not needed none — v2 tools have no `reference` input (packages/core/src/tool/plugin/write.ts) V1's only marker preserves a doc comment stating that named references are read-oriented and not accepted by mutation tools. V2 write.ts is upstream and no v2 tool exposes named-reference inputs, so the constraint documentation has nothing to document until reference search is ported (see glob/grep rows). L8: // kilocode_change - preserve named-reference mutation constraint documentation +packages/core/src/util/encode.ts 2,3 2 shared-upstream patch marker Package behavior indicated by path needs port packages/util/src/encode.ts:1-4 (`base64Encode` path normalization) V1 normalizes backslashes to `/` before encoding so Windows paths produce the same base64url IDs as POSIX paths (L2). V2 util encode.ts encodes UTF-8 bytes (the second v1 marker is native) but does not normalize Windows separators, so IDs derived from paths differ per platform. L2: const normalized = value.replace(/\\/g, "/") // kilocode_change - normalize Windows paths / L3: const bytes = new TextEncoder().encode(normalized) // kilocode_change - encode as UTF-8 bytes +packages/core/src/util/log.ts 4,9,10,68,73,124 6 shared-upstream patch marker Package behavior indicated by path partial packages/util/src/observability/logging.ts:49-60 (append-mode file logger per channel); no rotation or dev.log truncation V1 markers replace the log sink with a rotating-file-stream (50M, 10 files, `.log-history`) that truncates `dev.log` once per Kilo run (KILO_RUN_ID) and guards ENOENT on rotation (L4-10,68,73-124). V2 util logging.ts writes `opencode-.log` via Effect `Logger.toFile` in append mode with no rotation or size bound; the Kilo host only toggles stderr logging (interactive-server.ts:315) and points Global at the kilo2 log directory (paths.ts:19). Preserved: per-channel log file under Kilo-owned roots. Missing: size-based rotation/retention and per-run dev-log truncation. L4: import { existsSync, writeFileSync } from "fs" // kilocode_change / L9: import { createStream } from "rotating-file-stream" // kilocode_change +packages/core/src/v1/config/agent.ts 12,66,70,71,75,76,77,78,83,87,89,96,104,108,111,125,126,135,155,158 20 shared-upstream patch marker Configuration/settings partial Retained `packages/core/src/v1/config/agent.ts` consumed by `packages/kilo-cli/src/import-v1-config.ts`; target `packages/schema/src/config/agent.ts` Standard agent migration is live, but pinned skills/MCP/VS Code requirement fields are absent from retained schema/target and remain unmapped. L12: // kilocode_change start - agent skill/MCP/VS Code extension requirements schema / L66: // kilocode_change end +packages/core/src/v1/config/command.ts 6 1 shared-upstream patch marker Configuration/settings needs port Retained schema + `packages/core/src/config/plugin/command.ts`; importer consumer Pinned optional-template workflow override is not retained (current template required), so model/variant-only global workflow overrides cannot import exactly. L6: template: Schema.optional(Schema.String), // kilocode_change - allow global workflow model/variant overrides +packages/core/src/v1/config/config.ts 19,23,32,35,41,43,45,55,64,67,99,100,181,197,201,213,214,216,238,245,264,269,289,307,314,335 26 shared-upstream patch marker Configuration/settings partial Retained v1 decoder → `packages/kilo-cli/src/import-v1-config.ts` → schema/Core config plugins Config mapping report verifies native-carried and Kilo-owned mappings, while explicitly enumerating refused Kilo keys (sandbox, indexing, remote_control, display, commit-message, compaction, telemetry, image/notebook/task-model and others); refusal is not parity. L19: // kilocode_change start / L23: // kilocode_change end +packages/core/src/v1/config/mcp.ts 3,4,6,9,28,60 6 shared-upstream patch marker Configuration/settings partial Retained MCP decoder/migrator → v2 `packages/schema/src/config/mcp.ts` and Core MCP plugin Local/remote MCP configuration migrates, but pinned `env` alias normalization is absent from retained decoder; only canonical environment is accepted. L3: import { Schema, SchemaGetter } from "effect" // kilocode_change / L4: import { zod } from "@opencode-ai/core/effect-zod" // kilocode_change +packages/core/src/v1/config/migrate.ts 39,40,88,111,173,197,252 7 shared-upstream patch marker Configuration/settings native equivalent Current retained migrator + `packages/kilo-cli/src/import-v1-config.ts:819-946` Pinned null-delete handling is replaced by boundary normalizeNulls before decoding plus current migration; importer directly invokes ConfigMigrateV1.migrate and compares reduced mappings. L39: model: info.model ?? undefined, // kilocode_change - v1 null delete sentinel is not valid in v2 / L40: default_agent: info.default_agent ?? undefined, // kilocode_change +packages/core/src/v1/config/permission.ts 5,28,36,37,41,49 6 shared-upstream patch marker Configuration/settings partial Retained permission decoder/migrator → v2 permissions and settings collections Null sentinels are normalized at importer boundary, but pinned markdown_source, agent_manager, and notebook permission keys lack exact v2 import targets. L5: export const Action = Schema.NullOr(Schema.Literals(["ask", "allow", "deny"])) // kilocode_change - nullable allows null as a delete sentinel / L28: markdown_source: Schema.optional(Rule), // kilocode_change - explicitly authorize external agent/command sources +packages/core/src/v1/config/provider.ts 4,13,14,15,18,71,126 7 shared-upstream patch marker Configuration/settings partial Retained provider migration + `packages/kilo-gateway` catalog/routing and v2 provider schema Standard provider/model data migrates and Gateway supplies model metadata, but pinned prompt enum and Kilo-specific provider schema extensions are not retained field-for-field. L4: import { PROMPTS, AI_SDK_PROVIDERS } from "@kilocode/kilo-gateway" // kilocode_change / L13: prompt: Schema.optional(Schema.Literals(PROMPTS)), // kilocode_change +packages/core/src/v1/config/server.ts 13 1 shared-upstream patch marker Configuration/settings needs port No profile-config target; server launch remains host/CLI controlled Pinned custom mDNS domain is in a top-level server config that the config-mapping audit verifies v2 refuses as unsupported; no runtime profile consumer exists. L13: description: "Custom domain name for mDNS service (default: kilo.local)", // kilocode_change +packages/core/test/catalog.test.ts 30,47,73,105 4 shared-upstream patch marker Package behavior indicated by path not needed packages/core/test/catalog.test.ts (upstream; no auth.json import to isolate) V1 markers only inject a temporary Global data dir because Kilo's Credential layer imported Global.data/auth.json on startup (L30-32,47,73,105). V2 Credential has no startup legacy import (see packages/core/src/credential.ts row), so the isolation shim is obsolete; the v2 suite (445 lines, unmarked) runs against in-memory credentials. L30: // kilocode_change - Credential imports Global.data/auth.json on startup, so without this the suite / L47: [Global.node, globalLayer()], // kilocode_change +packages/core/test/config/config.test.ts 164,201,251,267,767,854 6 shared-upstream patch marker Configuration/settings partial packages/kilo-cli/test/project-config.test.ts, project-config-reader.test.ts, config-refresh.test.ts (owned ProjectConfig coverage); native config.test.ts covers upstream discovery V1 markers add three Kilo cases: project discovery disabled via KILO_DISABLE_PROJECT_CONFIG keeps only global config (L164-201), legacy config.json still loads (L251-267), and Kilo roots/precedence (.kilocode/.kilo directories, kilo.json vs opencode.json ordering up to the project boundary, L767-854). V2 relocates the Kilo discovery to kilo-cli ProjectConfig and its owned tests cover kilo.json/.kilo/.kilocode boundary walking and refresh; the single v2 marker in config.test.ts (L79) tests the separate refresh hook. Missing: no v2 test asserts the disable path (now the host `projectConfig` option) against mixed global+project documents, legacy config.json loading is intentionally dropped, and the v1 mixed opencode.json+kilo.json precedence matrix has no equivalent because v2 disables native discovery in the Kilo host. L164: // kilocode_change start / L201: // kilocode_change end +packages/core/test/credential.test.ts 2,4,7,10,14,23,27,35,69,338 10 shared-upstream patch marker Package behavior indicated by path partial packages/kilo-cli/test/import-v1-config.test.ts (explicit auth.json import planning/apply); no v2 test for KILO_AUTH_CONTENT or startup reconciliation V1 markers add a process-local KILO_AUTH_CONTENT isolation test (L69-138) and startup legacy auth.json reconciliation/import tests (L140-338) plus fixture plumbing (L2-35). V2 credential.test.ts (202 lines) is upstream-only. The explicit import replacement is tested in kilo-cli import-v1-config.test.ts (api-key normalization, Kilo OAuth device method, provider OAuth inventory, host-writer requirement). Missing: KILO_AUTH_CONTENT isolation and startup auth.json reconciliation cases, consistent with those behaviors being absent (credential.ts row). L2: import { Database as SQLite } from "bun:sqlite" // kilocode_change / L4: import { eq } from "drizzle-orm" // kilocode_change +packages/core/test/event.test.ts 25,27,86,92 4 shared-upstream patch marker Package behavior indicated by path not needed none — v2 core has no test/event.test.ts; database isolation is native (packages/core/src/database/database.ts:66-79 per-path locks, in-memory default) V1 markers only swapped in an in-memory `Database.layerFromPath(":memory:")` to isolate concurrent tests from process-wide migrations (L25-27,86,92). V2 Database defaults to `:memory:` per layer build and locks per file path, so no test-side isolation shim is needed; the v1 EventV2 suite itself does not exist in v2. L25: // kilocode_change start - keep concurrent tests isolated from process database migrations / L27: // kilocode_change end +packages/core/test/filesystem/search.test.ts 26,27,39,40,41 5 shared-upstream patch marker Package behavior indicated by path needs port packages/core/test/filesystem/search.test.ts or a Kilo-owned ripgrep test once SearchResult is ported V1 markers assert the `SearchResult{items,truncated}` shape returned by Ripgrep.glob/grep (L26-27,39-41). V2 search.test.ts (247 lines) tests plain arrays because v2 Ripgrep returns arrays (ripgrep.ts row: needs port). The assertions follow the source port. L26: expect(result.items.map((item) => item.path)).toEqual([RelativePath.make("src/match.ts")]) // kilocode_change / L27: expect(result.truncated).toBe(false) // kilocode_change +packages/core/test/filesystem/watcher.test.ts 19,231,235 3 shared-upstream patch marker Package behavior indicated by path partial packages/core/test/filesystem/watcher.test.ts:28 (`describeNative` skips on CI) V1 markers gate the native watcher suite to non-CI or the darwin KILO_TEST_PROFILE (L19) and accept FSEvents classifying a HEAD overwrite as `add` (L231-235). V2 skips native watcher tests entirely on CI (L28) — a broader gate that subsumes v1's, so the CI invariant is preserved but the darwin CI profile opt-in is gone. The FSEvents add/change tolerance was not located in the v2 suite (690 lines); it matters only if the HEAD-overwrite case exists there. L19: Watcher.hasNativeBinding() && (!process.env.CI || process.env.KILO_TEST_PROFILE === "darwin") // kilocode_change / L231: // kilocode_change start - FSEvents may classify this overwrite as an add. +packages/core/test/global.test.ts 9 1 shared-upstream patch marker Package behavior indicated by path not needed packages/kilo-cli/src/paths.ts layout tests (packages/kilo-cli/test/preview.test.ts) cover Kilo roots V1 asserted `Global.Path.tmp` ends in `kilo` (L9). V2 has no core/test/global.test.ts; util global stays `opencode` and the Kilo host injects `kilo2/` roots via Global.layerWith, exercised by kilo-cli preview/preflight tests. The v1 assertion targets a branding choice v2 intentionally makes elsewhere. L9: expect(Global.Path.tmp).toBe(path.join(os.tmpdir(), "kilo")) // kilocode_change +packages/core/test/integration.test.ts 15,36 2 shared-upstream patch marker Package behavior indicated by path not needed packages/core/test/integration.test.ts (upstream; no auth.json dual-write to isolate) V1 markers set KILO_AUTH_CONTENT and a temp Global data dir so Kilo's auth.json dual-write/re-import did not leak between tests (L15-36). V2 Credential neither dual-writes nor re-imports auth.json, so the isolation harness is obsolete; the v2 suite (676 lines) is unmarked. L15: // kilocode_change start - Kilo dual-writes stored credentials into Global.data/auth.json and re-imports / L36: // kilocode_change end +packages/core/test/location-filesystem.test.ts 70,91 2 shared-upstream patch marker Package behavior indicated by path needs port packages/core/test/location-filesystem.test.ts (symlink-escape rejection for read/list/glob/grep) V1 adds a test that in-worktree symlinks to outside paths are rejected for read, list, glob and grep (L70-91). V2 tests lexical escapes (L133) and, notably, allows listing through an external symlink while rejecting file reads (L148-167) — the opposite of v1's list rejection — and does not exercise glob/grep escape. Follows the packages/core/src/filesystem.ts hardening row; a port must reconcile v2's deliberate list-through-symlink behavior. L70: // kilocode_change start - canonical containment must reject in-worktree links to outside paths / L91: // kilocode_change end +packages/core/test/plugin/command.test.ts 40,41,42 3 shared-upstream patch marker Package behavior indicated by path needs port packages/core/src/plugin/command/initialize.txt (Kilo-branded init template) and its test in packages/core/test/plugin/command.test.ts V1 asserts the built-in `init` command template mentions 'future Kilo sessions' and `kilo.json` and never 'OpenCode' (L40-42). V2 initialize.txt says 'future OpenCode sessions' and `opencode.json` (L3,15,59) and command.test.ts asserts the upstream template; no Kilo-owned override of the init command exists in packages/kilo-cli. Branding of the init workflow is absent. L40: expect((yield* command.get("init"))?.template).toContain("future Kilo sessions") // kilocode_change / L41: expect((yield* command.get("init"))?.template).toContain("`kilo.json`") // kilocode_change +packages/core/test/plugin/fixture.ts 25,59 2 shared-upstream patch marker Package behavior indicated by path not needed packages/core/test/plugin/fixture.ts (upstream) V1 marker injects a temp Global data dir to avoid Credential's startup auth.json import (L25-27,59). Obsolete in v2 (no startup import). L25: // kilocode_change - Credential imports Global.data/auth.json on startup, so without this the suite / L59: [Global.node, globalLayer], // kilocode_change +packages/core/test/plugin/models-dev.test.ts 27,34 2 shared-upstream patch marker Provider/model routing or UI not needed packages/core/test/plugin/models-dev.test.ts (upstream) V1 marker injects a temp Global data dir for the same auth.json import reason (L27-34). Obsolete in v2. L27: // kilocode_change - Catalog pulls Credential, which imports Global.data/auth.json on startup, so / L34: [Global.node, globalLayer], // kilocode_change +packages/core/test/plugin/provider-kilo.test.ts 3,5,22,43,68,90,119,125,206 9 shared-upstream patch marker Provider/model routing or UI partial packages/kilo-gateway/test/plugin.test.ts, gateway.test.ts, account.test.ts (Gateway routing, anonymous key, auth precedence); attribution headers unported V1 markers test Kilo attribution headers on the kilo provider (L68,90,119), routing the Kilo catalog through the `@kilocode/kilo-gateway` SDK with `kilocodeToken` (L125-168), authenticated credentials winning over KILO_API_KEY/KILO_ORG_ID env (L170-190), and anonymous availability (L192-206). V2's owned Gateway suite covers anonymous provider-scope key (`apiKey` 'anonymous', plugin.test.ts:1669-1782), authenticated/anonymous transitions and Authorization precedence (L750). Missing: Kilo `HTTP-Referer`/`X-Title` header assertions (headers are upstream-branded in v2, see provider plugin rows) and KILO_API_KEY/KILO_ORG_ID env fallback (no v2 reference to those variables in kilo-gateway/kilo-cli). L3: import { AISDK } from "@opencode-ai/core/aisdk" // kilocode_change / L5: import { ModelV2 } from "@opencode-ai/core/model" // kilocode_change +packages/core/test/plugin/provider-llmgateway.test.ts 50,51 2 shared-upstream patch marker Provider/model routing or UI needs port packages/core/test/plugin/provider-llmgateway.test.ts header assertions (follows llmgateway.ts attribution port) V1 asserts Kilo attribution headers including `X-Source: kilo` (L50-51). V2 test (73 lines, unmarked) asserts opencode headers because the source sends them. Follows the packages/core/src/plugin/provider/llmgateway.ts row. L50: "X-Title": "Kilo Code", // kilocode_change / L51: "X-Source": "kilo", // kilocode_change +packages/core/test/plugin/provider-opencode.test.ts 71 1 shared-upstream patch marker Provider/model routing or UI needs port packages/core/test/plugin/provider-opencode.test.ts (drop console account/service-account and remote-catalog cases once the Kilo product decision is applied) V1 marker documents that Kilo removed upstream's 'registers account and service account methods' and 'loads providers and models from the connected OpenCode server' tests because Kilo does not register the OpenCode Console flow (L71-73). V2 test (664 lines) is upstream's full suite exercising those flows. Follows the packages/core/src/plugin/provider/opencode.ts row. L71: // kilocode_change - Kilo does not register the OpenCode Console device/OAuth flow and does not let +packages/core/test/project.test.ts 157,178,209 3 shared-upstream patch marker Package behavior indicated by path needs port packages/core/test/project.test.ts:393-414,519 (follows the `kilo` project id file name port) V1 markers write/assert the cached project id file as `.git/kilo` (L157,178,209). V2 project.test.ts asserts `.git/opencode` because v2 project.ts:237 uses the upstream file name. Follows the packages/core/src/project.ts row; if the `kilo` file name is not ported the v1 assertions are moot. L157: yield* Effect.promise(() => Bun.write(path.join(tmp.path, ".git", "kilo"), "old-id")) // kilocode_change / L178: expect(yield* Effect.promise(() => Bun.file(path.join(tmp.path, ".git", "kilo")).exists())).toBe(false) // kilocode_change +packages/core/test/pty/pty-session.test.ts 16,65,84,93,98,138,185,262,285 9 shared-upstream patch marker Session lifecycle/history/UI needs port packages/core/test/pty/pty-session.test.ts (explicit-args, UTF-8 round-trip, descendant termination, exited replay cases) V1 markers add a 15s PTY timeout with output-bearing diagnostics (L16,65,84-98), and four Kilo cases: explicit commands do not gain implicit login args (L138-148), non-ASCII output round-trips byte-identically (L153-161), background descendants outside the shell process group are terminated on remove (L162-184), and `allowExited` attachments replay exited output then end (L262-285). V2 pty-session.test.ts (225 lines) covers shell/login/cwd defaults (L210) but none of the four Kilo cases (grep for descendants/allowExited/explicit empty). Follows the packages/core/src/pty.ts row. L16: const PTY_TEST_TIMEOUT = "15 seconds" // kilocode_change - PTY startup can exceed the default test timeout on macOS CI / L65: duration: PTY_TEST_TIMEOUT, // kilocode_change +packages/core/test/ripgrep.test.ts 59,60,66,78 4 shared-upstream patch marker Package behavior indicated by path partial packages/core/test/ripgrep.test.ts:82-103 (hidden .opencode vs .git filtering native); missing underlying-reason failure case V1 markers assert `.items` on SearchResult (L59-60) and add a case that execution failures surface the underlying reason (`ripgrep execution failed: `, L66-78). V2 test natively asserts the hidden-directory filtering on plain arrays (L89-103). Missing: the underlying-reason error test, following the unported error-detail marker in packages/core/src/ripgrep.ts (L179-185). L59: expect(matches.items.map((item) => item.entry.path)).toContain(RelativePath.make(".opencode/config")) // kilocode_change / L60: expect(matches.items.map((item) => item.entry.path)).not.toContain(RelativePath.make(".git/config")) // kilocode_change +packages/core/test/session-projector.test.ts 23,247,288,353,402,404,408,414 8 shared-upstream patch marker Session lifecycle/history/UI not needed none — released-reader compatibility shapes are not written by v2 (packages/core/src/session/projector.ts row) V1 markers add a tool-output image scenario and assert the projector writes released-reader shapes: compaction `summary` with embedded 'Recent context' plus `kilo_summary`, and tool `content` as `{type:"file",source:{type:"data"}}` (L23,247-288,353-408,414). These exist solely so released v1 Kilo clients could read the shared database; kilo2's isolated store and import path make the shapes unnecessary, and v2's 750-line suite tests the native shapes. L23: import * as StoredMessage from "@opencode-ai/core/kilocode/session-message" // kilocode_change / L247: // kilocode_change start +packages/core/test/session-prompt.test.ts 101 1 shared-upstream patch marker Session lifecycle/history/UI not needed none — comment-only marker about released readers V1 marker is a comment explaining why the suite avoids a durable interrupt lookup (released database readers could not decode that event type, L101). V2 has no released-reader constraint; the v2 suite (1193 lines) exercises interruption through its own fixtures (L38-51). L101: // kilocode_change - no durable interrupt lookup: released database readers cannot decode that event type. +packages/core/test/session-runner.test.ts 748,781 2 shared-upstream patch marker Session lifecycle/history/UI not needed none — see packages/core/src/session/context-epoch.ts V1 adds a regression that a concurrent Session move during system-context load does not create a source-Location context epoch (L748-781). V2 removed context epochs and interrupts on Location mismatch at select time (context.ts:123); there is no epoch row to assert. A v2-specific move-during-load regression was not found in the 5548-line suite and was not proven necessary. L748: // kilocode_change start - prevent a source-location context epoch after the session moves mid-load / L781: // kilocode_change end +packages/core/test/tool-apply-patch.test.ts 205,230 2 shared-upstream patch marker Tool behavior/UI needs port packages/core/test/tool-patch.test.ts (oversized structured output case) following the patch.ts compaction port V1 adds 'omits oversized patches from durable structured output' asserting structured output stays within ToolOutputStore.MAX_BYTES (L205-230). V2 renamed the suite to tool-patch.test.ts and has no MAX_BYTES/oversized case (grep empty). Follows packages/core/src/tool/apply-patch.ts. L205: // kilocode_change start / L230: // kilocode_change end +packages/core/test/tool-edit.test.ts 162,193 2 shared-upstream patch marker Tool behavior/UI needs port packages/core/test/tool-edit.test.ts (oversized structured output case) following the edit.ts compaction port V1 adds 'omits an oversized patch from durable structured output' (L162-193). V2 tool-edit.test.ts (643 lines) has no such case. Follows packages/core/src/tool/edit.ts. L162: // kilocode_change start / L193: // kilocode_change end +packages/core/test/tool-read-filesystem.test.ts 18 1 shared-upstream patch marker Tool behavior/UI needs port packages/core/test/tool-read-filesystem.test.ts (inspect-then-read Target fixture) following the identity-bound read port V1 marker resolves an inspected Target before reads because Kilo binds reads to filesystem identity (L18-19). V2 test (307 lines) reads by path. Follows packages/core/src/tool/read-filesystem.ts. L18: // kilocode_change - Kilo binds reads to an inspected filesystem identity, so tests resolve a target first +packages/core/test/util/effect-flock.test.ts 62,89,372,373,391 5 shared-upstream patch marker Package behavior indicated by path native equivalent packages/core/test/fixture/lock-worker.ts:29-43 (`stopLockWorker` awaits close, taskkill on win32) V1 markers make worker shutdown await the process `close` event with a Windows taskkill fallback and widen readiness/test timeouts for slow hosted macOS (L62-89,372-373,391). V2 factors the same behavior into a shared lock-worker fixture (await `close`, win32 taskkill /T /F) used by effect-flock.test.ts (374 lines). The widened 20s/60s timeouts were not compared; the race-fix invariant is present natively. L62: // kilocode_change start - make worker finalization await the process close event without a Windows race / L89: // kilocode_change end +packages/core/test/util/flock.test.ts 84,113,184,203 4 shared-upstream patch marker Package behavior indicated by path native equivalent packages/core/test/fixture/lock-worker.ts:29-43 via spawnLockWorker/stopLockWorker (flock.test.ts:6,77,113) Same as effect-flock.test.ts: v1's inline stopWorker/close-await/taskkill helpers (L84-113,184,203) are now the shared v2 fixture consumed by flock.test.ts. L84: // kilocode_change start - make worker finalization await the process close event without a Windows race / L113: // kilocode_change end +packages/effect-drizzle-sqlite/src/sqlite-core/effect/session.ts 285 1 shared-upstream patch marker Session lifecycle/history/UI needs port packages/core/src/database/drizzle/sqlite-core/effect/session.ts:280 (redact `params` in EffectDrizzleQueryError) V1 redacted bound parameter values in query errors because bound values may contain credentials (L285). V2 vendored the drizzle Effect session into packages/core/src/database/drizzle and constructs `EffectDrizzleQueryError({ query, params, cause })` with the raw params (session.ts:280); packages/core/test/database-drizzle.test.ts:59 even asserts `error.params` equals the bound values. Credential values (e.g. credential table inserts) can leak into logged/serialized errors. L285: params: params.map(() => ""), // kilocode_change - bound values may contain credentials +packages/effect-drizzle-sqlite/test/sqlite.test.ts 133,149 2 shared-upstream patch marker Package behavior indicated by path needs port packages/core/test/database-drizzle.test.ts (redaction regression) following the session.ts port V1 test asserts query errors never contain bound credential values (L133-149). V2's database-drizzle.test.ts asserts the opposite shape (params preserved). Follows the effect-drizzle session.ts row. L133: // kilocode_change start - query errors must never expose bound credential values / L149: // kilocode_change end +packages/effect-sqlite-node/tsconfig.json 5 1 shared-upstream patch marker Configuration/settings not needed none — package removed in v2 (packages/core/src/database/sqlite.node.ts) V1 added `types: ["node"]` for node:sqlite with Kilo's tsgo version (L5). V2 has no packages/effect-sqlite-node; the node sqlite adapter lives in core with the core tsconfig. L5: "types": ["node"], // kilocode_change - required for node:sqlite with Kilo's tsgo version +packages/http-recorder/src/websocket.ts 153,208 2 shared-upstream patch marker Package behavior indicated by path native equivalent packages/http-recorder/src/websocket/recorder.ts:17-23,155-160 (progress Ref + Deferred + Semaphore ordering) V1 added a causal replay ordering state machine (progress position, per-step Deferred, semaphore-guarded client sends, unconsumed-event check on close, L153-208). V2 restructured the recorder into websocket/recorder.ts with the same progress Ref, changed Deferred, write/event locks and an unconsumed-events close check, so the ordering invariant is native. L153: // kilocode_change start - preserve causal client/server transcript order during replay / L208: // kilocode_change end +packages/http-recorder/test/record-replay.test.ts 312 1 shared-upstream patch marker Package behavior indicated by path native equivalent packages/http-recorder/test/websocket.test.ts:252 ('WebSocket replay preserves causal frame ordering') V1 added a regression for causal replay ordering and auto recording (L312-313). V2 carries a test with the same name plus binary-frame and public-decorator variants (websocket.test.ts:252,306,511). L312: // kilocode_change - regression coverage for internal replay ordering and auto recording +packages/http-recorder/tsconfig.json 12 1 shared-upstream patch marker Configuration/settings not needed packages/http-recorder/tsconfig.json (upstream, 22 lines) V1 added `types: ["bun"]` for Bun test globals (L12). V2 tsconfig omits it and the package typechecks under `bun typecheck`; no Kilo-owned source in http-recorder. L12: "types": ["bun"], // kilocode_change - include Bun test globals in workspace typecheck +packages/httpapi-codegen/test/generate.test.ts 5,626 2 shared-upstream patch marker Package behavior indicated by path native equivalent packages/httpapi-codegen/test/generate.test.ts:5,1100 (`fileURLToPath` for the generated directory glob) V1 converted the `generated` file URL with `fileURLToPath` for Windows correctness (L5,626). V2 test uses the same `fileURLToPath(new URL("generated", import.meta.url))` natively. L5: import { fileURLToPath } from "node:url" // kilocode_change - convert file URLs correctly on Windows / L626: () => Array.fromAsync(new Bun.Glob("*.ts").scan(fileURLToPath(new URL("generated", import.meta.url)))), // kilocode_change +packages/httpapi-codegen/test/write.test.ts 2,19,20,59 4 shared-upstream patch marker Package behavior indicated by path native equivalent packages/httpapi-codegen/test/write.test.ts:2,19-20 (`join("/generated", ...)` native separators) V1 used `join` for native separators in the expected write paths (L2,19-20,59). V2 test uses `join("/generated", ...)` natively. L2: import { join } from "node:path" // kilocode_change - use native separators in cross-platform tests / L19: { path: join("/generated", "session.ts"), content: "export const session = {}\n" }, // kilocode_change +packages/kilo-docs/pages/contributing/architecture/config-schema.md 74 1 documentation/reference Documentation/agent guidance partial kilocode/baseline/config-mapping-v2-gap.md (Kilo-only config keys audited against v2 schema/import); no contributor procedure page — packages/kilo-docs is absent from v2 (`git ls-files packages/kilo-docs` empty) V1 marked line documents the procedure for adding a Kilo-only config key: add the Effect Schema field with a `kilocode_change` marker in the shared config, then regenerate the JSON schema (L74). V2 has no docs site package; the Kilo-only key inventory and its v2 schema/import status are recorded in the baseline gap audit, and v2 conventions steer new Kilo config to owned seams (ProjectConfig) rather than marked shared schema edits. Missing: a contributor-facing procedure for Kilo-only keys on the v2 line. L74: 1. Add or update Effect Schema field with `kilocode_change` marker in `packages/opencode/src/config/config.ts`. +packages/kilo-docs/pages/contributing/architecture/development-patterns.md 27,28,47,51,52,53 6 documentation/reference Documentation/agent guidance partial kilocode/v2-fork-conventions.md ('Kilo-owned paths', 'Narrow shared hooks', '`kilocode_change` markers' sections) — the docs-site page is absent V1 marked table rows and 'Shared OpenCode files' section define where Kilo changes go (packages/opencode/src|test/kilocode, narrow marked seams in shared files, Kilo-owned packages without markers) and the marker grammar (inline, start/end block, new-file, JSX) with exemptions (L24-31,45-56). V2 relocates this guidance into kilocode/v2-fork-conventions.md with v2 paths (`kilocode/`, `packages//src/kilocode/`, `plans/`), the same marker rules, the no-markers-in-Kilo-owned-packages rule and merge-preservation rules. Preserved: ownership/marker conventions. Missing: the marker grammar table (block/new-file/JSX forms are not spelled out in v2), and the v1 placement rows for CLI endpoint/SDK regeneration (AGENTS.md covers `bun run generate`), JetBrains contract (no v2 package) and config-key overlay; packages/kilo-docs itself does not exist in v2. L27: | Required shared OpenCode edit | Small import, route, or injection seam in shared file plus `kilocode_change` marker | Keeps upstream diff narrow and / L28: | VS Code, JetBrains, docs, indexing, UI, gateway, or telemetry change | Existing Kilo-owned package | These packages are Kilo-owned; do not add `kilo +packages/kilo-docs/pages/contributing/development-environment.md 253 1 documentation/reference Documentation/agent guidance partial kilocode/v2-fork-conventions.md:127-140 (marker rule); docs-site page absent V1 marked bullet tells contributors to keep shared-file edits small and mark them with `// kilocode_change` (line or start/end block) and not to mark Kilo-owned paths (L253). The same rule is stated in the v2 fork conventions (root AGENTS.md points to it). Missing: the contributor development-environment page as a whole, since packages/kilo-docs is not part of v2. L253: - When editing shared `packages/opencode/` files, keep changes small and mark Kilo-only edits with `// kilocode_change` for a single line or `// kiloc +packages/kilo-docs/pages/contributing/index.md 100 1 documentation/reference Documentation/agent guidance partial kilocode/v2-fork-conventions.md:127-140; docs-site page absent V1 marked bullet (L100) is the same marker-discipline instruction as development-environment.md. Relocated to the v2 fork conventions; the docs-site contributing index has no v2 counterpart because packages/kilo-docs is absent. L100: - When editing shared `packages/opencode/` files, keep Kilo changes small and mark Kilo-only edits with `// kilocode_change` for a single line or `// +packages/kilo-indexing/src/tree-sitter/queries/java.ts 42 1 Kilo-owned inherited marker Package behavior indicated by path already ported `packages/kilo-indexing/src/tree-sitter/queries/java.ts:41-42` Current query retains exact method-start capture; index.ts:11 consumes definition.method.start. Only redundant marker removed. L42: type: (_) @definition.method.start ; kilocode_change +packages/kilo-vscode/AGENTS.md 218 1 documentation/reference Documentation/agent guidance native equivalent `kilocode/v2-fork-conventions.md:127-134` plus root AGENTS pointer Central v2 conventions encode the same no-owned-marker policy; package-local duplication unnecessary. L218: This package is entirely Kilo-specific — `kilocode_change` markers are NOT needed in any files under `packages/kilo-vscode/`. The markers are only nec +packages/kilo-vscode/package.json 1253 1 Kilo-owned inherited marker Package behavior indicated by path partial `packages/kilo-vscode/package.json` plus proposed owned guard script/CI wiring Manifest is transplanted, but its check-kilocode-change script subfeature has no automated v2 equivalent; only that guard needs port. L1253: "check-kilocode-change": "! grep -rIn 'kilocode_change' . ../kilo-ui/ --exclude='package.json' --exclude='*.md' --exclude-dir='node_modules' --exclude +packages/kilo-vscode/tests/unit/kilo-ui-contract.test.ts 136,145 2 Kilo-owned inherited marker Package behavior indicated by path partial `packages/kilo-ide-ui/src/context/data.tsx:45-86`; `packages/kilo-vscode/webview-ui/src/App.tsx:182-183`; replacement test absent OpenFile/OpenDiff contracts and consumers are ported, but dedicated source-contract regression coverage is absent. L136: // onOpenFile and OpenFileFn are `kilocode_change` additions — TypeScript types / L145: // onOpenDiff and OpenDiffFn are `kilocode_change` additions — TypeScript types +packages/llm/src/protocols/openai-chat.ts 227 1 shared-upstream patch marker Package behavior indicated by path native equivalent packages/ai/src/protocols/openai-chat.ts:363-367 (text-only user content collapsed to a string) V1 collapsed all-text user content into a single string for OpenAI Chat compatibility (L227, joined with newlines). V2 (package renamed to packages/ai) does the same when every part is text without cache_control (joined without separator, L363-367). The invariant (string content for text-only user messages) is native; the join separator differs. L227: return { role: "user" as const, content: content.map((part) => part.text).join("\n") } // kilocode_change +packages/llm/src/protocols/openai-responses.ts 139 1 shared-upstream patch marker Package behavior indicated by path native equivalent packages/ai/src/protocols/open-responses.ts + packages/ai/src/protocols/utils/open-responses-options.ts:64 V1 added `reasoning.summary` to the Responses request schema (L139). V2's open-responses options accept `reasoningSummary: auto|concise|detailed` natively (open-responses-options.ts:64) and providers/openai-options.ts forwards it (L25,43). L139: summary: Schema.optional(OpenAIOptions.OpenAIReasoningSummary), // kilocode_change +packages/llm/src/protocols/utils/openai-options.ts 10,13,33,39,65,70 6 shared-upstream patch marker Package behavior indicated by path native equivalent packages/ai/src/protocols/utils/open-responses-options.ts:64 V1 defined `OpenAIReasoningSummaries` (auto/concise/detailed), its schema literal and a `reasoningSummary` request resolver (L10-13,33,39,65-70). V2 declares the same three literals in the Open Responses options schema and forwards them from provider options; no Kilo seam needed. L10: // kilocode_change start - support every reasoning summary mode in the OpenAI Responses API / L13: // kilocode_change end +packages/llm/src/providers/openai-options.ts 12 1 shared-upstream patch marker Provider/model routing or UI native equivalent packages/ai/src/providers/openai-options.ts:25,43 V1 added `reasoningSummary?: "auto"|"concise"|"detailed"` to OpenAI provider options (L12). V2 providers/openai-options.ts passes `reasoningSummary` through and defaults it to `auto`. L12: readonly reasoningSummary?: "auto" | "concise" | "detailed" // kilocode_change +packages/llm/src/schema/ids.ts 45 1 shared-upstream patch marker Package behavior indicated by path native equivalent packages/core/src/aisdk.ts:786-788 (`other` mapped to `unknown`; raw reason retained at L755-760) V1 added `other` to FinishReason so the AI SDK's unexpected-provider finish reason decoded instead of failing (L45). V2 schema/src/llm.ts:5 omits `other`, but the AI SDK bridge maps `unified === "other"` to `unknown` while keeping the raw provider reason alongside, and native protocols fall back to `unknown` (e.g. anthropic-messages.ts:1141). Decoding cannot fail on `other`; only the literal name differs. L45: "other", // kilocode_change - preserve the AI SDK's unexpected provider finish reason +packages/llm/src/schema/messages.ts 1,45,47,55,62,69,71 7 shared-upstream patch marker Package behavior indicated by path native equivalent packages/ai/src/schema/messages.ts:66-90 (tolerant `LLM.ToolResult` union with `is`/`make`) V1 introduced a tolerant tool-result value union (json/text/error/content) with `ToolResultValue.is/make` helpers (L47-71) and re-exported StoredToolContent (L45). V2's ai schema defines the identical union and helpers natively (messages.ts:66-90) and tool-runtime.ts consumes `ToolResultValue.is`. The StoredToolContent re-export is not needed (see schema/src/llm.ts row). L1: import { Schema, SchemaGetter } from "effect" // kilocode_change / L45: export { StoredToolContent } from "@opencode-ai/schema/llm" // kilocode_change - shared with the durable event schema +packages/llm/test/tool-runtime.test.ts 9,260,264 3 shared-upstream patch marker Tool behavior/UI not needed none — StoredToolContent codec has no v2 consumer (packages/schema/src/llm.ts row) V1 markers import StoredToolContent and assert storage accepts released `media` shapes while public ToolContent rejects them (L9,260-264). V2 has no released-shape codec to test; packages/ai/test covers the native tool runtime. L9: StoredToolContent, // kilocode_change / L260: // kilocode_change start - storage accepts released shapes while public content remains canonical +packages/llm/tsconfig.json 6 1 shared-upstream patch marker Configuration/settings not needed packages/ai/tsconfig.json (upstream) V1 added `types: ["bun"]` for Bun test globals in workspace typecheck (L6). The package was renamed to packages/ai in v2 with upstream tsconfig; no Kilo-owned source in packages/ai requires the addition. L6: "types": ["bun"], // kilocode_change - include Bun test globals in workspace typecheck +packages/opencode/AGENTS.md 78 1 documentation/reference Documentation/agent guidance native equivalent `kilocode/v2-fork-conventions.md` (Kilo-owned paths, shared-hook limits, marker rules) and root AGENTS.md pointer Marked paragraph instructs extracting Kilo-specific code out of shared upstream files. V2 fork conventions encode the same rule with stricter Kilo-owned path tables. L78: `opencode/` is a fork of upstream opencode. When a change must touch a shared upstream file, extract the Kilo-specific logic into a mirror file under +packages/opencode/Dockerfile 10,13,16,19 4 shared-upstream patch marker Package behavior indicated by path deferred `packages/containers` (Kilo container batch assessed by the non-opencode worker) and `packages/kilo-cli/script/build-portable.ts` Markers copy the Kilo linux musl binary, bundled bwrap and licenses into the image. V2 builds a portable preview and Linux sandbox smoke (sandbox-linux-smoke.ts) but publishes no container image; container/registry defaults were classified deferred by the containers batch. L10: # kilocode_change start / L13: # kilocode_change end +packages/opencode/bin/kilo 10,17,20,21,22,38,42,49,50,55,72,235 12 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/script/build-portable.ts` + portable launcher (portable-launcher-smoke), baseline packaging docs PRESERVED — portable launcher resolves the bundled binary and resources (portable smoke tests). MISSING — npm-distributed launcher with cached binary fallback and tree-sitter resource pointing (npm distribution deferred). L10: // kilocode_change start - point packaged binaries at co-located tree-sitter WASM resources / L17: // kilocode_change end +packages/opencode/script/build-node.ts 17,23,30,31,32 5 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/script/build.ts`, `build-portable.ts` (sandbox assets for Linux smoke) PRESERVED — Kilo v2 builds include sandbox support for Linux (sandbox-linux-validation baseline). MISSING/unverified — mutation-worker/network-relay asset defines (v2 sandbox is process confinement without a network relay). L17: // kilocode_change start / L23: // kilocode_change end +packages/opencode/script/build.ts 5,9,14,22,27,35,54,106,172,258,265,287,292,298,301,308,311,314,317,325,332,335,338,341,348,354,357,360,365,391,395,400,406,407,413,416,429,435,439,446,457,459,461,465,467,471,474 47 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/script/build.ts`, `build-tui.ts`, `build-portable.ts` (compile flags recorded in corrections memory) PRESERVED — Kilo-owned build scripts compile the CLI/TUI with Bun for the preview (portable acceptance). MISSING — release archive naming, multi-target publishing and gh release upload (distribution deferred), indexing plugin bundling specifics. L5: import os from "os" // kilocode_change / L9: import { createRequire } from "module" // kilocode_change +packages/opencode/script/dev-local.ts 1 1 shared-upstream patch marker Package behavior indicated by path needs port `packages/kilo-cli/script/dev.ts` (source launcher) + cloud client env handling in `src/cloud/origin.ts` Whole file points the dev CLI at a locally running cloud dev server (manifest ports, KILO_API_URL/ingest/event URLs) with isolated state. V2 dev.ts launches the interactive preview but has no local-cloud manifest discovery; KILO_API_URL is honored by the Gateway client only. L1: // kilocode_change - new file +packages/opencode/script/generate.ts 3,12,49 3 shared-upstream patch marker Package behavior indicated by path not needed `packages/kilo-gateway/src/models.ts` decodes the live catalog Marker parses a Kilo models snapshot during SDK generation; v2 generation runs from Protocol and the Gateway decodes catalogs at runtime. L3: import { parseModelsSnapshot } from "../src/kilocode/provider/models-snapshot-shape" // kilocode_change / L12: // kilocode_change start +packages/opencode/script/kilocode/test-cli.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path native equivalent `packages/kilo-cli/script/test.ts` and `test/fixture.ts` guards Whole-file Kilo test runner for the v1 CLI package; v2 kilo-cli has its own test script and fixture guard. L1: // kilocode_change - new file +packages/opencode/script/postinstall.mjs 14,126,128,171 4 shared-upstream patch marker Package behavior indicated by path deferred npm distribution (deferred); portable preview uses `build-portable.ts` Markers select the platform variant and copy runtime resources for the npm-installed binary. V2 does not publish to npm in the current phase. L14: // kilocode_change start - variant detection matching bin/kilo logic / L126: // kilocode_change end +packages/opencode/script/publish.ts 6,24,31,35,37,48,53,55,58,65,66,70,75,86,88,106,116,128,129,130,131,132,133,134,135,165,167,178,179,187,188,198,199,206,207,221,224,225 38 shared-upstream patch marker Package behavior indicated by path deferred Release workflow (deferred; no publishing in current plan) 38 markers brand npm publish, container image and AUR PKGBUILD. Publishing is explicitly out of scope for the v2 preview. L6: import { NpmPublish } from "./kilocode/npm-publish" // kilocode_change / L24: // kilocode_change start +packages/opencode/script/run-workspace-server 3,8 2 shared-upstream patch marker Server/API integration not needed `bun run dev:live` (root AGENTS.md live TUI testing) Comment markers in a dev helper; v2 provides dev:live against the elected server. L3: // This script runs a separate Kilo server to be used as a remote // kilocode_change / L8: // In `.kilo/kilo.jsonc` in the root of this project add: // kilocode_change +packages/opencode/script/test-runner.ts 1,154,190,200 4 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/kilo-cli/script/test.ts` Whole-file test runner for the v1 opencode package; v2 kilo-cli has its own runner/fixture guard. L1: // kilocode_change - new file / L154: const candidates = patterns.length > 0 && !profile ? matched : matched.filter((file) => !skipped.has(file)) // kilocode_change +packages/opencode/src/acp/content.ts 79 1 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/kilo-cli/src/acp.ts` + `packages/cli` ACP bridge (acp.test.ts, acp-cli.test.ts) Marker narrows an error swallow to the expected non-URL uri case. V2 ACP bridge is exercised by Kilo acp tests; content mapping is upstream's with no Kilo divergence needed. L79: // kilocode_change - a non-URL uri is the expected fallback; only that is swallowed +packages/opencode/src/acp/permission.ts 15,62,70,72,91,94,99,126,133 9 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/src/acp.ts` and Core permission reply (no interactive flag) PRESERVED — ACP permission prompts map to v2 permission asks. MISSING — skill-shell batch prompt presentation and the human-answered `interactive` reply flag (permission groups row partial). L15: import { SkillShellPrompt } from "@/kilocode/acp/permission" // kilocode_change / L62: const skillShell = SkillShellPrompt.is(permission.metadata) // kilocode_change - skill batches list commands and never persist +packages/opencode/src/acp/service.ts 49,94,100,789,790,791,871 7 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/src/acp.ts` (endpoint auth via daemon credentials) and Gateway default model policy PRESERVED — ACP endpoint authentication is Kilo-owned (acp.ts). MISSING — `kilo-login` ACP auth method id and defaulting to the first sorted Kilo provider model when no model is configured (routed-model handles Auto but ACP default selection unverified). L49: export const AuthMethodID = "kilo-login" // kilocode_change / L94: // kilocode_change start +packages/opencode/src/agent/agent.ts 15,21,30,36,44,48,51,53,70,86,89,103,116,118,125,145,151,158,160,163,166,176,179,192,195,266,297,306,322,337,346,351,376,380,383,457,476,479,488,497,500,506,509,520,529,561,565,568,571,574,576,579,598,600,655 55 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/src/agent-policy.ts` (ask/debug added :165-194, explore/plan hardening, exploreBash ceiling), `plan-policy.ts`; baseline agent-policy-v2-parity 55 markers. PRESERVED — ask and debug agents, plan/explore permission patches, bash allowlist ceiling, human-driven tools (question/interactive) denied for delegated agents, telemetry via Kilo plugin. MISSING — build→code rename and `build` alias resolution (v2 keeps upstream `build`; agent-policy.ts:160 patches build rather than renaming), orchestrator agent, opt-in Scout repository-research agent with `requirements`/RequirementBlockedError guard, `deprecated` flag, typed metadata fields (id/displayName/source/reference) instead of provider options, default external_directory allow for global config dirs (test row partial), cache-key rebuild on permission-relevant config changes (v2 config transforms are reactive natively), hardenSystemAgents deny-only invariant (no v2 counterpart found). L15: import PROMPT_SCOUT from "@/kilocode/agent/scout.txt" // kilocode_change / L21: import { KilocodePaths } from "@/kilocode/paths" // kilocode_change +packages/opencode/src/auth/index.ts 7,9,91,96,104 5 shared-upstream patch marker Authentication/account integration partial `packages/core/src/credential.ts` + `packages/kilo-gateway/src/plugin.ts` integration methods; `packages/kilo-cli/src/telemetry.ts` PRESERVED — Kilo credential storage/removal via v2 Integration/Credential services (Gateway plugin registration, provider withdrawal on sign-out). MISSING/unverified — telemetry logout tracking and identity reset on Kilo auth removal (grep identity in telemetry.ts not confirmed), OAUTH_DUMMY_KEY placeholder pattern (v2 uses native OAuth authToken; not needed structurally). L7: import { Telemetry } from "@kilocode/kilo-telemetry" // kilocode_change / L9: export const OAUTH_DUMMY_KEY = "kilo-oauth-dummy-key" // kilocode_change +packages/opencode/src/bus/bus-event.ts 2,32,42 3 shared-upstream patch marker Package behavior indicated by path not needed None; v2 has no legacy Bus (`packages/core/src/bus.ts` is the native Effect bus) Markers expose EventV2 definitions through the deleted legacy bus schema layer for v1 compatibility. V2 consumers use native events only. L2: import { EventManifest } from "@/event-manifest" // kilocode_change / L32: // kilocode_change start - expose current Effect events through legacy bus schemas +packages/opencode/src/bus/global.ts 23 1 shared-upstream patch marker Package behavior indicated by path not needed None (v1 legacy GlobalBus removed; v2 uses Effect streams for `/api/event`) Single marker tunes Node EventEmitter listener warnings on the legacy GlobalBus, which does not exist in v2. L23: GlobalBus.setMaxListeners(50) // kilocode_change — surface warning if SSE listeners accumulate +packages/opencode/src/bus/index.ts 1,5,14,16,17,197,199,213,252 9 shared-upstream patch marker Package behavior indicated by path not needed None; v2 `packages/core/src/bus.ts` + event system Whole file is a compatibility layer restoring the Bus upstream deleted in v1.16.2 for legacy Kilo callers. V2 Kilo code is written against native events; no legacy callers remain to bridge. L1: // kilocode_change - Kilo compatibility layer. Upstream deleted this Bus (Effect PubSub) service in v1.16.2 / L5: import { Effect, Exit, Fiber, Layer, PubSub, Scope, Context, Stream, Schema } from "effect" // kilocode_change +packages/opencode/src/cli/cmd/agent.ts 112 1 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/src/commands.ts` (`agents` list only) and Core config discovery (`.opencode`) Marker targets `.kilo` for project-scoped agent creation. V2 Kilo CLI lists agents but has no agent-create command, and Core discovery does not scan `.kilo` (config/paths row); both the command and the directory convention are absent. L112: scope === "global" ? Global.Path.config : path.join(ctx.worktree, ".kilo"), // kilocode_change +packages/opencode/src/cli/cmd/attach.ts 6,9,13,52,79,87,134,152 8 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/src/commands.ts:249-262` (`attach` with directory/session) and `attach.test.tsx`; cloud in `cloud-command.ts` PRESERVED — attach to a running Kilo server with session selection (kilo-cli attach command, real-host tests). MISSING — importing a cloud session before the TUI renders (v2 cloud command has start/send/status/result but no attach-time cloud fork import), configurable basic-auth username (v2 uses daemon credentials). L6: // kilocode_change start - Kilo implementations (sdk client, cloud-session) are / L9: // kilocode_change end +packages/opencode/src/cli/cmd/config.ts 1 1 shared-upstream patch marker Configuration/settings native equivalent `packages/kilo-cli/src/settings-rpc.ts` + TUI settings dialog (`tui-plugin/settings.tsx`), `import-v1-config.ts` Whole-file Kilo config command (inspect/edit config from the CLI). V2 exposes Kilo settings through the settings RPC/TUI dialog and explicit import; a CLI subcommand is not present but the capability (read/update scoped settings with provenance) is delivered through the settings surfaces accepted in the settings row. L1: // kilocode_change - new file +packages/opencode/src/cli/cmd/db.ts 40 1 shared-upstream patch marker Package behavior indicated by path not needed None; v2 Kilo CLI has no `db` shell command Single marker hides the console window when spawning sqlite3; the command does not exist in v2 (paths.ts owns kilo2.db but exposes no shell). L40: windowsHide: true, // kilocode_change - prevent CMD window flash on Windows +packages/opencode/src/cli/cmd/debug/index.ts 61 1 shared-upstream patch marker Package behavior indicated by path not needed None; v2 kilo-cli has no debug subcommand (Core has protocol `debug` group) Branding-only marker in a command absent from the Kilo v2 CLI. L61: console.log(`kilo version: ${InstallationVersion}`) // kilocode_change +packages/opencode/src/cli/cmd/debug/ripgrep.ts 43,77 2 shared-upstream patch marker Package behavior indicated by path not needed None (debug command absent in v2 Kilo CLI) Markers adapt debug output to Core ripgrep result shapes; v1 compatibility only. L43: process.stdout.write(files.items.map((file) => file.path).join(EOL) + EOL) // kilocode_change / L77: process.stdout.write(JSON.stringify(results.items, null, 2) + EOL) // kilocode_change - preserve debug output shape +packages/opencode/src/cli/cmd/export.ts 29,31 2 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/kilo-cli/src/commands.ts` `export [--sanitize]` and `remote-transcript.ts` Marker retains Kilo summary counts in export output while accepting upstream optional file/patch values. V2 Kilo export renders sessions from the v2 projected history with its own sanitizer; the v1 summary-shape compatibility is not applicable. L29: // kilocode_change start - retain Kilo summary counts while accepting upstream's optional file and patch values / L31: // kilocode_change end +packages/opencode/src/cli/cmd/generate.ts 11,14,17,23,35,40,45 7 shared-upstream patch marker Package behavior indicated by path not needed `packages/client` `bun run generate` (unforked protocol wording) Markers replace the product name in generated descriptions; branding only. V2 generates the client from the Protocol without Kilo wording changes. L11: info: { title: string; description: string } // kilocode_change / L14: // kilocode_change start +packages/opencode/src/cli/cmd/github.handler.ts 36,142,144,202,204,230,288,324,328,333,380,441,509,519,525,536,709,758,805,808,912,1000,1011,1027,1104,1106,1371,1374,1434,1572 30 shared-upstream patch marker Package behavior indicated by path deferred Kilo GitHub action/agent integration (external `kilo-github-action`, kiloconnect app) — out of the local-runtime scope; would live in a Kilo-owned CLI module 30 markers rebrand the GitHub agent flow (bot user, workflow template, endpoints, session URLs, prompts). V2 Kilo CLI has no github subcommand or action handler; the feature depends on Kilo cloud/GitHub infrastructure and repository automation, which the plan keeps outside the current local phases (same treatment as docs-automation rows). L36: import { GitHubSecurity } from "@/kilocode/security/github" // kilocode_change / L142: const AGENT_USERNAME = "kiloconnect[bot]" // kilocode_change +packages/opencode/src/cli/cmd/import.ts 12,15,28,34,84,120,149,184,251,253 10 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/kilo-cli/src/commands.ts` `import`/`import-external`/`import-v1` and `import-v1-session.ts` Markers move the v1 import command onto the ShareNext data model. V2 Kilo has explicit import commands built on the v2 session store (proven-safe copy + external transcript import); the v1 architecture migration is superseded. L12: import * as Log from "@opencode-ai/core/util/log" // kilocode_change / L15: const log = Log.create({ service: "import" }) // kilocode_change +packages/opencode/src/cli/cmd/mcp.ts 21,24,124,192,397,426,439,442,476,518,532,583,770,814 14 shared-upstream patch marker Package behavior indicated by path needs port Kilo CLI subcommand over Core MCP service (protocol `mcp` group) and Kilo settings writer (`packages/kilo-cli/src/config-write.ts`) Markers make the MCP management command resolve supported Kilo config directories/profile, brand prompts, and use a kilo-debug client. V2 Kilo CLI has no `mcp` subcommand (commands.ts); MCP configuration is only via config files or the TUI/extension. The Kilo-config-directory resolution for writes is the substantive missing invariant. L21: // kilocode_change - KilocodeMcpConfig is dynamically imported in addMcpToConfig to keep startup fast / L24: import { Flag } from "@opencode-ai/core/flag/flag" // kilocode_change +packages/opencode/src/cli/cmd/models.ts 56,64 2 shared-upstream patch marker Provider/model routing or UI native equivalent `packages/kilo-cli/src/commands.ts:332` `models` command and Kilo-first ordering in `packages/kilo-cli/src/model-picker.ts` (host modelPicker groups/ranking per dialog-model row) Marked block sorts provider IDs so kilo/opencode providers come first. V2 Kilo model surfaces (picker) rank Kilo groups first; the CLI models command output ordering was not verified as Kilo-first, but the invariant is a presentation preference already applied at the primary selection surface. L56: // kilocode_change start / L64: // kilocode_change end +packages/opencode/src/cli/cmd/pr.ts 7,9,11,21,27,30,31,50,98,101,108,122,125,137 14 shared-upstream patch marker Package behavior indicated by path needs port Kilo CLI subcommand in `packages/kilo-cli/src/commands.ts` (absent) using worktree/vcs protocol groups Markers resolve the running CLI executable, detect app.kilo.ai session links in PR bodies and start `kilo -s `. V2 has no pr command. L7: import { existsSync } from "node:fs" // kilocode_change / L9: const subcommand = "pr" // kilocode_change +packages/opencode/src/cli/cmd/providers.ts 20,241,244,374,384,390,394,408,411,427,430,466,475,486,541,544 16 shared-upstream patch marker Provider/model routing or UI partial `packages/kilo-cli/src/credential-import.ts`, TUI integration dialog (`packages/tui` provider dialog via Gateway plugin), `kilo-gateway/src/plugin.ts` integration methods PRESERVED — Kilo login/logout via Gateway integration methods and TUI provider dialog (Kilo-first per dialog-provider row), credential import from v1. MISSING — CLI `auth login/logout/list` subcommand with codex alias and kilo.json guidance (no auth/providers command in commands.ts). L20: // kilocode_change - @/kilocode/auth/remove is dynamically imported in the logout handler to keep startup fast / L241: // kilocode_change start - keep "auth" as primary command name +packages/opencode/src/cli/cmd/remote.ts 1,22 2 shared-upstream patch marker Remote control/presence partial `packages/kilo-cli/src/remote-session.ts`, `remote-rpc.ts`, and TUI `/remote` V2 has a location-scoped preview remote adapter with explicit consent and relay authentication, but not the pinned standalone headless `kilo remote` process or its instance-advertisement lifecycle; the current schema explicitly documents this narrower surface. L1: // kilocode_change - new file / L22: // kilocode_change - K1 W1: advertise this instance on the relay +packages/opencode/src/cli/cmd/run.ts 3,13,30,33,135,168,173,215,281,288,294,295,309,435,447,454,465,475,486,506,532,753,760,768,769,770,771,794,795,797,799,801,818,820,888,891,894,902,912,920,924,930,931,940,942,959,966,981,989,1006,1008,1015,1020,1026,1045,1054,1119,1160,1206,1207 60 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/src/run.ts` (--auto :121-129, form cancel :132-139, root-only permission filter :142) and `commands.ts:283-330` (run options: session, model, agent, auto, sandbox, swarm, file, format) 60 markers. PRESERVED — headless run with --auto once-approval, file attachments, format, session resume, sandbox/swarm flags, interrupt on unanswered form/permission with failure message. MISSING — Task-child session tracking for --auto and child auto-reject (run-auto row partial), cloud fork (`--cloud-fork`), interactive direct mode (`-i/--mini` with footer/terminal), non-interactive suggestion dismissal (remote-suggestions exists but run.ts does not dismiss), bounded network retry (network row), built-in session command handling in run, daemon attach for run, skill-shell human-decision gating. L3: // kilocode_change start - use Kilo CLI branding / L13: // kilocode_change end +packages/opencode/src/cli/cmd/run/demo.ts 18 1 shared-upstream patch marker Package behavior indicated by path not needed None Marker only re-points a type import to the SDK; no behavior. L18: import type { Event, ToolPart } from "@kilocode/sdk/v2" // kilocode_change - revert to upstream native Event type +packages/opencode/src/cli/cmd/run/footer.permission.tsx 103,144,145,169,288 5 shared-upstream patch marker Package behavior indicated by path needs port Interactive direct mode does not exist in v2 kilo-cli (run.ts is headless); permission UI is the native TUI (`packages/tui`) — skill-shell option treatment would land in `packages/kilo-cli/src/skill-shell.ts` UI Markers change prompt copy and add skill-shell-aware permission options in `kilo run -i` footer. V2 has no run interactive footer; the TUI permission UI has no skill-shell batch option variant (permission.shared row partial). L103: placeholder={"Tell Kilo what to do differently" /* kilocode_change */} / L144: const skillShell = createMemo(() => props.request.metadata?.["skillShell"] === true) // kilocode_change +packages/opencode/src/cli/cmd/run/footer.prompt.tsx 15,180,422,443,449,868 6 shared-upstream patch marker Package behavior indicated by path needs port None in v2 (no `kilo run -i` mode); TUI autocomplete in `packages/tui/src/component/prompt/autocomplete.tsx` covers slash commands for the full TUI Markers surface skills as slash commands and add /exit in the direct-mode footer. The direct interactive run mode is absent in v2. L15: import { slashDisplay, slashMatches } from "@/kilocode/cli/cmd/command-display" // kilocode_change / L180: // kilocode_change +packages/opencode/src/cli/cmd/run/footer.ts 34,93,94,95,334,338,712,716 8 shared-upstream patch marker Package behavior indicated by path needs port None; interactive_terminal tool/run footer absent in v2 kilo-cli (PTY exists natively via protocol `pty`/`persistent-pty` groups) Markers add terminal write/resize/close callbacks for the Kilo interactive terminal in direct mode. V2 has native PTY services but no run-mode embedded terminal footer. L34: import { RUN_INTERACTIVE_TERMINAL_ROWS } from "@/kilocode/cli/cmd/run/interactive-terminal" // kilocode_change / L93: onTerminalWrite: (input: { terminalID: string; data: string }) => Promise // kilocode_change +packages/opencode/src/cli/cmd/run/footer.view.tsx 15,98,99,100,283,288,805,815 8 shared-upstream patch marker Package behavior indicated by path needs port None (see footer.ts) Same interactive-terminal footer feature; absent. L15: import { RunInteractiveTerminalBody } from "@/kilocode/cli/cmd/run/interactive-terminal" // kilocode_change / L98: onTerminalWrite: (input: { terminalID: string; data: string }) => Promise // kilocode_change +packages/opencode/src/cli/cmd/run/permission.shared.ts 80,82,84,130,134,151,156,157 8 shared-upstream patch marker Interactive permission footer partial `packages/tui/src/mini/permission.shared.ts` and permission service contract V2 preserves once/always/reject transitions, confirmation, rejection text and canonical presentation, but its shared footer does not carry the pinned `skillShell` option that removes Allow always; reply interactivity is represented by the native request path rather than the v1 wire flag. L80: export function permissionOptions(stage: PermissionStage, skillShell?: boolean): PermissionOption[] { // kilocode_change - skillShell param / L82: // kilocode_change start - skill-shell batches are never persisted, so no "Allow always" +packages/opencode/src/cli/cmd/run/runtime.lifecycle.ts 72,73,74,188,255,259 6 shared-upstream patch marker Package behavior indicated by path needs port None (direct interactive run mode absent) Markers wire terminal callbacks and enable mouse in the direct-mode renderer; feature absent. L72: onTerminalWrite: (input: { terminalID: string; data: string }) => Promise // kilocode_change / L73: onTerminalResize: (input: { terminalID: string; cols: number; rows: number }) => Promise // kilocode_change +packages/opencode/src/cli/cmd/run/runtime.ts 18,24,25,231,274,278,299,309,314,450,457 11 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/src/model-state-plugin.ts` (per-agent model/variant persistence) and TUI model picker (`model-picker.ts`); variant.shared row native PRESERVED — preserving compatible variants/effort when switching models (variant.shared.ts row native; model-state plugin persists variant). MISSING — direct-mode interactive terminal. L18: import { KiloRunTerminal } from "@/kilocode/cli/cmd/run-terminal" // kilocode_change / L24: // kilocode_change - preserve compatible variants when switching models +packages/opencode/src/cli/cmd/run/session-data.ts 27,28,30,83,222,231,245,249,264,1076,1112 11 shared-upstream patch marker Session lifecycle/history/UI partial `packages/kilo-cli/src/run.ts` event handling over v2 client types PRESERVED — run consumes typed v2 client events. MISSING — interactive terminal snapshot in session data (feature absent). L27: import type { Event, Part, PermissionRequest, QuestionRequest, ToolPart } from "@kilocode/sdk/v2" // kilocode_change - revert to upstream native Event / L28: import type { RunInteractiveTerminalSnapshot } from "@/kilocode/cli/cmd/run/types" // kilocode_change +packages/opencode/src/cli/cmd/run/splash.ts 3,197,237 3 shared-upstream patch marker Package behavior indicated by path needs port None (direct interactive run mode absent); TUI home logo is ported (`tui-plugin/logo.tsx`) Markers render the Kilo logo/badge and `kilo run -i -s ` resume hint in the direct-mode splash; mode absent. L3: // Renders the full Kilo entry logo and a compact [O] exit badge, plus // kilocode_change / L197: push(lines, body_left, top, "Kilo", right, undefined, TextAttributes.BOLD) // kilocode_change +packages/opencode/src/cli/cmd/run/stream.transport.ts 18,148,158,159,193,196,288,294,315 9 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/src/run.ts` event stream (`client.event.subscribe`) PRESERVED — consuming native events without sync copies (v2 has no sync copies). MISSING — interactive_terminal.* event routing (feature absent). L18: import type { Event, GlobalEvent, KiloClient } from "@kilocode/sdk/v2" // kilocode_change - revert to upstream native Event type / L148: if (event.type === "interactive_terminal.updated") return event.properties.info.sessionID // kilocode_change +packages/opencode/src/cli/cmd/run/subagent-data.ts 1 1 shared-upstream patch marker Package behavior indicated by path not needed None Single type-import marker; no behavior. L1: import type { Event, Message, Part, PermissionRequest, QuestionRequest, ToolPart } from "@kilocode/sdk/v2" // kilocode_change - revert to upstream nat +packages/opencode/src/cli/cmd/run/tool.ts 1,28,105,428,453,1163,1175 7 shared-upstream patch marker Tool behavior/UI partial `packages/kilo-cli/src/run.ts` text/json formatting of tool parts PRESERVED — run output formats tool parts natively. MISSING — interactive_terminal display rule (tool absent). L1: // Per-tool display rules shared across `kilo run` output paths. // kilocode_change / L28: import type { InteractiveTerminalTool } from "@/kilocode/tool/interactive-terminal" // kilocode_change +packages/opencode/src/cli/cmd/run/trace.ts 4 1 shared-upstream patch marker Package behavior indicated by path not needed `packages/kilo-cli/src/paths.ts` (isolated kilo2 log/data layout) Comment-only marker; v2 log locations follow the kilo2 layout. L4: // ~/.local/share/kilo/log/direct/-.jsonl. Also writes // kilocode_change +packages/opencode/src/cli/cmd/run/types.ts 15,178 2 shared-upstream patch marker Package behavior indicated by path needs port None (feature absent) Markers add the interactive_terminal footer state variant; the direct-mode terminal feature is absent in v2. L15: import type { RunInteractiveTerminalSnapshot } from "@/kilocode/cli/cmd/run/types" // kilocode_change / L178: | { type: "interactive_terminal"; terminal: RunInteractiveTerminalSnapshot } // kilocode_change +packages/opencode/src/cli/cmd/run/variant.shared.ts 6 1 shared-upstream patch marker Model variant persistence native equivalent `packages/tui/src/mini/variant.shared.ts`, `model-preference.ts`, `context/local.tsx:155`, and Kilo `layout().paths.state` The source comment is not runtime evidence. Kilo injects its `kilo2/` state directory into the TUI local context, which builds the model preference repository at that injected state plus `model.json`; behavior and Kilo-owned storage are preserved. L6: // The saved variant persists across sessions in ~/.local/state/kilo/model.json // kilocode_change +packages/opencode/src/cli/cmd/serve.ts 12,21,27,29,42,54 6 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/src/commands.ts:262` `serve` and `daemon.ts` lifecycle (native-lifecycle.test.ts) PRESERVED — serve command with graceful daemon lifecycle/shutdown tests. MISSING — session-ingest drain on shutdown (session export/ingest feature absent in v2). L12: instance: false, // kilocode_change / L21: // kilocode_change start +packages/opencode/src/cli/cmd/session.ts 86,98,100,104,108,117,163,175,177,216 10 shared-upstream patch marker Session lifecycle/history/UI partial `packages/kilo-cli/src/commands.ts:332` (`sessions [--directory]`) over v2 session pagination and project registry (experimental.session.list adapter in `packages/kilo-vscode/src/backend`) Marked blocks add `--all` (Session.listGlobal across projects with project name/worktree columns), `--search` title filter, and JSON formatters. V2 `sessions` command lists sessions for one directory only; the global cross-project listing exists as a backend adapter for the extension (board 433: experimental.session.list over v2 pagination + project registry) but is not exposed by the CLI command, and title search/JSON output are not present in commands.ts. L86: // kilocode_change start / L98: // kilocode_change end +packages/opencode/src/cli/cmd/stats.ts 88,90,188,192,203 5 shared-upstream patch marker Package behavior indicated by path needs port Kilo CLI stats command over Core `session/stats.ts` (v2 has session stats service but no CLI command) Markers export stats aggregation for tests and avoid double-counting propagated subagent cost. V2 Kilo CLI has no stats command; Core stats.ts exists but its treatment of child cost aggregation for a CLI report is not consumed. L88: // kilocode_change start - expose Effect stats aggregation for Kilo regression coverage / L90: // kilocode_change end +packages/opencode/src/cli/cmd/tui.ts 15,18,25,26,27,35,62,64,81,126,134,136,141,146,260,265,278,285,291,295,303,305,306,307,314,316,317,338,369,404,416,422,426,433,440,452,456,457,460,468,471,495 42 shared-upstream patch marker User-interface behavior partial `packages/kilo-cli/src/tui.ts`, `daemon.ts` (service status/start/reuse), `interactive-server.ts`; baseline cli-tui-closure-verification 42 markers. PRESERVED — default TUI attaches to the Kilo daemon with authenticated routes (daemon.ts credentials), graceful shutdown (native-lifecycle tests), worker/parent correlation via kilo2 identity. MISSING — cloud-fork session import before TUI render, stale PWD handling for `bun --cwd` wrappers (dev.ts launcher behavior unverified), explicit `--server`/remote-exit bridge for embedded worker transport (worker-shutdown row native). L15: import type { StartInput } from "@/kilocode/cli/cmd/tui/thread" // kilocode_change - runtime imports deferred into handlers / L18: // kilocode_change start - correlate the TUI worker with its parent process +packages/opencode/src/cli/cmd/uninstall.ts 11,28,59,133,134,135,136,137,138,139,184,185,186,187,188,189,190,196,213,227,264,273,289,308 24 shared-upstream patch marker Package behavior indicated by path deferred Kilo CLI command paired with package-manager distribution (out of current phase; portable preview only) 24 markers rebrand uninstall (npm/brew/choco/scoop names, thank-you copy, shell-rc marker cleanup for both opencode and kilo). V2 ships a portable preview with `update` only; publishing/package-manager distribution is explicitly out of scope, so uninstall parity is deferred with it. L11: import { Brew as KiloBrew } from "@/kilocode/installation" // kilocode_change / L28: describe: "uninstall kilo and remove all related files", // kilocode_change +packages/opencode/src/cli/cmd/upgrade.ts 9,20,31,46,49,61,69 7 shared-upstream patch marker Package behavior indicated by path deferred `packages/kilo-cli/src/updater.ts` (`update --check/--apply/--rollback` local portable path) Markers rebrand the upgrade command and its method list. V2 replaced this with a local portable updater; package-manager upgrade methods are deferred with distribution (installation row partial). L9: describe: "upgrade kilo to the latest or a specific version", // kilocode_change / L20: choices: ["curl", "npm", "yarn", "pnpm", "bun", "brew", "choco", "scoop"], // kilocode_change +packages/opencode/src/cli/effect-cmd.ts 5,92,96 3 shared-upstream patch marker Package behavior indicated by path not needed None (v2 commands are Effect-native) Markers bridge the legacy ALS context for yargs Effect commands; no legacy context in v2. L5: import { Instance } from "@/kilocode/instance" // kilocode_change / L92: // kilocode_change start - preserve legacy instance context across Promise callbacks +packages/opencode/src/cli/error.ts 50,58,67,68,69,106 6 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/src/run.ts`/`commands.ts` error output and TUI error guidance (dialog rows assessed needs port) PRESERVED — v2 CLI surfaces typed errors for models/sessions. MISSING — Kilo-branded remediation hints (`kilo models`, kilo.json names, `kilo auth login `), `modelsEmpty` message (TUI error guidance row needs port). L50: return `MCP server "${data}" failed.` // kilocode_change / L58: // ProviderModelNotFoundError: { providerID: string, modelID: string, suggestions?: string[], modelsEmpty?: boolean } // kilocode_change +packages/opencode/src/cli/logo.ts 1,8,11,12 4 shared-upstream patch marker Package behavior indicated by path already ported `packages/kilo-cli/src/tui-plugin/logo.tsx`/`logo-data.ts` replacing `home.logo` Markers blank upstream logo decorations because Kilo renders its own logo; v2 Kilo replaces the home logo slot entirely. L1: // kilocode_change start / L8: // kilocode_change end +packages/opencode/src/cli/network.ts 24,25,37,56,78,84 6 shared-upstream patch marker Package behavior indicated by path not needed None (no mDNS in v2 server) Branding on a removed feature. L24: describe: "custom domain name for mDNS service (default: kilo.local)", // kilocode_change / L25: default: "kilo.local", // kilocode_change +packages/opencode/src/cli/tui/worker-shutdown.ts 1 1 shared-upstream patch marker Embedded v1 TUI worker teardown ordering native equivalent V2 elected-service and interactive-server lifecycle ownership The exact drain → dispose → stopServer helper is obsolete because v2 no longer embeds that TUI worker. Its invariant—stop accepting/producing work, dispose scoped resources, then release the server—is owned by the service/interactive-server shutdown lifecycle, so no dormant feature is being deferred. L1: // kilocode_change - new file +packages/opencode/src/cli/tui/worker.ts 13,14,15,16,17,19,20,23,31,42,43,53,56,63,106,107,108,112 18 shared-upstream patch marker User-interface behavior partial `packages/kilo-cli/src/tui.ts` + `daemon.ts`; worker-shutdown row native equivalent PRESERVED — worker shutdown ordering and remote exit (worker-shutdown.ts row native), logs kept off the TUI terminal (kilo2 log paths). MISSING — session-ingest drain before dispose (ingest feature absent), remote-exit bridge specifics for embedded transport. L13: import { KiloLog } from "@/kilocode/log" // kilocode_change / L14: import { ensureProcessMetadata } from "@opencode-ai/core/util/opencode-process" // kilocode_change +packages/opencode/src/cli/ui.ts 4,6,8 3 shared-upstream patch marker Package behavior indicated by path already ported `packages/kilo-cli/src/tui-plugin/logo-data.ts` Single import marker; logo ported. L4: import { tui } from "../kilocode/cli/logo" // kilocode_change / L6: // kilocode_change start +packages/opencode/src/cli/upgrade.ts 12,14 2 shared-upstream patch marker Package behavior indicated by path deferred `packages/kilo-cli/src/updater.ts` Marker restricts auto-upgrade to registry installs since Kilo publishes only via npm. V2 updater is local-portable; registry publishing is deferred. L12: // kilocode_change start - only auto-upgrade for npm/yarn/pnpm/bun (we only publish @kilocode/cli via npm registry) / L14: // kilocode_change end +packages/opencode/src/command/index.ts 10,11,14,29,31,60,91,116,120,122,125,127,160,163,171,173,181,184,186,197,198,199,200,202,211,212,218,219,222,233 30 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/src/review-policy.ts` (review + deprecated aliases), `resume-external.ts`, Core `session/command.ts` command editor PRESERVED — /review and deprecated local-review aliases (review-policy.ts adds both), external resume commands (resume-external/external-sessions), config-defined commands overriding plugin defaults (v2 command editor). MISSING — `trusted` flag on skill-sourced templates gating `!cmd` shell (skill-shell.ts gates by config rather than a per-command trusted field), deferred override application across command/skill/MCP sources with shared names, `variant` on commands (v2 command schema has model but variant unverified). L10: import { legacyReviewCommand, reviewCommand } from "@/kilocode/review/command" // kilocode_change / L11: import { apply as applyOverride, type Override } from "@/kilocode/command/override" // kilocode_change +packages/opencode/src/config/agent.ts 11,12,17,21,29,37,39,43,52,60,86,87,97,102,110,118,120,124,133,144,157 21 shared-upstream patch marker Configuration/settings needs port `packages/core/src/config/variable.ts` (`substitute` :27 — no trust/scope gating) and `config/plugin/agent.ts` Markers confine untrusted project agent prompt `{file:}` reads to the project root, gate `{env:}` on trusted sources and keep invalid agent files non-fatal. V2 ConfigVariable.substitute resolves env and file tokens for every source without trust or root confinement (variable.ts:27-60), so a project agent file could read arbitrary files/env — the Kilo security invariant is absent. L11: import { ConfigVariable } from "./variable" // kilocode_change / L12: // kilocode_change start +packages/opencode/src/config/command.ts 6,11,17,22,30,38,40,44,53,71,82 11 shared-upstream patch marker Configuration/settings partial `packages/core/src/config/normalize.ts` diagnostics and Kilo settings warnings (`packages/kilo-cli/src/settings.ts` warnings RPC) PRESERVED — v2 config normalization produces structured diagnostics and Kilo settings expose warnings (settings collectionGet diagnostics per board 424). MISSING — trust/scope substitution for command templates (same gap as agent.ts) and Kilo command override fields carried by these markers (subtask/variant handled natively in v2 command schema). L6: import { SchemaIssue } from "effect" // kilocode_change - preserve Effect issue details in Kilo warnings / L11: // kilocode_change start +packages/opencode/src/config/config.ts 13,16,19,41,42,57,59,61,79,85,89,98,114,129,150,154,162,165,172,181,186,190,198,200,211,217,219,228,246,252,260,280,281,315,318,324,325,334,344,352,353,358,361,364,367,379,381,384,385,386,395,404,418,427,430,441,452,456,465,495,529,580,587,620,634,638,645,650,653,663,668,673,684,691,699,707,710,713,722,727,741,766,794,795,798,802,813,824,853,871,875,879,882,894,899,950,952,958,971,976,980,999,1026,1032,1035,1037,1073,1075,1090,1093,1106,1119,1127 113 shared-upstream patch marker Configuration/settings partial `packages/core/src/config.ts` (Kilo refresh markers :36,:322-325), `config/discovery.ts`, `config/watch.ts`; Kilo: `packages/kilo-cli/src/{settings,settings-rpc,project-config,config-write,import-v1-config}.ts`, baseline config-mapping-v2-gap 113 markers. PRESERVED — warnings accumulator surfaced to clients (settings-rpc warnings/diagnostics), explicit refresh when other processes edit config (core config.ts refresh RPC + native watch.ts), Kilo project config updates through a Kilo writer (project-config.ts, config-write.ts), serialized read-merge-write for concurrent approvals (settings.ts revision checks/atomic writes), $schema handling for Kilo documents (settings.ts), Kilo default plugins registered by the host instead of config injection. MISSING — trusted/untrusted substitution (`{env:}` gated to trusted sources; `{file:}` confined to projectRoot) in Core config loading (variable.ts has no trust), discovery of kilo.json/kilo.jsonc and .kilo/.kilocode config directories by Core (discovery.ts only .opencode/opencode.json), well-known org and console-managed org config sources, MDM/managed directory loading, primary-checkout config directories for worktrees, retireIndexingFlag/legacy Kilo config compatibility warnings, dispose-skipping for permission-only updates, extra .gitignore entries (pnpm-lock/yarn.lock/agent-manager.json). L13: import { applyEdits, findNodeAtLocation, modify, parseTree } from "jsonc-parser" // kilocode_change - parseTree/findNodeAtLocation used in patchJsonc / L16: // kilocode_change start +packages/opencode/src/config/managed.ts 23,25,27 3 shared-upstream patch marker Configuration/settings needs port Kilo paths in `packages/kilo-cli/src/paths.ts` and Core config discovery (`config/discovery.ts` has no managed/system directory) Markers point managed config to /Library/Application Support/kilo, ProgramData\kilo, /etc/kilo. V2 Core discovery reads global/explicit/project sources only (discovery.ts:24-70; grep ProgramData|/etc/kilo: none), so enterprise managed config has no location at all, let alone a Kilo-branded one. L23: return "/Library/Application Support/kilo" // kilocode_change / L25: return path.join(process.env.ProgramData || "C:\\ProgramData", "kilo") // kilocode_change +packages/opencode/src/config/markdown.ts 4,57,59,70,75,77,80,85,97,100,109 11 shared-upstream patch marker Configuration/settings partial `packages/core/src/config/markdown.ts` PRESERVED — frontmatter parsing with substitution exists natively (config/markdown.ts uses ConfigVariable). MISSING/unverified — preserving unquoted colon values as exact strings, retrying invalid frontmatter with permissive sanitization, and trust/fileScope confinement for `{file:}` in project markdown (variable.ts has no trust gating). Paired test/config/markdown.test.ts pending. L4: import { KilocodeMarkdown } from "../kilocode/config/markdown" // kilocode_change / L57: // kilocode_change start - preserve unquoted colon values as exact strings +packages/opencode/src/config/paths.ts 29,35 2 shared-upstream patch marker Configuration/settings partial "`packages/kilo-cli/src/project-config.ts:32` (`["".kilo"", "".kilocode""]`) for Kilo settings; Core `config/discovery.ts:41` still scans `.opencode`/opencode.json only" Kilo settings/project config on v2 read .kilo and .kilocode directories, but Core config discovery for agents, commands, skills and opencode.json does not include .kilo/.kilocode names, so project-local Kilo agent/command definitions are not discovered natively (config-mapping-v2-gap covers import, not discovery). L29: targets: [".kilocode", ".kilo"], // kilocode_change / L35: targets: [".kilocode", ".kilo"], // kilocode_change +packages/opencode/src/config/tui-migrate.ts 2,12,91,99,127,132 6 shared-upstream patch marker Configuration/settings not needed `packages/tui/src/config/v1/index.tsx` (v1 TUI config compatibility) and `packages/kilo-cli/src/import-v1-config.ts` Markers migrate legacy TUI config files into the kilo directory with the app.kilo.ai schema URL. V2 handles v1 TUI config via the retained v1 schema and explicit Kilo import; in-place migration of the legacy layout is intentionally not performed (copy-then-migrate policy), so this shim is not carried. L2: import { access, constants } from "fs/promises" // kilocode_change / L12: const TUI_SCHEMA_URL = "https://app.kilo.ai/tui.json" // kilocode_change +packages/opencode/src/config/tui.ts 24,44,101,108,110,114,142,144,159,162,164,166,186,189,202,208,214,217,224,227,233,244,246,252,256,257,287,293 28 shared-upstream patch marker Configuration/settings partial `packages/tui/src/config/index.tsx` + `config/v1/index.tsx:122` (sound_pack default opencode.default), Kilo plugin registration in `packages/kilo-cli/src/tui-plugin/tui.tsx` PRESERVED — Kilo default TUI plugins are registered by the Kilo host (tui.tsx kilo.preview plugin) rather than injected via config. MISSING — `kilo.default` sound pack default (v2 defaults to opencode.default; attention row partial already notes missing Kilo pack), loading tui.json from Kilo config directories (.kilo/.kilocode, KILO_CONFIG_DIR), trust-gated {env:}/{file:} substitution, `info()` editable config for Kilo Console. L24: import { KilocodeDefaultPlugins } from "@/kilocode/config/default-plugins" // kilocode_change / L44: readonly info: () => Effect.Effect // kilocode_change - editable config for Kilo console +packages/opencode/src/config/variable.ts 6,19,21,26,27,30,42,47,52,53,77,79,84,101,107,115,120,141,143 19 shared-upstream patch marker Configuration/settings needs port `packages/core/src/config/variable.ts:27-60` (`substitute`/`substituteFiles`) — Kilo hardening would need an upstream-facing patch or a Kilo config plugin gate Markers make untrusted project config unable to read env vars, confine `{file:}` to the project root, reject server-credential substitution, skip commented tokens, and read one opened file to avoid TOCTOU. V2 substitute() resolves `{env:}` from process.env for all sources and resolves `{file:}` to any absolute or config-relative path with only the commented-line skip present (variable.ts:44-58). The trust/confinement invariants are absent. L6: import { ConfigVariableGuard } from "@/kilocode/config/variable" // kilocode_change / L19: // kilocode_change start +packages/opencode/src/effect/app-runtime.ts 7,17,49,56,59,64,65,66,67,68,70,73,77,126,133 15 shared-upstream patch marker Package behavior indicated by path not needed `packages/kilo-cli/src/runtime.ts`/`interactive-server.ts` host composition Markers keep Credential/ModelCache/Memory/EventV2/etc. in the v1 AppRuntime layer graph. V2 Kilo composes services through the host and plugin contexts; this v1 layer-graph glue has no counterpart. L7: import { Credential } from "@opencode-ai/core/credential" // kilocode_change / L17: import { ModelCache } from "@/provider/model-cache" // kilocode_change +packages/opencode/src/effect/bridge.ts 6,15,24,37,46,51,53,57,62,76,78,82,91 13 shared-upstream patch marker Package behavior indicated by path not needed None; v2 Kilo code is Effect-native with Location services Markers restore legacy Instance/workspace ALS contexts around Effect runs for callback code. V2 has no legacy ALS instance context. L6: import { Instance, type InstanceContext } from "@/kilocode/instance" // kilocode_change / L15: // kilocode_change start - preserve legacy Kilo contexts across Promise callbacks +packages/opencode/src/effect/instance-state.ts 2,15 2 shared-upstream patch marker Package behavior indicated by path not needed None (v2 uses `Location.Service`/`InstanceRef` natively) Two markers fall back to a captured legacy instance context; v1-only compatibility. L2: import { capture, type InstanceContext } from "@/kilocode/instance" // kilocode_change / L15: const ctx = (yield* InstanceRef) ?? capture() // kilocode_change +packages/opencode/src/effect/run-service.ts 7,8,29,41,59,65 6 shared-upstream patch marker Package behavior indicated by path not needed None; v2 service lifetimes are scoped Layers Markers bridge the ALS instance context and allow Kilo runtimes to release resources. V2 Kilo services use scoped layers/plugin lifetimes. L7: import { context as instanceContext } from "@/project/instance-context" // kilocode_change / L8: import { LocalContext } from "@/util/local-context" // kilocode_change +packages/opencode/src/effect/runner.ts 2,84,94,96,110,119,147 7 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/core/src/session/run-coordinator.ts` and execution claim (write-ahead claim marks busy period per AGENTS Session Core) Markers ensure work publishes busy only after the Running state is committed. V2 SessionRunCoordinator commits a durable execution claim before running and publishes execution events from the coordinator, matching the ordering invariant. L2: import { KiloRunner } from "@/kilocode/effect/runner" // kilocode_change / L84: // kilocode_change start - do not let work publish busy before the Running state is committed +packages/opencode/src/effect/runtime-flags.ts 20,23,25,45,53 5 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/src/skill-shell.ts` (kill-switch via config), `packages/kilo-cli/src/paths.ts` env handling PRESERVED — skill-shell disable exists as Kilo policy configuration (skill-shell parity baseline). MISSING — KILO_DISABLE_CHANNEL_DB, KILO_SKIP_MIGRATIONS, experimental Scout and session-switcher flags (features absent in v2). L20: disableChannelDb: bool("KILO_DISABLE_CHANNEL_DB"), // kilocode_change / L23: disableSkillShell: bool("KILO_DISABLE_SKILL_SHELL"), // kilocode_change - disable shell injection in skill bodies +packages/opencode/src/event-v2-bridge.ts 6,7,41,42,44,46,49,53,63,75 10 shared-upstream patch marker Package behavior indicated by path not needed None; v2 event stream is native EventV2 Markers encode EventV2 payloads for legacy bus and SSE consumers and tag instance-less events as global. V2 has no legacy consumers. L6: import { EventManifest } from "@/event-manifest" // kilocode_change / L7: import * as EventWire from "@/kilocode/event-wire" // kilocode_change +packages/opencode/src/format/index.ts 12,89,90 3 shared-upstream patch marker Package behavior indicated by path needs port `packages/core/src/formatter.ts`/`formatter/*` spawn environment (no env scrubbing found) — Kilo could apply via sandbox env policy in `packages/kilo-cli/src/sandbox.ts` Markers run formatters with a model-facing sanitized environment (extendEnv:false). V2 formatter spawns inherit the process environment; sandbox.ts confines processes only when the sandbox is enabled and does not scrub credentials for formatters specifically. L12: import { model as modelEnv } from "@/kilocode/process/env" // kilocode_change / L89: env: modelEnv(item.environment), // kilocode_change - formatters must not inherit backend credentials +packages/opencode/src/id/id.ts 10 1 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/kilo-cli/src/remote-suggestions.ts` suggestion IDs Marker adds a suggestion ID prefix. V2 Kilo suggestions generate their own IDs in the remote-suggestions holder; the upstream ID table need not change. L10: suggestion: "sug", // kilocode_change +packages/opencode/src/ide/index.ts 35,39 2 shared-upstream patch marker Package behavior indicated by path not needed None; v2 `packages/kilo-vscode` bundles/launches the CLI itself Marker disables CLI-driven IDE extension installation because the Kilo extension bundles the CLI. Same architecture in v2; no install path to disable. L35: // kilocode_change start - Kilo's VS Code extension bundles the CLI; auto-install from CLI is not applicable / L39: // kilocode_change end +packages/opencode/src/image/image.ts 10,15,98 3 shared-upstream patch marker Package behavior indicated by path partial `packages/core/src/image.ts` (Image.Service normalization) PRESERVED — image validation/normalization service exists (core/image.ts; session prompt normalizeImageAttachment). MISSING/unverified — 5 MiB shared cap constant and Photon-unavailable fallback preserving valid images (not found). L10: export const MAX_BASE64_BYTES = 5 * 1024 * 1024 // kilocode_change - share user file pre-read limit / L15: // kilocode_change start - preserve valid in-limit images when Photon is unavailable +packages/opencode/src/index.ts 5,25,32,33,34,37,39,51,53,84,85,92,103,109,120,123,157 17 shared-upstream patch marker Package behavior indicated by path not needed `packages/kilo-cli/src/index.ts` + `commands.ts` (Kilo owns its CLI entry on v2) 17 markers thread KiloCli hooks into the upstream entrypoint. V2 Kilo has a separate CLI package with its own entry and command tree, so the upstream-entry patching is structurally unnecessary; individual Kilo commands are tracked by their own rows/plan phases. L5: // kilocode_change - upstream account console intentionally omitted; KiloCli registers `kilo console` for local settings / L25: // kilocode_change - upstream web command intentionally omitted; Kilo does not ship an embedded web UI +packages/opencode/src/installation/index.ts 16,24,51,139,140,141,142,143,160,188,210,214,218,231,240,259,271,274,284,298,305,317,325,332,334,337,343,348,365,368 30 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/src/updater.ts` (local portable staging/rollback; baseline updater-local-parity) and `App.useragent` via `packages/kilo-cli/src/host.ts:21` PRESERVED — user agent derived from Kilo app identity; local updater with check/apply/rollback for the portable preview. MISSING — package-manager upgrade paths (brew tap/formula, npm/pnpm/bun global, choco, scoop) and Kilo release/install endpoints (updater.ts has no package-manager methods; publishing/release channels are out of scope per plan). L16: // kilocode_change start / L24: // kilocode_change end +packages/opencode/src/kilo-sessions/attached-state.ts 1,93,109,116,123,131,133,155,183,190,212,217,223,228,267,269,276,318,366,373 20 Kilo-owned inherited marker Remote attached-session ownership state native equivalent `packages/kilo-cli/src/remote-session.ts:118-126,386-460,1183-1221` and `test/remote-session.test.ts:924-1139,1555-1723` The v2 relay owns authoritative heartbeat reconciliation, detach tombstones/fences, reconnect deduplication and fresh ownership removal; focused integration tests exercise ACK loss, reconnect and duplicate subscription behavior. L1: // kilocode_change - extracted state machine that separates presence-owned / L93: // kilocode_change - collision-safe union key. The historical "|" join +packages/opencode/src/kilo-sessions/instance-advertisement.ts 1 1 Kilo-owned inherited marker Spawn-capable remote instance advertisement deferred No current v2 destination; intentionally outside the preview relay surface This payload served the retired standalone spawn-capable `kilo remote` host. Current v2 advertises session capabilities but deliberately does not expose cloud-clone or instance-spawn metadata; revisit only if that product surface returns. L1: // kilocode_change - new file +packages/opencode/src/kilo-sessions/kilo-sessions.ts 47,267,615,658,698,722,750,759,823,858,876,882 12 Kilo-owned inherited marker Session lifecycle/history/UI partial `packages/kilo-cli/src/remote-session.ts`, `remote-status.ts`, `remote-plugin.ts` (heartbeat with capabilities; baseline remote-v2-parity, remote-share-next-contracts) Marked lines (12) cover: AppNodeBuilder shim (not needed on v2); module-level instance advertisement flag with default advertisement on enableRemote, immediate heartbeat on set/reconnect; per-session `platform` in heartbeat; attach/detach/has/ownedCount seams for exit_cli with negative-containment fence; lazy import cycle break. V2 remote-session/remote-plugin implement heartbeat (now with `capabilities.attachments`), attached-session state was assessed native (attached-state.ts row) and instance advertisement deferred (instance-advertisement.ts row). Preserved: attach/detach ownership and heartbeat; missing: advertisement flag/out-of-band heartbeat and per-session platform field pending the deferred advertisement row. L47: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change / L267: // kilocode_change - K1 W1: module-level instance advertisement flag. +packages/opencode/src/kilo-sessions/remote-command.ts 65,163,206 3 Kilo-owned inherited marker Remote command protocol native equivalent `packages/kilo-cli/src/remote-session.ts:863-873,1183-1228` and `test/remote-session.test.ts:189-248,924-1139` V2's adapter preserves bounded protocol-v1 command listing, always advertises `canExitSession: true`, rejects uncatalogued commands, and treats `exit_cli` as validated session detach; current integration tests exercise those invariants. L65: // kilocode_change - K1 W1: `canExitSession` is an INDEPENDENT producer / L163: // kilocode_change - K1 W1: always advertise `canExitSession: true`. This +packages/opencode/src/kilo-sessions/remote-protocol.ts 13,21,46 3 Kilo-owned inherited marker Remote relay wire protocol partial `packages/kilo-cli/src/remote-protocol.ts` and `remote-session.ts` V2 carries the strict protocol-v1 session/heartbeat/command schemas and platform-aware presence behavior, but intentionally omits the pinned spawn-capable instance advertisement because the preview adapter does not support cloud-session cloning or the standalone headless host. L13: // kilocode_change - K1 W1: per-session platform advertises the platform the / L21: // kilocode_change - K1 W1: instance advertisement. Presence on a heartbeat +packages/opencode/src/kilo-sessions/remote-sender.ts 13,52,81,85,91,93,104,152,155,160,162,164,273,284,285,323,324,327,621,708,796,886,887,929 24 Kilo-owned inherited marker Remote session command dispatcher partial `packages/kilo-cli/src/remote-session.ts` and `remote-suggestions.ts` V2 ports command discovery, model listing, inline prompt admission, suggestion lifecycle, detach and redacted failure handling. The pinned in-process create_session spawn/rollback/inheritance path and instance-picker host surface are intentionally absent. L13: import { Suggestion } from "@/kilocode/suggestion" // kilocode_change / L52: // kilocode_change start - create_session: strict v1 request with optional inheritance fields +packages/opencode/src/kilo-sessions/remote-ws.ts 17,254,275 3 Kilo-owned inherited marker Remote WebSocket heartbeat fencing partial `packages/kilo-cli/src/remote-session.ts:118-126,257-347,386-460,1183-1221` The v2 relay implements immediate/reconnect heartbeats and negative-containment detach fencing, but it does not propagate the pinned optional instance advertisement through heartbeat gathering and the wire. L17: // kilocode_change - K1 W1: widened return type so the optional `instance` / L254: // kilocode_change - K1 W1: spread optional `instance` so the +packages/opencode/src/kilocode/agent-manager/event.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path needs port Kilo-owned schema module (e.g. `packages/schema/src/kilocode/agent-manager.ts`) emitted by the still-missing agent-manager tool; consumer contract already exists in `packages/kilo-vscode/src/agent-manager/tool-start.ts` (ToolRequest/ToolTask mirror AgentManagerStart/AgentManagerTask) Marked file is the whole AgentManagerTask/AgentManagerMode/AgentManagerStart bus-event schema (requestID, worktree|local mode, tasks with prompt/name/branchName/model/variant). V2 has the transplanted extension consumer expecting exactly these fields, but no v2 Core/CLI producer or event definition exists (grep AgentManager in kilo-cli/core/schema: none); it depends on the agent-manager tool row already classified needs port. L1: // kilocode_change - new file +packages/opencode/src/kilocode/agent/index.ts 1,485,511 3 Kilo-owned inherited marker Package behavior indicated by path partial `packages/kilo-cli/src/agent-policy.ts` (Kilo agent policy) plus native v2 permission defaults; suggest tool permission at `packages/kilo-cli/src/remote-suggestions.ts` tool seam "Only marked lines (L485, L511) add `suggest: ""allow""` to Kilo's default and build-agent permission rulesets; the new-file marker covers the fork's own Agent module. V2 agent-policy.ts owns Kilo agent behavior and baseline agent-policy-v2-parity records that seam, and the v2 suggest tool is Kilo-owned and registered without a per-agent permission gate, so the marked default has no exact counterpart: allowed-by-default is preserved in effect, but the explicit per-agent `suggest` permission rule does not exist in v2's permission vocabulary." L1: // kilocode_change - new file / L485: suggest: "allow", // kilocode_change +packages/opencode/src/kilocode/agent/options.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path needs port Kilo agent-policy transform or a request-boundary hook in `packages/core/src/session/model-request.ts` (providerOptions assembly at :312-341) Marked file strips id/displayName/source/reference/resolved from agent.options so strict providers (NVIDIA NIM 400) do not reject them. V2 model-request builds providerOptions from context only and no Kilo code writes displayName/source metadata into agent options (grep stripInternalOptions/INTERNAL_OPTION_KEYS: none). If the v2 Kilo agent policy re-introduces org/marketplace agent metadata on options, this boundary strip must be ported; today the invariant is unimplemented rather than natively covered. L1: // kilocode_change - new file +packages/opencode/src/kilocode/bash-hierarchy.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path needs port Kilo permission policy over v2 `packages/core/src/permission.ts` saved rules (`permission/saved.ts`) Marked file generates `npm *`, `npm install *`, exact-text always patterns from BashArity prefixes for the v1 permission 'always' reply. V2 permission saved rules (core/src/permission/saved.ts) store request-shaped rules and no v2 code expands a shell command into arity-level wildcard patterns (grep BashHierarchy/hierarch in kilo-cli/core: none besides unrelated model-prompt-policy). Invariant absent. L1: // kilocode_change - new file +packages/opencode/src/kilocode/bootstrap.ts 23 1 Kilo-owned inherited marker Package behavior indicated by path not needed None; v2 composes Kilo services through plugin `define`/Layer nodes in `packages/kilo-cli/src/interactive-server.ts` and `runtime.ts` The only marked line is the `AppNodeBuilder` import used to build v1 LayerNode graphs for Kilo bootstrap; v2's Kilo bootstrap is the host composition in kilo-cli (interactive-server/runtime/daemon) using v2 plugin contexts, so this fork-specific effect-plumbing shim has no v2 counterpart to port. The unmarked bootstrap responsibilities are tracked by their own feature rows. L23: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change +packages/opencode/src/kilocode/claw/client.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path needs port New Kilo-owned TUI plugin module under `packages/kilo-cli/src/tui-plugin/` reusing `packages/kilo-cli/src/cloud/*` HTTP/WebSocket clients; extension counterpart exists at `packages/kilo-vscode/src/kiloclaw/` Whole file: KiloClaw chat client wrapper (list/create/select/rename/delete conversations, WS subscription rotation, legacy ChatMessage translation). V2 CLI/TUI has no Claw surface (grep claw in kilo-cli/src: only presence-service.ts string); master plan lists Claw among remaining feature-specific services. The transplanted VS Code KiloClaw provider is an IDE consumer, not the TUI client. L1: // kilocode_change - new file +packages/opencode/src/kilocode/claw/hooks.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path needs port Same new Kilo-owned TUI Claw plugin under `packages/kilo-cli/src/tui-plugin/` Whole file provides Solid signals/resources over the Claw client for the TUI sidebar/dialogs; no v2 TUI Claw consumer exists (grep claw in packages/tui/src and kilo-cli tui-plugin: none). Port together with claw/client.ts. L1: // kilocode_change - new file +packages/opencode/src/kilocode/claw/kilo-chat-client.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path needs port Kilo-owned client under `packages/kilo-cli/src/cloud/` (alongside client.ts/http.ts/websocket-stream.ts) Whole file is the HTTP client for the kilo-chat worker (conversations, tokens, bot status). V2 cloud/ owns Kilo Cloud tRPC/stream-ticket/WebSocket clients but has no kilo-chat endpoints (grep claw/kilo-chat: none). Concrete missing transport for the Claw feature. L1: // kilocode_change - new file +packages/opencode/src/kilocode/claw/sidebar.tsx 1 1 Kilo-owned inherited marker Package behavior indicated by path needs port Kilo-owned `sidebar.content` claim in `packages/kilo-cli/src/tui-plugin/` (same seam as sidebar-pr.tsx/sidebar-usage.tsx) Whole file renders Claw status in the v1 TUI sidebar. V2 Kilo sidebar registration in tui-plugin/tui.tsx installs usage/memory/indexing/PR/processes/bench/routed-model/account sections only; no Claw section. L1: // kilocode_change - new file +packages/opencode/src/kilocode/claw/types.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path needs port Kilo-owned schema in `packages/schema/src/kilocode/` or with the Claw plugin Whole file defines Kilo Chat protocol types (ChatMessage, ConversationListItem, status/typing events). V2 kilo-vscode/src/kiloclaw/types.ts holds the extension's own types but nothing in CLI/TUI/schema; needed by the Claw TUI port. L1: // kilocode_change - new file +packages/opencode/src/kilocode/cli/cmd/tui/app.tsx 285 1 Kilo-owned inherited marker User-interface behavior partial `packages/tui/src/app.tsx` / `packages/tui/src/routes/session/index.tsx` auto-approve command and `packages/kilo-vscode/src/kilo-provider/auto-approve.ts` Marked lines (L285-287) document that the /auto-approve toggle writes an allow-everything rule to global config so VS Code/JetBrains/headless runs stop prompting too. V2 TUI has an auto-approve command (tui/src/app.tsx, config/keybind.ts) and the transplanted extension has its own auto-approve module, but I found no evidence the v2 TUI toggle persists a server-side global permission rule shared with other clients (v2 permission saved rules are per-request-shape in core/src/permission/saved.ts). Preserved: TUI toggle exists; missing: the cross-client global-config persistence invariant. L285: // kilocode_change - the saved rule is server side, so it also stops VS Code, JetBrains and +packages/opencode/src/kilocode/cli/cmd/tui/component/dialog-headless-link.tsx 1 1 Kilo-owned inherited marker No-display documentation-link dialog needs port Owned v2 TUI dialog/plugin route No current v2 component presents the v1 no-display state with a readable documentation URL and keyboard/mouse close behavior; headless execution cancellation is a different consumer. L1: // kilocode_change - new file +packages/opencode/src/kilocode/cli/cmd/tui/context/nudge.tsx 1 1 Kilo-owned inherited marker Per-session soft maximum-cost alert needs port Owned v2 TUI context/plugin consuming durable session cost events Current v2 shows usage but has no consumer that seeds cumulative message cost, rearms on idle→running, prompts Stop/Continue at a configurable threshold, snoozes dismissal, and aborts on Stop. L1: // kilocode_change - new file: soft max-cost nudge wiring for the TUI +packages/opencode/src/kilocode/cli/cmd/tui/feedback.ts 1 1 Kilo-owned inherited marker Per-message thumbs feedback telemetry needs port Owned v2 TUI action/plugin plus Kilo telemetry feedback event No current v2 session action selects the latest non-reverted assistant message, gates on telemetry, adds Kilo session/message lineage, submits an up/down feedback event and reports success/error via toast. L1: // kilocode_change - new file +packages/opencode/src/kilocode/cli/logo.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path already ported `packages/kilo-cli/src/tui-plugin/logo-data.ts` (`supports()`, modern/fallback artwork) and `logo.tsx` replacing `home.logo` in `tui-plugin/tui.tsx:33`; `test/logo.test.tsx` Whole file: modern/fallback logo variants selected by `supports(env, platform)` (KILO_UNICODE_LOGO override, TERM=dumb, Windows terminal detection, ConEmu/ANSICON). V2 logo-data.ts reproduces the same supports() rules and both artwork sets, and logo.tsx renders them into the native home.logo slot with a test. L1: // kilocode_change - new file +packages/opencode/src/kilocode/cli/run-auto.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path partial `packages/kilo-cli/src/run.ts` permission reply loop (:112-145) over native `permission.asked` events Marked file tracks the root session plus Task-tool child sessionIDs so --auto approves (and suggestions are accepted from) exactly those sessions. V2 run.ts implements --auto by replying once to permission.asked, but filters `event.data.sessionID === sessionID` (root only) and has no child-session tracking, so subagent permission requests are neither approved nor auto-rejected-with-failure as in v1 (v1 run.ts L931-981). Preserved: root --auto; missing: tracked child scope and the failing auto-reject semantics. L1: // kilocode_change - new file +packages/opencode/src/kilocode/cli/server-urls.ts 1 1 Kilo-owned inherited marker Server/API integration native equivalent `packages/server/src/server-info.ts` (uses os.networkInterfaces for advertised addresses) Whole file formats localhost plus non-internal IPv4 interface URLs (IPv6 bracket-safe) for `kilo serve` output. V2 server-info.ts enumerates networkInterfaces for the same purpose in the native server; the Kilo-specific wrapper is redundant. Bracketed IPv6 formatting is only relevant when hostname is IPv6, which the native code also handles via URL. L1: // kilocode_change - new file +packages/opencode/src/kilocode/cli/setup.ts 36 1 Kilo-owned inherited marker Package behavior indicated by path not needed `packages/kilo-cli/src/index.ts`/`commands.ts` (Kilo owns its own CLI entry on v2) The single marked line is a comment stating that Kilo CLI customization is centralized here so upstream src/index.ts has few kilocode_change call-sites. On v2 Kilo owns a separate CLI package (packages/kilo-cli) and does not patch the upstream entrypoint, so the merge-minimization invariant is satisfied structurally. Individual v1 Kilo subcommands (cloud, daemon, profile, console, roll-call, dev-setup) are tracked by their own rows/plan phases, not by this marker. L36: // (src/index.ts) only needs a handful of thin call-sites behind kilocode_change markers. +packages/opencode/src/kilocode/command/override.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path native equivalent v2 command editor `ctx.command.transform` (used by `packages/kilo-cli/src/review-policy.ts`) and Core command config merge Whole file applies user config overrides (template/description/agent/model/variant/subtask) onto an existing command while recomputing hints. V2 exposes a command editor transform where config-defined commands override plugin defaults (review-policy comment: 'Config-defined commands can still override this default'), and Core `session/command.ts` resolves configured commands natively; no separate Kilo helper is required. L1: // kilocode_change - new file +packages/opencode/src/kilocode/commit-message/git-context.ts 136 1 Kilo-owned inherited marker Package behavior indicated by path native equivalent `packages/kilo-vscode/src/backend/generation.ts` / `src/util/process.ts` (windowsHide) and CLI generation-plugin Only marked line sets `windowsHide: true` on the commit-message git spawn to avoid a cmd.exe flash. The v2 extension's generation and process utilities already pass windowsHide (grep windowsHide: kilo-vscode backend/generation.ts, util/process.ts, cli/src/server-process.ts). Commit-message generation itself lives in `packages/kilo-cli/src/generation-plugin.ts` via the Core git service rather than raw spawnSync. L136: windowsHide: true, // kilocode_change - prevent cmd.exe flash on Windows +packages/opencode/src/kilocode/components/dialog-claw-setup.tsx 1 1 Kilo-owned inherited marker Package behavior indicated by path needs port Kilo-owned dialog via `ctx.ui`/dialog primitives in `packages/kilo-cli/src/tui-plugin/` (same seam as memory-dialog.tsx) Whole file is the Claw setup dialog; no v2 TUI Claw dialog exists. Part of the Claw TUI port. L1: // kilocode_change - new file +packages/opencode/src/kilocode/components/dialog-claw-upgrade.tsx 1 1 Kilo-owned inherited marker Package behavior indicated by path needs port Same Kilo-owned Claw TUI plugin Whole file is the Claw upgrade prompt dialog; absent in v2 TUI. L1: // kilocode_change - new file +packages/opencode/src/kilocode/config-validation.ts 1 1 Kilo-owned inherited marker Configuration/settings native equivalent `packages/core/src/config/normalize.ts` + `packages/kilo-cli/src/settings.ts:732-739` V2 settings writes run substitute/parse/normalize/decode verification and surface diagnostics before write, replacing the v1 owned validator. L1: // kilocode_change - new file +packages/opencode/src/kilocode/config/overlay.ts 136,191,195,245,256,259,269,271 8 Kilo-owned inherited marker Configuration/settings native equivalent `packages/kilo-cli/src/settings.ts` and SettingsRpc collection/field operations V2 settings store provides global/project scope folding, revision checks, atomic writes, strict verification, and project-root confinement for config substitution; baseline settings audit identifies these consumers. L136: // kilocode_change - project config is untrusted; confine {file:} reads to the project root / L191: // kilocode_change start - project agents untrusted, {file:} confined to the project root; global agents trusted +packages/opencode/src/kilocode/plan-followup.ts 27 1 Kilo-owned inherited marker Package behavior indicated by path not needed None for the marker; plan flow itself lives in `packages/kilo-cli/src/plan-policy.ts` (baseline plan-v2-parity) The single marked line imports the fork's AppNodeBuilder to build Agent/Provider runtimes lazily. V2 plan-policy is a plugin `define` with ctx services, so this layer-building shim is structurally unnecessary. The plan hand-over behavior is assessed under the plan tool rows (tool/plan.ts native equivalent). L27: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change +packages/opencode/src/kilocode/plugins/home-footer.tsx 1 1 Kilo-owned inherited marker Package behavior indicated by path partial `packages/kilo-cli/src/tui-plugin/tui.tsx:68` (`home.footer` append) and `tui-plugin/remote.tsx` remote status UI Whole file replaces upstream home_footer to render directory/branch, MCP counts, version plus RemoteIndicator. V2 appends a Kilo preview/account label to the native home.footer and remote.tsx exposes remote status through a dialog/command (`Remote control · Connected/Enabled/Disabled`), but no persistent remote indicator is rendered in the home footer, and Kilo does not replace the upstream footer contents. Preserved: Kilo footer slot; missing: always-visible remote connection indicator. L1: // kilocode_change - new file +packages/opencode/src/kilocode/plugins/model-usage.ts 95 1 Kilo-owned inherited marker Session-family model usage UI native equivalent `packages/core/src/session/stats.ts:100-182`, `packages/schema/src/kilocode/session-usage.ts:21-38`, and Kilo CLI usage UI V2 derives durable per-model cost/token aggregates and root-plus-descendant session usage from projected data, exposes it through browser-safe RPC, and renders the resulting family usage; inherited markers are unnecessary in owned destinations. L95: // When samples carry `elapsedMs` (kilocode_change: persisted on the +packages/opencode/src/kilocode/plugins/sidebar-pr.tsx 1 1 Kilo-owned inherited marker Package behavior indicated by path already ported `packages/kilo-cli/src/tui-plugin/sidebar-pr.tsx` installed from `tui.tsx` (`installPrSidebar`), documented in `kilocode/baseline/sidebar-pr-v2-parity.md` Whole file: sidebar section resolving the current branch's PR via cached gh probe. Baseline doc records a source-backed, isolated-host-tested port of this exact v1 file onto the native sidebar.content slot. L1: // kilocode_change - new file +packages/opencode/src/kilocode/provider/provider.ts 1,7,270 3 Kilo-owned inherited marker Provider/model routing or UI partial Split across `packages/kilo-gateway/src/{plugin,models,gateway}.ts`, `packages/ai/src/kilocode/`, and native Core provider plugins Gateway catalog fields, privacy request policy, Auto routing, variants and account refresh are source-verified in v2; the mixed v1 file also contains non-Gateway custom-loader behaviors that are not all reconciled in this batch. L1: // kilocode_change - new file / L7: // calls at well-defined injection points (each marked with kilocode_change). +packages/opencode/src/kilocode/review/worktree-diff.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path needs port Kilo-owned review data source over Core `packages/core/src/git.ts` / `plugin/vcs/git.ts` (`git diff --unified` :549/:383) feeding `packages/kilo-cli/src/review-policy.ts` Whole file computes WorktreeDiff items (before/after, tracked, generatedLike, summarized, stamp) honoring ignore rules for the review command. V2 review-policy only rewrites the prompt text (`reviewPrompt`) and relies on the model to gather diffs; Core has git diff primitives but no equivalent generated-file/summarization classification or WorktreeDiff schema (grep WorktreeDiff/cumulative: none). L1: // kilocode_change - new file +packages/opencode/src/kilocode/server/httpapi/server.ts 10,12,69 3 Kilo-owned inherited marker Server/API integration not needed Presence is Kilo-owned in `packages/kilo-cli/src/presence-service.ts`/`kilo-presence.ts`; v2 host composition in `interactive-server.ts` Marked lines import AppNodeBuilderV1 (because v1 defaultLayer aliases were removed) and provide KiloViewers.defaultLayer into the fork's HTTP server layer stack. V2 does not fork the upstream HttpApi server; Kilo presence is composed as a separate service in kilo-cli (presence-service.ts), so these layer-wiring shims have no counterpart. Presence semantics themselves are tracked by the presence rows/baseline, not this marker. L10: import { AppNodeBuilderV1 } from "@/effect/app-node-builder-v1" // kilocode_change - defaultLayer aliases are gone / L12: import { KiloViewers } from "@/kilocode/presence/service" // kilocode_change +packages/opencode/src/kilocode/server/provider-auth-lifecycle.ts 3,13,18 3 Kilo-owned inherited marker Provider/model routing or UI partial Gateway `packages/kilo-gateway/src/plugin.ts:567-568` plus CLI TUI/remote credential event consumers Credential changes refresh scoped catalogs and downstream clients, replacing global instance disposal; the specific v1 presence invalidateAuth side effect has not yet been reconciled with the v2 presence service. L3: import { KiloViewers } from "@/kilocode/presence/service" // kilocode_change / L13: // kilocode_change start - drop the old presence socket; callers invoke this for the "kilo" provider only +packages/opencode/src/kilocode/server/reference-reconciler.ts 8,19 2 Kilo-owned inherited marker Server/API integration not needed None; v2 has no ported reference reconciler (grep ReferenceReconciler: none), and the marker is only the v1 plugin-readiness wait Marked lines import PluginV2 and wait for `core/config-reference` before syncing references; this is v1 effect-runtime ordering glue, not product behavior. The underlying Kilo reference (Scout) feature is not present in v2 and is not marked here, so the marker itself has nothing to port. L8: import { PluginV2 } from "@opencode-ai/core/plugin" // kilocode_change / L19: yield* (yield* PluginV2.Service).wait(PluginV2.ID.make("core/config-reference")) // kilocode_change +packages/opencode/src/kilocode/server/server.ts 1,3 2 Kilo-owned inherited marker Server/API integration native equivalent v2 server OpenAPI generation in `packages/server` plus Kilo app-name/branding hooks in `packages/kilo-cli` File only exports DOC_TITLE 'kilo' and DOC_DESCRIPTION 'kilo api' for the upstream OpenAPI document. V2 Kilo does not patch upstream server.ts; the preview uses its own branding via the CLI/app-name hooks recorded in the master plan (Kilo Gateway/app-name hook), so a doc-title constant is not a behavior gap. L1: // kilocode_change - new file / L3: // Imported by ../../server/server.ts with minimal kilocode_change markers. +packages/opencode/src/kilocode/session-export/org-sources.ts 5 1 Kilo-owned inherited marker Session lifecycle/history/UI not needed None for the marker; org selection lives in `packages/kilo-gateway/src/account.ts` and `packages/kilo-cli/src/indexing.ts` Single marked line is the AppNodeBuilder import for building Config runtime. V2 Kilo services read organization state through the Gateway account service directly; session export itself is a separate unported feature not covered by this marker. L5: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change +packages/opencode/src/kilocode/session-import/service.ts 4,48 2 Kilo-owned inherited marker Session lifecycle/history/UI not needed `packages/kilo-cli/src/import-v1-session.ts` (Kilo v1 session-store copy) and Core `session/revert.ts` types Marked lines only adopt the shared `SessionMessage.ID` brand for revert state when inserting imported sessions. V2 import-v1-session.ts copies the store without re-typing rows and Core owns revert typing, so this brand-alignment patch has no counterpart to port. L4: import { SessionMessage } from "@opencode-ai/core/session/message" // kilocode_change - shared Revert.State brand / L48: messageID: SessionMessage.ID.make(input.revert.messageID), // kilocode_change - shared Revert.State brand +packages/opencode/src/kilocode/session-portability/cumulative-diff.ts 6 1 Kilo-owned inherited marker Session lifecycle/history/UI not needed None for the marker; cumulative session diff feature untracked in v2 (grep cumulativeSessionDiff: none) Single marked line is the AppNodeBuilder import; the cumulative-diff behavior itself is unmarked v1 code consumed by kilo-sessions remote and is not part of this marker row. L6: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change +packages/opencode/src/kilocode/session/cost-propagation.ts 1 1 Kilo-owned inherited marker Descendant-session cost accounting native equivalent `packages/core/src/session/stats.ts` plus `packages/kilo-cli/src/session-usage.ts` V2 reads durable session-family usage instead of mutating parent-message costs, preserving descendant-inclusive totals without the v1 process-local lock and lost-update risk. L1: // kilocode_change - new file +packages/opencode/src/kilocode/session/metrics.ts 1,28,47 3 Kilo-owned inherited marker Model throughput metrics native equivalent `packages/tui/src/routes/session/rows.ts` and `packages/tui/test/cli/tui/session-rows.test.ts:29-52` Current TUI measures output throughput across model steps from stream timing, excludes tool time, and omits it when the stream boundary is unavailable; focused current tests cover both cases. L1: // kilocode_change - new file / L28: // kilocode_change start - tokens/second throughput for #6579. +packages/opencode/src/kilocode/session/processor.ts 1 1 Kilo-owned inherited marker Session lifecycle/history/UI partial `packages/kilo-cli/src/telemetry.ts` and Core `session/runner/*`; offline handling tracked under session/network.ts (needs port) Whole file is Kilo's processor side-module: ReviewCommand telemetry emission, SessionNetwork offline waits, and status updates. V2 has Kilo telemetry (telemetry.ts, telemetry-command.ts) and native runner status, but the review-command telemetry hook (grep ReviewCommand in kilo-cli: none) and offline/reconnect wait are absent (network.ts row is needs port). Preserved: generic telemetry/status; missing: review telemetry and network gating hooks. L1: // kilocode_change - new file +packages/opencode/src/kilocode/session/prompt.ts 1,265,278 3 Kilo-owned inherited marker Session lifecycle/history/UI partial Core `packages/core/src/permission.ts` rule evaluation; Kilo provenance reporting in `packages/kilo-cli/src/settings.ts`/`project-config.ts` (config provenance only) Marked block (L265-278) tags agent/session permission rules with their origin before merge so the winning rule reports its true source (PermissionProvenance.classify). V2 permission evaluation (core/src/permission.ts) returns the matched saved rule without origin tagging, and v2 'provenance' code concerns config/settings scopes, not permission approvals (grep PermissionProvenance: none). Preserved: guard/hard permission concepts exist natively; missing: rule-origin provenance in approvals. L1: // kilocode_change - new file / L265: // kilocode_change start - tag every rule with its true origin before merging, so the winning +packages/opencode/src/kilocode/session/workflow-variant.ts 1 1 Kilo-owned inherited marker Workflow command variant resolution needs port Kilo-owned command/session admission policy seam V2 supports model variants generally, but the pinned precedence among command.variant, matching agent.variant and inherited input variant is absent with the unported workflow-command override surface; ordinary TUI selection is not equivalent. L1: // kilocode_change - new file +packages/opencode/src/kilocode/skills/builtin.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path already ported `packages/kilo-cli/src/skill-policy.ts` (`BUILTIN_LOCATION`, skill-policy-kilo-config.md imported with type text) — baseline skills-v2-parity Whole file inlines built-in skill markdown at compile time and registers it before discovery so user skills override. V2 skill-policy imports the builtin skill markdown as text, registers it after config discovery, and reports collisions; the reserved builtin cannot be replaced by project definitions (comment L25-26), which is the v2 policy decision for the same seam. L1: // kilocode_change - new file +packages/opencode/src/kilocode/snapshot/diff-full.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path native equivalent `packages/core/src/plugin/vcs/git.ts:383-401` and `core/src/git.ts:549` (`git diff --unified=N`, batched and capped) Whole file shells out to `git diff --unified=3` for snapshot patches to avoid O(N*M) JS diffs. V2 Core snapshot/VCS diffs already run git diff with unified context, batched per invocation and capped (vcs/git.ts:49), so the performance invariant is native. L1: // kilocode_change - new file +packages/opencode/src/kilocode/snapshot/index.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path native equivalent `packages/core/src/snapshot.ts` Two-line barrel re-exporting track/diff-full; assessed through those rows. L1: // kilocode_change - new file +packages/opencode/src/kilocode/snapshot/track.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path partial `packages/core/src/snapshot.ts` capture (`maximumUntrackedFileBytes: 2 MiB` :126, warning-and-skip :131) plus Kilo project-config writer `packages/kilo-cli/src/project-config.ts` Whole file forks Snapshot.track with a timeout, asks the user to continue/disable snapshots for the project (writing `snapshot:false` to .kilo/kilo.json without disposing the instance), and injects an 'Initializing snapshot…' part. V2 snapshot capture bounds untracked file size and swallows failures with a warning, which mitigates huge-repo stalls, but there is no timeout-driven question, per-project disable persistence, or transient progress part (grep in core/snapshot.ts: no timeout/prompt). Preserved: bounded capture; missing: interactive guard. L1: // kilocode_change - new file +packages/opencode/src/kilocode/storage/json-migration.ts 17 1 Kilo-owned inherited marker Package behavior indicated by path not needed None; v2 owns explicit v1 import in `packages/kilo-cli/src/import-v1-session.ts`/`import-v1-config.ts` Single marked line imports AppNodeBuilder for the v1 JSON→SQLite migration runtime. V2 never migrates legacy JSON storage in-process (import is a copy-then-migrate host command), so the shim and its target have no v2 counterpart. L17: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change +packages/opencode/src/kilocode/suggestion/tui/bar.tsx 2 1 Kilo-owned inherited marker Remote suggestion presentation native equivalent `packages/kilo-cli/src/remote-session.ts:595-622,1297-1334` plus Kilo VS Code suggestion handler/UI V2 emits replayable shown/accepted/dismissed frames to the subscribed remote viewer, while VS Code routes pending cards and accept/dismiss actions through its owned UI seam; no inherited marker is needed. L2: // kilocode_change - new file +packages/opencode/src/kilocode/system-prompt.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path partial `packages/core/src/plugin/system-prompt.ts` (Kilo-modified per git status) and `packages/kilo-cli/src/model-prompt-policy.ts` Whole file composes Kilo system-prompt sections from editor context (staticEnvLines) and Global paths. V2 has Kilo prompt policy (model-prompt-policy.ts, baseline model-prompt-policy-v2-parity) and a Kilo-touched core system-prompt plugin, but no editor-context injection producer (grep staticEnvLines/EditorContext in kilo-cli/core: none; hits are TUI editor UI). Preserved: prompt selector/policy seam; missing: IDE editor-context environment lines. L1: // kilocode_change - new file +packages/opencode/src/kilocode/tool/agent-manager.ts 1 1 Kilo-owned inherited marker Interactive multi-session agent manager needs port New owned v2 orchestration tool, durable manager service and TUI card surface Native subagents cover child execution, but no v2 consumer implements this tool's worktree/local batch start, section assignment, list/filter, prompt/stop/move protocol, model search, or manager event UI. L1: // kilocode_change - new file +packages/opencode/src/kilocode/tool/chart.ts 1 1 Kilo-owned inherited marker Chart rendering tool needs port New owned v2 tool plus typed tool-result/UI rendering seam No v2 tool accepts and validates Chart.js configuration, records chart telemetry, or returns chart metadata for a dedicated renderer; generic MCP text-content handling is not equivalent. L1: // kilocode_change - new file +packages/opencode/src/kilocode/tool/generate-image.ts 1 1 Kilo-owned inherited marker Image generation/editing tool needs port New owned v2 tool integrated through Core tool registry and Kilo Gateway/account policy V1 resolves Kilo/OpenRouter credentials, submits multimodal image requests, validates data URLs and writes authorized output paths. No current v2 consumer implements this tool or these boundaries. L1: // kilocode_change - new file +packages/opencode/src/kilocode/tool/model-search.ts 1 1 Kilo-owned inherited marker Provider/model routing or UI native equivalent CLI native model picker search plus original VS Code `webview-ui/src/components/shared/model-selector-utils.ts:72-111` Both shipped clients implement punctuation-insensitive collapsed model/provider matching; the v1 agent-manager helper is replaced by client-owned consumers rather than a backend tool helper. L1: // kilocode_change - new file +packages/opencode/src/kilocode/tool/send-file.ts 54,98 2 Kilo-owned inherited marker Remote file-delivery tool needs port New owned v2 tool and typed delivery-only attachment outcome The v1 tool gates on a live remote session, applies external-directory plus read authorization before target inspection, enforces size/MIME rules and emits delivery-only data attachments. V2 accepts user multipart input but has no equivalent outbound `send_file` tool. L54: // kilocode_change start — authorize missing and directory paths with the same / L98: // kilocode_change end +packages/opencode/src/kilocode/tool/task.ts 1 1 Kilo-owned inherited marker Kilo task/subagent policy partial `packages/core/src/tool/plugin/subagent.ts:129-145`, config normalization/migration, and Kilo plan/model policy V2 has native durable subagent execution, configurable depth and Kilo planning restrictions. The pinned helper additionally applies persisted per-agent model/variant precedence and inherited mutation/MCP denial ceilings; exact parity is not established. L1: // kilocode_change - new file +packages/opencode/src/kilocode/tool/websearch-kilo-exa.ts 1 1 Kilo-owned inherited marker Authenticated Kilo Exa gateway search needs port Owned Kilo web-search provider registered in the v2 WebSearch abstraction The marked file posts to Kilo `/api/exa/search` with the Kilo bearer token, caps results at 10, requests highlights, distinguishes 401/403 login failures, applies a 25-second timeout and decodes/formats the Exa response. V2's Tavily provider has different endpoint, credentials, request/result shape and error semantics; generic provider selection is not equivalent. L1: // kilocode_change - new file +packages/opencode/src/kilocode/ts-check.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path needs port Kilo-owned diagnostics provider over v2 LSP/diagnostics tool seam in `packages/core` (no v2 LSP module found under core/src) Whole file collects TS diagnostics by shelling to tsgo/tsc for edited files. V2 Core has no LSP or tsgo integration (grep tsgo/typescript-language-server: none), so neither the low-memory invariant nor diagnostics after edits exist. L1: // kilocode_change - new file +packages/opencode/src/kilocode/ts-client.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path needs port Same Kilo-owned diagnostics seam as ts-check.ts Whole file: shell-out TypeScript diagnostic client (~50MB peak vs ~500MB LSP). Absent in v2 (no LSP/diagnostics module). L1: // kilocode_change - new file +packages/opencode/src/lsp/launch.ts 3,14,15 3 shared-upstream patch marker Package behavior indicated by path not needed None yet; v2 has no LSP runtime (`packages/core/src/tool/plugin/edit.ts:107` TODO) Markers sanitize LSP server env. V2 has no LSP launch path at all; when LSP lands the env scrubbing invariant should be re-evaluated with format/index.ts (needs port). L3: import { model as modelEnv } from "@/kilocode/process/env" // kilocode_change / L14: env: modelEnv(cfg?.env), // kilocode_change - language servers must not inherit backend credentials +packages/opencode/src/lsp/lsp.ts 14,262,275 3 shared-upstream patch marker Package behavior indicated by path needs port Future v2 LSP/diagnostics seam (none today) Markers route TypeScript diagnostics to the Kilo tsgo client (ts-client.ts row needs port). V2 lacks any LSP/diagnostics runtime. L14: import { TsClient } from "../kilocode/ts-client" // kilocode_change / L262: // kilocode_change start - use lightweight tsgo-based client when persistent LSP is not enabled +packages/opencode/src/lsp/server.ts 5,17,20,119,144,989,993,1027,1029,1257,1462,1464 12 shared-upstream patch marker Server/API integration needs port Future v2 LSP runtime (none today) Markers add tsgo support, reject remote release metadata as local executable paths (security), fixed local archive names and a jdtls option. No v2 LSP server module exists, so both the feature and its hardening are absent. L5: import * as Log from "@opencode-ai/core/util/log" // kilocode_change / L17: import { TsCheck } from "../kilocode/ts-check" // kilocode_change +packages/opencode/src/mcp/catalog.ts 85 1 shared-upstream patch marker Package behavior indicated by path not needed None (marker is a comment on `collect` used by sandbox network accounting) Single marker comment; the sandbox network authority it references is a v1 design (v2 sandbox confines processes rather than classifying fetches). L85: export function collect( // kilocode_change - distinguish collection from direct network fetch +packages/opencode/src/mcp/index.ts 1,8,42,46,53,55,57,59,64,76,102,334,362,365,369,371,376,378,382,711,714,735,837,839,854,858,912,933,959 29 shared-upstream patch marker Package behavior indicated by path partial `packages/core/src/mcp/{index,stdio,oauth}.ts` (stderr drain stdio.ts:164-172, env stdio.ts:85) and `packages/kilo-cli/src/sandbox-mcp.ts` (connection-scoped fail-closed spawn hook) PRESERVED — piped stderr consumed to avoid blocking (stdio.ts:164), local MCP confinement via Kilo sandbox spawn hook (sandbox-mcp.ts + Core McpClient.connect hook), OAuth flow exists (mcp/oauth.ts). MISSING/unverified — `--rm` injection for docker-run MCP servers (grep docker in core/mcp: none), client name `kilo` in MCP handshake (branding), scrubbing backend credentials from local MCP env (v2 passes options.environment; scrub not verified), routing remote MCP calls through the sandbox network authority (remote MCP explicitly not confined per sandbox acceptance note), deferred callback-port binding until redirect. L1: // kilocode_change start / L8: // kilocode_change end +packages/opencode/src/mcp/oauth-callback.ts 4,6,8,17,19,30,32,41,43,155,170 11 shared-upstream patch marker Authentication/account integration partial `packages/core/src/mcp/oauth.ts` and `core/src/oauth/page.ts` PRESERVED — OAuth callback page and flow exist natively (oauth/page.ts renders offline-capable page). MISSING — Kilo branding blocks in the callback HTML and the Kilo-specific deferred callback binding delegate (see mcp/index row). L4: import * as Log from "@opencode-ai/core/util/log" // kilocode_change / L6: import * as KiloOAuthCallback from "../kilocode/mcp-oauth-callback" // kilocode_change +packages/opencode/src/permission/index.ts 2,4,9,10,12,14,15,20,22,25,50,52,64,66,73,76,79,83,88,92,99,114,125,126,148,154,162,163,169,170,188,192,194,196,213,215,217,219,223,225,228,233,234,239,243,245,249,257,265,266,272,273,274,282,290,300,333,335,338,350,362,370,400,459,461,480,482,490,512,514,539,544 72 shared-upstream patch marker Package behavior indicated by path partial `packages/core/src/permission.ts` (`evaluate` :87-99, `AskResult` :56, ask/assert :104-105) and `permission/saved.ts`; Kilo: `packages/kilo-cli/src/{skill-shell,agent-policy,tool-authorization}.ts` 72 markers. PRESERVED — ask returns a typed result (AskResult) rather than void, saved rules persisted natively, delegated agents cannot answer prompts (agent-policy explore comment; subagent tool asserts parent permission), hard Ask/Plan denials are enforced by Kilo policy transforms (plan-policy/agent-policy add deny rules; whether saved approvals can override them is not proven). MISSING — deciding-rule provenance returned to clients for auto-approval explanation, session-scoped rulesets, skill-shell batches forcing a human reply and ignoring machine approvals (skill-shell.ts gating exists but the permission-service `skillShell` metadata contract is absent), protected config path 'allow→ask' override and persistence downgrade except one exact global skill, headless subagent asks failing instead of hanging (#11903; v2 run.ts auto-rejects only root), allowEverything global toggle, Agent Manager permission hardening, null delete sentinels, toConfig inverse. L2: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change / L4: import * as Config from "@/config/config" // kilocode_change +packages/opencode/src/plugin/azure.ts 5,41 2 shared-upstream patch marker Package behavior indicated by path partial `packages/core/src/plugin/provider/azure.ts:43-76` (resourceName prompt; baseURL accepted when configured) Marker lets the auth form accept either a resource name or full endpoint. V2 Azure plugin prompts for resourceName and skips when settings.baseURL is configured (azure.ts:71), but the form itself does not offer an endpoint URL option; partial. L5: // kilocode_change start - allow configuring either Azure resource name or full endpoint URL from the UI / L41: // kilocode_change end +packages/opencode/src/plugin/digitalocean.ts 61,120,128 3 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/core/src/oauth/page.ts` shared branded page (`OauthCallbackPage`, WORDMARK :83) used by provider plugins Markers retained a Kilo-branded callback page because the shared page was OpenCode-branded. V2 has one shared OauthCallbackPage with a single brand wordmark kept in sync with the app brand (page.ts:12 comment); provider plugins use it uniformly, so the per-provider branded HTML copies are unnecessary once the shared brand is Kilo (branding string itself is part of the identity/app-name hook, not this file). L61: // kilocode_change start - retain Kilo-branded OAuth callback until the shared page supports Kilo branding / L120: // kilocode_change end +packages/opencode/src/plugin/github-copilot/copilot.ts 315 1 shared-upstream patch marker Package behavior indicated by path not needed `packages/core/src/plugin/provider/github-copilot.ts` Single marker is a comment on device flow; no behavior. L315: // (See https://www.rfc-editor.org/rfc/rfc8628.html#section-3.5) // kilocode_change - avoid unstable redirect +packages/opencode/src/plugin/github-copilot/models.ts 164,171 2 shared-upstream patch marker Provider/model routing or UI needs port `packages/core/src/plugin/provider/github-copilot.ts` model handling (grep opus/fable/sonnet: none) Marker extends Copilot's model-family handling to newer Anthropic models (opus-4.8, fable, sonnet-5). V2 github-copilot plugin has no such family mapping, so those models would miss the opus-4.7 treatment. L164: // kilocode_change start - treat opus-4.8, fable, and sonnet-5 like opus-4.7 / L171: // kilocode_change end +packages/opencode/src/plugin/index.ts 29,30,31,70,71,72,79,80,83,112,113 11 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/src/interactive-server.ts`/`runtime.ts` registering `packages/kilo-gateway` plus Kilo policy plugins via v2 `define` PRESERVED — Kilo Gateway auth plugin is bundled (kilo-gateway package registered by host), external plugins consume the v2 @opencode-ai/plugin API (no type bridge needed). MISSING — AtomicChat plugin and Anaconda Desktop provider plugin (grep atomic/anaconda in kilo-cli/gateway: none). L29: import { KiloAuthPlugin } from "@kilocode/kilo-gateway" // kilocode_change / L30: import { AtomicChatPlugin } from "@kilocode/plugin-atomic-chat" // kilocode_change +packages/opencode/src/plugin/install.ts 337 1 shared-upstream patch marker Package behavior indicated by path partial `packages/core/src/plugin/install.ts` (installs under .opencode) and Kilo layout `packages/kilo-cli/src/paths.ts` Marker points plugin installation at `.kilo`. V2 Core plugin install uses the upstream directory; Kilo's kilo2 layout in paths.ts isolates state/config but plugin install roots were not rebranded (grep \.kilo in core/plugin: none). L337: return path.join(root, ".kilo") // kilocode_change +packages/opencode/src/plugin/loader.ts 14,15,164,165 4 shared-upstream patch marker Package behavior indicated by path not needed None; v2 Kilo indexing is a host plugin (`packages/kilo-cli/src/indexing.ts` + `packages/kilo-indexing`), not an installed npm plugin Markers prevent install retries for the Kilo indexing and Atomic Chat plugin specs when loading. In v2 Kilo indexing is composed in-process by the host and Atomic Chat is absent, so the loader special-case has no subject. L14: import { isIndexingPlugin } from "@kilocode/kilo-indexing/detect" // kilocode_change / L15: import { isAtomicChatPlugin } from "@/kilocode/atomic-chat-feature" // kilocode_change +packages/opencode/src/plugin/openai/codex.ts 2,9,12,24,29,31,33,106,142,148,161,165,167,198,200,211,213,257,318,402,471,486 22 shared-upstream patch marker Package behavior indicated by path partial `packages/core/src/plugin/provider/openai.ts` (codexAllowed :23, codexDisallowed :24, originator :265, shared OauthCallbackPage :76-84) PRESERVED — Codex allow/disallow model sets, OAuth device/callback flow, shared callback page. MISSING — Kilo's additional Codex model list (v1 markers add models beyond upstream), `originator: kilo` identity (v2 sends opencode), Kilo codex-refresh handling of expired auth (CodexAuthExpiredError). Paired test/plugin/codex.test.ts pending. L2: import * as Log from "@opencode-ai/core/util/log" // kilocode_change / L9: import { refreshCodexAuth } from "@/kilocode/provider/codex-refresh" // kilocode_change +packages/opencode/src/plugin/openai/ws-pool.ts 2,9,145,221,227 5 shared-upstream patch marker Package behavior indicated by path partial `packages/core/src/plugin/provider/openai.ts` (v2 Responses transport; ws pool not found) Markers add Kilo logging and discard of leaked responses in the OpenAI websocket pool. V2 openai plugin has no websocket pool module (grep ws-pool: none); if v2 does not use the WS transport the fix is moot, but I could not confirm the transport choice, so partial. L2: import * as Log from "@opencode-ai/core/util/log" // kilocode_change / L9: const log = Log.create({ service: "plugin.openai.ws" }) // kilocode_change +packages/opencode/src/plugin/snowflake-cortex.ts 4 1 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/core/src/oauth/page.ts` shared page Single marker imports the Kilo-branded OauthCallbackPage; v2 uses one shared branded page across providers (see digitalocean row). L4: import { KiloOauthCallbackPage as OauthCallbackPage } from "@opencode-ai/core/kilocode/oauth/page" // kilocode_change - Kilo-branded callback page +packages/opencode/src/plugin/tui/internal.ts 3,7,17 3 shared-upstream patch marker User-interface behavior already ported `packages/kilo-cli/src/tui-plugin/tui.tsx` (`kilo.preview` plugin) loaded through `packages/kilo-cli/src/tui.ts` packages.prepare allowlist Marker wraps upstream internal TUI plugins with Kilo's. V2 Kilo host loads its own approved preview UI package (tui.ts:59 prepare guard) which registers logo/footer/sidebar/dialog claims. L3: import { withKiloTuiPlugins } from "@/kilocode/plugins/internal" // kilocode_change / L7: // kilocode_change start +packages/opencode/src/plugin/tui/runtime.ts 255,261,820 3 shared-upstream patch marker User-interface behavior partial `packages/tui/src/plugin/*` runtime and `packages/kilo-cli/src/paths.ts` (kilo2 XDG layout) PRESERVED — Kilo v2 uses an isolated config/data layout (paths.ts) so TUI plugin state lands under the Kilo identity. MISSING — installing local themes into `.kilo`/`.kilocode` project directories and treating `.kilo/tui.json` as the config source (v2 TUI config path is native; theme install dir not verified as Kilo-scoped). L255: // kilocode_change start - install local themes into supported Kilo config directories / L261: // kilocode_change end +packages/opencode/src/plugin/xai.ts 5,91,126,128,141,290,380,431,440,449,461,492,494,639 14 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/core/src/plugin/provider/xai.ts:230` (`App.useragent(app)`), `oauth/page.ts` error rendering; app name set by `packages/kilo-cli/src/host.ts:21` Markers set a kilocode UA/referrer and use branded, escaped error pages. V2 derives the UA from App metadata (host.ts sets the Kilo identity name) and renders errors through the shared OauthCallbackPage.error with escaping; referrer string remains `opencode` (branding only). L5: import { escapeHtml } from "@/util/html" // kilocode_change - retained OAuth error page uses escaped diagnostics / L91: "User-Agent": `kilocode/${InstallationVersion}`, // kilocode_change +packages/opencode/src/project/bootstrap.ts 9,12,31,34,40,45,49,62 8 shared-upstream patch marker Package behavior indicated by path not needed `packages/kilo-cli/src/interactive-server.ts` host registration Markers add KilocodeBootstrap to the v1 instance bootstrap and quiet startup logs. V2 Kilo initializes its services as host plugins; sharing is a separate unaccepted row. L9: // kilocode_change start - ShareNext init is handled by KilocodeBootstrap; upstream dropped File/FileWatcher bootstrap init / L12: // kilocode_change end +packages/opencode/src/project/instance-runtime.ts 13,16 2 shared-upstream patch marker Package behavior indicated by path not needed None; v2 fixtures use Location-scoped hosts (`packages/kilo-cli/test/fixture.ts`) Marker adds a fixture-friendly dispose ordering to the v1 instance runtime; v2 has no Instance runtime of that shape. L13: // kilocode_change start - test fixtures dispose a directory's instance before deleting the directory / L16: // kilocode_change end +packages/opencode/src/project/instance-store.ts 10,61,66,77,90,109,115,120,151,158,189,213 12 shared-upstream patch marker Package behavior indicated by path not needed None; v2 `packages/core/src/instance/*` and Location services replaced the v1 instance store 12 markers adjust v1 InstanceStore lifecycle around legacy ALS contexts and disposers. V2 Kilo does not use that store. L10: import { context as instanceContext, type InstanceContext } from "./instance-context" // kilocode_change / L61: // kilocode_change start - run bootstrap inside the Instance ALS so KilocodeBootstrap +packages/opencode/src/provider/auth.ts 10,12,15,120,186,191,234,245,252 9 shared-upstream patch marker Provider/model routing or UI partial `packages/kilo-gateway/src/plugin.ts` (integration/credential handling, startup-cache.ts snapshot) and `packages/kilo-cli/src/telemetry.ts` PRESERVED — Kilo credential lifecycle drives catalog reload and cached account snapshots (gateway plugin account()/startup-cache). MISSING/unverified — telemetry identity update on Kilo auth (grep identity in telemetry.ts not verified; telemetry-v2-overlap baseline lists gaps), the shared ModelCache service semantics (v2 catalog is Core-owned; kilo-gateway maintains its own snapshot). L10: import { errorMessage } from "@/util/error" // kilocode_change / L12: // kilocode_change start +packages/opencode/src/provider/error.ts 4,35,36,37,41,69,112,118,128,139,170 11 shared-upstream patch marker Provider/model routing or UI partial `packages/core/src/session/to-session-error.ts` and `runner/retry.ts` classification; Kilo hints would live in `packages/kilo-gateway/src/plugin.ts` or a Kilo error plugin PRESERVED — v2 classifies provider errors (AIError with http headers/status) for retry and session errors natively. MISSING — Kilo-branded hints (`kilo auth login`), expired Copilot token reauth hint, unwrapping response.failed frames/bare error objects before envelope check, empty rate-limit stream normalization, KiloError.fallback clean rendering (grep KiloError/hint in core: none). Paired TUI error guidance rows were assessed needs port. L4: import * as KiloError from "@/kilocode/provider/error" // kilocode_change / L35: const hint = KiloError.hint(providerID, e) // kilocode_change +packages/opencode/src/provider/model-cache.ts 1,10,11,330 4 shared-upstream patch marker Provider/model routing or UI native equivalent `packages/kilo-gateway/src/startup-cache.ts` + `models.ts` (scoped catalog snapshots) and Core `catalog.ts`/`models-dev.ts` caching; baseline model-loading-v2-parity Whole file caches Kilo/OpenRouter model lists across instances. V2 Gateway plugin persists per-scope startup snapshots (readSnapshot/writeSnapshot, retained on failed reads per Gateway regression fix) and Core caches models.dev; the v1 model-cache-effect test row was already assessed native equivalent. L1: // kilocode_change - new file / L10: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change +packages/opencode/src/provider/models.ts 1,10,103 3 shared-upstream patch marker Provider/model routing or UI native equivalent `packages/kilo-gateway/src/models.ts` (`catalogModels` :188, catalogEntries) feeding Core catalog via `ctx.catalog.transform` Whole file assembles dynamic Kilo models around the upstream core catalog. V2 Gateway plugin fetches the scoped catalog and maps it into v2 catalog models/variants (models.ts:243-246 overlays→variants), replacing this service. L1: // kilocode_change - new file / L10: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change +packages/opencode/src/provider/provider.ts 13,27,33,49,151,250,268,271,276,288,480,481,491,501,502,512,618,639,866,876,903,1051,1057,1073,1080,1084,1096,1124,1278,1279,1283,1315,1445,1457,1527,1528,1530,1535,1537,1545,1552,1560,1601,1603,1605,1613,1626,1628,1633,1638,1642,1651,1691,1713,1791,1792,1796,1804,1805,1810,1818,1823,1828,1882,1883,1892,1893,1973,1981,2017,2020 71 shared-upstream patch marker Provider/model routing or UI partial `packages/kilo-gateway/src/plugin.ts:348-420` (kilo catalog/settings/headers/variants), `packages/core/src/plugin/provider/{kilo,azure,openrouter,...}.ts`, `packages/kilo-cli/src/routed-model*.ts` 71 markers. PRESERVED — Kilo provider baseURL/org header/variants (gateway plugin), Kilo Auto routed model resolution and small-model fallback (routed-model rows; baseline model-loading), model description/metadata (KiloModels.Entry description/reasoning/family per board 60s), OAuth plugin providers (core plugin/provider/*). MISSING/unverified — 'Kilo Code' X-Title/X-Source/X-BILLING-INVOKE-ORIGIN/Cerebras/GitLab UA attribution headers for third-party providers (core kilo.ts sets opencode titles only; gateway headers() only adds org id), explicit Azure endpoint preference (core azure.ts not verified), null delete sentinels for provider/model config (config-mapping gap), OAuth-over-env credential precedence and Codex/config coexistence, keeping discovery failures visible (`failed` list; provider-list-failed-state test row native — so failed-state exists), first-byte timeout extension (header-timeout test row pending), `modelsEmpty` hint, per-provider Kilo privacy patch (prompt-training filter exists). L13: import * as ModelsDev from "./models" // kilocode_change - assemble dynamic Kilo models around upstream core catalog / L27: import { optional, optionalOmitUndefined } from "@opencode-ai/core/schema" // kilocode_change +packages/opencode/src/provider/transform.ts 8,9,56,167,170,331,348,373,384,394,422,489,507,559,569,580,675,682,686,690,702,728,746,754,797,798,799,810,821,825,828,829,836,841,845,849,856,857,861,1016,1027,1034,1038,1046,1138,1145,1148,1214,1322,1324,1339,1340,1434,1468,1495,1504,1560,1561,1564,1572,1579,1624,1629,1630,1688,1692,1921,1929 68 shared-upstream patch marker Provider/model routing or UI partial `packages/core/src/aisdk-native.ts` (reasoning/effort mapping :99-237) and `packages/kilo-gateway/src/models.ts` variants (:243-246 from catalog overlays) 68 markers. PRESERVED — variants/reasoning efforts derived from catalog data (gateway models.ts maps source overlays to v2 variants; aisdk-native maps effort/thinking/budget per provider family), OpenRouter-shaped reasoning for the Kilo Gateway (v2 Kilo provider is @ai-sdk/openai-compatible at api.kilo.ai gateway). MISSING/unverified — explicit prompt-cache breakpoints (Anthropic/GPT-5.6+/alibaba) placed before trailing environment details (grep cache_control in aisdk: none), Ling model temperature/top_p/top_k defaults, Claude 5 alias adaptive-thinking detection, MiniMax thinking toggle, Bedrock Claude-only filtering, Gemini required-property schema fix, gpt-5 textVerbosity gating, reasoningSummary/encrypted reasoning include. Paired transform.test row pending. L8: import { isLing } from "@/kilocode/model-match" // kilocode_change / L9: import { reasoningSummary } from "@/kilocode/provider/reasoning-summary" // kilocode_change +packages/opencode/src/question/index.ts 6,53,62,93,105,109,111,118,124,169,176,178,182 13 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/core/src/form.ts` (form cancel/dismiss, terminal outcomes) and protocol `form` group Markers add blocking questions, dismissAll for superseded prompts and guaranteed terminal events. V2 forms provide cancellation with terminal outcomes and the runner dismisses superseded prompts at delivery boundaries (prompt-dismiss-contract test row already assessed partial for remote specifics); paired question tests pending. L6: import { KiloQuestion } from "@/kilocode/question" // kilocode_change / L53: blocking?: boolean // kilocode_change +packages/opencode/src/server/auth.ts 19,40 2 shared-upstream patch marker Server/API integration native equivalent `packages/server/src/auth.ts:17,32` (username fixed to `opencode`) consumed by `packages/kilo-cli/src/daemon.ts:174` and `acp.ts:38` Markers only rename the default/env-configurable basic-auth username from opencode to kilo (KILO_SERVER_USERNAME). V2 Kilo uses the native server auth with username `opencode` and its own daemon-managed password; clients (daemon.ts, acp.ts) are Kilo-owned and agree on that username, so the rename carries no behavior. Branding-only difference accepted by Kilo v2 daemon design. L19: username: EffectConfig.string("KILO_SERVER_USERNAME").pipe(EffectConfig.withDefault("kilo")), // kilocode_change / L40: const username = credentials?.username ?? Flag.KILO_SERVER_USERNAME ?? "kilo" // kilocode_change +packages/opencode/src/server/mdns.ts 11,12 2 shared-upstream patch marker Server/API integration not needed None; v2 server has no mDNS advertisement (grep mdns in packages/server/src: none) Two markers rebrand the mDNS domain and service name. V2 removed mDNS discovery entirely (daemon registration file replaces it per kilo2 service contract), so no branding hook exists or is required. L11: const host = domain ?? "kilo.local" // kilocode_change / L12: const name = `kilo-${port}` // kilocode_change +packages/opencode/src/server/routes/instance/httpapi/api.ts 10,29,47,48,66,99,118 7 shared-upstream patch marker Server/API integration partial Kilo-owned RPC definitions `packages/kilo-cli/src/{memory,settings,privacy,indexing,session-usage,remote,cloud,model-state}-rpc.ts` via plugin `ctx.rpc` plus `packages/schema/src/kilocode/*` Markers add Kilo groups (memory, indexing, presence, review, agent-builder, anaconda, branch-name, background-process, etc.) and legacy BusEvent payload schemas into the OpenAPI event union. V2 exposes Kilo APIs as plugin RPCs (memory-rpc, settings-rpc, indexing-rpc, session-usage-rpc, remote-rpc, model-state; baseline docs) without forking the upstream API, and Kilo events use native protocol events. PRESERVED: memory/indexing/settings/usage/remote/model-state/PR/processes surfaces; MISSING: agent-builder, anaconda-desktop, branch-name, review/worktree-diff, background-process, session-import/export, next-edit and console groups (no v2 RPC or route found). L10: import { BusEvent } from "@/bus/bus-event" // kilocode_change - include legacy Kilo events until they migrate to EventV2 / L29: // kilocode_change start - Kilo HttpApi groups +packages/opencode/src/server/routes/instance/httpapi/groups/config.ts 4,13,19,32,44,47,58 7 shared-upstream patch marker Configuration/settings native equivalent `packages/protocol/src/groups/config.ts` plus Kilo settings RPC (`packages/kilo-cli/src/settings-rpc.ts`) Markers replace 'OpenCode' with 'Kilo' in endpoint descriptions and import Kilo config schema pieces. V2 does not fork the protocol wording; Kilo-only settings ride the settings RPC. Description branding is not a behavior. L4: import { Schema } from "effect" // kilocode_change / L13: // kilocode_change start +packages/opencode/src/server/routes/instance/httpapi/groups/experimental.ts 2,6,65,69,88,92,100,110,120,121,122,203,209,249,283 15 shared-upstream patch marker Server/API integration partial `packages/protocol/src/groups/worktree.ts` (list/create/remove/refresh) and `groups/vcs.ts:83` (vcs.diff); worktree diff/summary/file for review missing Markers add WorktreeList description, worktree diff/diff-file/diff-summary routes backed by WorktreeDiff. V2 has native worktree list/create/remove and a vcs.diff endpoint, but no worktree diff summary/single-file detail endpoints tailored for the Agent Manager or /review (worktree-diff.ts row needs port). L2: import { Snapshot } from "@/snapshot" // kilocode_change / L6: import { WorktreeDiff } from "@/kilocode/review/worktree-diff" // kilocode_change +packages/opencode/src/server/routes/instance/httpapi/groups/global.ts 5,8,42,85,94,103,114,123,133,134 10 shared-upstream patch marker Server/API integration partial `packages/protocol/src/groups/{event,health,config}.ts`; indexing status in `packages/kilo-cli/src/indexing-rpc.ts` and sidebar-indexing (baseline indexing-v2-parity) Markers are wording changes plus registering indexing.status and legacy BusEvent payloads on the global event schema. V2 keeps upstream wording; indexing status is delivered through Kilo RPC/sidebar rather than a global SSE event type. Preserved: indexing status surface; missing: legacy Kilo bus event payloads on the public event schema (intentionally not carried to v2 protocol). L5: import { BusEvent } from "@/bus/bus-event" // kilocode_change - include legacy Kilo events until they migrate to EventV2 / L8: import "@/kilocode/indexing-event" // kilocode_change - register indexing.status before HttpApi event schemas +packages/opencode/src/server/routes/instance/httpapi/groups/instance.ts 69,79,145,155,165 5 shared-upstream patch marker Server/API integration not needed None (v2 protocol wording unforked) Five markers replace OpenCode with Kilo in descriptions only; no behavior. L69: description: "Clean up and dispose the current Kilo instance, releasing all resources.", // kilocode_change / L79: description: "Retrieve the current working directory and related path information for the Kilo instance.", // kilocode_change +packages/opencode/src/server/routes/instance/httpapi/groups/permission.ts 15,18,29,58,84 5 shared-upstream patch marker Server/API integration partial `packages/protocol/src/groups/permission.ts` reply payload and `packages/kilo-cli/src/skill-shell.ts` trust gating Markers add an `interactive` (human-answered) boolean to permission replies that gates skill-shell approvals and import Kilo permission schemas. V2 permission.reply payload has no interactive flag (protocol permission group: reply once/always/reject only) and skill-shell.ts gates by trust/config; the human-answered distinction is not represented. L15: interactive: Schema.optional(Schema.Boolean), // kilocode_change - human-answered flag; gates skill-shell approvals / L18: // kilocode_change start +packages/opencode/src/server/routes/instance/httpapi/groups/question.ts 70 1 shared-upstream patch marker Server/API integration not needed None Single marker sets the OpenAPI title to 'Kilo HttpApi'; wording only. L70: title: "Kilo HttpApi", // kilocode_change +packages/opencode/src/server/routes/instance/httpapi/groups/session.ts 41,42,78,90,119,133,156,227,258,260,460,473 12 shared-upstream patch marker Session lifecycle/history/UI partial `packages/protocol/src/groups/session.ts` (`session.fork` :260 with boundary payload; viewed :753) and vcs.diff PRESERVED natively — viewed endpoint, fork endpoint (typed boundary payload; v1's bodyless-fork compatibility shim is obsolete). MISSING — `full`/single-file query on session diff for editor detail, Kilo wording (not a behavior). Fork-to-directory is tracked in the workspace-routing middleware row. L41: ...Struct.omit(SessionSummary.DiffInput.fields, ["sessionID", "full"]), // kilocode_change - full is a query boolean / L42: full: Schema.optional(QueryBoolean), // kilocode_change - request full-content detail +packages/opencode/src/server/routes/instance/httpapi/handlers/config.ts 2,8,12,31,35,38,45,47,50,56,68 11 shared-upstream patch marker Configuration/settings needs port `packages/kilo-gateway/src/plugin.ts` catalog/config transform (no default-model fetch today; grep defaultModel in kilo-gateway/kilo-cli: none) Markers fetch the default model from the Kilo API when the kilo provider is authenticated and overlay it onto config responses. V2 Gateway plugin loads catalog/profile/balance and a routed 'Auto' model but does not read or apply a server-provided default model; consumers (TUI model picker, extension) pick from settings instead. L2: // kilocode_change start - preserve Kilo API default model overlay / L8: // kilocode_change end +packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts 2,7,13,24,27,34,38 7 shared-upstream patch marker Server/API integration partial `packages/kilo-gateway/src/plugin.ts` integration/credential handling and `packages/kilo-cli/src/presence-service.ts` Markers route auth removal through Kilo's remove helper and drop the presence socket before instance disposal on Kilo auth changes. V2 Gateway plugin withdraws the provider on sign-out (reliability checkpoint: signed-out Gateway provider withdrawal) and presence-service is a separate Kilo service; explicit presence teardown ordering on auth change is not evidenced (presence scope fix was still in flight per latest session digest). L2: // kilocode_change start / L7: // kilocode_change end +packages/opencode/src/server/routes/instance/httpapi/handlers/event.ts 30,45 2 shared-upstream patch marker Server/API integration not needed None; v2 event stream is EventV2-native (`/api/event` server-wide) Marker documents that GlobalBus mixes encoded EventV2 events, sync envelopes and Kilo legacy bus events. V2 has only native events; nothing to port. L30: // kilocode_change start - GlobalBus includes encoded EventV2 events, sync envelopes, and Kilo's legacy / L45: // kilocode_change end +packages/opencode/src/server/routes/instance/httpapi/handlers/experimental.ts 5,10,11,16,17,18,19,24,25,49,112,115,118,119,133,146,170,216,220,227,228,230,231,232,233,241,249,252,288,292 30 shared-upstream patch marker Server/API integration partial `packages/protocol/src/groups/worktree.ts` + Core worktree service; session list filters in Core `session/session.ts` (Location scoped); Agent Manager owned by `packages/kilo-vscode` PRESERVED — worktree listing including whether managed (worktree.list/refresh natively), model info on requests. MISSING — worktree diff endpoints (WorktreeDiff), Review integration, session list with `worktrees`/`directories` roots and per-session worktree folder resolution across a project family (v2 lists by Location only; experimental-session-list test row exists separately), model.family in responses. L5: import { EffectBridge } from "@/effect/bridge" // kilocode_change / L10: import { Provider } from "@/provider/provider" // kilocode_change +packages/opencode/src/server/routes/instance/httpapi/handlers/file.ts 28,42 2 shared-upstream patch marker Server/API integration not needed `packages/protocol/src/groups/fs.ts` (v2 owns its response shape) Marker keeps a released HTTP response shape while Core retained extra search metadata — a v1 compatibility constraint for released clients. V2 clients are generated from the v2 protocol; no compatibility shim needed. L28: // kilocode_change start - preserve the released HTTP response shape while Core retains search metadata. / L42: // kilocode_change end +packages/opencode/src/server/routes/instance/httpapi/handlers/global.ts 6,34,37,57,61,87,88,97,103 9 shared-upstream patch marker Server/API integration partial `packages/server/src` event route (`/api/event`) and Core event subscription lifetimes Markers use a Kilo SSE helper to disconnect stale clients so full diff payloads are not retained. V2 event streaming is server-wide via Effect streams with scoped subscriptions; whether disconnected clients release buffered payloads was not verified in server source, so equivalence is not claimed. L6: import { disconnect } from "@/kilocode/server/sse" // kilocode_change / L34: // kilocode_change start +packages/opencode/src/server/routes/instance/httpapi/handlers/permission.ts 1,4,7,11,13,29,33,48,87 9 shared-upstream patch marker Server/API integration partial Kilo-owned permission policy over `packages/core/src/permission/saved.ts`; TUI auto-approve command in `packages/tui/src/app.tsx` Markers implement AllowEverythingPermission (global saved rule toggled server-side) and pass `interactive` through replies. V2 has TUI-side auto-approve but no server RPC that persists an allow-everything global rule for all clients, and no interactive flag (see groups/permission row and kilocode tui app.tsx row). L1: import { AllowEverythingPermission } from "@/kilocode/permission/allow-everything" // kilocode_change / L4: // kilocode_change start +packages/opencode/src/server/routes/instance/httpapi/handlers/provider.ts 6,7,11,12,13,14,37,47,51,59,67,68,81,86,132,134,135 17 shared-upstream patch marker Provider/model routing or UI partial `packages/kilo-gateway/src/plugin.ts` catalog/integration transforms, `packages/kilo-cli/src/request-policy.ts` (`hidePromptTrainingModels`), provider-auth-lifecycle row (partial) PRESERVED — hiding prompt-training models (request-policy.ts + settings-rpc hidePromptTrainingModels), Kilo catalog/model metadata via Gateway plugin, provider auth lifecycle partially (own row). MISSING — Anaconda Desktop provider overlay (grep anaconda: none), presence drop before instance disposal on OAuth callback, Kilo providerMetadata map for non-Gateway providers. L6: import { mapValues, pickBy } from "remeda" // kilocode_change / L7: import { ModelCache } from "@/provider/model-cache" // kilocode_change +packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts 1,2,3,4,5,7,43,68,109,116,231,302,304,315,331,334,340,345,346,347,409,419,448,453,467,483 26 shared-upstream patch marker Session lifecycle/history/UI partial `packages/protocol/src/groups/session.ts` (fork/viewed native), Core inbox (`session_inbox` cancel/change before delivery per AGENTS Session Core), `packages/kilo-cli/src/presence-service.ts` PRESERVED — viewed transition and fork natively; deleting/cancelling queued prompts behind the active turn (v2 durable inbox items may be cancelled before delivery); interrupts not treated as errors (v2 outcome interrupted). MISSING — presence viewers integration on session routes (Kilo presence is a separate service; wiring to viewed unverified), typed user-image validation errors as 4xx, SQLite busy message mapping, full-content diff query, agent requirement errors. L1: import { Image } from "@/image/image" // kilocode_change - classify user image validation defects / L2: import { busyMessage, isBusy } from "@/kilocode/database/sqlite-error" // kilocode_change +packages/opencode/src/server/routes/instance/httpapi/middleware/authorization.ts 15,17,47,50,61,65,135,136,138 9 shared-upstream patch marker Server/API integration native equivalent `packages/server/src/auth.ts` (basic auth always required on v2 daemon; `packages/kilo-cli/src/daemon.ts:174` issues credentials to every client) Markers fail closed on high-risk endpoints (e.g. allow-everything permission toggle) even when server auth is optional. V2 server auth is mandatory for all API routes via the daemon password, so the endpoint-specific escalation is subsumed by the stricter native policy. L15: // kilocode_change start - require auth for high-risk permission toggles even when global auth is optional / L17: // kilocode_change end +packages/opencode/src/server/routes/instance/httpapi/middleware/error.ts 3,20,32 3 shared-upstream patch marker Server/API integration partial `packages/core/src/database/database.ts:40` (PRAGMA busy_timeout = 5000) and server error middleware Marker maps SQLITE_BUSY into a friendly busy message because multiple local clients contend. V2 mitigates contention with a 5 s busy_timeout at the connection level, but no server error middleware translates residual lock errors into a busy message (grep busyMessage/isBusy: none). Preserved: mitigation; missing: user-facing mapping. L3: import { busyMessage, isBusy } from "@/kilocode/database/sqlite-error" // kilocode_change / L20: // kilocode_change start - SQLite lock contention is expected with multiple local clients +packages/opencode/src/server/routes/instance/httpapi/middleware/workspace-routing.ts 9,182,188 3 shared-upstream patch marker Server/API integration needs port `packages/protocol/src/groups/session.ts:260` session.fork payload (`boundary` only) and Core `session/move.ts` — a Location-targeted fork would need protocol extension or fork+move composition in the Agent Manager backend (`packages/kilo-vscode/src/backend/session.ts`) Marker lets a fork target another directory instead of inheriting the source session's directory (Agent Manager worktree flows). V2 fork payload only carries a boundary and the child inherits the parent Location; session move exists separately (core/session/move.ts) but the combined fork-into-worktree behavior consumed by the transplanted extension is not present. L9: import { forkTargetDirectory } from "@/kilocode/server/routes/fork-routing" // kilocode_change - fork honors explicit target directory / L182: // kilocode_change start - a fork targeting an explicit directory (e.g. a worktree) must not inherit the source session's directory +packages/opencode/src/server/routes/instance/httpapi/public.ts 2,5,67,71,85,93,97,119,197,298,302,523,535,557,570,582,586,588 18 shared-upstream patch marker Server/API integration partial `packages/client` generation (`bun run generate`) from v2 Protocol; Kilo RPC schemas in `packages/schema/src/kilocode/*` typed directly Markers fix generated SDK schemas for Kilo endpoints (nullable Agent Manager move targets, indexing model reset sentinels, ServerConfig description, doc title/description, legacy spec matching). V2 client generation runs from the Protocol HttpApi and Kilo RPCs are consumed through typed Schema contracts (schema/src/kilocode), so most fixups are structurally unnecessary; the Agent Manager move-target and indexing-sentinel cases only matter once those Kilo endpoints exist (agent manager: IDE-owned; indexing config patch: indexing-rpc.ts present — sentinel handling unverified). L2: // kilocode_change start / L5: // kilocode_change end +packages/opencode/src/server/routes/instance/httpapi/server.ts 6,30,33,37,51,56,60,115,122,127,191,198,200,238,249,315,316,321,323,344,380 21 shared-upstream patch marker Server/API integration partial `packages/kilo-cli/src/interactive-server.ts` and `runtime.ts` host composition (memory-plugin, gateway plugin, settings) over native Core services Markers wire Credential, ModelCache, MemoryService, Kilo HttpApi handlers and a reference-reconciler readiness gate into the upstream server layer. V2 composes Kilo services as plugins/hosts in kilo-cli (interactive-server.ts registers memory/gateway/settings/generation plugins; Core owns credential and catalog). PRESERVED: memory, credential, model catalog composition; MISSING: reference reconciler readiness gate (no v2 reconciler) and Kilo handler groups listed as missing in the api.ts row. L6: import { EffectFlock } from "@opencode-ai/core/util/effect-flock" // kilocode_change / L30: import { ModelCache } from "@/provider/model-cache" // kilocode_change +packages/opencode/src/server/server.ts 4,17,26,32,88,105,112,137 8 shared-upstream patch marker Server/API integration partial `packages/server/src/server-info.ts` (network URLs) and `packages/kilo-cli/src/server.ts`/`daemon.ts` listener ownership PRESERVED — advertised URLs including network interfaces (server-info.ts uses networkInterfaces) and Kilo owning listener lifecycle in kilo-cli server/daemon. MISSING/unverified — the KiloListener.build layer semantics (Kilo-specific listener routes composition) have no direct counterpart; v2 Kilo composes routes through interactive-server.ts rather than wrapping upstream listeners, so remaining v1 listener extensions (if any beyond URL reporting) are not mapped. L4: import { serverUrls } from "@/kilocode/cli/server-urls" // kilocode_change / L17: import * as KiloListener from "@/kilocode/server/listener" // kilocode_change +packages/opencode/src/server/shared/ui.ts 5,64,73,77 4 shared-upstream patch marker Server/API integration needs port Kilo-owned static route in `packages/kilo-cli/src/interactive-server.ts`/`server.ts` (v2 packages/console is upstream's cloud console, not the CLI-embedded Kilo Console) Markers mount ConsoleAssets under /console and remove the upstream proxy fallback to app.opencode.ai. V2 kilo-cli serves no embedded console assets (grep console in kilo-cli/src: none) and v2 server ui routing is upstream's; the Kilo Console command (v1 KiloConsoleCommand) is untracked here. Fallback removal is moot because v2 Kilo does not use the upstream web UI proxy. L5: import { ConsoleAssets } from "@/kilocode/console/assets" // kilocode_change / L64: // kilocode_change start - serve Kilo Console under /console +packages/opencode/src/server/shared/workspace-routing.ts 22 1 shared-upstream patch marker Server/API integration native equivalent `packages/protocol/src/groups/session.ts:753` (`session viewed` endpoint natively defined; Location-based routing replaces directory workspace routing) Marker keeps the Kilo static viewed route out of session-ID parsing. V2 defines the viewed transition natively in the session group and routes by Location instead of path-based workspace parsing, so the special case is unnecessary. L22: if (url.pathname === "/session/viewed") return null // kilocode_change - Kilo static route is not a session ID +packages/opencode/src/session/compaction.ts 2,17,21,27,30,31,34,64,66,96,103,153,185,214,220,268,319,342,360,389,435,449,469,472,490,492,494,510,511,524,527,538,572,598,618,621,650,655,681,683,697,703,717 43 shared-upstream patch marker Session lifecycle/history/UI partial `packages/core/src/session/compaction.ts` (`estimateTokens` :128-164, one overflow-compaction rebuild per AGENTS Session Core) and `runner/step.ts`; queue retarget covered by v2 durable inbox 43 markers. PRESERVED — token estimation over model messages (core compaction.ts:162-164 Token.estimate), compaction of oversized turns instead of replay loops (v2 single compaction rebuild then continuation), queued prompts surviving compaction (v2 inbox stops steers before compaction control items), compaction markers visible during queued turns (v2 durable compaction records). MISSING — opt-in `prune` with default payload/compaction cleanup and safe pruning at cache-invalidating boundaries (core/config/normalize.ts:323 marks compaction.prune unsupported), preflight threshold compaction replay with media preserved-then-stripped after provider overflow (overflow row partial: proactive economic threshold unproven), self-contained compaction export capture (no export module), `include: recent` field for released Core V2 readers (v1-only compatibility). L2: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change / L17: import * as DateTime from "effect/DateTime" // kilocode_change +packages/opencode/src/session/instruction.ts 14,15,63,65,91,93,96,111,128,186,190,191,196,200,204 15 shared-upstream patch marker Session lifecycle/history/UI partial `packages/core/src/instruction-discovery.ts` (global/project ordering :30, `global` option :42-100) and `core/src/instructions/*`; Kilo layout in `packages/kilo-cli/src/paths.ts` PRESERVED — global-then-nearest project instruction ordering and 'Instructions from:' attribution (v2 InstructionDiscovery renders sources with paths). MISSING — preferring the KILO_CONFIG_DIR profile directory over global.config (v2 discovery uses Core Global paths; kilo2 layout is handled in paths.ts for state, not instruction roots — unverified linkage), refusing env/file reads outside the project root for project-declared instruction globs (v2 confinement of `{file:}`/glob expansion not found in instruction-discovery.ts), declaration provenance through path expansion. L14: import { KilocodeInstruction } from "@/kilocode/session/instruction" // kilocode_change / L15: import type { KilocodeMarkdown } from "@/kilocode/config/markdown" // kilocode_change +packages/opencode/src/session/llm.ts 7,19,27,36,43,60,61,100,129,159,167,263,299,372,374,395,398,419,422,423,439,472,474 23 shared-upstream patch marker Session lifecycle/history/UI partial `packages/core/src/session/runner/llm.ts` and `model-request.ts`; Kilo timeout/header policy would live in `packages/kilo-gateway/src/plugin.ts` http.request hook PRESERVED — one explicit llm.stream per attempt with overflow compaction (v2 runner), tool-call name repair by lowercasing (unverified; v2 uses schema-validated tool names — treat as not proven), allowSystemInMessages (v2 request assembly renders system natively). MISSING — proactive preflight threshold compaction (overflow row partial), provider-reported context tokens as output-cap source, KiloLLM per-model timeout options, session-export request start/completion capture and stream observer (no export module), disabling AI SDK span recording (v2 uses aisdk-native.ts; telemetry overlap doc does not cover spans). L7: import { Log } from "@opencode-ai/core/util/log" // kilocode_change / L19: import { usable } from "./overflow" // kilocode_change +packages/opencode/src/session/llm/ai-sdk.ts 5,6,56,65,110,115 6 shared-upstream patch marker Session lifecycle/history/UI partial `packages/kilo-cli/src/routed-model.ts`/`routed-model-plugin.ts` (routed model native), `packages/core/src/aisdk-native.ts` PRESERVED — Kilo routed-model resolution (routed-model rows, baseline model-loading-v2-parity). MISSING — preserving raw provider billing details (`providerMetadata.aiSdk`) on assistant parts for Kilo cost display (grep raw/billing in core/aisdk-native: none). L5: import { KiloRoutedModel } from "@/kilocode/session/routed-model" // kilocode_change / L6: import { KiloResponseMetadata } from "@/kilocode/session/response-metadata" // kilocode_change +packages/opencode/src/session/llm/request.ts 12,17,18,31,71,74,76,108,112,121,123,154,161,180,189,190,192,223,234,247,249,256 22 shared-upstream patch marker Session lifecycle/history/UI partial `packages/kilo-gateway/src/plugin.ts:310-345` http.request hook (auth/org header, body rewrite) and `packages/kilo-cli/src/model-prompt-policy.ts` PRESERVED — Kilo model-specific system prompt selection (prompt-policy selectors incl. gpt55/ling/beast), Kilo Gateway auth and X-KILOCODE-ORGANIZATIONID (plugin.ts:40, account.ts:11). MISSING — soul/persona prepend policy (grep soul/persona: none), stripping id/displayName/source agent metadata (options.ts row), gpt-5 via openai-compatible proxy handling, project ID + machine ID + root product session attribution headers and DEFAULT_HEADERS for non-anthropic providers (gateway hook sets only authorization/org). L12: import { USER_AGENT } from "@/installation" // kilocode_change / L17: import { DEFAULT_HEADERS } from "@/kilocode/const" // kilocode_change +packages/opencode/src/session/message-v2.ts 19,38,39,40,41,42,55,82,86,114,128,183,199,201,216,401,420,426,632,675,756,763,764,767 24 shared-upstream patch marker Session lifecycle/history/UI partial `packages/core/src/session/{message,history,to-llm-message}.ts`, `runner/to-llm-message.ts`; Kilo error mapping in `packages/kilo-gateway` and codex provider plugin PRESERVED — chronology-based ordering before projection (v2 ordered aggregate history), ignored parts excluded from prompts (v2 history projection). MISSING/unproven — stripping bloated part metadata on all read paths to prevent multi-MB payloads (grep stripPartMetadata: none), avoiding malformed Unicode in compacted tool output, not replaying send_file attachments (send-file needs port), CodexAuthExpiredError and SessionNetwork.disconnected mapping to session errors (network needs port; codex refresh in v2 core/plugin/provider/openai.ts handles OAuth natively but Kilo-specific expiry error class absent), EditorContext re-export. Paired test row (test/session/message-v2.test.ts) already partial. L19: export { EditorContext } from "@/kilocode/editor-context" // kilocode_change / L38: import { Snapshot } from "@/snapshot" // kilocode_change +packages/opencode/src/session/network.ts 1,158,180,352,379 5 shared-upstream patch marker Offline detection and reconnect wait needs port Kilo-owned Core session execution status plus TUI/Protocol event seam Pinned v1 classifies nested socket/browser/TimeoutError failures, publishes an offline wait, probes restoration, permits manual reply/reject and cancels safely. V2 generic retry and simulation networking do not implement this user-visible lifecycle. L1: // kilocode_change - new file / L158: // kilocode_change - recognize AbortSignal.timeout() errors +packages/opencode/src/session/overflow.ts 7,32,33,35 4 shared-upstream patch marker Context overflow accounting and recovery partial `packages/core/src/session/runner/step.ts:100-152` and `test/session-runner.test.ts:2395-2615` V2 detects provider context-overflow, performs one compaction rebuild, respects auto-compaction disablement and tests failure/interrupt boundaries. The pinned Kilo token counter and proactive economic threshold invariant are not proven. L7: import { KiloSessionOverflow } from "@/kilocode/session/overflow" // kilocode_change / L32: const count = KiloSessionOverflow.count(input.tokens) // kilocode_change +packages/opencode/src/session/processor.ts 16,24,26,31,47,52,63,70,73,89,94,97,101,124,130,136,142,147,150,155,158,159,161,173,178,192,198,211,229,257,270,273,280,286,290,303,307,313,392,412,434,436,440,446,452,455,480,520,537,549,553,566,576,583,588,601,602,608,615,623,624,643,645,647,657,658,659,660,661,665,685,709,716,719,723,744,769,771,842,843,848,850,852,857,862,870,873,895,899,906,912,914,918,955,1031,1044,1046,1071 98 shared-upstream patch marker Session lifecycle/history/UI partial Core `packages/core/src/session/runner/step.ts`/`llm.ts`/`retry.ts` natively; Kilo: `packages/kilo-cli/src/remote-suggestions.ts`, `session-usage-plugin.ts`, `telemetry.ts` 98 markers. PRESERVED — retry only terminally incomplete attempts / consistent retry state (v2 runner retry.ts + generic pre-output retries per AGENTS Session Core), deleted-session FK tolerance (v2 durable projections), finish-step without start-step guard (v2 step model), cost reconciliation from subagents (cost-propagation native), step timing/model on step-finish (v2 durable step records), suggestion dismissal ending the turn (remote-suggestions.ts accept/dismiss flow), compactError as internal preflight signal (v2 compaction rebuild). MISSING — configured output-token ceiling via RuntimeFlags and stronger reasoning-only output-limit message (grep output limit in core: only v1-migration text), per-step metrics/generationID/vercelID capture (metrics row native covers usage only; no generationID/vercelID fields in v2 schema), send_file delivery attachments (send-file row needs port), offline handler abort (network row needs port), permission approval provenance merged into tool metadata (no PermissionProvenance in v2), buffered tool metadata before tool-call registration (grep toolmeta: none; v2 step model may not need it — unverified), accepted-suggest review telemetry tagging. L16: import { RuntimeFlags } from "@/effect/runtime-flags" // kilocode_change - configured output token ceiling / L24: // kilocode_change start +packages/opencode/src/session/prompt.ts 2,5,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,69,70,71,77,84,90,91,117,119,121,141,145,148,152,171,189,201,204,225,237,241,247,253,256,315,325,342,359,375,383,430,441,445,450,458,462,464,474,487,504,509,542,547,590,656,664,672,682,691,698,700,709,743,757,782,786,790,828,831,844,851,860,890,910,913,919,936,942,1034,1044,1046,1053,1057,1059,1063,1090,1093,1216,1295,1304,1331,1348,1366,1403,1406,1407,1415,1422,1425,1446,1450,1456,1464,1468,1471,1476,1477,1479,1480,1481,1491,1495,1496,1497,1499,1502,1509,1514,1515,1520,1530,1545,1551,1552,1594,1600,1609,1624,1675,1676,1693,1697,1698,1702,1706,1723,1728,1730,1733,1734,1759,1762,1770,1774,1785,1794,1797,1800,1817,1828,1835,1838,1843,1845,1858,1864,1865,1868,1878,1879,1893,1910,1920,1933,1948,1961,1964,2233,2244,2245,2252,2257,2258,2323,2351,2359,2362,2367,2383,2393,2395,2403,2406,2422,2447,2448,2470,2490,2494,2495,2497,2507,2522,2526,2548,2552,2607,2639,2640 212 shared-upstream patch marker Session lifecycle/history/UI partial Core `packages/core/src/session/{prompt,inbox,runner/*,compaction}.ts` for native equivalents; Kilo plugins `packages/kilo-cli/src/{plan-policy,memory-plugin,skill-shell,sandbox-mcp,resume-external,review-policy,routed-model}.ts` for ported Kilo behavior 212 markers; per-invariant: PRESERVED natively — queued follow-ups/hide later prompts (v2 durable inbox + steer boundaries), trimBeforeLastSummary/compaction cap (v2 one overflow-compaction rebuild per step, compaction epoch), plan_exit hard stop (kilo-cli plan-policy.ts), memory injection + recall marker (memory-plugin.ts:662-673 system push), skill `!cmd` trusted/kill-switch (skill-shell.ts), sandbox MCP denial (sandbox-mcp.ts fail-closed connect hook), external resume commands (resume-external.ts/external-sessions.ts), deprecated review aliases static notice (review-policy.ts), user image normalization (core/session/prompt.ts:123-140), subagent cost propagation (cost-propagation row native), interrupted-cause handling (Effect interrupt), title-request isolation (core/session/title.ts). MISSING / unproven — configured @reference mention expansion into prompt parts and reference-root attachment authorization (v2 Core has `core/src/reference.ts` + protocol `reference.list`, but `core/src/session/prompt.ts` performs no reference mention expansion), prompt-attachment authorization through read/external_directory permissions (core external_directory is agent-config only), editorContext carry-through (grep editorContext core/kilo-cli: none), ephemeralTools remote tool restrictions (grep: none), workflow explicit model/variant for subtasks (workflow-variant row needs port), REQUEST_PRUNE_BYTES persistent tool-output pruning and SWE-Pruner (grep prune/Pruner: none), provider-reported context tokens feeding output cap, review-command LLM telemetry marker, BUILTIN_COMMANDS error hint, snapshotInitialization wait policy (slow-snapshot guard partial), 'No models are currently available' hint, v2-shell dual-write correlation (v2 native), guardRequirements before turn (no RequirementBlockedError in v2). Each missing item is a Kilo-specific behavior with no v2 consumer today. L2: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change / L5: import fs from "node:fs" // kilocode_change +packages/opencode/src/session/reminders.ts 3,12,25,35,40,46,64,65,72,74 10 shared-upstream patch marker Session lifecycle/history/UI native equivalent `packages/kilo-cli/src/plan-policy.ts` + `plan-prompt.txt` (plan-v2-parity baseline) and Kilo agent naming in `agent-policy.ts` Markers rename BUILD_SWITCH to CODE_SWITCH and route Kilo's shared planning reminder/plan-file execution hint. V2 plan-policy owns the plan hand-off text and follow-up choices (newSession/continue/refine) with plan file paths, replacing the reminder mechanism; no Kilo agent is named build in v2 policy. L3: import { KiloSessionPrompt } from "@/kilocode/session/prompt" // kilocode_change / L12: import CODE_SWITCH from "./prompt/code-switch.txt" // kilocode_change +packages/opencode/src/session/retry.ts 5,6,12,68,74,76,82,86,134,137,141,145,151,163 14 shared-upstream patch marker Session lifecycle/history/UI partial `packages/core/src/session/runner/retry.ts` (`isRetryable` :29-59, bounded delay) PRESERVED — bounded retry delays and classification (retry.ts:59 caps retry-after), no OpenCode Go upsell reasons (v2 Kilo does not expose Go). MISSING — treating Kilo auth/signup-required errors and FreeUsageLimitError as non-retryable (grep isKiloError/Kilo in runner/retry.ts: none), network-disconnect offline handler (network row needs port). Retry limit exists natively via generic pre-output retry accounting. L5: import { isKiloError } from "@/kilocode/kilo-errors" // kilocode_change / L6: import { SessionNetwork } from "./network" // kilocode_change +packages/opencode/src/session/revert.ts 5,13,39,73,83,85,123,132,150,185,190,210 12 shared-upstream patch marker Session lifecycle/history/UI partial `packages/core/src/session/revert.ts` and `snapshot.ts` restore PRESERVED — v2 revert is durable and committed revert clears instruction epoch (AGENTS Session Core), snapshot restore exists. MISSING/unproven — keeping the whole workspace transition atomic and preserving the reverted workspace when redo cannot complete, clearing a reverted provider error from the retained assistant message, honoring Config snapshot toggle in revert (no evidence in core/session/revert.ts; not source-verified as equivalent). L5: import { Config } from "@/config/config" // kilocode_change / L13: import { KiloSessionRevert } from "@/kilocode/session/revert" // kilocode_change +packages/opencode/src/session/run-state.ts 2,112 2 shared-upstream patch marker Session run coordination layer native equivalent `packages/core/src/session/execution/` and SessionRunCoordinator V2 replaces legacy defaultLayer composition with its process-global Session-ID coordinator and execution claims; same-session joins, different-session concurrency, cancellation and restart recovery are native architecture. L2: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change / L112: export const defaultLayer: Layer.Layer = Layer.suspend(() => AppNodeBuilder.build(node)) // kilocode_change - build from the LayerNode graph +packages/opencode/src/session/session.ts 2,31,36,45,47,50,79,89,157,187,222,266,278,279,282,328,333,402,406,409,414,423,467,475,513,522,526,601,613,614,616,641,643,657,659,663,685,689,691,709,727,742,744,745,746,750,758,763,769,778,804,812,820,821,831,832,833,836,844,846,847,863,870,871,872,886,891,899,907,909,915,1123,1149,1156,1175,1179,1206,1221,1223 79 shared-upstream patch marker Session lifecycle/history/UI partial `packages/schema/src/session.ts` Info + `packages/core/src/session/{session,transfer}.ts` for forks; Kilo: `packages/kilo-cli/src/sandbox.ts`, `import-v1-session.ts`; Agent Manager filters belong to `packages/kilo-vscode/src/agent-manager/` (IDE-owned) 79 markers. PRESERVED — fork copies model context and history (v2 SessionFork boundary in schema/session-fork.ts; transfer.ts carries info.cost), revert state brand alignment (v2 Revert schema shared), FK-safe deletes (v2 projections), plans directory `.kilo/plans` (plan-policy.ts:194). MISSING — per-session `platform` telemetry attribution field (v2 Session.Info has no platform), worktreeName and cross-project worktree-family/directories[] list filters for Agent Manager (v2 list is Location/project scoped; Agent Manager stays with VS Code per plan), server-issued sandbox inheritance grant/sourceID/sandboxFallback across forks (v2 sandbox.ts confines processes but has no per-session inherited policy grant), fork zeroing pre-fork cost on copied assistant/step-finish parts (transfer.ts copies cost unchanged — v2 counts fork cost differently; not proven equivalent), lightweight `diffs` summary and workspace restore status on messages, provider-reported cost preference for OpenRouter/Kilo (unverified in v2 usage), SessionExport close hooks (no export module), requirement failures over session.error. L2: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change / L31: import { SessionMessage } from "@opencode-ai/core/session/message" // kilocode_change - shared Revert.State brand +packages/opencode/src/session/status.ts 54 1 shared-upstream patch marker Legacy status layer composition native equivalent Core durable execution/status projection and Server event delivery The marker only preserved a legacy default Layer for old callers. V2 owns status through native Core/Server services and projected execution events; the indexing status filename is unrelated. L54: // kilocode_change - preserve legacy layer composition for Kilo callers +packages/opencode/src/session/summary.ts 8,9,70,83,111,113,129,138,142,147,148,166,167,183,204,205,212 17 shared-upstream patch marker Session lifecycle/history/UI partial `packages/core/src/snapshot.ts` diff (`DiffInput` :29, structured per-file diffs :58) and session stats; extension consumer `packages/kilo-vscode/src/agent-manager/worktree-diff-controller.ts` PRESERVED — per-file structured diffs between snapshots (snapshot.ts diff). MISSING — respecting `snapshot:false` in summary (v2 snapshot enabled() gate exists at capture, equivalent in effect), cumulative/imported diff retention for cloud-forked sessions (no import of session_diff storage), `full`/`file` request for authoritative single-file full-content detail used by editor diff tabs (extension port relies on adapter; not found in v2 Core API). L8: import { appendSessionDiffs, readSessionDiffBase } from "@/kilocode/session-portability/cumulative-diff" // kilocode_change / L9: import { Storage } from "@/storage/storage" // kilocode_change +packages/opencode/src/session/system.ts 11,13,27,29,36,38,46,49,74,88,93,106,109,149,195 15 shared-upstream patch marker Session lifecycle/history/UI native equivalent `packages/kilo-cli/src/model-prompt-policy.ts` (ling/gpt55/beast selectors :18-23) and `packages/core/src/plugin/system-prompt.ts` environment builtins Markers select PROMPT_LING/PROMPT_GPT55 by model, wait for plugin readiness and pass editorContext into environment. V2 prompt policy adapts all eight v1 catalog selectors from source and Core builtins render platform/environment (instructions/builtins.ts:35); editorContext injection is tracked under kilocode/system-prompt.ts (partial) rather than here. L11: import PROMPT_GPT55 from "./prompt/kilocode-gpt-5.5.txt" // kilocode_change / L13: import PROMPT_LING from "./prompt/ling.txt" // kilocode_change +packages/opencode/src/session/todo.ts 10,73 2 shared-upstream patch marker Legacy todo layer composition native equivalent Native v2 session projection and TUI tool rendering The marked invariant is only dependency-layer compatibility in the old graph, which v2's native services do not require. This does not claim parity for unrelated legacy Todo workflow policy. L10: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change / L73: // kilocode_change - preserve legacy layer composition for Kilo callers +packages/opencode/src/session/tools.ts 2,3,23,26,30,51,55,61,64,68,73,83,133,138,141,145,158,164,174,175,191,444,446,462,471 25 shared-upstream patch marker Session lifecycle/history/UI partial `packages/core/src/session/runner/*` tool resolution, `packages/kilo-cli/src/memory-plugin.ts` (recall), `sandbox-mcp.ts` PRESERVED — memory recall tool and injected marker (memory-plugin.ts recall :221-334), MCP tools withheld under sandbox network restriction (sandbox-mcp fail-closed hook prevents connections). MISSING — SWE-Pruner focus parameter advertisement (experimental; grep: none), model.family passed to tool definitions, Kilo ask-time permission resolution hooks via KiloSessionPrompt (v2 permission is native and resolves at request time). L2: import { KiloSessionPrompt } from "@/kilocode/session/prompt" // kilocode_change / L3: import { MemoryMarker } from "@/kilocode/memory/marker" // kilocode_change +packages/opencode/src/share/session.ts 7,28,34,54 4 shared-upstream patch marker Kilo public session sharing partial Kilo Gateway session share/fork/unshare plugin and `packages/kilo-cli/test/gateway-integration.test.ts:255-276` A local fixture proves authenticated share URL creation, public retrieval/fork and unshare. Deployed viewer compatibility, organization/team policy and production service behavior remain unverified, so this is not blanket end-to-end parity. L7: import { KiloSession } from "@/kilocode/session" // kilocode_change / L28: const result = yield* KiloSession.shareSession(sessionID) // kilocode_change - use Kilo public share URLs +packages/opencode/src/share/share-next.ts 94,107 2 shared-upstream patch marker Legacy share transport normalization partial Kilo Gateway share serializer and v1 session import boundary V2 normalizes imported legacy rows and uses a different Gateway-backed path, but no current test proves the pinned missing-file diff normalization at the deployed incremental-sync boundary; architecture change alone is insufficient parity evidence. L94: // kilocode_change start - preserve the share transport contract when stored legacy summary diffs omit file details / L107: // kilocode_change end +packages/opencode/src/skill/discovery.ts 9,52,55,70,104,106,157 7 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/core/src/skill/discovery.ts:16-40` (`isSafeSegment`, `isSafeRelativePath`) with origin-resolved downloads Markers validate remote index-controlled names/paths and pin downloads to the index origin. V2 discovery.ts contains the same safe-segment/relative-path validators natively (upstream adopted the hardening). L9: import { isSafeSegment, isSafeRelativePath } from "@/kilocode/skill/discovery-validate" // kilocode_change / L52: // kilocode_change start - resolve the index origin so file downloads can be pinned to it +packages/opencode/src/skill/index.ts 17,18,19,21,23,27,29,39,85,102,112,115,117,124,129,132,146,150,154,156,164,186,195,210,211,221,224,228,230,233,234,241,246,248,260,272,280,286,291,301,308,311,313,329,340,341,414 47 shared-upstream patch marker Package behavior indicated by path partial `packages/core/src/skill.ts` + `skill/discovery.ts`; Kilo `packages/kilo-cli/src/skill-policy.ts` (builtin) and `skill-shell.ts` (trust gating) PRESERVED — builtin skill seeded before discovery with user override handling (skill-policy.ts BUILTIN_LOCATION; collision reporting), shell injection gated by trust in skill-shell.ts. MISSING — `trusted` provenance field on skill records propagated from the declaring config source (grep trust in core/skill: none), project skills' frontmatter substitution confined to project root, primary-worktree fallback scanning for worktrees (.kilocode/.kilo primary paths), KILO_CONFIG_DIR skill roots, duplicate/location diagnostics. L17: import { BUILTIN_SKILLS } from "../kilocode/skills/builtin" // kilocode_change / L18: import { primaryPaths } from "../kilocode/primary-worktree" // kilocode_change +packages/opencode/src/snapshot/index.ts 2,4,12,16,25,30,33,37,42,45,56,63,69,75,80,83,90,183,188,190,202,212,214,215,219,223,250,252,317,334,341,343,357,359,364,374,383,386,420,432,437,480,487,495,502,526,539,845,865,889,898,912,914,918,922,931,958,969,988,990,995,1000,1005 63 shared-upstream patch marker Package behavior indicated by path partial `packages/core/src/snapshot.ts` (git-backed capture/compare/restore, `maximumUntrackedFileBytes`, enabled() gate) and `git.ts` tree operations 63 markers. PRESERVED — git-based diff generation instead of JS diffs, structured per-file diffs, restore through git trees, snapshot disabled when Location has no git VCS. MISSING/unverified — SummaryFileDiff lightweight diffs and MAX_DIFF_SIZE cap, 7-day retention/pruning, EffectFlock serialization across CLI and extension processes, worktree-index seeding and object pinning with background materialization/resume, per-checkpoint validation before mutating files and never reporting success for unrestored files, `diffFile` authoritative full-content detail for editor tabs, ACP client opt-out, prompt-context slow-snapshot policy (track.ts row). L2: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change / L4: import { Struct } from "effect" // kilocode_change +packages/opencode/src/storage/db.ts 8,18,35,37,38,39,40,107 8 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/kilo-cli/src/paths.ts:26` (`kilo2.db` under isolated data dir; preflight()/layout()) and Core `database/path.ts` Markers name the DB kilo.db (with opencode-*.db fallback) and assert writability with self-heal. V2 Kilo owns an isolated kilo2.db path with layout/preflight checks in paths.ts; fallback to old opencode DB names is intentionally replaced by explicit import. L8: import { DbPreflight } from "@opencode-ai/core/kilocode/db-preflight" // kilocode_change / L18: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change +packages/opencode/src/suggestion/index.ts 1 1 shared-upstream patch marker Suggestion lifecycle API native equivalent `packages/kilo-cli/src/remote-suggestions.ts:43-208` The owned v2 state machine preserves pending requests, eligibility, single settlement, dismissal and lifecycle cleanup; the old shared re-export is unnecessary. L1: // kilocode_change - new file +packages/opencode/src/sync/index.ts 10,15,20,21,40,52,57,117,130,219,239,242,243,246,335,351,355,367,369,372,382,394,427,428,433,436 26 shared-upstream patch marker Package behavior indicated by path not needed None; v2 durable events are native (`packages/core/src/event/*`) 26 markers make the v1 legacy sync layer carry encoded EventV2 rows. V2 has no legacy sync layer (context/sync TUI rows assessed separately). L10: import { EventSequenceTable, EventTable } from "@opencode-ai/core/event/sql" // kilocode_change - upstream moved the event tables to core / L15: import { EventManifest } from "@/event-manifest" // kilocode_change +packages/opencode/src/tool/apply_patch.ts 15,16,17,20,72,79,106,122,135,139,176,184,196,210,251,256,263,310,314,325,330,337 22 shared-upstream patch marker Tool behavior/UI partial `packages/core/src/tool/plugin/patch.ts` (BOM-aware via @opencode-ai/util/bom) and `edit.ts:107` (LSP diagnostics TODO) PRESERVED — BOM preservation on patched files (core patch.ts imports Bom; edit.ts:207-214 syncTextBom). MISSING — non-UTF-8 encoding detection/round-trip (EncodedIO; core reads utf-8 only), Kilo config validation warnings appended after edits (grep ConfigValidation: none), assertMutablePath protection of Agent Manager-managed paths (grep: none), diagnostics filtering to changed paths (v2 has no LSP diagnostics yet: edit.ts:107 TODO). L15: import { filterDiagnostics } from "./diagnostics" // kilocode_change / L16: import { ConfigValidation } from "../kilocode/config-validation" // kilocode_change +packages/opencode/src/tool/diagnostics.ts 1 1 shared-upstream patch marker Tool behavior/UI needs port Core tool diagnostics once LSP runtime exists (`packages/core/src/tool/plugin/edit.ts:107` TODO); test row test/tool/diagnostics-filter.test.ts pairs Whole new file filters diagnostics to the edited paths so unrelated errors are not echoed. V2 has no LSP/diagnostics pipeline at all, so the filter and its motivating behavior are absent. L1: // kilocode_change - new file +packages/opencode/src/tool/edit.ts 21,22,23,24,25,27,29,47,110,116,131,139,142,157,161,180,188,192,212,217,228,232,234 23 shared-upstream patch marker Tool behavior/UI partial `packages/core/src/tool/plugin/edit.ts` and `file-diff.ts` PRESERVED — BOM-aware replace and structured file diff on results (edit.ts:207-214, file-diff.ts). MISSING — Encoding.read/EncodedIO non-UTF-8 handling, MAX_DIFF_CONTENT 500k cap on diff content (not found in file-diff.ts), ConfigValidation warnings for edited config files, assertMutablePath protection, diagnostics filtering (no LSP). L21: import { filterDiagnostics } from "./diagnostics" // kilocode_change / L22: import { ConfigValidation } from "../kilocode/config-validation" // kilocode_change +packages/opencode/src/tool/external-directory.ts 14,22,35,37 4 shared-upstream patch marker Tool behavior/UI native equivalent `packages/core/src/location-mutation.ts` (externalDirectory resolution) + `tool/plugin/shell.ts:125-140` permission.assert on each external directory Markers stop root workspace boundaries from auto-allowing external_directory while keeping root-workspace behavior outside prompts. V2 resolves every path through LocationMutation and asserts external_directory permission per resource (shell.ts:134-140; read/edit via mutation.resolve), with agent config allows only via explicit rules (core/config/plugin/agent.ts), matching the fail-closed invariant. L14: // kilocode_change start - root boundaries must not auto-allow external_directory / L22: // kilocode_change end +packages/opencode/src/tool/glob.ts 10,26,46,57,60,71,74,76,80,92 10 shared-upstream patch marker Tool behavior/UI partial `packages/core/src/tool/plugin/glob.ts` (absolute target resolution :89-98, timeout) PRESERVED — absolute paths resolved via Environment/LocationMutation and bounded search with timeout. MISSING/unverified — splitting absolute glob patterns like `~/.config/kilo/command/*.md` into cwd+relative glob (v2 resolves `path` param, not absolute `pattern` — not confirmed), passing ctx.abort to ripgrep and the '(Some discovered files could not be read.)' partial notice (grep partial in glob.ts: none). L10: // kilocode_change start — support absolute glob patterns (e.g. ~/.config/kilo/command/*.md) / L26: // kilocode_change end +packages/opencode/src/tool/grep.ts 6,12,19,28,32,33,51,67,71,74,75,77,80,83,87,88,91,95,96,106,111,113,114 23 shared-upstream patch marker Tool behavior/UI partial `packages/core/src/tool/plugin/grep.ts` (limit :35, truncation notice :55-58) over Core FileSystem.GrepInput PRESERVED — limit parameter and truncation notice, path-scoped searches. MISSING/unverified — context lines, literal and ignoreCase fields (KiloGrep.fields; core GrepInput exposes limit; context/literal not found in grep.ts), abort signal to ripgrep, exact-file basename constraint, '(Some paths were inaccessible.)' partial notice, per-row textTruncated markers. L6: import * as KiloGrep from "@/kilocode/tool/grep-signal-controls" // kilocode_change / L12: pattern: Schema.String.annotate({ description: "Pattern to search for in file contents (regex by default)" }), // kilocode_change +packages/opencode/src/tool/plan.ts 1 1 shared-upstream patch marker Plan completion tool hook native equivalent Kilo CLI plan policy/tool registration and `packages/kilo-cli/test/plan-policy.test.ts` V2 registers and authorizes native `plan_exit`, requires a saved plan path, carries configured denials, and tests completion/dismissal/fail-closed behavior. The memory capture filename is a consumer, not the implementation seam. L1: export { Parameters, PlanExitTool } from "@/kilocode/tool/plan" // kilocode_change +packages/opencode/src/tool/question.ts 5,13,26,37 4 shared-upstream patch marker Tool behavior/UI native equivalent `packages/core/src/tool/plugin/question.ts` + Core `form.ts` (form.cancel produces cancelled outcome; kilo-cli run.ts:132 cancelForm) Markers surface Question.dismissAll rejection as a normal `dismissed` result instead of an error. V2 forms model cancellation as a first-class outcome (client.form.cancel; runner treats cancelled forms as tool results), covering the invariant; the prompt-dismiss-contract test row is separately partial for remote specifics. L5: import { KiloQuestionTool } from "@/kilocode/tool/question" // kilocode_change / L13: dismissed?: boolean // kilocode_change +packages/opencode/src/tool/read.ts 1,4,11,15,22,26,28,87,101,124,144,182,185,212,222,223,233,235,252,263,265,298,391,403,405,421,424 27 shared-upstream patch marker Tool behavior/UI partial `packages/core/src/tool/plugin/read.ts` and `tool/read-filesystem.ts` PRESERVED — path resolution and permission before content read via LocationMutation (read.ts absolute resolution), missing-target failure before authorization details. MISSING/unverified — binding every content read to the same reopened authorized object and rejecting canonical path changes after approval (TOCTOU guard; not found), UTF-16/32 BOM NUL heuristic skip and Unicode-safe slice reporting, V1 configured reference resolution, bounded directory listing without child bodies, zero-limit guard. L1: import { Effect, Schema, Scope } from "effect" // kilocode_change - stable object reads do not use Option / L4: import { Readable } from "stream" // kilocode_change +packages/opencode/src/tool/recall.ts 1,8,9,10,11,12,13,14,36,72,84,131,132,139 14 shared-upstream patch marker Historical session transcript recall partial `packages/kilo-memory/src/tool.ts` and `recall/`, with a missing session-transcript search seam V2 has permissioned Kilo memory catalog/search/digest recall with injection-safe output, but it does not search arbitrary historical session titles/transcripts across sibling worktrees or read a selected transcript with active queued-boundary exclusion. Project memory is not full transcript-recall parity. L1: // kilocode_change - new file / L8: import { Filesystem } from "../util/filesystem" // kilocode_change +packages/opencode/src/tool/registry.ts 2,7,10,33,34,35,36,37,38,39,46,48,63,64,65,68,69,72,73,74,80,97,104,115,128,129,138,143,230,234,235,249,250,256,259,265,269,284,294,303,325,337,340,342,344,348,350,354,357,369,385,400,478,518 54 shared-upstream patch marker Tool behavior/UI partial `packages/kilo-cli/src/{agent-policy,skill-policy,tool-authorization,remote-suggestions,memory-plugin,sandbox-mcp}.ts` registering via `ctx.tool.transform`; Core `tool/plugin/opencode.ts` PRESERVED — suggest tool (remote-suggestions.ts), memory tools (memory-plugin), skill inventory in skill tool (skill-policy), question tool availability by client (native forms), sandbox-aware tool network (sandbox-mcp/sandbox.ts). MISSING — notebook, agent-manager, repo-overview/repo-clone (experimental Scout), notify_user, KiloToolRegistry.usePatch edit/patch switching and per-agent availability rules, `web_search: true` config forcing websearch on, ToolNetwork.builtin wrapping (no v2 tool-level network policy), warpgrep/codebase_search. L2: import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" // kilocode_change / L7: // kilocode_change start +packages/opencode/src/tool/repo_clone.ts 4,5,6 3 shared-upstream patch marker Tool behavior/UI not needed None; v2 has no Scout/repo_clone tool and Core `repository-cache` remained upstream Three markers only import Global/Repository/RepositoryCache for the experimental Scout clone tool. The tool itself is unmarked v1 experimental code not present in v2; the marker is import glue. L4: import { Global } from "@opencode-ai/core/global" // kilocode_change / L5: import { Repository } from "@opencode-ai/core/repository" // kilocode_change +packages/opencode/src/tool/shell.ts 1,15,21,22,23,24,56,58,81,83,89,136,142,288,289,291,302,311,320,324,409,415,427,432,437,456,458,460,464,466,517,526,536,589,643,660,664,672,677,698,702,731,732,741 44 shared-upstream patch marker Tool behavior/UI partial `packages/core/src/tool/plugin/shell.ts` (ShellParse.scan :124, external_directory assert :134-140, DEFAULT_TIMEOUT_MS :22) and `core/src/shell/{parse,scan,select}.ts`; Kilo skill batching in `packages/kilo-cli/src/skill-shell.ts` PRESERVED — tree-sitter/portable scan feeding permissions, PowerShell/pwsh handling (shell/select.ts, scan.ts), heredoc handling in scan.ts, timeout parameter, decomposition reused by skill-shell (skill-shell.ts imports ShellParse/ShellSelect), description shown as title (v2 shell tool description field). MISSING/unverified — explicit fail-closed on grammar parse failure (#12326) not confirmed in shell.ts, CommandTimeout clamp bounds, `modelEnv` stripping backend credentials from model shells (v2 sandbox.ts confines but env scrubbing unverified), normalizeUrls on command metadata, trailing-output drain fiber semantics. L1: import { Effect, Fiber, Stream } from "effect" // kilocode_change - Fiber / L15: import { model as modelEnv } from "@/kilocode/process/env" // kilocode_change +packages/opencode/src/tool/shell/prompt.ts 10,17,30,87,131,274,277 7 shared-upstream patch marker Tool behavior/UI native equivalent `packages/core/src/tool/plugin/shell.ts:47-56` (description field optional) Markers make the shell description optional with guidance text. V2 shell tool schema already has optional description with its own guidance; equivalent. L10: // kilocode_change start - description is optional, prefixed with "Recommended:" so the model knows it can omit it / L17: // kilocode_change end +packages/opencode/src/tool/skill.ts 7,14,25,26,27,45,57,59,67,76,95 11 shared-upstream patch marker Tool behavior/UI already ported `packages/kilo-cli/src/skill-shell.ts` + `skill-shell-render.ts`, `skill-policy.ts` (BUILTIN_LOCATION); baseline skill-shell-parity Markers gate and render shell injection in skill bodies and handle built-in skills lacking a filesystem dir. V2 Kilo skill-shell plugin decomposes commands like the shell tool, enforces trust and kill-switch, and skill-policy supplies the builtin skill from a bundled path; baseline doc records parity. L7: // kilocode_change start - gate + run shell injection in skill bodies / L14: // kilocode_change end +packages/opencode/src/tool/suggest.ts 1 1 shared-upstream patch marker Suggest tool registration native equivalent `packages/kilo-cli/src/remote-suggestions.ts:212-331` and `remote-plugin.ts:56-63` V2 ports the model-facing suggest tool, gates it on viewer eligibility, awaits accept/dismiss, and admits an accepted prompt exactly once through the native session API. L1: // kilocode_change - new file +packages/opencode/src/tool/task.ts 13,14,15,16,17,18,19,20,24,80,83,88,101,132,134,136,145,148,163,164,172,173,174,181,182,184,185,190,198,208,213,218,233,241,244,245,252,257,261,263,292,294,320,325,347,360,395,400,416,440 50 shared-upstream patch marker Tool behavior/UI partial `packages/core/src/tool/plugin/subagent.ts` (parent chain :110-125, permission :138-143, background :17-21) and `packages/kilo-cli/src/agent-policy.ts`; kilocode/tool/task.ts row partial PRESERVED — background subagent results returned to parent (subagent.ts backgroundResult + subagent-completion.ts), parent-chain walk limiting nesting (subagent.ts:117-125 with configurable depth per master plan), question/interactive tools unavailable to delegated agents (agent-policy.ts:107 comment), cross-session resume prevention (existing.parentID check :164), cost accounting (cost-propagation row native). MISSING — inheriting edit/bash/MCP restriction ceilings from the calling agent (task.ts kilocode row: 'inherited denial ceilings not proven'), resumable task_id hint on failure, workflow/variant override precedence, review telemetry into child sessions, per-session platform attribution, background-process transfer on child end. L13: import { Provider } from "@/provider/provider" // kilocode_change / L14: import { KiloTask } from "../kilocode/tool/task" // kilocode_change +packages/opencode/src/tool/todo.ts 5,7,15,17,37,40,52,54 8 shared-upstream patch marker Tool behavior/UI native equivalent `packages/core/src/tool/plugin/opencode.ts` todo handling and session todo state (todo.ts row native) Four small marker blocks adjust todo tool description/validation for Kilo. V2 todo state and tool are native (session todo row already native equivalent); wording differences carry no behavior. L5: // kilocode_change start / L7: // kilocode_change end +packages/opencode/src/tool/tool.ts 10,122,135 3 shared-upstream patch marker Tool behavior/UI native equivalent `packages/core/src/tool/runtime.ts` (formatInputIssues :86, formatEffectIssues) Markers route tool output/error formatting through a Kilo helper. V2 runtime formats tool input issues and outputs natively; no Kilo-specific format contract remains. L10: import { format } from "@/kilocode/tool/tool" // kilocode_change / L122: // kilocode_change start +packages/opencode/src/tool/warpgrep.ts 3,4,5,6,7,17,24,36,60,64,73,83 12 shared-upstream patch marker Tool behavior/UI needs port Kilo-owned tool plugin in `packages/kilo-cli` using `ctx.tool.transform` and Kilo telemetry (`telemetry.ts`); indexing/semantic search is separate (`indexing.ts`) Markers wire the morph warp-grep client, telemetry trackToolUsed, and a Kilo toast on quota errors. V2 has no warp-grep/morph dependency (grep warp/morph: none); Kilo indexing (semantic_search) is a different feature. L3: import { WarpGrepClient } from "@morphllm/morphsdk/tools/warp-grep/client" // kilocode_change / L4: import { Telemetry } from "@kilocode/kilo-telemetry" // kilocode_change +packages/opencode/src/tool/webfetch.ts 7,8,40,44,47,79,91,92,112,113 10 shared-upstream patch marker Tool behavior/UI partial `packages/core/src/tool/plugin/webfetch.ts` (User-Agent :53, image rejection :85-141) PRESERVED — User-Agent header (v2 uses its own userAgent constant) and rejecting image/icon content types (isImageAttachment; SVG excluded). MISSING — IDN/homograph URL normalization to punycode before permission patterns and fetch (url.test row needs port), Kilo-specific UA string. L7: import { isIconMimeType, isImageAttachment } from "@/util/media" // kilocode_change / L8: import { normalizeUrls } from "@/kilocode/util/url" // kilocode_change +packages/opencode/src/tool/websearch.ts 1,5,10,12,17,31,36,45,95,108,122,145,146,151,167,181,186 17 shared-upstream patch marker Tool behavior/UI partial `packages/core/src/tool/plugin/websearch.ts` (provider selection :13-63 via WebSearch providers) and Kilo Exa row (needs port) PRESERVED — provider abstraction with runtime selection (v2 WebSearch providers + ProviderRequiredError flow). MISSING — Kilo-REST Exa transport authenticated with the Kilo bearer (websearch-kilo-exa row needs port), env override for kilo-exa, numResults cap at 10 with metadata.transport. L1: import { Effect, Option, Schema } from "effect" // kilocode_change - Option added for kilo-exa transport dispatch / L5: import * as KiloExa from "@/kilocode/tool/websearch-kilo-exa" // kilocode_change - Kilo-REST Exa transport +packages/opencode/src/tool/write.ts 14,16,17,18,19,48,52,56,63,71,75,102,107,110,111 15 shared-upstream patch marker Tool behavior/UI partial `packages/core/src/tool/plugin/write.ts` + `file-diff.ts` PRESERVED — BOM handling and structured file diff on write results. MISSING — non-UTF-8 encoding round-trip, ConfigValidation warnings, assertMutablePath protection, LSP diagnostics filtering (no LSP in v2). L14: import { trimDiff, buildFileDiff } from "./edit" // kilocode_change / L16: import { filterDiagnostics } from "./diagnostics" // kilocode_change +packages/opencode/src/util/archive.ts 9,18 2 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/kilo-cli/src/updater.ts` staging (no PowerShell extraction) and Core archive handling Marker passes archive paths as arguments instead of embedding them in PowerShell programs. V2 updater stages directories via cp/verbatimSymlinks without PowerShell scripts; the injection surface does not exist. L9: // kilocode_change start - keep paths out of the PowerShell program / L18: // kilocode_change end +packages/opencode/src/util/filesystem.ts 1,4,6,27,33,69,82,85,115 9 shared-upstream patch marker Package behavior indicated by path partial `packages/util/src/fs-util.ts` and `packages/core/src/file-mutation.ts`; Kilo settings atomic writes in `packages/kilo-cli/src/settings.ts` PRESERVED — Kilo settings store performs atomic writes (settings.ts), LocationMutation containment for tools. MISSING/unverified — generic atomic write helper with Windows transient-lock retries in the shared FS util (grep rename/atomic in fs-util.ts: none), ENOTDIR/EACCES-as-absent semantics. L1: import { chmod, mkdir, readFile, rename, stat as statFile, writeFile } from "fs/promises" // kilocode_change / L4: // kilocode_change start - harden containment checks +packages/opencode/src/util/media.ts 2,12,22 3 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/core/src/tool/plugin/webfetch.ts:85-141` (image attachment rejection) Markers define icon MIME types rejected by webfetch. V2 webfetch rejects unsupported fetched image content types (all image/* except svg), a superset of the icon set. L2: const icons = new Set(["image/vnd.microsoft.icon", "image/x-icon", "image/x-ico", "image/ico", "image/icon"]) // kilocode_change / L12: // kilocode_change start +packages/opencode/src/util/process.ts 13,67,119 3 shared-upstream patch marker Package behavior indicated by path partial `packages/core/src/process.ts`/`child-process` utilities Markers allow spawning with a complete sanitized environment instead of extending process.env. V2 process utilities pass `env` but a no-inherit option used by formatter/LSP/MCP callers is not evidenced (format row needs port). L13: extendEnv?: boolean // kilocode_change - allow a complete sanitized environment / L67: env: opts.env === null ? {} : opts.env ? (opts.extendEnv === false ? opts.env : { ...process.env, ...opts.env }) : undefined, // kilocode_change +packages/opencode/src/worktree/index.ts 19,369,377,414 4 shared-upstream patch marker Package behavior indicated by path partial `packages/core/src/worktree.ts:277-288` (Windows-aware removal commands) PRESERVED — v2 worktree removal has Windows-specific command execution. MISSING/unverified — retry/wait helper for slow Windows handle release before deleting worktree directories. L19: import { WorktreeCleanup } from "@/kilocode/worktree-cleanup" // kilocode_change / L369: // kilocode_change start - use Kilo cleanup helper for slow Windows handle release +packages/opencode/test/account/service.test.ts 39 1 shared-upstream patch marker Package behavior indicated by path not needed `packages/kilo-cli/test/gateway-*.test.ts` (Gateway account via real loopback fixtures) Marker compiles the v1 Account node with a mocked HttpClient layer; v2 Kilo account tests use real loopback Gateway fixtures, no layer-graph shim. L39: LayerNode.compile(Account.node, [[httpClient, Layer.succeed(HttpClient.HttpClient, client)]]) // kilocode_change +packages/opencode/test/acp/permission.test.ts 183,187,336,367 4 shared-upstream patch marker Package behavior indicated by path needs port `packages/kilo-cli/test/acp.test.ts`/`acp-cli.test.ts` once the interactive flag and skill-shell batch prompt exist Marked tests assert human selections are marked interactive and skill-shell batches list commands and cannot be persisted. V2 acp tests cover initialize/session basics but the interactive flag and skill-shell batch semantics are absent (acp/permission.ts row partial). L183: // kilocode_change start - human selections are marked interactive / L187: // kilocode_change end +packages/opencode/test/agent/agent.test.ts 18,25,34,64,81,91,134,150,182,197,637 11 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/test/agent-policy.test.ts`, `packages/core/test/agent.test.ts` PRESERVED — agent permission matrix tests for plan/explore denials exist (agent-policy tests). MISSING — Scout opt-in/exclusion assertions, interactive_terminal allow/deny matrix (tool absent), `.kilo/skill` directory usage (discovery scans .opencode). L18: import { MCP } from "../../src/mcp" // kilocode_change / L25: [MCP.node, Layer.mock(MCP.Service)({})], // kilocode_change +packages/opencode/test/agent/plan-mode-subagent-bypass.test.ts 8,163,179 3 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/test/plan-policy.test.ts`, `agent-policy.test.ts` and Core `test/tool/subagent` coverage (inherited denial ceilings 'not proven' per task.ts row) Marked block asserts Plan-mode mutation denials survive delegation through the Kilo task tool. V2 plan-policy tests cover Plan restrictions for the agent, but inheritance of ceilings by delegated child sessions is not proven (kilocode/tool/task.ts row); regression absent. L8: import { KiloTask } from "../../src/kilocode/tool/task" // kilocode_change / L163: // kilocode_change start - preserve Plan mutation ceilings across Kilo task delegation +packages/opencode/test/agent/plugin-agent-regression.test.ts 13,15,50 3 shared-upstream patch marker Package behavior indicated by path not needed `packages/core/test/agent.test.ts` (native fixtures) Markers add Git/MCP layer mocks required by the v1 agent node graph; test-plumbing only. L13: import { Git } from "../../src/git" // kilocode_change / L15: import { MCP } from "../../src/mcp" // kilocode_change +packages/opencode/test/bun/registry.test.ts 4,44 2 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/core/test/plugin*.test.ts` / models-dev retry & parsing (upstream v2 hardened plugin spec parsing) Marked tests guard against crashes on invalid semver in the Bun registry helper. V2 plugin install/loader parse specs with typed schemas and retryTransient; the v1 registry helper does not exist. Not verified as a dedicated test, but the failure mode is structurally different. L4: // kilocode_change — test the semver guard logic that prevents crashes on invalid version strings / L44: // kilocode_change — invalid semver guard tests +packages/opencode/test/cli/acp/acp-test-client.ts 47 1 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/kilo-cli/test/acp-fixture.ts` Marker adds a 30s receive timeout; v2 Kilo has its own ACP fixture with bounded waits. L47: const received = yield* acp.receive.pipe(Effect.timeout(Duration.seconds(30))) // kilocode_change +packages/opencode/test/cli/acp/initialize-auth.test.ts 24,36,39,57 4 shared-upstream patch marker Authentication/account integration partial `packages/kilo-cli/test/acp.test.ts:15-16`, `acp-cli.test.ts:52` (agentInfo name/title Kilo) PRESERVED — agentInfo.name/title `Kilo` asserted in v2 acp tests. MISSING — `kilo-login` auth method id assertion (acp/service.ts row partial; v2 uses daemon credentials). L24: expect(initialized.agentInfo?.name).toBe("Kilo") // kilocode_change / L36: expect(initialized.authMethods?.[0]?.id).toBe("kilo-login") // kilocode_change +packages/opencode/test/cli/acp/lifecycle.test.ts 21 1 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/kilo-cli/test/acp-cli.test.ts` lifecycle coverage Marker bounds exit wait; v2 acp-cli tests cover process lifecycle with their own bounds. L21: const code = yield* Effect.promise(() => acp.exited).pipe(Effect.timeout(Duration.seconds(15))) // kilocode_change +packages/opencode/test/cli/auto-mode.test.ts 1 1 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/test/run.test.ts` (auto approval once for root permissions) Whole-file v1 auto-mode test. V2 run tests cover --auto root approval; Task-child auto approval and headless auto-reject failure are not covered (run-auto row). L1: // kilocode_change - new file +packages/opencode/test/cli/bin-kilo.test.ts 1 1 shared-upstream patch marker Package behavior indicated by path deferred npm distribution (deferred); portable launcher covered by `packages/kilo-cli/test/portable.test.ts` Whole-file test of the npm bin launcher; distribution deferred, portable launcher has its own smoke tests. L1: // kilocode_change - new file +packages/opencode/test/cli/cmd/tui/attention.test.ts 92,164,179,188,193,242,387 7 shared-upstream patch marker User-interface behavior partial `packages/tui/test/cli/cmd/tui/notifications.test.ts` (native notifications; attention row partial: Kilo title/pack absent) Markers assert `Kilo` notification title and `kilo.default` soundboard. V2 notification tests exist but Kilo title and default pack are absent per the TUI attention row. L92: sound_pack: "kilo.default", // kilocode_change / L164: expect(renderer.notifications).toEqual([{ title: "Kilo", message: "focused" }]) // kilocode_change +packages/opencode/test/cli/error.test.ts 77 1 shared-upstream patch marker Package behavior indicated by path needs port Paired with cli/error.ts (partial) — Kilo-branded hints absent Marker asserts the kilo.json hint; the hint does not exist in v2 CLI error output. L77: "Or check your kilo.json provider/model names", // kilocode_change +packages/opencode/test/cli/github-remote.test.ts 4,34 2 shared-upstream patch marker Package behavior indicated by path deferred Deferred with github.handler.ts Rebranded fixtures for the GitHub agent command, which is deferred. L4: // kilocode_change start: rebrand fixtures off upstream repo path / L34: // kilocode_change end +packages/opencode/test/cli/help/help-snapshots.test.ts 11,14,32,38,39,41,52,57,105,107,135,146 12 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/kilo-cli/test/commands.test.ts` (Kilo command tree/help coverage) Markers snapshot Kilo-branded help for the v1 command list. V2 Kilo owns its command tree and tests it in commands.test.ts; the v1 snapshot set does not apply. L11: // kilocode_change start - describe Kilo's branded CLI / L14: // kilocode_change end +packages/opencode/test/cli/mcp-add.test.ts 4,26,62,76,97 5 shared-upstream patch marker Package behavior indicated by path needs port Paired with cli/cmd/mcp.ts (needs port) Asserts MCP add persists into the Kilo global config file; command absent in v2. L4: import { mkdir } from "node:fs/promises" // kilocode_change / L26: Bun.file(path.join(home, ".config", "kilo", "kilo.json")).json(), // kilocode_change +packages/opencode/test/cli/pr.test.ts 1 1 shared-upstream patch marker Package behavior indicated by path needs port Paired with cli/cmd/pr.ts (needs port) Whole-file test for the absent pr command. L1: // kilocode_change - new file +packages/opencode/test/cli/run/footer.view.test.tsx 838,905,971,972,973 5 shared-upstream patch marker Package behavior indicated by path needs port None (direct interactive run mode absent) Markers test Kilo skill picker transitions and terminal callback props in `kilo run -i` footer; mode absent in v2. L838: // kilocode_change start - cover direct-mode Kilo skill picker transitions / L905: // kilocode_change end +packages/opencode/test/cli/run/permission.shared.test.ts 9,33,46,63,137,142 6 shared-upstream patch marker Interactive permission footer tests partial `packages/tui/test/mini/permission.shared.test.ts` Current tests cover the real v2 footer state machine and presentation, but omit the pinned skill-shell prohibition on persistent approval because that input is absent from the current shared implementation. L9: permissionOptions, // kilocode_change / L33: interactive: true, // kilocode_change +packages/opencode/test/cli/run/run-process.test.ts 8,98,111,169,219,299,307,312,316,317,328,334,336,407,412,414,417,471 18 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/test/run.test.ts` (auto-reject blocker + interrupt; exit codes) PRESERVED — headless runs surface auto-rejected permissions as failures (run.ts sets blocker and interrupts; run tests cover). MISSING/unverified — exact exit-code 1 on denial and the Kilo double-record/settlement ordering assertions (v2 event model differs; not asserted). L8: import { createKiloClient } from "@kilocode/sdk/v2" // kilocode_change / L98: // kilocode_change - settle bash up front so the run exercises the stream finish rather than +packages/opencode/test/cli/run/stream.transport.test.ts 1 1 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/kilo-cli/test/run.test.ts` real-host event stream coverage Whole-file v1 transport tests; v2 run consumes the native event stream and is tested against a real host. L1: // kilocode_change - new file +packages/opencode/test/cli/tui/markdown.test.ts 1 1 shared-upstream patch marker User-interface behavior partial `packages/tui/test` markdown coverage; Kilo table normalization row needs port Whole-file Kilo markdown tests (incl. table normalization). V2 TUI has native markdown tests but the Kilo table-normalization behavior is missing per the TUI batch. L1: // kilocode_change - new file +packages/opencode/test/cli/tui/plugin-lifecycle.test.ts 182 1 shared-upstream patch marker User-interface behavior not needed None CI skip marker only. L182: // kilocode_change - skipped flaky test on Windows #9496 +packages/opencode/test/cli/tui/plugin-loader.test.ts 171,173,552,1234 4 shared-upstream patch marker User-interface behavior partial `packages/tui/test` plugin loader tests (theme install path native) Markers relocate theme install expectations to `.kilo/themes`; v2 theme install directory is not Kilo-scoped (plugin/tui/runtime row partial). L171: const localDest = path.join(dir, ".kilo", "themes", localThemeFile) // kilocode_change / L173: const preloadedDest = path.join(dir, ".kilo", "themes", preloadedThemeFile) // kilocode_change +packages/opencode/test/cli/tui/thread.test.ts 40,44 2 shared-upstream patch marker User-interface behavior partial `packages/kilo-cli/script/dev.ts` (caller cwd preserved) and `tui.ts` directory handling Marker tests stale PWD handling for `bun --cwd` wrappers. V2 dev launcher preserves caller cwd (board 229) but no unit test for stale PWD resolution exists. L40: test("ignores stale PWD when resolving a relative mini project", async () => { // kilocode_change / L44: expect(resolveThreadDirectory(".", pwd.path, cwd.path)).toBe(cwd.path) // kilocode_change +packages/opencode/test/config/config.test.ts 20,45,46,116,153,155,157,164,213,324,325,349,356,363,388,402,407,417,494,502,516,520,523,532,549,551,717,762,776,778,788,794,818,842,864,877,916,921,938,944,959,964,973,998,1000,1005,1013,1033,1035,1040,1048,1068,1070,1096,1106,1226,1309,1313,1327,1343,1359,1369,1375,1392,1427,1440,1453,1471,1488,1494,1497,1500,1507,1521,1565,1612,1617,1620,1638,1647,1667,1668,1670,1682,1686,1695,1710,1715,1725,1727,1736,1743,1945,1973,1992,2035,2043,2047,2064,2077,2107,2142,2147 103 shared-upstream patch marker Configuration/settings partial `packages/core/test/config/config.test.ts` (Kilo-touched per git status: refresh regression) and `packages/kilo-cli/test/settings.test.ts`, `project-config.test.ts`, `config-refresh.test.ts` 103 markers. PRESERVED — Kilo settings/project config tests cover $schema, scoped writes, refresh (settings 27 pass). MISSING — Core-level discovery of .kilo/kilo.json, managed directory loading, trusted/untrusted substitution, Kilo plugin detection assertions (features absent per config rows). L20: import { Git } from "../../src/git" // kilocode_change / L45: import { isIndexingPlugin } from "@kilocode/kilo-indexing/detect" // kilocode_change +packages/opencode/test/config/markdown.test.ts 94,175,185,195,215 5 shared-upstream patch marker Configuration/settings partial `packages/core/test/config` markdown tests Markers pass `trusted: true` to parse; v2 parse has no trust parameter (markdown row partial), tests otherwise native. L94: const parsed = await ConfigMarkdown.parse(import.meta.dir + "/fixtures/frontmatter.md", { trusted: true }) // kilocode_change / L175: const result = await ConfigMarkdown.parse(import.meta.dir + "/fixtures/empty-frontmatter.md", { trusted: true }) // kilocode_change +packages/opencode/test/config/tui.test.ts 101,143,165,694,771,778,782,932,946 9 shared-upstream patch marker Configuration/settings partial `packages/kilo-cli/test/tui-config.test.ts`, `packages/tui/test` config tests PRESERVED — Kilo TUI config tests exist (tui-config.test.ts). MISSING — .kilo/tui.json discovery, kilo.default pack, trust-gated substitution assertions (config/tui.ts row partial). L101: const local = path.join(test.directory, ".kilo") // kilocode_change / L143: path.join(test.directory, ".kilo", "tui.json"), // kilocode_change +packages/opencode/test/event-manifest.test.ts 12,18 2 shared-upstream patch marker Package behavior indicated by path not needed None; v2 event manifest is native (`packages/core/src/event`) Markers adjust the v1 manifest size/entries for Kilo events; v2 has no legacy manifest. L12: expect(EventManifest.Latest.size).toBe(89) // kilocode_change - include global.config.updated / L18: expect(EventManifest.Latest.has("global.config.updated")).toBe(true) // kilocode_change +packages/opencode/test/fixture/db.ts 6,10 2 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/kilo-cli/test/fixture.ts` + `fixture-guard.test.ts` (isolated kilo2 store guard) Marker refuses to reset a disk-backed DB. V2 Kilo fixtures enforce isolated storage via fixture guards (cli-fixture-storage-incident baseline). L6: // kilocode_change start / L10: // kilocode_change end +packages/opencode/test/fixture/fixture.ts 11,12,13,14,21,25,52,54,58,64,75,82,97,138,163,186,222,246 18 shared-upstream patch marker Package behavior indicated by path not needed `packages/kilo-cli/test/fixture.ts` (Location-scoped real hosts) Markers provide legacy ALS instance context and disposal ordering for v1 fixtures; v2 fixtures are host-based. L11: import { Database } from "@opencode-ai/core/database/database" // kilocode_change / L12: import { ProjectV2 } from "@opencode-ai/core/project" // kilocode_change +packages/opencode/test/fixture/log-init-worker.ts 1 1 shared-upstream patch marker Package behavior indicated by path not needed None Whole-file v1 fixture for Kilo log initialization in workers; v2 logs use kilo2 layout without this worker. L1: // kilocode_change - new file +packages/opencode/test/fixture/tui-plugin.ts 100,201,307,311,326 5 shared-upstream patch marker User-interface behavior partial `packages/kilo-cli/test/tui-fixture.tsx` and sidebar-*-ui fixtures PRESERVED — Kilo TUI plugin fixtures exist (processes sidebar fixture). MISSING — kilo.default soundboard default (attention row). L100: globalConfig?: HostPluginApi["state"]["globalConfig"] // kilocode_change / L201: current: () => opts.attention?.soundboard?.current?.() ?? "kilo.default", // kilocode_change +packages/opencode/test/fixture/tui-runtime.ts 31 1 shared-upstream patch marker User-interface behavior partial `packages/kilo-cli/test/tui-fixture.tsx` Default pack marker; Kilo pack absent in v2 (attention row partial). L31: sound_pack: "kilo.default", // kilocode_change +packages/opencode/test/fixture/tui-sdk.ts 51,55,56,60,72,75,81,82,85 9 shared-upstream patch marker User-interface behavior not needed Real loopback hosts in `packages/kilo-cli/test/*fixture*.ts` (no fake SDK per remote_v2 decision) Markers stub Kilo endpoints in a fake SDK; v2 Kilo tests use real hosts, and those endpoints are either native or absent per their rows. L51: // case "/experimental/workspace": // kilocode_change / L55: case "/network": // kilocode_change +packages/opencode/test/image/image.test.ts 58,96 2 shared-upstream patch marker Package behavior indicated by path needs port `packages/core/test/image*.test.ts` paired with image.ts row Marked block covers Kilo's Photon fallback; behavior unverified/absent in v2 image.ts. L58: // kilocode_change start - cover Kilo's Photon-unavailable fallback / L96: // kilocode_change end +packages/opencode/test/installation/installation.test.ts 71,95,107,121,135,160,161,179 8 shared-upstream patch marker Package behavior indicated by path deferred Deferred with package-manager distribution; `packages/kilo-cli/test/updater.test.ts` covers local updater Markers assert Kilo npm/brew identities in upgrade resolution; distribution deferred. L71: // kilocode_change start - curl/unknown fallback now resolves from the public npm / L95: // kilocode_change end +packages/opencode/test/kilocode/agent-global-config-dirs.test.ts 1 1 Kilo-owned inherited marker Configuration/settings partial `packages/kilo-cli/test/agent-policy.test.ts` and native Core permission tests Test asserts the built-in code agent allows external_directory reads under Global.Path.config by default. V2 agent-policy.ts appends Kilo skill/semantic_search allows and the exploreBash ceiling (:235-252) but I found no rule granting the global config directory as an external_directory allow, and no v2 test for it. Preserved: agent permission transform seam and tests; missing: this specific default and its regression. L1: // kilocode_change - new file +packages/opencode/test/kilocode/agent-remove.test.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path needs port Kilo agent removal RPC/command in `packages/kilo-cli` (master plan lists agent/skill removal among missing backend consumers) with a paired test Test covers 'removes config-backed imported agents' for the v1 Kilo agent remove path. V2 has no agent-remove producer (grep remove in agent-policy.ts: none) and the transplanted extension still lacks that backend consumer per the master plan, so both behavior and test are absent. L1: // kilocode_change - new file +packages/opencode/test/kilocode/agent-skill-permissions.test.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path partial `packages/kilo-cli/test/agent-policy.test.ts`, `test/skill-policy.test.ts` Test asserts non-system native agents can use skills while system agents get skill denied/disabled. V2 agent-policy adds `skill: * allow` for supervised agents (agent-policy.ts:252) and skill-policy tests exist, but no v2 rule or test denies the skill tool for system agents (grep deny/system in skill-policy.ts: none). Preserved: allow half; missing: system-agent denial regression. L1: // kilocode_change - new file +packages/opencode/test/kilocode/cli-run-auto-helper.test.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path needs port `packages/kilo-cli/test/run.test.ts` once run.ts tracks Task-child sessions Unit tests for create/allowed/track of the --auto child-session tracker. V2 run.ts has no child tracking (source row partial), so these regressions have no target yet. L1: // kilocode_change - new file +packages/opencode/test/kilocode/cli/cmd/remote.test.ts 1 1 Kilo-owned inherited marker Standalone remote command contract partial `packages/kilo-cli/test/remote-ui.test.tsx`, `remote-rpc.test.ts`, and `remote-session.test.ts` Current tests cover consent, preview limitations, device authentication, relay protocol, commands and detach, but the pinned instance-advertisement helper belongs to the unsupported standalone headless-host surface and has no exact v2 test. L1: // kilocode_change - new file +packages/opencode/test/kilocode/config-gitignore.test.ts 1 1 Kilo-owned inherited marker Configuration/settings needs port Kilo project-config writer `packages/kilo-cli/src/project-config.ts` / paths.ts plus a paired test Test asserts the .kilo config directory .gitignore lists pnpm and yarn lockfile patterns because Kilo's Npm.Service may write them. V2 kilo-cli has no .gitignore writer for the project config directory (grep gitignore in kilo-cli/src: none) and no plugin dependency installer writing lockfiles there; if v2 gains plugin install into .kilo, this invariant and test need porting. L1: // kilocode_change - new file +packages/opencode/test/kilocode/config-validation.test.ts 1 1 Kilo-owned inherited marker Configuration/settings native equivalent `packages/kilo-cli/test/settings.test.ts` Replacement settings suite covers schema refusal, queue recovery, boundary refusal, no-secret diagnostics, and native verification pipeline. L1: // kilocode_change - new file +packages/opencode/test/kilocode/config/opentelemetry-default.test.ts 1 1 Kilo-owned inherited marker Configuration/settings deferred `packages/kilo-cli/src/telemetry-settings.ts` owner-only opt-in Pinned default-true telemetry behavior is deliberately not mapped; v2 consent is separate opt-in. Product/consent decision required, so refusal is not parity. L1: // kilocode_change - new file +packages/opencode/test/kilocode/custom-provider-delete.test.ts 1 1 Kilo-owned inherited marker Provider/model routing or UI native equivalent SettingsRpc provider collection unset + CLI settings tests V2 collection translation implements provider null-removal as leaf removal through verified queued writes; replacement tests cover the original caller semantics. L1: // kilocode_change - new file +packages/opencode/test/kilocode/diff-full.test.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path native equivalent Core VCS/git diff tests under `packages/core/test` covering `git diff --unified` batching Tests the v1 Kilo diffFull replacement for JS Myers. V2 Core already produces diffs via git (plugin/vcs/git.ts:383-401) and its tests exercise the native path; the Kilo-specific module does not exist to test. L1: // kilocode_change - new file +packages/opencode/test/kilocode/encoding.test.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path needs port Kilo encoding module port over Core read/write tools (`packages/core/src/tool/read.ts`/`write.ts`) with paired tests Tests Kilo's Encoding namespace: BOM/UTF-16 detection, round-trip write preserving encoding. V2 Core tools read files as UTF-8 only (grep utf-16/BOM in core/src/tool and filesystem: none), so neither behavior nor tests exist. L1: // kilocode_change - new file +packages/opencode/test/kilocode/help.test.ts 10 1 Kilo-owned inherited marker Package behavior indicated by path not needed `packages/kilo-cli/test/commands.test.ts` help coverage The single marked line adapts the import after upstream renamed auth→providers; it is merge glue, not a Kilo invariant. Kilo v2 owns its own command tree and tests it in kilo-cli/test/commands.test.ts. L10: import { ProvidersCommand } from "../../src/cli/cmd/providers" // kilocode_change — upstream renamed auth → providers +packages/opencode/test/kilocode/instruction.test.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path needs port Core `packages/core/src/instruction-discovery.ts` global path resolution via Kilo `paths.ts` (KILO_CONFIG_DIR handled in kilo-cli/src/paths.ts) plus a test Test verifies instruction discovery falls back across KILO_CONFIG_DIR profile directories. V2 paths.ts knows KILO_CONFIG_DIR for the kilo2 layout, but Core instruction discovery uses its own Global paths and no test covers a Kilo profile fallback; invariant unproven. L1: // kilocode_change - new file +packages/opencode/test/kilocode/kilo-loader-auth.test.ts 1 1 Kilo-owned inherited marker Authentication/account integration native equivalent `packages/kilo-gateway/src/models.ts:111-188` (anonymous scope returns only isFree models) with `packages/kilo-cli/test/gateway-anonymous.test.ts` V1 tests assert paid models are listed anonymously with apiKey 'anonymous' and free status is not inferred from prices. V2 deliberately changed the contract: signed-out catalog exposes only isFree records (models.ts:150-153 documents this as stricter than v1) and gateway-anonymous.test.ts covers anonymous startup. Behavior is intentionally different and tested; the v1 assertions must not be ported verbatim. L1: // kilocode_change - new file +packages/opencode/test/kilocode/kilo-models-401-fallback.test.ts 1 1 Kilo-owned inherited marker Provider/model routing or UI native equivalent `packages/kilo-gateway/test/{gateway,plugin}.test.ts` Current Gateway tests cover 401/403 auth failures, signed-out public/free catalog behavior, failed public reads, and withdrawal without cross-account fallback. L1: // kilocode_change - new file +packages/opencode/test/kilocode/kilo-sessions.test.ts 1,280,331,528 4 Kilo-owned inherited marker Session lifecycle/history/UI partial `packages/kilo-cli/test/remote-session.test.ts`, `remote-status.test.ts`, `remote-rpc.test.ts` Marked blocks test setInstanceAdvertisement default/replace semantics and out-of-band heartbeat plus per-session platform. V2 remote tests cover heartbeat, attach/ownership and capabilities but the advertisement feature is deferred (instance-advertisement.ts row), so those assertions have no target; the rest of the file (new-file marker) maps to existing remote tests. L1: // kilocode_change - new file / L280: // kilocode_change start - K1 W1 / DEF-1: instance advertisement + per-session platform. +packages/opencode/test/kilocode/local-model.test.ts 1 1 Kilo-owned inherited marker Provider/model routing or UI native equivalent Native provider config/model catalog plus current Core provider tests Local/custom providers are represented by native v2 provider configuration and provider plugin tests; no Kilo-only runtime fork is required for the tested local-model path. L1: // kilocode_change - new file +packages/opencode/test/kilocode/model-cache-effect.test.ts 1 1 Kilo-owned inherited marker Provider/model routing or UI native equivalent `packages/kilo-gateway/src/startup-cache.ts` and `packages/kilo-gateway/test/{startup-cache,plugin}.test.ts` V2 owns account/location-scoped startup catalog snapshots with explicit lifetime, credential identity isolation, stale-response discard, and failure isolation tests. L1: // kilocode_change - new file +packages/opencode/test/kilocode/patch-jsonc.test.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path native equivalent `packages/kilo-cli/src/settings.ts:378,622` (jsonc-parser modify/applyEdits) with `packages/kilo-cli/test/settings.test.ts` Tests jsonc-parser modify semantics when a permission rule changes from string to object. V2 settings writes use the same jsonc-parser modify/applyEdits path and settings tests cover field set/unset on jsonc sources; the specific string→object permission case is a library behavior now exercised through settings collection writes. L1: // kilocode_change - new file +packages/opencode/test/kilocode/permission/config-paths.test.ts 1 1 Kilo-owned inherited marker Configuration/settings native equivalent Core config source/variable pipeline + CLI settings scope tests V2 has explicit source provenance and project-confined file substitution; settings tests exercise profile/project folding and missing file references. L1: // kilocode_change - new file +packages/opencode/test/kilocode/permission/permission-origins.test.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path needs port Kilo permission provenance over `packages/kilo-cli/src/project-config.ts` scopes (config provenance exists; permission-rule provenance does not) Tests that each permission key is attributed to global vs project scope to drive auto-approval provenance. V2 tracks settings provenance per scope but has no permission_origins map or provenance in approvals (see kilocode/session/prompt.ts row); needs port with the provenance feature. L1: // kilocode_change - new file +packages/opencode/test/kilocode/project-config-update.test.ts 1 1 Kilo-owned inherited marker Configuration/settings native equivalent SettingsRpc collectionSet/Unset and `packages/kilo-cli/src/settings.ts` Project-scoped writes use expected revisions, atomic update, verification, provenance and refresh; replacement settings/config-refresh coverage exercises the consumer path. L1: // kilocode_change - new file +packages/opencode/test/kilocode/prompt-dismiss-contract.test.ts 6,39 2 Kilo-owned inherited marker Queued-prompt interactive dismissal partial `packages/kilo-cli/src/remote-session.ts:187-195`, `remote-suggestions.ts:177`, and `test/remote-suggestions.test.ts:524-580` V2 explicitly dismisses pending remote suggestions when session input is enqueued and tests replay suppression, but the pinned contract also dismisses pending questions without cancelling the active stream; that combined question-and-suggestion boundary is not proven. L6: * stream" behaviour lives inside a kilocode_change block. An upstream merge / L39: /kilocode_change start[^\n]*register the queued follow-up[\s\S]*?Suggestion\.dismissAll[\s\S]*?question\.dismissAll[\s\S]*?KiloSessionPromptQueue\.enq +packages/opencode/test/kilocode/provider-list-failed-state.test.ts 1 1 Kilo-owned inherited marker Provider/model routing or UI native equivalent `packages/kilo-gateway/src/plugin.ts` catalog editor plus plugin tests Failed or empty account/public catalog reads withdraw only the affected scoped offer and never reuse another account snapshot; current tests exercise failed team/public/cold refresh states. L1: // kilocode_change - new file +packages/opencode/test/kilocode/question-option-schema.test.ts 55,57,59,61,64,66 6 Kilo-owned inherited marker Package behavior indicated by path needs port `packages/schema/src/form.ts`/question schema extension via Kilo-owned schema, consumed by `packages/kilo-vscode` localization Tests (incl. static source guards on the marker block) that Question options carry optional mode, labelKey, descriptionKey for localization and mode hints. V2 schema has no such fields (grep labelKey/descriptionKey in schema/core: none); the transplanted extension's localized option rendering would need them. L55: // Static source checks — guard the kilocode_change markers so a conflict / L57: test("source declares mode as an optional field inside a kilocode_change block", () => { +packages/opencode/test/kilocode/sandbox/session-tools.test.ts 115 1 Kilo-owned inherited marker Sandboxed tool confinement partial `packages/kilo-cli/test/sandbox.test.ts` and `sandbox-mcp.test.ts` Current tests prove native shell and local-MCP confinement, protected paths, fail-closed configuration and cleanup. V2 intentionally does not reproduce v1's per-session filesystem decoration across every direct file tool, so this is narrower than exact parity. L115: clients: () => Effect.succeed({}), // kilocode_change - upstream's MCP resource tools probe the clients +packages/opencode/test/kilocode/sandbox/session.test.ts 41 1 Kilo-owned inherited marker Per-session sandbox lifecycle partial `packages/kilo-cli/src/sandbox.ts`, `host.ts`, `sandbox-pty.ts` and their tests V2 provides opt-in OS confinement at host launch, wraps shell/local MCP, refuses PTYs, and cleans launchers, but it does not port v1's persisted per-session toggle, fork inheritance, family activation or background-job guards. L41: Layer.provide(AppNodeBuilder.build(SessionV2.node, [[SessionExecution.node, SessionExecution.noopLayer]])), // kilocode_change +packages/opencode/test/kilocode/server/httpapi-kilo-edit.test.ts 33 1 Kilo-owned inherited marker Server/API integration needs port Kilo Gateway route in `packages/kilo-gateway` (FIM/next-edit is listed as a remaining feature-specific service in the master plan) Marked line only keeps the filewatcher-disable flag visible in the test layer, but the whole test covers the Kilo next-edit proxy (gateway auth, autocomplete headers, BYOK passthrough, 504 mapping). V2 has no next-edit route (grep next-edit/autocomplete in kilo-gateway/kilo-cli: none). L33: // kilocode_change - keep the filewatcher-disable flag visible (see httpapi-instance-route-auth.test.ts) +packages/opencode/test/kilocode/server/httpapi-memory.test.ts 17 1 Kilo-owned inherited marker Memory API integration native equivalent `packages/schema/src/kilocode/memory.ts:108-145`, `packages/kilo-cli/src/memory-rpc.ts`, and `packages/kilo-cli/test/memory.test.ts:365-403,655-778` The HTTP group is replaced by location-scoped Memory RPC. Current integration tests cover enable/configure/remember/recall/correct/disable/purge, disabled failures, and location isolation; engine tests cover safe purge ownership. L17: // kilocode_change - keep the filewatcher-disable flag visible (see httpapi-instance-route-auth.test.ts) +packages/opencode/test/kilocode/session-export/source-contract.test.ts 8 1 Kilo-owned inherited marker Session lifecycle/history/UI needs port Kilo session export feature port with paired tests Static-source tests guard the export compaction block, absence of rawParts retention and windowsHide on git spawns in the v1 session-export module. V2 has no session export/ingest module (grep session-export: none), so these contracts have no subject. L8: text.indexOf("// kilocode_change start - export self-contained compaction capture"), +packages/opencode/test/kilocode/session-metrics.test.ts 1 1 Kilo-owned inherited marker Throughput computation tests native equivalent `packages/tui/test/cli/tui/session-rows.test.ts:29-52` Current focused tests exercise real turn-output throughput across multiple model steps, exclude tool time, and omit throughput without a valid stream boundary, superseding the narrower helper-only v1 tests. L1: // kilocode_change - new file +packages/opencode/test/kilocode/session-processor-review-telemetry.test.ts 1 1 Kilo-owned inherited marker Session lifecycle/history/UI needs port `packages/kilo-cli/src/telemetry.ts` hook in review-policy with test Tests KiloSessionProcessor emitting ReviewCommand telemetry. V2 review-policy has no telemetry emission (grep ReviewCommand in kilo-cli: none). L1: // kilocode_change - new file +packages/opencode/test/kilocode/sessions/remote-command.test.ts 119,162 2 Kilo-owned inherited marker Remote command exit capability native equivalent `packages/kilo-cli/src/remote-session.ts:863-873,1183-1210` and `test/remote-session.test.ts:189-248,924-1139` The v2 relay always advertises `canExitSession: true` independently of synthetic command discovery, and `exit_cli` detaches without ending the durable session; focused tests cover both invariants. L119: // kilocode_change - K1 W1: canExitSession is always true, independent of / L162: // kilocode_change - K1 W1: canExitSession is true even when the synthetic +packages/opencode/test/kilocode/sessions/remote-protocol.test.ts 269 1 Kilo-owned inherited marker Remote presence and instance advertisement partial `packages/kilo-cli/src/remote-session.ts:386-406` and current remote protocol/presence schemas V2 carries heartbeat capabilities and per-session presence/platform behavior, but its preview relay intentionally omits the pinned spawn-capable instance advertisement and cloud-clone metadata surface. L269: // kilocode_change - K1 W1: instance advertisement + per-session platform +packages/opencode/test/kilocode/sessions/remote-sender.test.ts 118,122,1215,1425,2266,2676,2797,2849,2906,4070 10 Kilo-owned inherited marker Session lifecycle/history/UI partial `packages/kilo-cli/test/remote-rpc.test.ts`, `remote-session.test.ts`, `remote-suggestions.test.ts` Marked blocks add mock.restore and tests for send_message model-string normalization to kilo/ and ephemeralTools mapping. V2 remote tests use real loopback hosts (no mock.restore need) and cover send_message delivery, but I found no assertion normalizing an unprefixed model string to the Kilo provider (remote-sender.ts source row is partial). Preserved: delivery tests; missing: model-normalization case. L118: // kilocode_change start / L122: // kilocode_change end +packages/opencode/test/kilocode/sessions/remote-ws.test.ts 420,1996 2 Kilo-owned inherited marker Remote WebSocket heartbeat and advertisement partial `packages/kilo-cli/src/remote-session.ts:386-406` and `test/remote-session.test.ts:75-91,924-1139` Current relay tests cover immediate heartbeat capabilities, reconnect and detach behavior, but no current consumer or test carries the pinned instance-advertisement propagation invariant. L420: // kilocode_change - K1 W1: with the immediate heartbeat on FIRST open, / L1996: // kilocode_change - K1 W1: instance advertisement flows through the +packages/opencode/test/kilocode/snapshot-freeze-repro.test.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path native equivalent Core git diff batching/cap in `packages/core/src/plugin/vcs/git.ts` and Snapshot capture bounds (`maximumUntrackedFileBytes`) Regression for the JS Myers freeze; v2 never uses the JS diff path for snapshots (git-based, capped), so the freeze mode is structurally absent. L1: // kilocode_change - new file +packages/opencode/test/kilocode/snapshot-track-timeout.test.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path needs port Paired with the missing interactive guard (kilocode/snapshot/track.ts partial) in Core snapshot or a Kilo snapshot plugin 969-line unit suite for the timeout/continue/disable/progress-part guard. V2 has only size bounds, so the guard's tests have no subject. L1: // kilocode_change - new file +packages/opencode/test/kilocode/telemetry/feedback.test.ts 1 1 Kilo-owned inherited marker Feedback telemetry event contract needs port Test accompanying the proposed v2 thumbs-feedback telemetry event The pinned test freezes the externally consumed `Feedback Submitted` event name. V2 has no feedback event/action consumer, so neither this wire label nor its emission is currently covered. L1: // kilocode_change - new file +packages/opencode/test/kilocode/tool/generate-image.test.ts 1 1 Kilo-owned inherited marker Image-tool parsing/provider/path regressions needs port Tests accompanying the proposed owned v2 image tool No current v2 tests cover Kilo-vs-OpenRouter provider selection, image data-URL parsing/MIME normalization, model fallback catalog, or output-extension correction. L1: // kilocode_change - new file +packages/opencode/test/kilocode/tool/send-file.test.ts 182,221,223,262,512,542 6 Kilo-owned inherited marker Remote file-delivery authorization regressions needs port Tests accompanying the proposed typed outbound file-delivery seam Current attachment tests cover inbound user files. They do not cover pre-inspection authorization for missing/directory/symlink targets, remote-connect gating, file caps/sniffing, or outbound delivery attachment semantics. L182: // kilocode_change start — send_file now authorizes missing files with external_directory + read / L221: // kilocode_change end +packages/opencode/test/kilocode/tool/websearch-kilo-exa.test.ts 1 1 Kilo-owned inherited marker Tool behavior/UI needs port Paired with websearch-kilo-exa.ts (needs port) in a Kilo-owned tool plugin Tests the authenticated Kilo Exa route, response and error contract; source row already classified needs port with no v2 consumer. L1: // kilocode_change - new file +packages/opencode/test/kilocode/tui/sync-v2.test.tsx 19,27 2 Kilo-owned inherited marker V2 TUI projection and project-filter regressions partial `packages/tui/src/context/data.tsx` and `packages/tui/test/cli/tui/data.test.tsx:810-1478` Current TUI data tests extensively cover durable execution/status projection, stale refreshes and live-event convergence. The pinned suite additionally proves resolved-project filtering and duplicate-event behavior for tool failure and prompt admission/promotion; exact project-filter parity was not located. L19: // kilocode_change start - live events are filtered by the resolved project ID / L27: // kilocode_change end +packages/opencode/test/kilocode/util/url.test.ts 1 1 Kilo-owned inherited marker Package behavior indicated by path needs port Kilo-owned URL normalization used by webfetch/websearch tools in a kilo-cli tool policy Tests normalizeUrls converting look-alike/unicode hostnames to punycode. V2 has no equivalent (grep punycode/toASCII in core and kilo-cli: none). L1: // kilocode_change - new file +packages/opencode/test/lib/cli-process.ts 32,36,40,215,225,294,340,413,540,546 10 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/kilo-cli/test/fixture.ts`, `daemon-fixture.ts`, `interactive-fixture.ts` Markers tune the v1 CLI subprocess helper. V2 Kilo has its own real-host fixtures with timeouts and isolation. L32: import { TestCli } from "../../script/kilocode/test-cli" // kilocode_change / L36: // kilocode_change start - reuse the runner's once-built CLI graph instead of transpiling it in every child +packages/opencode/test/lib/effect.ts 139,144 2 shared-upstream patch marker Package behavior indicated by path not needed `packages/core/test/lib` Effect test helpers Marker exports thin helper aliases for v1 layer tests; v2 core has its own. L139: // kilocode_change start / L144: // kilocode_change end +packages/opencode/test/lib/llm-server.ts 512,520 2 shared-upstream patch marker Server/API integration native equivalent `packages/core/test/lib` LLM test server / `packages/kilo-cli/test/*` scripted model fixtures Marker adds a finish-reason control to the fake LLM server; v2 test LLM servers support scripted finishes. L512: // kilocode_change start / L520: // kilocode_change end +packages/opencode/test/lsp/index.test.ts 12,14,17,18,20,83,170,172,213 9 shared-upstream patch marker Package behavior indicated by path needs port Future v2 LSP tests (no LSP runtime) Markers gate typescript spawn behind KILO_EXPERIMENTAL_LSP_TOOL and TsClient; v2 has no LSP. L12: import * as launch from "../../src/lsp/launch" // kilocode_change - spy on spawn / L14: import { provideTestInstance, provideTmpdirInstance, TestInstance, tmpdir } from "../fixture/fixture" // kilocode_change +packages/opencode/test/lsp/launch.test.ts 8,37 2 shared-upstream patch marker Package behavior indicated by path needs port Future v2 LSP launch (see lsp/launch.ts) and formatter env scrub (format row needs port) Marked test asserts KILO_SERVER_PASSWORD/USERNAME/CONFIG* are stripped from language server env; no v2 LSP, and env scrubbing for spawned helpers is not implemented. L8: // kilocode_change start / L37: // kilocode_change end +packages/opencode/test/mcp/lifecycle.test.ts 9,10,283,291,298,352 6 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/test/sandbox-mcp.test.ts` (native MCP add/disconnect/reconnect under sandbox; 20 pass) PRESERVED — MCP lifecycle under Kilo sandbox confinement is tested. MISSING — sandbox network-relay authority semantics (v2 confines processes, no relay). L9: import * as SandboxNetwork from "../../src/kilocode/sandbox/network" // kilocode_change / L10: import { run as runSandbox, type Profile } from "@kilocode/sandbox" // kilocode_change +packages/opencode/test/mcp/oauth-browser.test.ts 21,30 2 shared-upstream patch marker Authentication/account integration native equivalent `packages/core/test` MCP oauth tests Marker fixes a test race; not a product invariant. L21: // kilocode_change start - buffer the error until the consumer attaches / L30: // kilocode_change end +packages/opencode/test/permission/next.test.ts 24,575,807,1122 4 shared-upstream patch marker Package behavior indicated by path needs port `packages/core/test/permission.test.ts` (AskResult without rule provenance) Marked assertions expect `{manual:false, rule:{...}}` from ask; v2 AskResult does not carry the deciding rule (permission row partial). L24: AppNodeBuilder.build(InstanceStore.node, [[InstanceStore.bootstrapNode, noopBootstrap]]), // kilocode_change / L575: expect(result).toEqual({ manual: false, rule: { permission: "bash", pattern: "*", action: "allow" } }) // kilocode_change - ask returns the auto-appro +packages/opencode/test/plugin/auth-override.test.ts 48 1 shared-upstream patch marker Authentication/account integration partial `packages/core/test/plugin*.test.ts` (.opencode/plugin) Path-convention marker; Core plugin dirs are not Kilo-scoped (plugin/install row partial). L48: const pluginDir = path.join(tmp.directory, ".kilo", "plugin") // kilocode_change +packages/opencode/test/plugin/codex.test.ts 134,204 2 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/core/src/plugin/provider/openai.ts:23-24` (codexAllowed/codexDisallowed) with core provider tests Marked tests assert disallowed models are filtered for OAuth users and kept for API-key users. V2 openai plugin encodes allowed/disallowed sets natively. L134: // kilocode_change start / L204: // kilocode_change end +packages/opencode/test/plugin/install-concurrency.test.ts 85,108,111,119,140 5 shared-upstream patch marker Package behavior indicated by path partial `packages/core/test/plugin*.test.ts` Path-convention markers (.kilo); concurrency behavior native, directory not Kilo-scoped. L85: const cfg = await read(path.join(tmp.path, ".kilo", "opencode.jsonc")) // kilocode_change / L108: // kilocode_change start +packages/opencode/test/plugin/install.test.ts 125,128,149,152,160,219,264,271,291,311,331,374,393,396,413,430,445,448,465,468,470,486,489,495,498,522,551,554,570,584 30 shared-upstream patch marker Package behavior indicated by path partial `packages/core/test/plugin*.test.ts` 30 path markers; same as install-concurrency. L125: // kilocode_change start / L128: // kilocode_change end +packages/opencode/test/plugin/meta.test.ts 53,57 2 shared-upstream patch marker Package behavior indicated by path not needed None Test workaround for a Bun 1.3.11 bug; v2 pins Bun 1.4.0. L53: // kilocode_change start / L57: // kilocode_change end +packages/opencode/test/plugin/openai-ws.test.ts 520,571 2 shared-upstream patch marker Package behavior indicated by path not needed None (ws pool absent) Test timing tweak for a module absent in v2. L520: idleTimeout: 100, // kilocode_change - leave enough time for WebSocket callbacks on loaded CI runners / L571: idleTimeout: 100, // kilocode_change - leave enough time for WebSocket callbacks on loaded CI runners +packages/opencode/test/plugin/trigger.test.ts 13 1 shared-upstream patch marker Package behavior indicated by path not needed None Layer-graph plumbing marker. L13: import { Git } from "../../src/git" // kilocode_change +packages/opencode/test/plugin/workspace-adapter.test.ts 16 1 shared-upstream patch marker Package behavior indicated by path not needed None Layer-graph plumbing marker. L16: import { Git } from "../../src/git" // kilocode_change +packages/opencode/test/plugin/xai.test.ts 96,140,180,466 4 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/core/test` xai tests + App identity (`host.ts:21`) Markers assert kilocode UA/referrer; v2 derives UA from App identity (xai row native). L96: expect(params.get("referrer")).toBe("kilocode") // kilocode_change / L140: expect(captured[0].get("user-agent")).toMatch(/^kilocode\//) // kilocode_change +packages/opencode/test/preload.ts 8,33,43,94,95,98 6 shared-upstream patch marker Package behavior indicated by path not needed `packages/kilo-cli/test/fixture.ts` and `packages/kilo-memory` tests Markers wire v1 test environment; v2 Kilo tests have their own fixtures and memory package tests. L8: import { remove as cleanup } from "./kilocode/cleanup" // kilocode_change / L33: await cleanup(dir) // kilocode_change +packages/opencode/test/project/instance.test.ts 9,52,55,73,176,194,276 7 shared-upstream patch marker Package behavior indicated by path not needed None Tests for v1 instance ALS bridging; not applicable. L9: import { capture } from "../../src/kilocode/instance" // kilocode_change / L52: // kilocode_change start - capture prefers legacy ALS, then falls back to the Effect fiber reference +packages/opencode/test/provider/header-timeout.test.ts 28,44,200,215 4 shared-upstream patch marker Provider/model routing or UI needs port Paired with provider.ts first-byte timeout (partial/unverified) in `packages/core/src/plugin/provider` fetch Marked tests assert a delayed-body server trips the header timeout; v2 fetch timeout extension to first byte is unverified/absent. L28: Effect.promise(() => delayedBodyServer(1000, ":\n\n")), // kilocode_change / L44: { config: providerConfig(server.url, { headerTimeout: 500 }) }, // kilocode_change +packages/opencode/test/provider/provider.test.ts 81,730,736,1242,1243,1244,1255,1256,1257,1539,1545,1775,1832,1836 14 shared-upstream patch marker Provider/model routing or UI partial `packages/kilo-cli/test/routed-model*.test.ts`, `gateway-protocol.test.ts`, `model-picker.test.ts` PRESERVED — Kilo auto/small-model fallback and gateway variants tested (routed-model, gateway tests). MISSING — Kilo attribution headers (X-Title/X-BILLING-INVOKE-ORIGIN) assertions, opencode-provider drop rule. L81: if (!item) return 0 // kilocode_change - Kilo drops opencode provider without apiKey/auth / L730: // kilocode_change start - Kilo always has an auto-routed small-model fallback +packages/opencode/test/provider/transform.test.ts 76,140,1270,1315,3033,3197,3296,3330,3709,3725,3758,3866,3887,3889,4250,4272,4341,4358,4395,4411,4480,4481,5171,5255,5460,5547 26 shared-upstream patch marker Provider/model routing or UI partial `packages/core/test/aisdk-native.test.ts` PRESERVED — reasoning/effort mapping tests exist natively. MISSING — MiniMax thinking toggle, Azure helper exclusions and reasoningSummary guard cases (transform row partial). L76: // kilocode_change start / L140: // kilocode_change end +packages/opencode/test/question/question.test.ts 21,123,150,231,235,309,313 7 shared-upstream patch marker Package behavior indicated by path partial `packages/core/test/form.test.ts` PRESERVED — form request/cancel/unknown-request tests native. MISSING — non-blocking question mode used by review follow-up. L21: blocking?: boolean // kilocode_change / L123: // kilocode_change start - review follow-up uses non-blocking question prompts +packages/opencode/test/server/auth.test.ts 26,31,58 3 shared-upstream patch marker Server/API integration native equivalent `packages/kilo-cli/test/http-boundary.test.ts`, `daemon.test.ts` (username opencode) Branding assertion; v2 uses opencode username by design (auth row). L26: // kilocode_change / L31: Authorization: `Basic ${Buffer.from("kilo:secret").toString("base64")}`, // kilocode_change +packages/opencode/test/server/experimental-session-list.test.ts 1 1 shared-upstream patch marker Session lifecycle/history/UI partial `packages/kilo-vscode/test/backend-session.test.ts` (experimental.session.list adapter over v2 pagination/registry) Whole-file v1 test for worktree-family session listing. V2 has an extension-backend adapter with real-host tests, but no server endpoint with worktrees/directories filters (handlers/experimental row partial). L1: // kilocode_change - new file +packages/opencode/test/server/httpapi-compression.test.ts 98 1 shared-upstream patch marker Server/API integration not needed None Comment marker about /config payload size. L98: // kilocode_change - /config now serialises enough defaults to clear the threshold on its own, +packages/opencode/test/server/httpapi-config.test.ts 59,61 2 shared-upstream patch marker Configuration/settings native equivalent `packages/kilo-cli/test/settings.test.ts` / core config tests Marker adjusts expected file path; native config write tests exist. L59: // kilocode_change start / L61: // kilocode_change end +packages/opencode/test/server/httpapi-cors.test.ts 67,76 2 shared-upstream patch marker Server/API integration not needed None Test plumbing marker. L67: // kilocode_change start - keep the filewatcher-disable flag visible (see httpapi-instance-route-auth.test.ts) / L76: // kilocode_change end +packages/opencode/test/server/httpapi-event.test.ts 4,6,20,32,41,43,78,80,123,158,179,185,220,256,265 15 shared-upstream patch marker Server/API integration not needed None; v2 `/api/event` native stream tested in server tests 15 markers verify legacy SSE encoding of EventV2/Kilo bus events; legacy wire removed in v2. L4: import * as Sse from "effect/unstable/encoding/Sse" // kilocode_change - decode the legacy SSE wire format / L6: // kilocode_change start - verify transformed EventV2 values at the legacy SSE boundary +packages/opencode/test/server/httpapi-exercise/backend.ts 48,66,78,86,119 5 shared-upstream patch marker Server/API integration not needed None (v1 route exerciser) Test harness plumbing for the v1 HttpApi exerciser. L48: // kilocode_change start - an in-flight SSE fiber can leave the in-process router scope unable / L66: // kilocode_change end +packages/opencode/test/server/httpapi-exercise/dsl.ts 30,58,62,168 4 shared-upstream patch marker Server/API integration not needed None v1 exerciser harness. L30: validAuthProbe: true, // kilocode_change / L58: // kilocode_change start - blocking routes only prove they reject missing credentials so no valid request leaks into app disposal +packages/opencode/test/server/httpapi-exercise/environment.ts 14,15,16,18 4 shared-upstream patch marker Server/API integration not needed None v1 exerciser harness. L14: process.env.KILO_DISABLE_SESSION_INGEST = "true" // kilocode_change - isolate the exerciser from async Kilo session sync / L15: process.env.KILO_DISABLE_PRESENCE = "1" // kilocode_change - presence now has a default Event Service URL; never open real sockets from the exerciser +packages/opencode/test/server/httpapi-exercise/index.ts 20,39,301,344,1716,1722,1768,1787,1799,1808,1850,1863 12 shared-upstream patch marker Server/API integration not needed None v1 exerciser harness; Kilo endpoints tracked by their rows. L20: import { Effect, Layer } from "effect" // kilocode_change / L39: import { kiloScenarios } from "../../kilocode/server/httpapi-exercise-scenarios" // kilocode_change +packages/opencode/test/server/httpapi-exercise/runner.ts 52,267 2 shared-upstream patch marker Server/API integration not needed None v1 exerciser harness. L52: if (!scenario.validAuthProbe) return // kilocode_change - blocking routes skip the valid probe; a leaked valid request hangs final app disposal / L267: // kilocode_change - each exerciser process already owns an isolated DB; unlinking it between scenarios races async Kilo callbacks +packages/opencode/test/server/httpapi-exercise/types.ts 91,108 2 shared-upstream patch marker Server/API integration not needed None v1 exerciser harness. L91: validAuthProbe: boolean // kilocode_change / L108: validAuthProbe: boolean // kilocode_change +packages/opencode/test/server/httpapi-experimental.test.ts 2,280,281 3 shared-upstream patch marker Server/API integration native equivalent `packages/core/test` worktree tests / protocol `worktree.list` Marker asserts managed worktree listing with realpath; v2 worktree.list is native. L2: import { realpath } from "node:fs/promises" // kilocode_change / L280: const directory = yield* Effect.promise(() => realpath(info.directory)) // kilocode_change +packages/opencode/test/server/httpapi-file.test.ts 55 1 shared-upstream patch marker Server/API integration not needed None CI skip marker. L55: // kilocode_change - skip on Windows: Kilo file search returns [] for hello.txt. +packages/opencode/test/server/httpapi-instance-route-auth.test.ts 17,25,64,81 4 shared-upstream patch marker Server/API integration native equivalent `packages/kilo-cli/test/http-boundary.test.ts` Branding in auth header; v2 route auth tested with opencode username. L17: // kilocode_change start - keep the filewatcher-disable flag visible so the / L25: // kilocode_change end +packages/opencode/test/server/httpapi-listen.test.ts 296,304 2 shared-upstream patch marker Server/API integration not needed None Test adaptation to absent proxy. L296: // kilocode_change start - use an authenticated local route instead of proxy-dependent status / L304: // kilocode_change end +packages/opencode/test/server/httpapi-mdns.test.ts 62 1 shared-upstream patch marker Server/API integration not needed None (mDNS removed) Branding on removed feature. L62: expect(published[0]!.name).toBe(`kilo-${listener.port}`) // kilocode_change +packages/opencode/test/server/httpapi-provider.test.ts 9,109,112,144,147,186,189,218,221 9 shared-upstream patch marker Provider/model routing or UI partial `packages/core/test/plugin*.test.ts` Path markers (.kilo/plugin) and dependency preparation helper; plugin dir not Kilo-scoped. L9: import { preparePluginDependencies } from "../kilocode/plugin-dependencies" // kilocode_change / L109: yield* Effect.promise(() => preparePluginDependencies(dir)) // kilocode_change +packages/opencode/test/server/httpapi-pty.test.ts 84,95 2 shared-upstream patch marker Server/API integration native equivalent `packages/kilo-cli/test/native-pty*.test.ts`, `sandbox-pty.test.ts` Marked block tests initial PTY dimensions; v2 native PTY tests cover spawn parameters. L84: // kilocode_change start - test initial spawn dimensions / L95: // kilocode_change end +packages/opencode/test/server/httpapi-query-schema-drift.test.ts 87,96 2 shared-upstream patch marker Server/API integration not needed None (v2 protocol schema generated) Test list of v1 path-parameter patterns; not applicable to v2 protocol. L87: // kilocode_change start / L96: // kilocode_change end +packages/opencode/test/server/httpapi-reference.test.ts 64,98,100,140 4 shared-upstream patch marker Server/API integration needs port Core `reference.ts` + protocol `reference.list` tests once Kilo reference config is consumed Marked blocks assert Kilo reference reconciliation semantics; v2 has native references but no Kilo reconciler (reference-reconciler row), so these assertions have no subject. L64: // kilocode_change start - reference reads must reconcile config changes after instance disposal. / L98: // kilocode_change end +packages/opencode/test/server/httpapi-sdk.test.ts 27,182,189,297,371,373,378,385,388,389,429,436,528,539,782,804,809,873,909,963 20 shared-upstream patch marker Server/API integration partial `packages/kilo-cli/test/http-boundary.test.ts`, `packages/client` generated client tests PRESERVED — v2 session create returns Location (native). MISSING — .kilo skills path, legacy directory header routing (not needed), invalid-image request classification (handlers/session row). L27: import { awaitWithTimeout, pollWithTimeout, testEffect } from "../lib/effect" // kilocode_change / L182: // kilocode_change start +packages/opencode/test/server/httpapi-session.test.ts 38,157,194,562,590,1016,1071,1073 8 shared-upstream patch marker Session lifecycle/history/UI partial `packages/core/test/session*.test.ts` (inbox cancel semantics) PRESERVED — cancelling/deleting inbox items (v2 inbox tests). MISSING — released-client media content compatibility and mixed-version DB compatibility (v1-only). L38: import { pollWithTimeout, testEffect } from "../lib/effect" // kilocode_change / L157: // kilocode_change start - released V2 clients persisted media-shaped tool content +packages/opencode/test/server/httpapi-ui.test.ts 64,72,115,121,199,215,287,298,301,303,307,310,314,320,323,325,329,334,338,352,366,378 22 shared-upstream patch marker Server/API integration needs port Paired with shared/ui.ts (needs port: Kilo Console) 22 markers assert no proxy fallback and Kilo auth; v2 Kilo serves no embedded console, proxy tests not applicable but console absence stands. L64: // kilocode_change start - keep the filewatcher-disable flag visible (see httpapi-instance-route-auth.test.ts) / L72: // kilocode_change end +packages/opencode/test/server/httpapi-v2-location.test.ts 81,91,132,147 4 shared-upstream patch marker Server/API integration not needed None (v2 Location routing native) v1 compatibility assertions for Kilo location headers and legacy manifest encoding. L81: // kilocode_change start - malformed Kilo location headers are client errors / L91: // kilocode_change end +packages/opencode/test/server/httpapi-v2-pty.test.ts 17,20,187,283,287 5 shared-upstream patch marker Server/API integration native equivalent `packages/kilo-cli/test/sandbox-pty.test.ts`, `native-lifecycle.test.ts` Marked block verifies child env precedence and credential stripping via the PTY route. V2 Kilo PTY tests cover native PTY spawn and sandbox refusal; credential stripping is exercised through the sandbox path (mcp/shell env scrub for non-sandbox remains unverified — see format row). L17: // kilocode_change start - PTY route tests do not need an indexing worker per temp project; / L20: // kilocode_change end +packages/opencode/test/server/project-init-git.test.ts 74 1 shared-upstream patch marker Server/API integration not needed None Asserts a v1 path is not created; v2 snapshot storage lives under global data (snapshot.ts:96). L74: expect(yield* fs.exists(path.join(tmp.directory, ".git", "kilo"))).toBe(false) // kilocode_change +packages/opencode/test/server/session-diff-missing-patch.test.ts 7,8,11,44,63 5 shared-upstream patch marker Session lifecycle/history/UI partial Core snapshot diff (`snapshot.ts:58`) via vcs.diff/session stats Whole-file test of cumulative diff endpoint; v2 has native structured diffs but not the cumulative/imported session_diff row semantics (summary.ts row partial). L7: * session never loaded. // kilocode_change / L8: * Kilo still surfaces cumulative session diffs to its TUI and VS Code clients. // kilocode_change +packages/opencode/test/server/workspace-routing.test.ts 54,59 2 shared-upstream patch marker Server/API integration not needed None (viewed endpoint native in protocol) v1 routing special-case test; v2 route is native. L54: // kilocode_change start / L59: // kilocode_change end +packages/opencode/test/session/compaction.test.ts 14,207,255,256,266,268,286,301,972,1004,1301,1303,1325,1335,1341,1343,1812,2008,2009 19 shared-upstream patch marker Session lifecycle/history/UI partial `packages/core/test/session-runner.test.ts:2395-2615` (overflow/compaction) PRESERVED — compaction/overflow tests native. MISSING — configured output ceiling controlling tail budgeting (processor/prompt rows). L14: import { Snapshot } from "@/snapshot" // kilocode_change / L207: metadata: Effect.fn("TestSessionProcessor.metadata")(() => Effect.void), // kilocode_change +packages/opencode/test/session/llm-native-recorded.test.ts 30,32,63,66,164,226,230,280,285,297 10 shared-upstream patch marker Session lifecycle/history/UI not needed `packages/kilo-cli/test/fixtures` + `packages/http-recorder` Fixture path/branding markers for v1 recorded tests. L30: const KILO_FIXTURES_DIR = path.join(FIXTURES_DIR, "kilocode") // kilocode_change / L32: const zenURL = (connection: string) => `https://console.opencode.ai/proxy/connections/${connection}/v1` // kilocode_change +packages/opencode/test/session/llm.test.ts 25,32,283,296,452,479,506,511,845,1836,1839,1952,1956,1973,2067 15 shared-upstream patch marker Session lifecycle/history/UI partial `packages/core/test/aisdk-native.test.ts`, `packages/kilo-cli/test/gateway-protocol.test.ts` PRESERVED — Kilo Gateway request wire tests (gateway-protocol). MISSING — AI SDK raw usage retention for billing (ai-sdk row), Kilo-Code UA assertion (branding via App). L25: import { USER_AGENT } from "../../src/installation" // kilocode_change / L32: type ConfigModel = NonNullable[string]>["models"]>[string] // kilocode_change +packages/opencode/test/session/message-v2.test.ts 497,555 2 shared-upstream patch marker Tool-delivery attachment model visibility partial `packages/core/src/session/runner/to-llm-message.ts`, `model-request.ts`, and `packages/kilo-cli/test/remote-session.test.ts:647-722` V2 has a different typed attachment pipeline with admission and model-request normalization, but current coverage proves user multipart attachments, not the pinned `send_file` tool-delivery rule that delivery-only attachments stay out of later model history. L497: // kilocode_change start — send_file delivery attachments must not be replayed to the model / L555: // kilocode_change end +packages/opencode/test/session/network.test.ts 1 1 shared-upstream patch marker Offline reconnect lifecycle tests needs port Core execution tests plus proposed Kilo offline-status integration tests No current test maps to pinned code/message classification, restored-delay race, reply/reject, already-aborted signal and cleanup behavior; the simulation network test is unrelated. L1: // kilocode_change - new file +packages/opencode/test/session/processor-effect.test.ts 20,21,190,200,225,251,462,502,558,562,600,1103,1105,1145,1199,1291 16 shared-upstream patch marker Session lifecycle/history/UI partial `packages/core/test/session-runner.test.ts` MISSING — configured output ceiling, offline reconnection prompt, send_file attachments (rows needs port/partial); PRESERVED — step/finish accounting natively tested. L20: import { SessionNetwork } from "../../src/session/network" // kilocode_change / L21: import { Bus } from "../../src/bus" // kilocode_change +packages/opencode/test/session/prompt.test.ts 43,48,51,175,294,806,815,1275,1507,1791,1904,1909,2094,2103 14 shared-upstream patch marker Session lifecycle/history/UI partial `packages/core/test/session*.test.ts` (inbox/steer/interrupt coverage per AGENTS Session Core) PRESERVED — cancel then reprompt settles to idle promptly (v2 interruption + inbox tests). MISSING — environment details appended to queued prompts (Kilo-specific prompt caching detail). L43: import { SessionProjector } from "@opencode-ai/core/session/projector" // kilocode_change / L48: // kilocode_change start - Item 14 cancel→reprompt proof helpers +packages/opencode/test/session/retry.test.ts 257,273,328,343 4 shared-upstream patch marker Session lifecycle/history/UI native equivalent `packages/core/test/session-runner.test.ts` retry cases Marked block removes Go upsell expectations; v2 Kilo has no Go upsell. L257: // kilocode_change start - Kilo does not support OpenCode Go upsells / L273: // kilocode_change end +packages/opencode/test/session/schema-decoding.test.ts 62,241,243 3 shared-upstream patch marker Session lifecycle/history/UI not needed None v1 schema fixture adjustments. L62: diffs: [{ additions: 1, deletions: 0, file: "a.ts" }], // kilocode_change / L241: message: "Subscribe to OpenCode Go.", // kilocode_change +packages/opencode/test/session/session.test.ts 9,255,281,283,337 5 shared-upstream patch marker Session lifecycle/history/UI not needed None Type-only marker. L9: type SessionModel = NonNullable // kilocode_change / L255: // kilocode_change start +packages/opencode/test/session/shell-v2.test.ts 1 1 shared-upstream patch marker Session lifecycle/history/UI native equivalent `packages/core/test/session*.test.ts` shell events (v2 native shell record) Whole-file v1 test of the v2 shell dual-write; native in v2. L1: // kilocode_change - new file +packages/opencode/test/session/system.test.ts 11 1 shared-upstream patch marker Session lifecycle/history/UI not needed None Import marker. L11: import { Config } from "../../src/config/config" // kilocode_change +packages/opencode/test/share/share-next.test.ts 256,279,314,317 4 shared-upstream patch marker Share transport compatibility partial `packages/kilo-cli/test/gateway-integration.test.ts:255-276` and v1 import fixtures Coverage is local-fixture acceptance for create/retrieve/fork/unshare plus legacy decoding. It does not exercise a deployed viewer, team/org gates, or incremental sync with omitted legacy diff filenames, so the exact marked regression proof remains open. L256: // kilocode_change start / L279: // kilocode_change end +packages/opencode/test/skill/discovery.test.ts 40,51,143,164 4 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/core/src/skill/discovery.ts:16-40` validators (core tests) Marked tests assert `../` names cannot escape the cache; v2 discovery has the validators natively. L40: // kilocode_change start - serve a crafted index whose skill name escapes the cache via `../` / L51: // kilocode_change end +packages/opencode/test/skill/skill.test.ts 9,27,61,91,93,113,132,145,153,155,185,200,209,219,243,246,276,299,315,370,409,450,489,522,533,538,571,592 28 shared-upstream patch marker Package behavior indicated by path partial `packages/kilo-cli/test/skill-policy.test.ts`, `packages/core/test` skill tests PRESERVED — builtin skill exclusion/collision tests (skill-policy). MISSING — `.kilo/skill` primary directory discovery. L9: import { Git } from "../../src/git" // kilocode_change / L27: const itWithoutClaudeCodeSkills = testEffect(Layer.mergeAll(skills(false, true), node, testInstanceStoreLayer)) // kilocode_change +packages/opencode/test/storage/db.test.ts 16 1 shared-upstream patch marker Package behavior indicated by path native equivalent `packages/kilo-cli/test/project-config*.test.ts`/paths (kilo2.db) Path-naming assertion; v2 has isolated kilo2.db. L16: : path.join(Global.Path.data, `kilo-${InstallationChannel.replace(/[^a-zA-Z0-9._-]/g, "-")}.db`) // kilocode_change +packages/opencode/test/suggestion/suggestion.test.ts 1 1 shared-upstream patch marker Suggestion lifecycle tests native equivalent `packages/kilo-cli/test/remote-suggestions.test.ts:210-308,312-614` Current tests cover singleton/lifecycle isolation, eligibility, accept, dismiss, reconnect replay and unsubscribed viewers against the actual v2 adapter. L1: // kilocode_change - new file +packages/opencode/test/tool/apply_patch.test.ts 27 1 shared-upstream patch marker Tool behavior/UI not needed None (v2 keeps build) Fixture rename only. L27: agent: "code", // kilocode_change +packages/opencode/test/tool/diagnostics-filter.test.ts 1 1 shared-upstream patch marker Tool behavior/UI needs port Paired with tool/diagnostics.ts (needs port) No LSP diagnostics in v2. L1: // kilocode_change - new file +packages/opencode/test/tool/external-directory.test.ts 21 1 shared-upstream patch marker Tool behavior/UI not needed None Fixture rename only. L21: agent: "code", // kilocode_change +packages/opencode/test/tool/glob.test.ts 4,7,46,48,51,53,96,119 8 shared-upstream patch marker Tool behavior/UI partial `packages/core/test/tool` glob tests MISSING — absolute glob pattern test (glob row partial). L4: // kilocode_change start / L7: // kilocode_change end +packages/opencode/test/tool/grep.test.ts 37,148,173,192 4 shared-upstream patch marker Tool behavior/UI partial `packages/core/test/tool` grep tests MISSING — Kilo limit notice wording and exact-file constraint (grep row partial). L37: agent: "code", // kilocode_change / L148: expect(result.output).toContain("100 matches limit reached. Use limit=200 for more, or refine pattern.") // kilocode_change +packages/opencode/test/tool/parameters.test.ts 117,121,166,180 4 shared-upstream patch marker Tool behavior/UI partial `packages/core/test/tool` parameter tests PRESERVED — optional shell description native. MISSING — grep signal controls. L117: // kilocode_change start - description is optional in kilo (see bash.ts Parameters) / L121: // kilocode_change end +packages/opencode/test/tool/read.test.ts 40,273,275 3 shared-upstream patch marker Tool behavior/UI not needed None Fixture rename only. L40: agent: "code", // kilocode_change / L273: // kilocode_change start - renamed from "build" to "code" +packages/opencode/test/tool/recall.test.ts 1,66,184 3 shared-upstream patch marker Historical transcript recall tests needs port Proposed v2 session-history recall integration tests; memory tests cover only the replacement subset Current memory tests prove memory indexing/recall and safe rendering, but not pinned current-worktree-family search, exclusion of active/future queued text, cross-project rejection, sibling worktree allowance under project-ID drift, or full transcript formatting. L1: // kilocode_change - new file / L66: await Bun.write(path.join(first.path, ".git", "kilo"), "stale-project-id") // kilocode_change +packages/opencode/test/tool/registry.test.ts 5,20,21,26,32,62,70,72,85,91,99,102,163,211,214,238,241,274,316,319,346,399,401,413,433,464,509,541 28 shared-upstream patch marker Tool behavior/UI partial `packages/kilo-cli/test/sandbox*.test.ts`, core tool tests PRESERVED — sandbox-confined tool behavior tested. MISSING — .kilo/tool loading, Scout tools. L5: import { Effect, Exit, Layer, Result, Schema } from "effect" // kilocode_change / L20: import * as SandboxNetwork from "@/kilocode/sandbox/network" // kilocode_change +packages/opencode/test/tool/shell.test.ts 78,1002,1005 3 shared-upstream patch marker Tool behavior/UI partial `packages/core/test/shell*.test.ts` MISSING — BashHierarchy always-pattern generation (bash-hierarchy row needs port). L78: agent: "code", // kilocode_change / L1002: // kilocode_change start — arity prefix produces "ls *" with space before wildcard +packages/opencode/test/tool/skill.test.ts 12,32,39,73,74,138,184 7 shared-upstream patch marker Tool behavior/UI partial `packages/kilo-cli/test/skill-policy.test.ts` PRESERVED — skill inventory in tool description (skill-policy). MISSING — .kilo/skill directory. L12: import { disposeAllInstances, provideTmpdirInstance, TestInstance } from "../fixture/fixture" // kilocode_change / L32: // kilocode_change - skip on windows: address windows ci failures #9496 +packages/opencode/test/tool/suggest.test.ts 1 1 shared-upstream patch marker Suggest tool tests native equivalent `packages/kilo-cli/test/remote-suggestions.test.ts:312-614` The v2 tests drive the real tool/relay path and verify accepted prompt admission once, dismissal, cleanup, ownership, reconnection and viewer eligibility. L1: // kilocode_change - new file +packages/opencode/test/tool/task.test.ts 6,14,16,19,20,53,102,123,272,333,335,369,536,561,563,564,566,583,589,646,648,703,913,946,948,1016,1257,1466 28 shared-upstream patch marker Tool behavior/UI partial `packages/core/test/tool` subagent tests + cost-propagation native row PRESERVED — child cost accounting native. MISSING — resume hint on failure, platform attribution rebuild (task row partial). L6: import { Cause, Deferred, Effect, Exit, Fiber, Layer } from "effect" // kilocode_change - Cause for resume-hint coverage / L14: import { MessageV2 } from "@/session/message-v2" // kilocode_change +packages/opencode/test/tool/tool-define.test.ts 41,52,152,160,215 5 shared-upstream patch marker Tool behavior/UI not needed None Test helper marker. L41: // kilocode_change start / L52: // kilocode_change end +packages/opencode/test/tool/websearch.test.ts 41,42,43 3 shared-upstream patch marker Tool behavior/UI needs port Paired with websearch.ts/kilo-exa rows (needs port) Asserts websearch enabled for Kilo provider without exa/parallel keys; Kilo Exa transport absent in v2. L41: // kilocode_change / L42: expect(webSearchEnabled(ProviderV2.ID.kilo, { exa: false, parallel: false })).toBe(true) // kilocode_change +packages/opencode/test/util/process.test.ts 80,97 2 shared-upstream patch marker Package behavior indicated by path needs port Paired with util/process.ts (partial) / format env scrub (needs port) Marked test verifies inherited env is excluded; v2 has no extendEnv option consumer. L80: // kilocode_change start / L97: // kilocode_change end +packages/opencode/tsconfig.json 13 1 shared-upstream patch marker Configuration/settings not needed None; v2 Kilo TUI plugin imports `@opencode-ai/plugin`/`@opencode-ai/tui` packages directly Marker adds a path alias for Kilo components importing the extracted TUI; v2 uses workspace packages. L13: "@tui/*": ["../tui/src/*"], // kilocode_change - extracted TUI imports used by Kilo-owned components +packages/plugin/script/publish.ts 34 1 shared-upstream patch marker Package behavior indicated by path partial packages/plugin/script/publish.ts:34 (add `--provenance`) V1 published the plugin package with `--provenance` (L34). V2 publishes with `--tag --access public` only. Preserved: channel-tagged publish; missing: npm provenance attestation — a release-policy choice for Kilo's v2 plugin package. L34: await $`npm publish *.tgz --tag ${Script.channel} --access public --provenance` // kilocode_change +packages/plugin/tsconfig.json 11 1 shared-upstream patch marker Configuration/settings not needed packages/plugin/tsconfig.json (upstream, 12 lines) V1 added `types: ["node"]` (L11). V2 plugin tsconfig omits it and typechecks; no Kilo-owned plugin source depends on Node globals. L11: "types": ["node"] // kilocode_change +packages/protocol/src/api.ts 41,58 2 shared-upstream patch marker Package behavior indicated by path partial packages/kilo-cli/src/run.ts:56-60 (client supplies `location` on session.create); OpenAPI title stays upstream V1 markers pass the configured-location middleware into session creation so SDK callers omitting `location` get the configured directory (L41) and brand the OpenAPI title 'Kilo HttpApi' (L58). V2 api.ts:159 wires `makeSessionGroup(sessionLocationMiddleware)` without the location fallback and titles the API 'opencode HttpApi' (L188); Kilo callers compensate by always passing `location` explicitly (run.ts:56). Preserved: sessions land in the intended directory for owned callers. Missing: server-side default for third-party SDK callers and Kilo branding of the generated OpenAPI document. L41: .add(makeSessionGroup(sessionLocationMiddleware, locationMiddleware)) // kilocode_change - provide configured location to session creation / L58: title: "Kilo HttpApi", // kilocode_change - public API grouping is Kilo-branded +packages/protocol/src/groups/event.ts 58 1 shared-upstream patch marker Package behavior indicated by path native equivalent packages/protocol/src/groups/event.ts:62-68 (`EventManifest.ServerDefinitions` encoded stream) V1 exported a `KiloEvent` schema encoding the full Kilo event bus (L58). V2 encodes the server event manifest natively (`make(EventManifest.ServerDefinitions)`), and Kilo-owned events are defined via schema/src/kilocode RPC/event definitions that ride the same manifest. No separate Kilo event schema export is needed. L58: export const KiloEvent = schema(EventManifest.Definitions) // kilocode_change - encode the full Kilo event bus +packages/protocol/src/groups/fs.ts 13,18,30 3 shared-upstream patch marker Package behavior indicated by path needs port packages/protocol/src/groups/fs.ts:24-27 (`fs.read` query `path` for the wildcard resource) and regenerate packages/client V1 exposes an optional `path` in the fs.read query so generated SDK callers can address the wildcard resource without URL-path encoding (L13-18,30). V2 fs.read takes only `LocationQuery` (L25) — the wildcard is path-only — so SDK callers must encode the path into the route. Port only if an owned client needs the query form; regenerate the client after changing the HttpApi. L13: // kilocode_change start - expose the wildcard resource to generated SDK callers / L18: // kilocode_change end +packages/protocol/src/groups/session.ts 24,114,154,322,343 5 shared-upstream patch marker Session lifecycle/history/UI partial packages/protocol/src/groups/session.ts:170-186 (session.create without location fallback), :646-653 (native durable log) V1 markers: accept the configured-location middleware so session.create defaults `location` from the SDK directory (L114,154), and type history/event streams as the released `SessionDurable` union (L24,322,343). V2 session.create requires callers to pass `location` (Kilo callers do, run.ts:56) and serves native `SessionEvent.Durable` items — the released-key widening is obsolete (isolated store). Missing: the server-side configured-directory default for session.create; everything else is native or not needed. L24: import { SessionDurable } from "@opencode-ai/schema/durable-event-manifest" // kilocode_change - released history keys / L114: locationMiddleware: Context.Key, // kilocode_change - supply the configured location for session creation +packages/schema/src/command.ts 14 1 shared-upstream patch marker Package behavior indicated by path needs port packages/schema/src/command.ts (optional `variant` on CommandV2.Info) paired with packages/core/src/config/plugin/command.ts override pass V1 adds an optional `variant` so kilo.json can override only the model variant of a command (L14). V2 schema/src/command.ts has no `variant` field (grep empty) and the config command schema (config/command.ts) has none either, so variant-only command overrides cannot be expressed. Same feature as the core config/command.ts and config/plugin/command.ts rows. L14: variant: Model.VariantID.pipe(optional), // kilocode_change - support variant-only command overrides +packages/schema/src/durable-event-manifest.ts 4,7,9,15,18,19,24 7 shared-upstream patch marker Package behavior indicated by path not needed none — released-storage replay is obsolete (packages/core/src/session/projector.ts row); v2 manifest is packages/schema/src/durable-event-manifest.ts (7 lines, upstream) V1 markers add the released `PromptPromoted` event to the durable inventory and expose a `SessionDurable` schema/definitions so history/replay could decode rows written by released Kilo clients (L4-25). V2 reduced the manifest to upstream's 7-line inventory and kilo2's isolated store never contains released Kilo rows; session history serves native `SessionEvent.Durable` (protocol/groups/session.ts:653). L4: import { Schema } from "effect" // kilocode_change / L7: import { PromptPromoted } from "./kilocode/durable-event" // kilocode_change - released storage key +packages/schema/src/file-diff.ts 9,10 2 shared-upstream patch marker Package behavior indicated by path native equivalent packages/kilo-ide-ui/src/components/session-diff.ts:30-36,53-66 (client-side before/after reconstruction from the patch) V1 adds optional full-content `before`/`after` sides so editor diff tabs could show whole files (L9-10). V2 schema has no such fields, but the owned IDE UI accepts optional `before`/`after` on its DiffSource and otherwise reconstructs both sides from a complete unified patch (`completePatchContents`), so editor diff tabs render full content without a server-side schema change. Remainder: diffs whose patch is incomplete fall back to hunk parsing rather than full sides. L9: before: optional(Schema.String), // kilocode_change - full-content sides for editor diff tabs / L10: after: optional(Schema.String), // kilocode_change - full-content sides for editor diff tabs +packages/schema/src/llm.ts 30,85 2 shared-upstream patch marker Package behavior indicated by path not needed none — see packages/core/src/kilocode/event-storage.ts and packages/core/src/event.ts rows V1 defines `StoredToolContent`, a codec between current `ToolContent` and the released `{type:"file",source}`/`{type:"media"}` persisted shapes (L30-85). V2 schema/src/llm.ts is a 6-line upstream re-export; no v2 path reads released Kilo tool rows in place, so the codec has no consumer. L30: // kilocode_change start - durable events must keep decoding released tool content shapes / L85: // kilocode_change end +packages/schema/src/pty.ts 7,32,48,55 4 shared-upstream patch marker Package behavior indicated by path partial packages/schema/src/pty.ts:51-55 (native `size`); `sessionID` association absent V1 adds `sessionID` to Pty.Info so nested Kilo TUI terminals associate with the viewed session (L7,32) and an initial `size{rows,cols}` on CreateInput (L48-55). V2 natively includes `size` on CreateInput (L51-55, though core pty.ts:176 does not yet pass it to spawn) but has no `sessionID` field and no Kilo consumer writes one. Follows the packages/core/src/pty.ts row. L7: import { SessionID } from "./session-id" // kilocode_change / L32: sessionID: optional(Schema.NullOr(SessionID)), // kilocode_change - Kilo associates PTYs with sessions +packages/schema/src/server-event.ts 7,9,11 3 shared-upstream patch marker Server/API integration native equivalent packages/schema/src/config.ts:135 (`config.updated` event) consumed by packages/kilo-cli/src/project-config.ts:80 and telemetry.ts:26 V1 adds a `global.config.updated` server event emitted via GlobalBus when config updates without a full dispose (L7-11). V2 has a native `config.updated` bus event (schema/src/config.ts:135) that the Kilo ProjectConfig reload publishes after refresh and that Kilo telemetry observes, so the notification invariant exists without a separate server-event definition. L7: // kilocode_change start - emitted (via GlobalBus) when config updates without a full dispose / L9: // kilocode_change end +packages/schema/src/session-event.ts 429 1 shared-upstream patch marker Session lifecycle/history/UI not needed none — released-reader compatibility (packages/core/src/session/compaction.ts row) V1 adds an optional `include` field on the compaction-ended durable event for released v1 readers (L429). V2 session-event.ts (690 lines) has no such field and no shared-store reader exists. L429: include: Schema.String.pipe(Schema.optional), // kilocode_change - released v1 readers recognize this compatibility field +packages/schema/src/session-status-event.ts 9,34,40 3 shared-upstream patch marker Session lifecycle/history/UI needs port packages/schema/src/session-status-event.ts (or a Kilo-owned status extension) plus the offline-question feature it represents V1 adds an `offline` session status carrying a QuestionID and message for a session paused on an offline Kilo question (L9,34-40). V2 SessionStatus has only idle/retry/busy variants (L11-30) and no Kilo schema extension declares `offline`. This status is the wire representation of the v1 offline/network-wait feature already recorded as needing a port in the 'Network and overflow session slice'. L9: const QuestionID = Schema.String.check(Schema.isStartsWith("que")).pipe(Schema.brand("QuestionID")) // kilocode_change / L34: // kilocode_change start - represent a session paused for an offline Kilo question +packages/schema/src/v1/permission.ts 44 1 shared-upstream patch marker Package behavior indicated by path needs port packages/schema/src/permission.ts reply schema (`interactive` flag) consumed by packages/kilo-cli/src/skill-shell.ts V1 adds an `interactive` flag on permission replies, set by clients when a human answered, so the server can refuse machine approvals of skill-shell batches (L44-45). V2 permission schemas have no `interactive` field and skill-shell.ts asserts permission without distinguishing human from machine replies (skill-shell.ts:198,212). Consistent with the earlier partial permission assessment (skill-shell requests no longer remove persistent approval). L44: // kilocode_change - set by clients when a human answered the prompt; the server refuses machine approvals of skill-shell batches +packages/schema/src/v1/question.ts 18,26,34,41,58 5 shared-upstream patch marker Package behavior indicated by path needs port Kilo-owned question schema extension (see canonical row for packages/opencode/test/kilocode/question-option-schema.test.ts) V1 adds `labelKey`/`descriptionKey`/`mode` on options, `questionKey`/`headerKey` on questions and a `blocking` flag (L18-26,34-41,56-58) for Kilo client localization and mode pre-selection. V2 schema (v1/question.ts 66 lines, question.ts) has none of these fields (grep empty) and no packages/schema/src/kilocode extension declares them; the canonical test row already records the gap. L18: // kilocode_change start - Kilo client localization and mode selection hints / L26: // kilocode_change end +packages/schema/src/v1/session.ts 216,246,274,362,372,396,398,494 8 shared-upstream patch marker Session lifecycle/history/UI partial packages/schema/src/v1/session.ts (legacy import schema, `variant` native at L350,483,540); editorContext now Session metadata (packages/kilo-vscode/src/backend/session.ts:160); generation metrics absent V1 markers add `variant` on subtask parts (L216,494), assistant `model`/`generationID`/`vercelID`/`metrics`/`time{start,end,elapsed}` for weighted throughput (L246-274), and a user-message `editorContext` (L362-398). In v2 this file is only the legacy schema decoded by the v1 import migration (database/v1-migration.bun.ts:218-219, `decodeUnknownOption` ignores unknown fields). Preserved: `variant` is native; editor context is carried in v2 as Session metadata by the owned VS Code backend rather than on user messages. Missing: per-assistant generation metrics/elapsed time are not imported from v1 rows and have no v2 message field (usage aggregation is durable root/descendant totals, schema/src/kilocode/session-usage.ts), so v1 throughput history is dropped on import. L216: variant: Schema.optional(Schema.String), // kilocode_change - preserve workflow subtask variant / L246: // kilocode_change start +packages/script/src/index.ts 19,27,29,30,32,33,34,35,39,90,95,100,103,104,107,146,165 17 shared-upstream patch marker Package behavior indicated by path needs port packages/script/src/index.ts (v2, 85 lines, reads OPENCODE_* env) — Kilo release scripts need KILO_* env, `rc` pre-release channel, GH_REPO highest-release version resolution, `@kilocode/cli` latest lookup and the Kilo team roster V1 markers: KILO_CHANNEL/BUMP/VERSION/RELEASE/PRE_RELEASE env (L19-35), `rc` channel for pre-releases, plain-semver rc versions bumped from the highest GitHub release or `@kilocode/cli@latest` (L39-104), the Kilo contributor team list used to strip changelog thanks (L107-146), and a `kilo script` log line (L165). V2 packages/script reads OPENCODE_* variables only (L21-29), has no rc/pre-release channel, resolves versions from upstream's npm package, and carries upstream's team list. Every Kilo release/publish script depends on this module, so the Kilo variant must accompany the publish lane. L19: // kilocode_change start / L27: // kilocode_change end +packages/script/tests/check-opencode-annotations.test.ts 45,56,66,72,225,226,227,228,229,230,231,232,235,236,237,238,239,240,241,242,245,246,247,248,251,252,253,254,255,256,257,266,267,268,269,270,315,396,401,406,411,416,421,428,431,441,444,453,454,456,457,464,470,476,482,488,494,500,507,509,517,519,521,523,533,535,543,544,546,548,561,586,602,609,613,625,629,641,643,645,647,655,657,665,673,685,738,745,747,759,768,774,784,788,792,803,815,817,842,844,869,873,877,881,885,889,892,896,900,901,902,903,904,923,925,932,938,940,948,955,957,958,960,967,994,996,998,1000,1010,1018,1025,1027 132 shared-upstream patch marker Package behavior indicated by path deferred packages/script/tests/check-opencode-annotations.test.ts with script/check-opencode-annotations.ts when marker enforcement is added to the v2 line V1 1032-line test suite (132 marker-string occurrences, mostly fixture lines) verifies the annotation checker's coverage rules (inline/block/new-file markers, JSX/YAML/shell comment styles). V2 has neither the checker nor its tests; kilocode/v2-fork-conventions.md documents the marker rules but nothing enforces them. Deferred with the Kilo CI lane; note the scope list would change (no packages/opencode, Kilo-owned paths exempt). L45: const MARKER_PREFIX = /(?:\/\/|\{?\s*\/\*|#)\s*kilocode_change\b/ / L56: if (first?.match(/(?:\/\/|\{?\s*\/\*|#)\s*kilocode_change\s*-\s*new\s*file\b/)) { +packages/sdk-next/test/embedded.test.ts 9,21,117,156,199,224 6 shared-upstream patch marker Package behavior indicated by path not needed packages/sdk/test/embedded.test.ts (upstream; no temp-directory database cleanup loop) V1 markers add an EBUSY retry loop around temp-dir removal because Windows SQLite handles are released by GC finalizers (L9-21,117,156,199,224). V2's embedded test (packages/sdk/test/embedded.test.ts) does not create per-test database directories that need this cleanup (no rm/cleanup calls), so the Windows workaround has no target; revisit only if Kilo CI runs the sdk suite on Windows and hits EBUSY. L9: // kilocode_change start - retry Windows SQLite locks until GC finalizers release them / L21: // kilocode_change end +packages/sdk/js/script/publish.ts 41 1 shared-upstream patch marker Package behavior indicated by path partial packages/sdk/script/publish.ts (v2 sdk publish) — add `--provenance` if Kilo publishes the v2 SDK with npm provenance V1 published the SDK tarball with `--provenance` (L41). The v2 SDK lives at packages/sdk (no js/ subtree); its publish script is upstream and the sibling packages/plugin/script/publish.ts:34 shows v2 publishes without `--provenance`. Preserved: channel-tagged public publish; missing: provenance attestation. Whether Kilo publishes the v2 SDK at all is a release decision, so this is a low-priority partial rather than a hard gap. L41: await $`npm publish *.tgz --tag ${Script.channel} --access public --provenance` // kilocode_change +packages/sdk/js/src/client.ts 28 1 shared-upstream patch marker Package behavior indicated by path not needed none — v2 SDK (packages/sdk/src) uses `location[directory]` query / `x-opencode-directory` header natively (packages/server/src/location.ts:45-46) V1 rewrote outgoing requests to move the `x-kilo-directory` header into a `directory` query parameter (L28-30). V2's server accepts `location[directory]` in the query and the directory header, and the v2 client (packages/client, packages/sdk) is generated against that contract; there is no Kilo header rename in v2, so no rewrite is required. L28: const next = new Request(url.href, request) // kilocode_change +packages/sdk/js/src/error-interceptor.ts 40 1 shared-upstream patch marker Package behavior indicated by path not needed none — v2 SDK error handling is upstream (packages/sdk/src); no `kilo server` branded message consumer V1 branded SDK network/empty-body errors as `kilo server ...` (L40-41). V2 removed the v1 SDK error interceptor entirely; the owned Kilo clients (kilo-cli, kilo-vscode backend) format their own errors. Cosmetic branding with no v2 seam. L40: // kilocode_change +packages/sdk/js/src/server.ts 5,36,69,72,90,92,155,160 8 shared-upstream patch marker Server/API integration needs port packages/kilo-cli/src/host.ts:30 (`content` option on ProjectConfig) plus a Kilo-owned config-content merge when kilo-cli spawns nested hosts V1 merged an existing KILO_CONFIG_CONTENT with the caller's config (agents/commands/mcp/mode/plugin/instructions) so nested CLI instances kept Kilo-injected modes (L5-36,68-73,160-161) and launched the `kilo` binary, waiting for 'kilo server listening' (L91). V2 has no packages/sdk/js/src/server.ts; the Kilo host accepts a single `content` string (ProjectConfig.configured({ content }), host.ts:30) with no merge of an inherited KILO_CONFIG_CONTENT (grep in kilo-cli/kilo-vscode returns only the unrelated skill-policy constant), and nested spawns are done by the owned CLI/VS Code backend rather than an SDK helper. The merge-preserving-injected-modes invariant is absent. L5: // kilocode_change start - Merge existing KILO_CONFIG_CONTENT with new config / L36: // kilocode_change end +packages/sdk/js/src/v2/server.ts 5,36,68,70,73,91,93,155,157,162 10 shared-upstream patch marker Server/API integration needs port same as packages/sdk/js/src/server.ts Duplicate of the v1 SDK server helper for the v2 client surface: KILO_CONFIG_CONTENT merge, `kilo` binary launch, 'kilo server listening' detection (L5-36,68-73,91-93,155-163). No v2 counterpart exists; see the server.ts row. L5: // kilocode_change start - Merge existing KILO_CONFIG_CONTENT with new config / L36: // kilocode_change end +packages/sdk/js/test/server.test.ts 1,111 2 shared-upstream patch marker Server/API integration needs port Kilo-owned test for the config-content merge once the server.ts behavior is ported V1 test suite covers buildConfigEnv merging semantics (empty, incoming-only, preserve existing, override same keys, plugin concatenation, L1-111). V2 has no buildConfigEnv and no equivalent test. Follows the sdk server rows. L1: // kilocode_change start - Tests for KILO_CONFIG_CONTENT merging / L111: // kilocode_change end +packages/server/src/cors.ts 2,20 2 shared-upstream patch marker Server/API integration needs port packages/server/src/cors.ts:12-20 (CorsOptions) — supply `https://*.kilo.ai` via the Kilo host's `cors` option or a Kilo-owned origin matcher V1 allowed any `https://*.kilo.ai` origin through a Kilo-owned matcher (L2,20; v1 server/src/kilocode/cors.ts). V2 cors.ts allows localhost, oc://renderer, tauri and `*.opencode.ai` plus explicit `opts.cors` entries; no Kilo host code passes kilo.ai origins (grep for kilo.ai/cors in kilo-cli interactive-server.ts and daemon.ts is empty). Browser clients served from kilo.ai cannot reach a v2 local server without a port. L2: import { corsOrigin } from "./kilocode/cors" // kilocode_change / L20: if (corsOrigin(input)) return true // kilocode_change +packages/server/src/handlers/event.ts 2,16 2 shared-upstream patch marker Server/API integration native equivalent packages/server/src/handlers/event.ts:24 with packages/protocol/src/groups/event.ts:62 (server manifest encoding) V1 encoded SSE payloads with the Kilo-wide `KiloEvent` schema so Kilo bus events serialized correctly (L2,16). V2 encodes via the shared server event manifest, which Kilo-owned events join through the schema/kilocode definitions; see packages/protocol/src/groups/event.ts row. L2: import { KiloEvent } from "@opencode-ai/protocol/groups/event" // kilocode_change - encode the full Kilo event bus / L16: data: JSON.stringify(Schema.encodeUnknownSync(KiloEvent)(data)), // kilocode_change +packages/server/src/handlers/fs.ts 16 1 shared-upstream patch marker Server/API integration needs port packages/server/src/handlers/fs.ts:16-17 (`ctx.query.path ?? wildcard`) paired with packages/protocol/src/groups/fs.ts V1 preferred the typed `path` query when present and fell back to the raw wildcard (L16-17). V2 fs.ts only decodes the wildcard path. Follows the protocol fs.ts row; both must change together. L16: // kilocode_change - generated clients use the typed query; retain raw wildcard compatibility +packages/server/src/handlers/pty.ts 186 1 shared-upstream patch marker Server/API integration needs port packages/server/src/handlers/pty.ts attach (replay-exited query flag) following the core pty `allowExited` port V1 passes `allowExited` from a `PTY_REPLAY_EXITED_QUERY` websocket query parameter so canonical clients can replay retained output of an exited terminal (L186). V2 pty.ts has no such flag (grep empty) because core Pty.attach has no allowExited option. Follows packages/core/src/pty.ts. L186: allowExited: url.searchParams.get(PTY_REPLAY_EXITED_QUERY) === "1", // kilocode_change +packages/server/src/handlers/reference.ts 5,9 2 shared-upstream patch marker Server/API integration native equivalent packages/server/src/handlers/reference.ts (8 lines, upstream) fed by packages/kilo-cli/src/project-config.ts config entries V1 wrapped reference.list in a Kilo request-scoped reconciler so kilo.json references were applied before listing (L5,9). V2 has no reconciler because Kilo config entries flow through Config.Service and the native reference plugin (see packages/core/src/reference.ts row). L5: import { reconcile } from "../kilocode/reference-reconciler" // kilocode_change / L9: response(reconcile(Reference.Service.use((reference) => reference.list()))), // kilocode_change +packages/server/src/handlers/session.ts 14,70,76,80 4 shared-upstream patch marker Session lifecycle/history/UI needs port packages/server/src/handlers/session.ts:123,136 (`location ?? process.cwd()` default) — default from the request Location instead of process.cwd() V1 defaulted a missing `payload.location` on session.create to the configured request Location (directory + workspaceID) so `createKiloClient` callers landed in their configured directory (L14,70-80). V2 defaults to `process.cwd()` of the server process (L123,136) — wrong for a shared daemon serving several directories. Owned callers avoid it by always passing `location` (kilo-cli run.ts:56), so the gap affects third-party SDK callers only. Paired with protocol api.ts/session.ts rows. L14: import { Location } from "@opencode-ai/core/location" // kilocode_change / L70: const location = yield* Location.Service // kilocode_change +packages/server/src/location.ts 8,14,35,47,57 5 shared-upstream patch marker Server/API integration needs port packages/server/src/location.ts:53-58 (`decode` swallows URIError) — return InvalidRequestError 400 for malformed encoded directory headers V1 rejected malformed percent-encoded directory headers with a 400 InvalidRequestError via the middleware error channel (L8,14,35-47,57). V2 `decode` catches the URIError and silently uses the raw header value as the directory (L53-58), so a malformed header becomes a bogus location instead of a client error. L8: import { InvalidRequestError } from "@opencode-ai/protocol/errors" // kilocode_change / L14: { error: InvalidRequestError }, // kilocode_change - surface malformed headers as 400s +packages/server/src/routes.ts 21,58,63 3 shared-upstream patch marker Server/API integration native equivalent packages/server/src/routes.ts (188 lines, upstream); Kilo host overrides via packages/kilo-cli/src/host.ts:26-47 V1 provided the plain location layer and a no-op reference reconciler for the standalone server because only the CLI had a Kilo config reconciler (L21,58,63). V2 has no reconciler concept; Kilo behavior is injected as node replacements/overrides through `ServerFetch.make(..., { overrides })` (host.ts), so the standalone/CLI split the markers worked around no longer exists. L21: import * as ReferenceReconciler from "./kilocode/reference-reconciler" // kilocode_change / L58: Layer.provide(locationLayer), // kilocode_change - standalone server has no Kilo config reconciler +packages/server/tsconfig.json 5 1 shared-upstream patch marker Configuration/settings not needed packages/server/tsconfig.json (upstream) V1 added `types: ["bun"]` because typechecking imported core sources needed Bun globals (L5). V2 server tsconfig has no marker and `bun typecheck` is the authoritative check per AGENTS.md; no Kilo-owned server source requires the addition. L5: "types": ["bun"], // kilocode_change - required when typechecking imported core sources +packages/session-ui/src/components/markdown-worker.ts 2,120 2 shared-upstream patch marker Session lifecycle/history/UI not needed packages/session-ui/src/components/markdown-worker.ts (228 lines, upstream; theme no longer posted at init) V1 imported the renamed `KiloTheme` from `@opencode-ai/ui/context/marked` and posted it in the worker init message (L2,120). V2's worker protocol has no `init`/theme message (only parse/dispose, L68,91,117) and session-ui theming is handled through CSS color-scheme (markdown-mermaid.ts:15); Kilo theming lives in packages/kilo-ui (theme/default-themes.ts). The rename marker has no v2 consumer. L2: import { KiloTheme } from "@opencode-ai/ui/context/marked" // kilocode_change - Kilo renamed the shared theme / L120: worker.postMessage({ type: "init", theme: KiloTheme } satisfies MarkdownWorkerRequest) // kilocode_change +packages/storybook/.storybook/mocks/app/context/platform.ts 1,12 2 shared-upstream patch marker Package behavior indicated by path not needed packages/storybook/.storybook/mocks/app/context/platform.ts (imports the real `Platform` type) V1 inlined a local `Platform` type for the storybook mock (L1-12). V2 imports the type from `app/src/runtime/platform/platform` and mocks the current surface (openExternal/restart/notify/fetch). The inline type was a v1 typing workaround with no behavior. L1: // kilocode_change start / L12: // kilocode_change end +packages/tui/src/app.tsx 9,26,59,62,90,91,92,153,193,202,233,249,256,260,303,321,338,371,380,461,466,469,475,489,827,833,957,968,1004,1043,1095,1141,1145 33 shared-upstream patch marker User-interface behavior partial packages/tui/src/app.tsx; packages/tui/src/context/runtime.tsx; packages/kilo-cli/src/tui.ts; packages/kilo-cli/src/tui-plugin/tui.tsx V1 markers combine renderer exit bridging, terminal/Kitty cleanup, headless-link and Nudge providers, display detection, and Kilo plugin installation. V2 preserves host-driven app identity/plugins and renderer exit/lifecycle through TuiApp/runtime plus kilo-cli, but no concrete v2 Nudge provider or v1 headless-link dialog is present; terminal handling is reorganized and exact cleanup parity is not proven. L9: import type { Exit } from "./context/exit" // kilocode_change / L26: untrack, // kilocode_change +packages/tui/src/attention.ts 41,42,48 3 shared-upstream patch marker Kilo-branded attention defaults partial `packages/tui/src/attention.ts` plus a Kilo-owned branding override/plugin seam V2 has a substantially stronger focus-aware notification and configurable sound implementation with tests, but its fixed default title is `OpenCode` and it no longer exposes the marked `kilo.default` / `Kilo Default` pack identity. L41: const DEFAULT_TITLE = "Kilo" // kilocode_change / L42: const DEFAULT_PACK_ID = "kilo.default" // kilocode_change +packages/tui/src/component/dialog-agent.tsx 14,24 2 shared-upstream patch marker Agent display-name selection native equivalent `packages/tui/src/component/dialog-agent.tsx:9-27` The current dialog displays `item.name` while retaining canonical `item.id` as option value/current selection. This preserves the marked invariant despite the v2 agent schema renaming `displayName/name` to `name/id`. L14: title: item.displayName ?? item.name, // kilocode_change / L24: current={local.agent.current()?.name ?? ""} // kilocode_change +packages/tui/src/component/dialog-model.tsx 1,2,5,10,13,14,21,25,33,37,74,78,83,97,99,133,139,141,143,147,162,165,181,194,224,257,268,276,281,283,286,290,296,298 34 shared-upstream patch marker Provider/model routing or UI partial packages/tui/src/component/dialog-model.tsx; packages/tui/src/context/runtime.tsx; packages/kilo-cli/src/model-picker.ts; packages/kilo-cli/src/tui-plugin/model-info-dialog.tsx Ported core picker policy through the v2 host-owned TuiApp.modelPicker seam: Kilo catalog groups/ranking, hidden models (including favorite/recent removal), preferred provider ordering, disclosures, native favorites/recents and a separate model-info dialog are concrete consumers. Partial because the v1 inline wide selection preview/ModelInfoPanel and its exact per-section fuzzy-search/header behavior are not reproduced in the native v2 dialog; the owned info dialog is a separate command flow, not selection-preserving inline parity. Ported core picker policy through the v2 host-owned TuiApp.modelPicker seam: Kilo catalog groups/ranking, hidden models (including favorite/recent removal), preferred provider ordering, disclosures, native favorites/recents and a separate model-info dialog are concrete consumers. Partial because the v1 inline wide selection preview/ModelInfoPanel and its exact per-section fuzzy-search/header behavior are not reproduced in the native v2 dialog; the owned info dialog is a separate command flow, not selection-preserving inline parity. +packages/tui/src/component/dialog-provider.tsx 15,18,21,56,89,127,132,135,137,140,143,190,201,214,223,382,387,388,390,391,396,405 22 shared-upstream patch marker Provider/model routing or UI partial packages/tui/src/component/dialog-integration.tsx; packages/server/src/integration; packages/kilo-cli/src/integration.ts V2 replaces the provider-auth dialog with the Integration workflow and concretely preserves Kilo-first/recommended placement, method selection, key/form/OAuth/command flows, connection refresh, and provider-to-model return routing. Partial because the v1 KiloProvider adapter's per-provider titles/descriptions, failed-provider gutters/details, provider-specific automatic method rendering, local-provider optional-key placeholder/description, and custom-provider kilo.json credential guidance are not all evidenced in the v2 integration UI; adjacent generic connection support is not parity for those remainders. V2 replaces the provider-auth dialog with the Integration workflow and concretely preserves Kilo-first/recommended placement, method selection, key/form/OAuth/command flows, connection refresh, and provider-to-model return routing. Partial because the v1 KiloProvider adapter's per-provider titles/descriptions, failed-provider gutters/details, provider-specific automatic method rendering, local-provider optional-key placeholder/description, and custom-provider kilo.json credential guidance are not all evidenced in the v2 integration UI; adjacent generic connection support is not parity for those remainders. +packages/tui/src/component/dialog-retry-action.tsx 10,43 2 shared-upstream patch marker Kilo pricing retry/upgrade dialog needs port Owned v2 retry error action component or plugin-provided dialog The marked behavior recognizes the Kilo pricing URL and applies a distinct upgrade treatment. The component and URL-specific rendering branch are absent from current v2; generic integration/retry errors do not preserve this Kilo conversion path. L10: const KILO_PRICING_URL = "https://kilo.ai/pricing" // kilocode_change / L43: const showGoTreatment = () => props.link === KILO_PRICING_URL // kilocode_change +packages/tui/src/component/dialog-session-list.tsx 58,63,86,198,220,258,286,362,386,388,390 11 shared-upstream patch marker Session lifecycle/history/UI needs port packages/tui/src/component/dialog-session-list.tsx with location/worktree-aware session queries V1 adds current-worktree-by-default listing, Ctrl+A all-worktrees scope, retained worktree labels, selection/deletion/rename behavior across scopes. Current v2 dialog has no scope toggle or worktreeName presentation; location-keyed data is adjacent infrastructure, not this user-facing cross-worktree behavior. L58: const [global, setGlobal] = createSignal(false) // kilocode_change - show current worktree by default / L63: // kilocode_change start - experimental sessions retain worktree labels and scope +packages/tui/src/component/dialog-session-rename.tsx 9,10,22,29 4 shared-upstream patch marker Rename dialog prefill and successful completion native equivalent `packages/tui/src/component/dialog-session-rename.tsx` and `packages/tui/src/routes/session/index.tsx:862-875` V2 accepts `currentTitle`, trims/refuses empty titles, calls the typed rename API, closes only after success, and reports failures. The old optional callback let session-list callers return after rename; v2's durable rename event updates shared data while post-success clear provides the completion boundary. L9: title?: string // kilocode_change / L10: onConfirm?: () => void // kilocode_change +packages/tui/src/component/dialog-status.tsx 9,27,57,59,60,74,105 7 shared-upstream patch marker Kilo-branded status, paths, and MCP guidance partial Kilo-owned status contribution/dialog; generic MCP status remains in `packages/tui/src/component/dialog-status.tsx` V2 retains typed MCP status and no longer parses plugin file URLs, eliminating the marked Windows separator bug. It omits Kilo version/config/project path rows and `kilo mcp auth` guidance; generic authentication text and footer branding do not preserve those dialog details. L9: import { Global } from "@opencode-ai/core/global" // kilocode_change / L27: const parts = path.split(/[/\\]/) // kilocode_change: fix Windows backslash paths +packages/tui/src/component/error-component.tsx 10,19,22,26,76,114,127,213,227,231 10 shared-upstream patch marker User-interface behavior partial packages/tui/src/component/error-component.tsx plus a Kilo-owned recovery/help action seam V1 guards ErrorBoundary rendering when renderer/theme/keyboard contexts are unavailable and adds Kilo-specific recovery/help actions. V2 has a native error boundary and generic error rendering, but the marked context-safe fallbacks and full Kilo recovery guidance are not evidenced together. L10: // kilocode_change start — guard against missing renderer context in ErrorBoundary fallback / L19: // kilocode_change end +packages/tui/src/component/kilo-logo.tsx 1 1 Kilo-owned inherited marker Kilo TUI home logo native equivalent `packages/kilo-cli/src/tui-plugin/logo.tsx`, `logo-data.ts`, and `test/logo.test.tsx` The inherited marker disappears because the implementation moved into the Kilo-owned CLI package. V2 retains the branded responsive logo, shadow tinting, compact fallback, Unicode capability selection, and focused renderer tests; its `home.logo` slot supplies the component without a shared TUI fork. L1: // kilocode_change - new file +packages/tui/src/component/logo.tsx 1,4 2 shared-upstream patch marker Kilo TUI home logo native equivalent `packages/kilo-cli/src/tui-plugin/tui.tsx:31-35` and `logo.tsx` The shared replacement is superseded by the Kilo preview plugin's explicit `replace: "home.logo"` registration. The production KiloLogo is owned by `packages/kilo-cli`, so no marker is required in v2's generic responsive OpenCode logo component. L1: import { KiloLogo } from "./kilo-logo" // kilocode_change / L4: return // kilocode_change +packages/tui/src/component/prompt/autocomplete.tsx 20,21,22,23,65,68,374,393,480,493,522,672,680,691,697,725,729,763,776 19 shared-upstream patch marker User-interface behavior partial packages/tui/src/component/prompt/autocomplete.tsx; packages/tui/src/context/runtime.tsx; Kilo-owned session-mention plugin/dialog V1 markers preserve slash suffix insertion and cursor-safe overlay closing, add Past Chats with searchable session mentions, and render Kilo command labels. V2 retains native autocomplete and host command presentation, but no searchable Past Chats/session-part insertion flow is present; exact overlay/cursor regressions are unproven. L20: import { slashDisplay } from "@/kilocode/cli/cmd/command-display" // kilocode_change / L21: import { createSessionPart, sessionMentionText } from "../../kilocode/session-mentions" // kilocode_change +packages/tui/src/component/prompt/index.tsx 27,55,61,64,66,73,173,225,234,310,321,334,359,449,462,555,563,624,625,659,686,692,812,828,846,878,897,969,1006,1046,1048,1056,1081,1088,1109,1200,1210,1224,1259,1324,1332,1355,1433,1436,1449,1456,1490,1547,1551,1556,1562,1621,1626,1628,1639,1677,1838,1844 58 shared-upstream patch marker User-interface behavior partial packages/tui/src/component/prompt/index.tsx; packages/kilo-cli/src/tui-plugin; packages/core/src/session/inbox.ts V1's 58 markers span vim editing, nudge delivery, agent/model synchronization, double-Ctrl+C exit, directory-aware submission, attachments/references, queued prompts and Kilo commands. V2 preserves durable queueing, native prompt editing and several Kilo plugin commands, but Nudge/vim/reference and exact lifecycle behaviors are not all present, so the large block is mixed. L27: import { useNudge } from "@/kilocode/cli/cmd/tui/context/nudge" // kilocode_change / L55: // kilocode_change start +packages/tui/src/component/use-connected.tsx 4,22 2 shared-upstream patch marker Authenticated-provider connectivity detection native equivalent `packages/tui/src/util/connected-provider.ts` consumed by `packages/tui/src/component/use-connected.tsx` V2 no longer infers authentication from provider identity or paid-model cost. It reads typed integration connections and returns connected only when a credential or environment connection exists, so anonymous built-in catalog availability cannot produce a false positive; this is a stronger boundary-equivalent implementation. L4: // kilocode_change start - anonymous Kilo and OpenCode providers do not prove authentication / L22: // kilocode_change end +packages/tui/src/config/index.tsx 7,62,68,110 4 shared-upstream patch marker Kilo TUI title, prompt mode, and attention defaults partial `packages/tui/src/config/index.tsx` plus Kilo-owned config/plugin contributions for branding; prompt editing mode requires an explicit v2 design V2 retains and expands the typed TUI config/resolve boundary and attention sound-pack override, but defaults to `opencode.default`; the marked `title_icon` and `vim` options have no current schema or consumers. Kilo runtime disables the generic terminal title rather than implementing the old icon selector, so this is not full parity. L7: import { KiloTitleIcon } from "@/kilocode/cli/cmd/tui/title-icon" // kilocode_change / L62: title_icon: Schema.optional(KiloTitleIcon.Value), // kilocode_change +packages/tui/src/config/keybind.ts 148,151,223,230,358,361,415 7 shared-upstream patch marker Kilo feedback, Vim-mode, and news commands needs port Kilo-owned TUI command registrations and bindings; add shared binding definitions only for commands implemented in the generic TUI The marked feedback up/down, `prompt.vim.toggle`, and `news.toggle` bindings are absent from v2's binding catalogue, and no corresponding TUI command consumers exist. Generic feedback-colored UI, input editing, and home footer content do not implement these commands. L148: // kilocode_change start - message feedback / L151: // kilocode_change end +packages/tui/src/context/data.tsx 22,27,60,65,67,82,102,408,422,443,463,475,595,600,601,614 16 shared-upstream patch marker User-interface behavior partial packages/tui/src/context/data.tsx and packages/tui/src/context/event.ts V1 adds global-event routing, disposal-safe refresh error classification, legacy promotion compatibility and hydration that reconciles live events received during snapshot load. V2 has location-keyed data/event projection and current sync tests, but earlier event assessment found server-wide project isolation and legacy/hydration race remainders; generic data loading is not full parity. L22: import { createStore, produce, reconcile } from "solid-js/store" // kilocode_change / L27: import { hydrate } from "../kilocode/hydration" // kilocode_change +packages/tui/src/context/event.ts 3,61,65,73 4 shared-upstream patch marker Event envelope normalization and project routing partial `packages/tui/src/context/event.ts` plus location-scoped client/event filtering before generic subscribers V2 events arrive in the native `OpenCodeEvent` shape, so the legacy `syncEvent` envelope normalization is obsolete and metadata is preserved from `event.location`. However, `useEvent.subscribe` forwards every non-connection event and does not enforce the marked current-project/global filter; raw `/api/event` subscription is server-wide, so that isolation invariant is not proven by the renamed architecture. L3: // kilocode_change start - normalize the runtime SyncEvent wire envelope to the legacy consumer shape / L61: // kilocode_change end +packages/tui/src/context/local.tsx 10,57,99,105,124,149,166,179,188,191,210,218,226,238,287,288,297,305,309,310,318,355,356,384,400,405,592,603 28 shared-upstream patch marker User-interface behavior partial packages/tui/src/context/local.tsx; packages/tui/src/context/runtime.tsx; packages/kilo-cli/src/model-picker.ts V1 serializes preference writes, separates process-local configured-agent overrides, falls back after agent removal and persists model/agent choices per project. V2 retains local model preferences and host picker policy, including some serialized state markers, but the full configured-agent/org-switch fallback and persistence contract is not evidenced. L10: import { useProject } from "./project" // kilocode_change / L57: const project = useProject() // kilocode_change +packages/tui/src/context/route.tsx 1,17,21,29,39,41,48,51,58 9 shared-upstream patch marker User-interface behavior partial packages/tui/src/context/route.tsx plus a Kilo-owned KiloClaw route/plugin if Claw returns V1 adds KiloClawRoute and clone-safe navigation/history state. V2 native routing preserves ordinary home/session/plugin navigation, but KiloClaw is absent and the old specialized route cannot be called native; only the generic history portion remains. L1: import { createStore, reconcile, unwrap } from "solid-js/store" // kilocode_change / L17: // kilocode_change start +packages/tui/src/context/sync.tsx 12,13,24,25,26,36,39,40,41,90,93,95,106,109,125,139,144,145,153,154,162,168,170,198,201,202,203,204,233,238,325,357,377,416,495,617,622,635,639,660,757,765,774,777,792,799,810,820,829,840,858,882,893,908,931,942,958,1002 58 shared-upstream patch marker Kilo TUI projection, compatibility events, and workspace rebootstrap partial `packages/client/src/solid/data.ts` and TUI `DataProvider`, plus owned Kilo remote/sidebar services and explicit ports for network/tool-terminal surfaces V2 replaces the monolithic sync store with location-keyed client projections and native durable events. Session eviction, shell/background state, VCS-by-location, configuration refresh, indexing/account/sidebar consumers, and remote suggestions are covered by current native or owned seams. The v1 network-wait prompt and tool-emitted interactive-terminal projection remain unported, legacy released-v1 Sync event folding is intentionally absent, and direct generic event subscribers still lack project filtering; the mixed aggregate is therefore partial. L12: SuggestionRequest, // kilocode_change / L13: SessionNetworkWait, // kilocode_change +packages/tui/src/context/theme.tsx 41,43,59,67,106,131,132,148,154,173,188,277,317,322 14 shared-upstream patch marker User-interface behavior partial packages/tui/src/context/theme.tsx; packages/kilo-cli/src/tui-plugin/theme.ts; packages/kilo-ui/src/theme V1 discovers Kilo theme roots, isolates malformed custom themes, defaults/falls back to the built-in kilo theme and validates required colors. V2 has native semantic theme resolution and owned Kilo theme packs, but prior theme audit found the released built-in pack/fallback surface incomplete; malformed-file isolation alone is not full parity. L41: // kilocode_change start - discover Kilo config roots, not OpenCode roots / L43: // kilocode_change end +packages/tui/src/feature-plugins/home/tips-view.tsx 4,136,143,168,224,227,236,238,255 9 shared-upstream patch marker Kilo-branded contextual home tips needs port Kilo-owned TUI plugin rendering an appended `home.footer` or a new narrow shared `home.tips` slot V1 replaces the upstream tip catalogue with Kilo-specific commands, config paths, branding, provider copy, and shortcut-aware tips. V2 has no tips component or catalogue; the existing generic and Kilo footer slots are static status/footer content, not rotating contextual guidance, so adjacent home chrome is not parity. L4: import { KILO_TIPS } from "@/kilocode/cli/cmd/tui/feature-plugins/home/tips" // kilocode_change / L136: // kilocode_change start +packages/tui/src/feature-plugins/home/tips.tsx 47 1 shared-upstream patch marker Persistent home-tip visibility and toggle needs port Kilo-owned TUI home contribution plus plugin KV/command binding; add a narrow slot only if the current home footer cannot host it V1 deliberately shows tips beyond first-run state while retaining the `tips.toggle` hidden preference. V2 has no tip registration, toggle command, or `tips_hidden` state; `packages/tui/src/routes/home.tsx` exposes logo and footer slots, but no current consumer implements this invariant. L47: const show = createMemo(() => !hidden()) // kilocode_change - always show tips regardless of first-time status +packages/tui/src/feature-plugins/sidebar/context.tsx 4,14,16,20,28,50,68 7 shared-upstream patch marker User-interface behavior partial packages/tui/src/feature-plugins/sidebar/context.tsx; packages/tui/src/plugin/slots.tsx; packages/kilo-cli/src/tui-plugin/sidebar-* V1 markers support plugin sidebar sections, ordering/visibility and reactive content. V2 has a concrete sidebar.content slot with many Kilo consumers, but exact v1 ordering/collapse/context behavior is not fully retained; consumers prove the seam, not every marked invariant. L4: import { createMemo, createSignal, Show } from "solid-js" // kilocode_change / L14: // kilocode_change start +packages/tui/src/feature-plugins/sidebar/footer.tsx 56,58,73,77 4 shared-upstream patch marker Kilo getting-started and sidebar branding partial `packages/kilo-cli/src/tui-plugin/tui.tsx` sidebar footer slot plus an owned onboarding contribution V2's owned Kilo plugin preserves the branded version line through `sidebar.footer`, while the generic footer preserves the working-directory consumer. The marked free-model onboarding sentence and its getting-started card are absent; Kilo's static home footer/account status is not the same onboarding behavior. L56: {/* kilocode_change start */} / L58: {/* kilocode_change end */} +packages/tui/src/feature-plugins/system/notifications.ts 59,75,82 3 shared-upstream patch marker Completion/error attention semantics partial `packages/tui/src/feature-plugins/system/notifications.ts` with a Kilo-owned notification policy seam or narrowly configurable terminal-event policy V2 natively deduplicates forms/permissions and terminal executions, suppresses a duplicate done after failure, and uses focus-aware attention. It does not preserve the marked Kilo invariant: `session.execution.interrupted` calls the same completion path as success, subagents receive `subagent_done`, errors expose raw messages, and no manual-abort/timeout normalization matches v1's silent abort and `Model stopped responding` behavior. L59: // kilocode_change start - only completed turns need completion attention / L75: // kilocode_change end +packages/tui/src/keymap.tsx 108 1 shared-upstream patch marker Kilo-branded internal error text not needed No v2 destination; retain generic keymap diagnostics in the shared TUI The marker changes only an unreachable-in-normal-use error string from OpenCode to Kilo. V2 no longer exposes this mode-stack helper, and product behavior does not depend on branded internal exception wording; porting it would require an unnecessary shared-file fork. L108: if (!value) throw new Error("Kilo mode stack is not registered for this keymap") // kilocode_change +packages/tui/src/plugin/adapters.tsx 69,71,110,114,143,151 6 shared-upstream patch marker User-interface behavior native equivalent packages/tui/src/plugin/adapters.tsx and packages/tui/src/context/runtime.tsx V1 markers adapt plugin dialog/toast/clipboard services to TUI contexts. V2 exposes the same host UI capabilities through current runtime adapters and Kilo plugins actively consume ctx.ui.dialog/select/toast; the adapter mechanism was reorganized without losing the service boundary. L69: // kilocode_change start / L71: // kilocode_change end +packages/tui/src/plugin/slots.tsx 3,28,38 3 shared-upstream patch marker User-interface behavior already ported packages/tui/src/plugin/slots.tsx The current v2 file contains the marked stateful-fallback behavior: fallback children stay mounted while reactive slot props update. This is the same invariant and same shared seam, with an active Kilo sidebar/home consumer. L3: import { children, createSignal, mergeProps } from "solid-js" // kilocode_change / L28: // kilocode_change start - keep stateful fallback children mounted while preserving reactive slot props +packages/tui/src/routes/session/footer.tsx 6,9,10,25,26,61,68,85,99 9 shared-upstream patch marker Remote presence and connected-service footer partial Kilo-owned TUI remote-status contribution plus native sidebar/home status consumers V2 replaces the old footer layout with tabs, home/footer, sidebar, and status surfaces; MCP/location information remains available through native consumers. The marked `RemoteIndicator` live presence surface has no equivalent registration in the Kilo TUI plugin, so the mixed row is not fully ported. L6: import { useSDK } from "../../context/sdk" // kilocode_change / L9: import { useEvent } from "../../context/event" // kilocode_change +packages/tui/src/routes/session/index.tsx 6,39,66,71,76,92,99,154,157,255,302,344,346,382,433,977,981,1021,1034,1040,1046,1052,1055,1096,1102,1106,1322,1328,1425,1479,1622,1623,1642,1660,1685,1706,1723,1727,1742,1749,1753,1776,1816,1818,1845,1883,1885,1895,1897,1905,1962,1972,2053,2157,2197,2223,2224,2255,2256,2309,2331,2337,2367,2371,2628,2691,2697,2730,2757,2793,2838,2865,2917,2992,2996 75 shared-upstream patch marker Session lifecycle/history/UI partial packages/tui/src/routes/session/index.tsx; packages/kilo-cli/src/tui-plugin/{memory,remote,sidebar-*}.tsx V1's 75 markers mix approval badges, feedback, memory attachment, session-scoped processes, routed-model/status/sidebar actions and older session behavior. V2 visibly carries several Kilo markers and owned plugin consumers for memory, usage, PR, processes, account and routed models, but feedback and all marked session lifecycle subfeatures are not uniformly present. L6: onCleanup, // kilocode_change / L39: StepFinishPart, // kilocode_change +packages/tui/src/routes/session/network.tsx 1 1 shared-upstream patch marker Offline wait/reconnect prompt needs port Kilo-owned TUI session contribution backed by a v2 network-wait protocol/control seam V1 renders durable disconnect/restored state, a retry countdown, resume-now, stop-turn, and app-exit rejection wired to network reply/reject APIs. V2 has retry/reconnecting presentation but no `SessionNetworkWait`, reply/reject control contract, or equivalent session prompt; generic transport retry is not this user-controlled offline state machine. L1: // kilocode_change - new file +packages/tui/src/routes/session/permission.tsx 17,23,53,68,99,165,170,196,296,309,310,323,354,423,424,451,461,465,471,478,504 21 shared-upstream patch marker Session lifecycle/history/UI partial packages/tui/src/routes/session/permission.tsx; packages/core/src/permission.ts; packages/kilo-cli/src/skill-shell.ts V1 renders diff hunks, sends interactive:true for human replies, normalizes URL permissions, and supplies Kilo skill-shell/memory custom renderers. V2 has native permission UI and policy services, but schema audit shows interactive is absent and skill-shell human-vs-machine approval plus specialized renderers are incomplete. L17: // kilocode_change start / L23: // kilocode_change end +packages/tui/src/routes/session/question.tsx 14,17,219,221 4 shared-upstream patch marker Session lifecycle/history/UI partial packages/tui/src/routes/session/question.tsx and packages/schema/src/v1/question.ts Kilo extension V1 avoids intrusive key capture for non-blocking review questions and consumes localized/mode-aware question fields. V2 has native form/question UI, but the schema fields and non-blocking review interaction contract are not fully ported. L14: // kilocode_change start / L17: // kilocode_change end +packages/tui/src/routes/session/sidebar.tsx 91,95 2 shared-upstream patch marker Kilo version in session sidebar native equivalent `packages/kilo-cli/src/tui-plugin/tui.tsx:91-99` through `packages/tui/src/routes/session/sidebar.tsx:77-79` V2 replaces the shared fallback with an additive `sidebar.footer` slot. The Kilo-owned plugin registers `Kilo {ctx.app.version}` into that production slot, preserving the branded version line without forking the generic sidebar; other owned sidebar contributions coexist. L91: {/* kilocode_change start */} / L95: {/* kilocode_change end */} +packages/tui/src/routes/session/subagent-footer.tsx 6,7,16,20,32,105,109 7 shared-upstream patch marker Running subagent identity and navigation footer partial `packages/tui/src/component/session-tabs.tsx` and child-session navigation in `packages/tui/src/routes/session/index.tsx`; owned agent-colored running affordance if still required V2 natively tracks child sessions, running/unread status, parent/child navigation, and agent identity across tabs and session routes. The exact marked footer spinner derived from unfinished assistant messages and colored by the selected agent is absent, so generic tab activity is only a partial presentation equivalent. L6: import { Spinner } from "@tui/component/spinner" // kilocode_change / L7: import { useLocal } from "@tui/context/local" // kilocode_change +packages/tui/src/routes/session/suggest.tsx 1 1 shared-upstream patch marker Suggestion route hook native equivalent Owned Kilo CLI remote suggestion adapter and Kilo VS Code UI The shared TUI forwarding hook is removed because suggestion presentation is hosted by Kilo-owned remote and VS Code surfaces; behavior remains without editing the upstream TUI route. L1: // kilocode_change - new file +packages/tui/src/routes/session/terminal.tsx 1 1 shared-upstream patch marker Embedded interactive tool terminal partial `packages/tui/src/component/terminal-pane.tsx` and session terminal contexts; an owned tool-terminal bridge remains required V2 has a richer native PTY pane with selection, resize, input, tabs, persistence, and session commands. The v1 row specifically consumes tool-emitted `InteractiveTerminalSnapshot` in the transcript; no v2 consumer bridges tool-created snapshots into the pane, and Kilo runtime currently disables the generic session terminal. L1: // kilocode_change - new file +packages/tui/src/runtime.tsx 8 1 shared-upstream patch marker Portable home-path abbreviation native equivalent `packages/tui/src/util/path-format.ts:3-10`, re-exported by `packages/tui/src/runtime.tsx` V2 preserves the invariant and strengthens it: it selects `path.win32` or `path.posix` from the supplied path instead of the host platform, then joins relative segments with `/`. Existing consumers import the runtime re-export or the owned formatter directly. L8: return "~/" + relative.replaceAll(path.sep, "/") // kilocode_change - keep displayed paths portable +packages/tui/src/theme/index.ts 5,17,18,147,148,169 6 shared-upstream patch marker Bundled Kilo and accessibility theme catalogue needs port Kilo-owned theme documents registered through the v2 plugin theme API, or explicit additions to `packages/tui/src/theme/v1.ts` when they must be generic built-ins V1 adds `kilo`, `kilo-v1`, and `colorblind` assets to the built-in theme map. V2's `DEFAULT_THEMES` contains none of those names or assets; the new `packages/theme` logo semantic token preserves branded logo coloring but is not a substitute for the three complete selectable themes. L5: import colorblind from "./assets/colorblind.json" with { type: "json" } // kilocode_change / L17: import kilo from "./assets/kilo.json" with { type: "json" } // kilocode_change +packages/tui/src/ui/dialog-alert.tsx 5,40,46 3 shared-upstream patch marker Multiline alert rendering needs port `packages/tui/src/ui/dialog-alert.tsx` or a Kilo-owned alert renderer supplied through the plugin API V1 normalizes escaped `\\n` and literal newlines, then renders each line in its own row. V2 passes the message to one text node unchanged; no alert test or helper proves escaped-newline normalization, so the marked behavior is absent. L5: import { For } from "solid-js" // kilocode_change / L40: {/* kilocode_change start - render escaped and literal newlines on separate rows */} +packages/tui/src/ui/dialog-export-options.tsx 129,132,147,150,165,168,183,186 8 shared-upstream patch marker User-interface behavior needs port packages/tui/src/ui/dialog-export-options.tsx or a Kilo-owned export dialog plugin V1 markers add Kilo export actions/options and guarded per-action completion. Current v2 has no matching marked branches or owned Kilo export-options consumer; generic session export support does not establish parity for those choices. L129: // kilocode_change start / L132: } /* kilocode_change end */ +packages/tui/src/ui/dialog-select.tsx 39,49,69,459,464 5 shared-upstream patch marker User-interface behavior needs port packages/tui/src/ui/dialog-select.tsx list-level action API V1 explicitly permits DialogSelect actions when no option is selected, enabling empty-list scope toggles such as the session-list Ctrl+A action. Current v2 select lacks this action surface, and the missing session-scope behavior is its concrete consumer. L39: actions?: DialogSelectAction[] // kilocode_change - supports actions without a selected option / L49: // kilocode_change start - support list-level actions when no option is selected +packages/tui/src/ui/dialog.tsx 43 1 shared-upstream patch marker Vertically centered dialogs partial `packages/tui/src/ui/dialog.tsx` through its current `centered` state/API V2 preserves centering as a supported dialog and plugin option, and consumers such as the diff viewer request it. The default is now quarter-height rather than centered, so the v1 invariant for every dialog is not global; this is a caller-selectable equivalent rather than an absent capability. L43: justifyContent="center" // kilocode_change +packages/tui/src/ui/toast.tsx 65,79 2 shared-upstream patch marker User-interface behavior partial packages/tui/src/ui/toast.tsx and packages/tui/src/plugin/adapters.tsx V1 markers adjust toast lifecycle/rendering for Kilo flows. V2 has a native toast service heavily used by Kilo plugins, but the exact marked timing/cleanup behavior is not evidenced by current source or a paired regression. L65: // kilocode_change start / L79: // kilocode_change end +packages/tui/src/util/error.ts 27,28,73 3 shared-upstream patch marker Kilo-branded model and MCP error guidance needs port Kilo-owned CLI error presentation hook or app-aware command/config labels in `packages/tui/src/util/error.ts` The v2 formatter retains structured model suggestions but emits `opencode models` and `opencode.json`; its MCP failure message also claims authentication is unsupported even though v2 has integration authentication. No Kilo runtime override rewrites these strings, so the marked Kilo commands and capability-accurate guidance are absent. L27: "Try: `kilo models` to list available models", // kilocode_change / L28: "Or check your config (kilo.json) provider/model names", // kilocode_change +packages/tui/src/util/markdown.ts 1 1 shared-upstream patch marker Fixed-width Markdown table normalization needs port Kilo-owned Markdown preprocessing/render hook before `packages/tui/src/routes/session/index.tsx` transcript rendering V1 parses pipe tables, preserves alignment, measures wide Unicode, pads columns, and preprocesses assistant text before rendering, with a dedicated test suite. V2 renders Markdown directly and has no `formatMarkdownTables` implementation or call site; generic rendering is not evidence of this alignment invariant. L1: // kilocode_change - new file +packages/tui/src/util/presentation.ts 1 1 shared-upstream patch marker Kilo exit banner and resume command native equivalent `packages/kilo-cli/src/tui-plugin/epilogue.ts` injected by `packages/kilo-cli/src/tui.ts` V2 keeps generic `opencode2` presentation in the shared TUI and injects Kilo's owned `sessionEpilogue` through the app contract. The owned renderer emits responsive Kilo artwork, session title, and `kilo2 -s `, so removal of the shared-file marker reflects relocation rather than behavior loss. L1: // kilocode_change - Kilo exit banner instead of the opencode wordmark +packages/tui/test/app-lifecycle.test.tsx 122 1 shared-upstream patch marker Kilo exit epilogue regression already ported `packages/kilo-cli/test/epilogue.test.ts` plus generic lifecycle coverage in `packages/tui/test/app-lifecycle.test.tsx:261` The shared v2 lifecycle test verifies cleanup, renamed session title, and generic `opencode2 -s` output. Kilo's owned epilogue test separately asserts artwork, title, `kilo2 -s ses_123`, absence of OpenCode branding, and parsing of the displayed resume command; this is stronger architectural separation than changing the generic assertion. L122: expect(stdout).toContain("kilo -s dummy") // kilocode_change +packages/tui/test/cli/cmd/tui/notifications.test.ts 139,298 2 shared-upstream patch marker Completion/error attention regression coverage needs port `packages/tui/test/cli/cmd/tui/notifications.test.ts` or Kilo-owned plugin tests accompanying a notification-policy override Current v2 tests intentionally expect completion notifications for terminal lifecycle events and `subagent_done`, and cover failure deduplication. They do not test v1's completed-only rule, interrupted silence, suppressed subagent completion, silent manual abort, or friendly SSE-timeout message; those missing invariants require explicit tests with any policy port. L139: // kilocode_change start / L298: // kilocode_change end +packages/tui/test/cli/cmd/tui/provider-options.test.ts 26 1 shared-upstream patch marker Kilo provider ordering regression needs port Kilo-owned integration/model-picker ordering policy and focused test around the v2 dialog consumer V1 pins Anthropic/OpenAI ahead of alphabetically sorted providers and the custom-provider sentinel. V2's integration dialog no longer exposes the old `providerOptions` helper, and no source-backed priority table or test preserves this exact Kilo ordering; unrelated model sorting and account-popularity UI are not equivalent. L26: ).toEqual(["anthropic", "openai", "aws", "mistral", "custom-z", "__opencode_custom_provider__"]) // kilocode_change - preserve Kilo provider priority +packages/tui/test/cli/cmd/tui/sync-fixture.tsx 9,10,52,54,58,64,68,70,77 9 shared-upstream patch marker Legacy monolithic SyncProvider fixture wiring not needed No v2 destination; current tests compose `ClientProvider`, `ProjectProvider`, and `DataProvider` through v2 fixture contexts The marked Toast/Exit providers and explicit `project.sync()` were dependencies of v1's monolithic `SyncProvider` bootstrap and project-filtered compatibility stream. V2's data projection and client lifecycle own those boundaries directly; restoring the obsolete fixture nesting would test removed architecture rather than product behavior. L9: import { ToastProvider } from "../../../../src/ui/toast" // kilocode_change / L10: import { ExitProvider } from "../../../../src/context/exit" // kilocode_change +packages/tui/test/cli/cmd/tui/sync.test.tsx 49 1 shared-upstream patch marker Workspace-isolated VCS projection regression native equivalent `packages/tui/test/cli/tui/data.test.tsx:104-151` and location-keyed handling in `packages/client/src/solid/data.ts:1129-1160` V2's data test bootstraps VCS for the active location and verifies a branch event updates that location's keyed projection. The production handler indexes by `event.location`, while `data.location.vcs.info()` reads the selected/default location, preventing another workspace key from replacing active VCS state; the old fixture-specific rebootstrap comment is no longer required. L49: ) // kilocode_change - workspace re-bootstrap retains the selected test workspace +packages/tui/test/cli/tui/data.test.tsx 5,6,28,38,196,202,463,513,568,1094 10 shared-upstream patch marker User-interface behavior partial packages/tui/test/data.test.tsx and packages/tui/test/context/event.test.ts V1 regressions cover project-filter initialization, promoted-prompt compatibility and hydration/live-event races. V2 tests location-keyed data and event projection, but earlier source assessment retained project-isolation and hydration/legacy gaps, so only part of this suite has a native target. L5: import { createSignal, onMount, Show, type ParentProps } from "solid-js" // kilocode_change / L6: import { ProjectProvider, useProject } from "../../../src/context/project" // kilocode_change +packages/tui/test/cli/tui/inline-tool-wrap-snapshot.test.tsx 228,232 2 shared-upstream patch marker Tool behavior/UI partial packages/tui test snapshots plus packages/kilo-cli TUI plugin renderer tests V1 snapshot keeps dedicated Kilo tool renderers when inline output wraps. V2 has Kilo plugin renderers and native wrapping, but no equivalent snapshot proving their combined layout; implementation adjacency is not regression parity. L228: // kilocode_change start - Kilo tools keep their dedicated renderers / L232: // kilocode_change end +packages/tui/test/cli/tui/use-event.test.tsx 119,134 2 shared-upstream patch marker Cross-project event isolation regression needs port `packages/tui/test/cli/tui/use-event.test.tsx` alongside the v2 event filtering seam The v2 suite covers metadata, global/current events, durable logging, reconnect, backoff, and cleanup, but removed the marked assertion that a foreign-project event is ignored while a current-project event is delivered. Those adjacent tests do not prove project isolation, so the focused regression remains required. L119: // kilocode_change start - the compatibility stream contains events from every loaded project / L134: // kilocode_change end +packages/tui/test/config.test.tsx 52 1 shared-upstream patch marker Kilo attention default regression needs port Kilo-owned TUI config/attention test accompanying a branding default override Current v2 config tests assert `opencode.default`, while Kilo's runtime merely disables attention and does not supply `kilo.default`. The old default-pack assertion is therefore not ported; it should accompany any Kilo attention branding policy rather than alter the generic host-neutral test. L52: sound_pack: "kilo.default", // kilocode_change +packages/tui/test/fixture/tui-sdk.ts 90,95 2 shared-upstream patch marker User-interface behavior partial packages/tui/test/fixture/tui-runtime.ts and packages/kilo-cli/test/fixture.ts V1 fixture adds Kilo bootstrap endpoints used by TUI tests. V2 test fixtures provide native runtime/plugin clients, but not every legacy Kilo endpoint; the fixture is obsolete in shape while feature-specific coverage remains split. L90: // kilocode_change start - Kilo bootstrap endpoints / L95: // kilocode_change end +packages/tui/test/use-connected.test.ts 1 1 shared-upstream patch marker Authenticated-provider connectivity regression already ported `packages/tui/test/util/connected-provider.test.ts` The renamed v2 test proves empty integration lists and integrations without connections are disconnected, while credential and environment connections are connected. That directly tests the new typed boundary and prevents anonymous catalog entries from counting as authentication without retaining a Kilo marker. L1: // kilocode_change - new file +packages/tui/test/util/error.test.ts 50,64 2 shared-upstream patch marker Kilo error-guidance regression needs port Kilo-owned error-presentation tests accompanying the model/MCP guidance port V2's error tests cover generic formatting and opaque throwables but do not call `cliErrorMessage` for model-not-found or MCP failures. The old assertions for `kilo models`, `kilo.json`, absence of OpenCode branding, and capability-accurate MCP text are missing and should be restored with the presentation seam. L50: // kilocode_change start - preserve Kilo-branded CLI guidance / L64: // kilocode_change end +packages/tui/test/util/presentation.test.ts 7 1 shared-upstream patch marker User-interface behavior needs port packages/tui/test/util/presentation.test.ts with Kilo CLI epilogue override V1 asserts the CLI epilogue uses `kilo -s `. Current v2 presentation is upstream-oriented and no owned epilogue override/test proves this exact Kilo resume command; nearby branding does not preserve it. L7: expect(epilogue).toContain("kilo -s ses_123") // kilocode_change +packages/tui/test/util/transcript.test.ts 70,83,88,99,265,280,292,310,321,347,399,417 12 shared-upstream patch marker User-interface behavior partial packages/tui/test/util/transcript.test.ts and Kilo agent/model presentation policy V1 tests Code agent naming, friendly model names, duration display and suppression rules across transcript variants. V2 retains native transcript formatting and host model presentation, but the build-to-Code rename is explicitly missing in the agent audit and these exact Kilo expectations are not all covered. L70: agent: "code", // kilocode_change / L83: expect(result).toBe("## Assistant (Code · claude-sonnet-4-20250514 · 5.4s)\n\n") // kilocode_change +packages/tui/tsconfig.json 9,15 2 shared-upstream patch marker Legacy cross-package Kilo source resolution not needed No v2 destination; Kilo integrations compile from their owned packages and public package exports The v1 aliases reached back into the removed `packages/opencode` tree so retained TUI Kilo imports and ambient declarations compiled. V2's Kilo logo/sidebar integrations live in `packages/kilo-cli` and consume public TUI/plugin/theme APIs; the TUI tsconfig has no such cross-tree imports, so restoring these aliases would violate the v2 package boundary rather than preserve behavior. L9: // kilocode_change - resolve Kilo integrations retained during upstream's standalone TUI extraction / L15: // kilocode_change - include ambient declarations used by retained Kilo imports from opencode +packages/ui/src/components/basic-tool.tsx 6,36,37,40,95,137,180,181,184,187,193,202,206,265,276,317,323 17 shared-upstream patch marker Tool behavior/UI already ported `packages/kilo-ide-ui/src/components/basic-tool.tsx` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L6: import { Icon, type IconProps } from "./icon" // kilocode_change: added Icon / L36: retainDetails?: boolean // kilocode_change +packages/ui/src/components/button.css 1 1 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/components/button.css` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L1: /* kilocode_change - reverted to main */ +packages/ui/src/components/card.css 10,16,88,121 4 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/components/card.css` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L10: /* kilocode_change start — preserve Kilo's bordered card surface */ / L16: /* kilocode_change end */ +packages/ui/src/components/dock-surface.tsx 28 1 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/components/dock-surface.tsx` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L28: data-component="prompt-input-form" // kilocode_change +packages/ui/src/components/icon.tsx 89,113 2 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/components/icon.tsx` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L89: lock: ``, // +packages/ui/src/components/list.tsx 184,187 2 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/components/list.tsx` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L184: // kilocode_change start - fall back to first result when no item is active (noInitialSelection) / L187: // kilocode_change end +packages/ui/src/components/markdown-shiki.worker.ts 23,32 2 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/components/markdown-shiki.worker.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L23: let theme = "Kilo" // kilocode_change - use the Kilo theme supplied during worker initialization / L32: theme = event.data.theme.name // kilocode_change +packages/ui/src/components/markdown-worker.ts 2,120 2 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/components/markdown-worker.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L2: import { KiloTheme } from "../context/marked" // kilocode_change / L120: worker.postMessage({ type: "init", theme: KiloTheme } satisfies MarkdownWorkerRequest) // kilocode_change +packages/ui/src/components/markdown.css 69,70,80,106,110,115,117,235,248,266 10 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/components/markdown.css` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L69: margin-inline-start: 0; /* kilocode_change */ / L70: padding-inline-start: 32px; /* kilocode_change */ +packages/ui/src/components/markdown.tsx 2,29,32,46,380,389,398,406,452,455,457,459,470,473,487,498,519,522,543,545,560,563,567,570,579,609,629,664,666,670,673,674,693,713,723,732,768 37 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/components/markdown.tsx` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L2: import { deferredHighlight, fnv1a } from "../context/marked" // kilocode_change / L29: // kilocode_change start: Mermaid rendering and morphdom guards for highlighted blocks +packages/ui/src/components/message-part.css 24,46,185,252,485,495,513,523,1290,1300,1345,1354 12 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/components/message-part.css` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L24: gap: 8px; /* kilocode_change */ / L46: cursor: pointer; /* kilocode_change */ +packages/ui/src/components/message-part.tsx 163,859,1065,1155,1181,1184,1190,1555,1567,1570,1575,1593 12 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/components/message-part.tsx` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L163: queued?: boolean // kilocode_change / L859: queued={props.queued} // kilocode_change +packages/ui/src/components/popover.tsx 23,38,57,141,163 5 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/components/popover.tsx` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L23: contentLabel?: string // kilocode_change / L38: "contentLabel", // kilocode_change +packages/ui/src/components/resize-handle.tsx 35,37,40,60 4 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/components/resize-handle.tsx` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L35: // kilocode_change start - set resize cursor on body during drag / L37: // kilocode_change end +packages/ui/src/components/select.css 172 1 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/components/select.css` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L172: max-width: min(23rem, var(--kb-popper-content-available-width)); /* kilocode_change */ +packages/ui/src/components/select.test.ts 1 1 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/components/select.test.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L1: // kilocode_change - new file +packages/ui/src/components/select.tsx 92,93,94 3 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/components/select.tsx` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L92: overlap={local.triggerVariant === "settings"} // kilocode_change / L93: fitViewport={local.triggerVariant === "settings"} // kilocode_change +packages/ui/src/components/session-turn.tsx 162,209,234,244,386,415,417 7 shared-upstream patch marker Session lifecycle/history/UI already ported `packages/kilo-ide-ui/src/components/session-turn.tsx` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L162: queued?: boolean // kilocode_change / L209: if (typeof props.active === "boolean" && typeof props.queued === "boolean") return // kilocode_change +packages/ui/src/components/switch.tsx 8,12,14,17 4 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/components/switch.tsx` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L8: inputProps?: ComponentProps // kilocode_change / L12: // kilocode_change start +packages/ui/src/components/text-shimmer.css 24,59 2 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/components/text-shimmer.css` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L24: white-space: pre; /* kilocode_change - preserve inactive fallback whitespace */ / L59: /* kilocode_change — gate animation on data-active directly (was data-run +packages/ui/src/components/text-shimmer.tsx 1,51 2 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/components/text-shimmer.tsx` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L1: // kilocode_change start — the previous implementation used a createEffect that / L51: // kilocode_change end +packages/ui/src/components/todo-panel-motion.stories.tsx 2,13,16,142,184,191,254,280 8 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/components/todo-panel-motion.stories.tsx` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L2: import { createMemo, onCleanup } from "solid-js" // kilocode_change / L13: // kilocode_change start +packages/ui/src/components/tool-error-card.css 3,4 2 shared-upstream patch marker Tool behavior/UI already ported `packages/kilo-ide-ui/src/components/tool-error-card.css` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L3: background-color: transparent; /* kilocode_change — override restored card background */ / L4: border: none; /* kilocode_change — override restored card border */ +packages/ui/src/context/data.tsx 44,49,50,51,58,60,61,70,71,72,73,74,85,86,87,88,89 17 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/context/data.tsx` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L44: // kilocode_change start / L49: before?: string // kilocode_change - optional, kilo uses `patch` +packages/ui/src/context/index.ts 2 1 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/context/index.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L2: export * from "./data" // kilocode_change - Kilo typechecks packages/ui, whose components import useData from here +packages/ui/src/context/marked.tsx 2,9,11,13,15,16,18,25,27,401,403,406,408,413,422,427,433,499,501,541,542,652,654,656,667,698,700,710,712,760,761,766,786 33 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/context/marked.tsx` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L2: // kilocode_change: marked-shiki highlighted code blocks synchronously during / L9: // kilocode_change start: import types for double-dollar math extension +packages/ui/src/file-path.test.ts 1 1 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/file-path.test.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L1: // kilocode_change - new file +packages/ui/src/file-path.ts 1 1 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/file-path.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L1: // kilocode_change - new file +packages/ui/src/i18n/ar.ts 108,119,124,160,176,178 6 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/i18n/ar.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L108: // kilocode_change start / L119: // kilocode_change end +packages/ui/src/i18n/br.ts 108,119,124,160,176,178 6 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/i18n/br.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L108: // kilocode_change start / L119: // kilocode_change end +packages/ui/src/i18n/bs.ts 112,123,128,164,180,182 6 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/i18n/bs.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L112: // kilocode_change start / L123: // kilocode_change end +packages/ui/src/i18n/da.ts 107,118,123,159,175,177 6 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/i18n/da.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L107: // kilocode_change start / L118: // kilocode_change end +packages/ui/src/i18n/de.ts 113,124,129,165,181,183 6 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/i18n/de.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L113: // kilocode_change start / L124: // kilocode_change end +packages/ui/src/i18n/en.ts 114,125,130,172,189,191 6 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/i18n/en.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L114: // kilocode_change start / L125: // kilocode_change end +packages/ui/src/i18n/es.ts 108,119,124,160,176,178 6 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/i18n/es.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L108: // kilocode_change start / L119: // kilocode_change end +packages/ui/src/i18n/fr.ts 108,119,124,160,176,178 6 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/i18n/fr.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L108: // kilocode_change start / L119: // kilocode_change end +packages/ui/src/i18n/it.ts 1,134,173,191,193 5 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/i18n/it.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L1: // kilocode_change - new file / L134: "ui.tool.swePruned": "SWE-Pruner · {{kept}} di {{total}} righe mantenute", // kilocode_change +packages/ui/src/i18n/ja.ts 107,118,123,159,175,177 6 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/i18n/ja.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L107: // kilocode_change start / L118: // kilocode_change end +packages/ui/src/i18n/ko.ts 108,119,124,160,176,178 6 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/i18n/ko.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L108: // kilocode_change start / L119: // kilocode_change end +packages/ui/src/i18n/nl.ts 56,65,111,122,137,176,194,196 8 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/i18n/nl.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L56: // kilocode_change start - complete upstream usage-exceeded translations / L65: // kilocode_change end +packages/ui/src/i18n/no.ts 111,122,127,163,179,181 6 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/i18n/no.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L111: // kilocode_change start / L122: // kilocode_change end +packages/ui/src/i18n/pl.ts 107,118,123,159,175,177 6 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/i18n/pl.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L107: // kilocode_change start / L118: // kilocode_change end +packages/ui/src/i18n/ru.ts 107,118,123,159,175,177 6 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/i18n/ru.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L107: // kilocode_change start / L118: // kilocode_change end +packages/ui/src/i18n/th.ts 109,120,125,161,177,179 6 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/i18n/th.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L109: // kilocode_change start / L120: // kilocode_change end +packages/ui/src/i18n/tr.ts 114,125,130,166,182,184 6 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/i18n/tr.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L114: // kilocode_change start / L125: // kilocode_change end +packages/ui/src/i18n/uk.ts 15,18,29,31,34,52,53,56,60,69,72,75,89,92,94,96,99,102,126,137,138,142,147,149,185,199,202,204 28 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/i18n/uk.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L15: // kilocode_change start / L18: // kilocode_change end +packages/ui/src/i18n/zh.ts 111,122,127,163,179,181 6 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/i18n/zh.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L111: // kilocode_change start / L122: // kilocode_change end +packages/ui/src/i18n/zht.ts 111,122,127,163,179,181 6 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/i18n/zht.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L111: // kilocode_change start / L122: // kilocode_change end +packages/ui/src/pierre/index.ts 4,156 2 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/pierre/index.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L4: import { KILO_DIFF_THEME } from "./kilo-diff-theme" // kilocode_change / L156: theme: KILO_DIFF_THEME, // kilocode_change +packages/ui/src/pierre/kilo-diff-theme.ts 1 1 Kilo-owned inherited marker User-interface behavior already ported `packages/kilo-ide-ui/src/pierre/kilo-diff-theme.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L1: // kilocode_change - new file +packages/ui/src/pierre/worker.ts 3,5,11,31 4 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/pierre/worker.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L3: import { ensureKiloDiffTheme, KILO_DIFF_THEME } from "./kilo-diff-theme" // kilocode_change / L5: // kilocode_change start: register the "Kilo" theme as a precondition of creating +packages/ui/src/styles/index.css 37 1 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/styles/index.css` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L37: @import "../kilocode/markdown-mermaid.css" layer(components); /* kilocode_change */ +packages/ui/src/styles/theme.css 135,395 2 shared-upstream patch marker User-interface behavior already ported `packages/kilo-ide-ui/src/styles/theme.css` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L135: --surface-critical-base: #feefeb; /* kilocode_change */ / L395: --surface-critical-base: #42120b; /* kilocode_change */ +packages/ui/vite.config.ts 39,43 2 shared-upstream patch marker Configuration/settings already ported `packages/kilo-ide-ui/vite.config.ts` (Kilo-owned; marker removed by design) Full changed-line inspection found only marker/comment cleanup, v2 import/type retargeting, or preserved JSX whitespace; marked behavior retained and consumed by original webview build. L39: if (!process.env.KILO_FETCH_PROVIDER_ICONS) return // kilocode_change / L43: if (!process.env.KILO_FETCH_PROVIDER_ICONS) return // kilocode_change +script/changelog-github.cjs 1 1 shared-upstream patch marker CI/repository tooling deferred script/changelog-github.cjs with the Kilo release/changelog lane (this repo does not use Changesets per AGENTS.md) V1 Kilo-owned changelog generator wraps @changesets/changelog-github and strips 'Thanks @user!' for team members. V2 has no changesets (AGENTS.md: 'This repository does not use Changesets') and generates release notes through script/changelog.ts + raw-changelog.ts instead, so the Changesets wrapper has no consumer unless Kilo re-adopts Changesets for the v2 line. L1: // kilocode_change - new file +script/changelog.ts 51 1 shared-upstream patch marker CI/repository tooling needs port script/changelog.ts:51 (`["opencode", "run"]` → Kilo CLI) with the Kilo release lane V1 runs the changelog command through the `kilo` CLI (L51). V2 changelog.ts invokes `opencode run`; in a Kilo v2 release the notes generator would need the kilo2 binary/command name. L51: const cmd = ["kilo", "run"] // kilocode_change - opencode -> kilo +script/check-architecture.ts 2 1 shared-upstream patch marker CI/repository tooling deferred script/check-architecture.ts with the Kilo CI lane; the v2 dependency-direction rules it would encode are documented in AGENTS.md ('Keep runtime dependencies directed from Schema to Core and Protocol…') and partly tested by packages/client/test/import-boundaries.test.ts V1 Kilo-owned CI script enforces domain architecture boundaries and ratchets. V2 has no such script; the dependency-direction invariant is stated in AGENTS.md and enforced for the client package by its import-boundaries test only. Repository-wide ratchets would need a v2-specific rule set. L2: // kilocode_change - new file +script/check-forbidden-strings.ts 2 1 shared-upstream patch marker CI/repository tooling deferred script/check-forbidden-strings.ts with the Kilo CI lane V1 Kilo-owned guard fails CI when forbidden strings (competitor branding, leaked identifiers) appear in Kilo-facing sources. V2 has no equivalent; the fork intentionally keeps upstream branding in upstream files (kilocode/v2-fork-conventions.md), so the rule set would need to be scoped to Kilo-owned paths. L2: // kilocode_change - new file +script/check-kilo-generated-artifacts.ts 2 1 authored tooling/check CI/repository tooling deferred script/check-kilo-generated-artifacts.ts with the Kilo CI lane; upstream `check:generated` covers client/www only V1 Kilo-owned script verifies committed Kilo generated artifacts. V2 relies on manual `bun run check:generated` in packages/client and packages/www (pinned-v2-baseline.md:97-98); Kilo-owned generated outputs (kilo-vscode/kilo-ui) have no check. L2: // kilocode_change - new file +script/check-model-tool-network.ts 2,109 2 shared-upstream patch marker Provider/model routing or UI deferred script/check-model-tool-network.ts with the Kilo CI lane, rewritten against the v2 tool registry (packages/core/src/tool/runtime.ts) and sandbox (packages/kilo-cli/src/sandbox*.ts) V1 Kilo-owned CI-only architecture test asserts model tools route network access through the sandbox network layer and that the registry wires ToolNetwork.httpLayer (L107-111 marker tracks the v1.17.13 LayerNode change). V2 has a different tool registry and a launch-scoped sandbox (MCP spawn/PTY only), so the assertions would have to be rewritten; the invariant belongs to the sandbox slice already marked partial. L2: // kilocode_change - new file / L109: // kilocode_change - v1.17.13 moved registry wiring from Layer.provide onto the LayerNode graph +script/check-opencode-annotations.ts 5,13,14,16,137,142,192,203,205,206,216,218,228,234,270,272,285,301,304,306,309,310,312,315,316,318,321 27 shared-upstream patch marker CI/repository tooling deferred script/check-opencode-annotations.ts (marker coverage checker) with the Kilo CI lane; scopes must be updated for v2 (no packages/opencode; Kilo-owned paths per kilocode/v2-fork-conventions.md are exempt) V1 Kilo-owned checker verifies every Kilo change in shared upstream files carries a `kilocode_change` marker (27 marker-string occurrences are its own rules/exempt list, L23-61). V2 documents the same marker convention (v2-fork-conventions.md '`kilocode_change` markers') but has no automated enforcement. Deferred with CI; scope/exempt lists need a v2 rewrite. L5: * is annotated with a kilocode_change marker. / L13: * - contains a kilocode_change marker comment (inline annotation) +script/check-opencode-promise-facades.ts 2 1 shared-upstream patch marker CI/repository tooling deferred script/check-opencode-promise-facades.ts with the Kilo CI lane (v2 sdk exposes promise facades in packages/sdk/src/promise.ts) V1 Kilo-owned allowlist check for Effect→Promise facades. V2 has no such check; whether the facade allowlist is still meaningful depends on the v2 SDK design (promise.ts is upstream's facade layer). L2: // kilocode_change - new file +script/check-workflows.ts 2 1 shared-upstream patch marker CI/repository tooling deferred script/check-workflows.ts with the Kilo CI lane V1 Kilo-owned workflow allowlist check (which GitHub workflows may exist/run). V2 has none; relevant only when the Kilo v2 repository curates its workflow set (see .github rows). L2: // kilocode_change - new file +script/extract-source-links.ts 47,52,89,96,104,110,114,127,139 9 shared-upstream patch marker CI/repository tooling deferred script/extract-source-links.ts with the docs/link-check lane (.github/workflows/source-check-links.yml row) V1 markers add Kilo API/Azure exclusion patterns, skip the forbidden-strings data file, the unregistered v1 account command and non-English i18n files, and normalize source paths (L47,52,89,96,104,110-139). V2 has no link extractor; deferred with the docs lane. L47: /^https?:\/\/supermassive-black-hole\.kiloapps\.io\/v1\/session-export\//, // kilocode_change / L52: /^https?:\/\/[^/]+\.openai\.azure\.com\/openai/, // kilocode_change +script/jetbrains-release-pr.ts 2 1 shared-upstream patch marker CI/repository tooling deferred script/jetbrains-release-pr.ts with the JetBrains lane V1 Kilo-owned script opens JetBrains release PRs. No v2 JetBrains package. L2: // kilocode_change - new file +script/jetbrains-release-validate.ts 2 1 shared-upstream patch marker CI/repository tooling deferred script/jetbrains-release-validate.ts with the JetBrains lane V1 Kilo-owned JetBrains release validation. No v2 JetBrains package. L2: // kilocode_change - new file +script/publish-start.ts 40,41,92,95 4 shared-upstream patch marker CI/repository tooling needs port script/publish.ts / script/release (v2 publish lane) — KILO_SKIP_NOTES handling and draft-flag control for Kilo stable publishes V1 markers honor `KILO_SKIP_NOTES=1` to skip changelog generation and drop the draft flag for the stable publish workflow that lacks a publish-complete step (L40-43,92-95). V2 has no publish-start.ts (publish orchestration lives in script/publish.ts and script/release with upstream semantics). Kilo's stable-release workflow relied on this switch; it must be re-expressed in the Kilo v2 publish lane. L40: const skipNotes = process.env["KILO_SKIP_NOTES"] === "1" // kilocode_change / L41: if (skipNotes) console.log("changelog skipped: KILO_SKIP_NOTES=1") // kilocode_change +script/publish.ts 9,12,14,41,50,56,74,101,103,111,117,125,127,130,132,137,142,144,146,174 20 shared-upstream patch marker CI/repository tooling needs port script/publish.ts (v2 upstream publish orchestrator: versions all packages, pushes to upstream branches, publishes @opencode-ai packages, desktop finalize) — a Kilo v2 publish must replace it with Kilo package set (kilo-cli/kilo-vscode/…), rebase+retry push to the Kilo default branch, channel-aware release notes, and skip upstream preview/ui/desktop steps V1 markers (20): preserve the JetBrains CLI pin across version bumps and open a follow-up pin-bump PR (L9-12,50-56,142-143), consume changesets on the publish runner and fix changelog headings (L14-41), commit/tag/push with rebase+retry against `main` (L74-101), publish channel-aware GitHub release notes via kilocode/release-notes (L103-111), skip upstream preview CLI and @opencode-ai/ui publishing (L117,125), publish the Kilo VS Code extension (L127-130) and skip desktop finalize (L132-137). V2 script/publish.ts is upstream's orchestrator (publishes opencode/desktop/ui packages); none of the Kilo release invariants exist and running it from a Kilo fork would attempt upstream publishes. The changesets step is not applicable (v2 line has no Changesets) and JetBrains steps follow that lane; the remaining Kilo publish behavior belongs to the needs-port release lane together with .github/workflows/publish.yml and packages/script/src/index.ts. L9: // kilocode_change start - keep JetBrains CLI pin reviewable outside CLI release commits / L12: // kilocode_change end +script/raw-changelog.ts 34,75,121 3 shared-upstream patch marker CI/repository tooling needs port script/raw-changelog.ts:34,77,123 (section order, priority, scanned paths) for the Kilo release lane V1 markers set the changelog sections to Core/TUI/SDK/Extensions, prioritize core/tui/sdk/plugin/extensions/vscode/github, and scan packages/opencode, sdk, plugin, extensions, github (L34,75,121). V2 raw-changelog.ts adds Desktop/app/tauri sections and scans upstream paths (packages/desktop, packages/app, sdks/vscode). A Kilo v2 changelog must scan Kilo-owned packages (kilo-cli, kilo-vscode, kilo-ui, kilo-gateway, …) and drop upstream desktop sections; the current script would produce an OpenCode-shaped changelog. L34: const order = ["Core", "TUI", "SDK", "Extensions"] as const // kilocode_change / L75: const priority = ["core", "tui", "sdk", "plugin", "extensions/vscode", "github"] // kilocode_change +script/setup-git.ts 2 1 shared-upstream patch marker CI/repository tooling deferred script/setup-git.ts with the Kilo CI lane (bot git identity; see .github/workflows/test.yml row) V1 Kilo-owned helper configures the kilo-maintainer bot git identity for CI. V2 workflows configure identity inline or via setup-git-committer; no Kilo identity exists in v2 CI. L2: // kilocode_change - new file +script/sync-versions.ts 2 1 shared-upstream patch marker CI/repository tooling deferred script/sync-versions.ts with the Kilo release lane V1 Kilo-owned script syncs every Kilo version string across the monorepo (CLI, VS Code, JetBrains, docs). V2 has no version-sync script; upstream versions packages through packages/script + publish.yml. Needed once Kilo v2 ships multiple versioned Kilo packages together. L2: // kilocode_change - new file +script/upstream/README.md 38,105,192,208,261,305,370 7 documentation/reference Documentation/agent guidance deferred kilocode/v2-fork-conventions.md ('Upstream snapshot tracking', 'Branches') documents the v2 manual process; tooling for the v2 line is not built V1 Kilo-owned upstream-merge guide (branding transforms, reset candidates, marker rebuild, conflict recommendations; 7 marker-string mentions). V2 replaces the process with pinned-SHA forward merges and Kilo-owned paths, documented in v2-fork-conventions.md, and has no script/upstream tooling or kilocode/script merge helpers (kilocode/script contains only v2-baseline.ts). Tooling for the v2 process is deferred. L38: | `fix-kilocode-markers.ts` | Rebuild `kilocode_change` markers for one file against the last merged upstream | / L105: - Remaining conflicts are files with actual code differences (kilocode_change markers) +script/upstream/find-reset-candidates.ts 10 1 shared-upstream patch marker CI/repository tooling deferred kilocode/script (marker/drift classifier for the v2 merge process) V1 Kilo-owned classifier finds files whose only local drift is markers/cosmetics against transformed upstream. V2 has no merge tooling; a v2 variant would compare against pinned `upstream/v2` without branding transforms. Useful but unbuilt. L10: * - markers-only : only diff is kilocode_change markers wrapping +script/upstream/fix-kilocode-markers.ts 3,25,29,65,88 5 Kilo-owned inherited marker CI/repository tooling deferred kilocode/script (marker normalizer for the v2 merge process) V1 Kilo-owned helper rebuilds `kilocode_change` markers for a file against the last merged upstream version. The v2 conventions require preserving/reapplying markers on every upstream merge (v2-fork-conventions.md) but provide no tooling; deferred. L3: * Rebuild kilocode_change markers for one file by comparing it with the last / L25: Rebuilds kilocode_change markers by: +script/upstream/merge.ts 594,614,623,652,670,690,710,730,750,790,794,804 12 shared-upstream patch marker CI/repository tooling not needed kilocode/v2-fork-conventions.md merge-forward process; no branding-transform merge driver for v2 V1 Kilo-owned merge driver applies branding transforms pre-merge and flags marker-bearing files for manual resolution (12 marker-string mentions, L594-805). The v2 line explicitly does not rebrand upstream files or fork packages ('Prefer new Kilo-owned files over edits to upstream files'; 'Do not fork or rename an upstream package'), so the transform-driven merge is the wrong model for v2; the manual merge-forward process replaces it. L594: // These are likely files with kilocode_change markers or actual logic differences. / L614: // Files with kilocode_change markers are flagged for manual resolution instead. +script/upstream/transforms/transform-extensions.ts 168,170 2 shared-upstream patch marker CI/repository tooling not needed none — v2 does not rebrand upstream extension files V1 branding transform for extension files, skipping marker-bearing files (L168-171). Obsolete under the v2 no-rebrand convention. L168: // If our version has kilocode_change markers, flag for manual resolution / L170: warn(`${file} has kilocode_change markers — skipping auto-transform, needs manual resolution`) +script/upstream/transforms/transform-i18n.ts 8,21,139,142,146,158,300,302 8 shared-upstream patch marker CI/repository tooling not needed none — v2 keeps upstream i18n; Kilo strings live in packages/kilo-i18n V1 branding transform for i18n files preserving marked lines (L8-21,139-159,300-303). Obsolete under the v2 no-rebrand convention; Kilo-owned i18n is a separate package. L8: * 3. Preserving lines marked with `// kilocode_change` / L21: * - Lines with `// kilocode_change` +script/upstream/transforms/transform-package-json.ts 470,472,905,915 4 shared-upstream patch marker CI/repository tooling not needed none — v2 does not rename upstream packages (v2-fork-conventions.md 'Do not fork or rename an upstream package') V1 branding/reconcile transform for package.json files (L470-473,904-916). Obsolete for v2. L470: // If our version has kilocode_change markers, flag for manual resolution / L472: warn(`${file} has kilocode_change markers — skipping auto-transform, needs manual resolution`) +script/upstream/transforms/transform-scripts.ts 141,143 2 shared-upstream patch marker CI/repository tooling not needed none — v2 keeps upstream scripts unbranded V1 branding transform for shell/TS scripts (L141-144). Obsolete under v2 conventions. L141: // If our version has kilocode_change markers, flag for manual resolution / L143: warn(`${file} has kilocode_change markers — skipping auto-transform, needs manual resolution`) +script/upstream/transforms/transform-take-theirs.ts 11,149,150,190,192 5 shared-upstream patch marker CI/repository tooling not needed none — v2 merges upstream forward without take-theirs rebranding V1 transform that takes upstream content for predictable files while preserving marked lines (L11,148-151,190-193). Obsolete for v2. L11: * - Files without kilocode_change logic blocks / L149: // Skip lines with kilocode_change marker (already customized) +script/upstream/transforms/transform-web.ts 177,179 2 shared-upstream patch marker CI/repository tooling not needed none — v2 web/docs are upstream-owned; Kilo docs live in packages/kilo-docs V1 branding transform for web/docs files (L177-180). Obsolete under v2 conventions. L177: // If our version has kilocode_change markers, flag for manual resolution / L179: warn(`${file} has kilocode_change markers — skipping auto-transform, needs manual resolution`) +script/upstream/utils/git.ts 439,446 2 shared-upstream patch marker CI/repository tooling not needed none — helper for the v1 transform merge (`oursHasKilocodeChanges` on stage :2:) V1 git helpers for the transform-driven merge (L439-447). Obsolete with merge.ts; a v2 marker-detection helper would be part of the deferred kilocode/script tooling if built. L439: * Check if the "ours" version of a conflicted file contains kilocode_change markers. / L446: return result.stdout.toString().includes("kilocode_change") +script/upstream/utils/markers.ts 3,50,51,52,54,55,56,105,106,239,240,241,242,246,247,248,249,364 18 shared-upstream patch marker CI/repository tooling deferred kilocode/script (shared marker regexes/normalizer for the v2 merge process) V1 Kilo-owned marker helper library (standalone/start/end/new-file regexes, comment-style aware wrapping, L49-57,104-107,239-249,363-365) used by the marker fixer and reset classifier. The v2 convention keeps the same marker grammar but has no tooling; deferred with fix-kilocode-markers/find-reset-candidates. L3: * Shared kilocode_change marker helpers used by both the marker fixer and the / L50: /^\s*\/\/\s*kilocode_change\b.*$/, +script/upstream/utils/report.ts 107,149,150,153,164,165,168,176,179,187,190,198,201,214,217,227,269 17 shared-upstream patch marker CI/repository tooling not needed none — conflict recommendation report for the v1 transform merge V1 report classifies conflicted files and recommends manual review when markers are present (L107-227,269-270). Obsolete with merge.ts under the v2 merge-forward process. L107: * Pass currentContent (our version of the file) to detect kilocode_change markers / L149: // If our version has kilocode_change markers, flag for manual review +script/version.ts 9,12,16,23 4 shared-upstream patch marker CI/repository tooling needs port script/version.ts:18-23 (v2 creates draft releases with `--target`/notes-file and handles `beta` only) — add the `rc` pre-release channel and Kilo draft/notes split V1 markers create a draft release with empty notes (notes generated later by publish.ts on the committing runner) and handle both `beta` and `rc` preview channels with `--prerelease` (L9-23). V2 version.ts creates the draft with `--notes-file` and handles only `beta` (L18-23). The `rc` channel is Kilo's VS Code Marketplace pre-release lane (packages/script row) and is absent. L9: // kilocode_change start - create draft release; changelog generation and / L12: // kilocode_change end diff --git a/migration-tracking/marker-audit/v2-current-shared-change-inventory.md b/migration-tracking/marker-audit/v2-current-shared-change-inventory.md new file mode 100644 index 000000000000..3e1c1c6501e4 --- /dev/null +++ b/migration-tracking/marker-audit/v2-current-shared-change-inventory.md @@ -0,0 +1,87 @@ +# Current v2 shared-change inventory + +Updated September 10, 2026 after annotation cleanup in `6315d9da32`. Unlike the historical broad marker search, this inventory starts with the **actual upstream diff**, so metadata changes without inline markers are included. + +## Scope + +- Upstream ancestor: `59b29de40966803e2c7cd734d439843fb773f6a6` (`git merge-base upstream/v2 HEAD` using the local upstream ref). +- Reviewed commit: `6315d9da325e7201aab9d77547ef855ab03ba1e0`. +- Existing shared paths changed: **54**. +- Files containing the exact marker string: **52** — **24 production source**, **4 tests**, **22 README variants**, **AGENTS.md** and **.gitignore**. +- Unmarked metadata exceptions: **package.json** and **bun.lock**. +- All other committed additions are in Kilo-owned paths and do not need patch markers. + +Marker-line numbers below are anchored to the reviewed commit. AGENTS.md includes both an annotation and a prose mention. Marker presence is not proof that every hunk is individually bracketed; review full diffs as well. This inventory does not establish behavioral or E2E acceptance. + +## Review links + +| Shared file | Category | Marker lines at reviewed commit | +|---|---|---| +| [.gitignore](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/.gitignore) | Repository configuration | [39](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/.gitignore#L39) | +| [AGENTS.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/AGENTS.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/AGENTS.md#L1) [3](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/AGENTS.md#L3) | +| [README.ar.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.ar.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.ar.md#L1) | +| [README.bn.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.bn.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.bn.md#L1) | +| [README.br.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.br.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.br.md#L1) | +| [README.bs.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.bs.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.bs.md#L1) | +| [README.da.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.da.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.da.md#L1) | +| [README.de.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.de.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.de.md#L1) | +| [README.es.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.es.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.es.md#L1) | +| [README.fr.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.fr.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.fr.md#L1) | +| [README.gr.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.gr.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.gr.md#L1) | +| [README.it.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.it.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.it.md#L1) | +| [README.ja.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.ja.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.ja.md#L1) | +| [README.ko.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.ko.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.ko.md#L1) | +| [README.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.md#L1) | +| [README.no.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.no.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.no.md#L1) | +| [README.pl.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.pl.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.pl.md#L1) | +| [README.ru.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.ru.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.ru.md#L1) | +| [README.th.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.th.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.th.md#L1) | +| [README.tr.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.tr.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.tr.md#L1) | +| [README.uk.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.uk.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.uk.md#L1) | +| [README.vi.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.vi.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.vi.md#L1) | +| [README.zh.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.zh.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.zh.md#L1) | +| [README.zht.md](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.zht.md) | Branding / instructions | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/README.zht.md#L1) | +| [bun.lock](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/bun.lock) | Repository configuration | **Exception — review diff** | +| [package.json](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/package.json) | Repository configuration | **Exception — review diff** | +| [packages/core/src/config.ts](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/config.ts) | Source | [36](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/config.ts#L36) [322](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/config.ts#L322) [325](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/config.ts#L325) | +| [packages/core/src/mcp/index.ts](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/mcp/index.ts) | Source | [185](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/mcp/index.ts#L185) [193](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/mcp/index.ts#L193) [533](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/mcp/index.ts#L533) [878](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/mcp/index.ts#L878) | +| [packages/core/src/plugin/sdk.ts](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/plugin/sdk.ts) | Source | [22](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/plugin/sdk.ts#L22) [25](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/plugin/sdk.ts#L25) [36](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/plugin/sdk.ts#L36) [40](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/plugin/sdk.ts#L40) [49](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/plugin/sdk.ts#L49) | +| [packages/core/src/plugin/supervisor.ts](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/plugin/supervisor.ts) | Source | [27](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/plugin/supervisor.ts#L27) [91](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/plugin/supervisor.ts#L91) [100](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/plugin/supervisor.ts#L100) [145](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/plugin/supervisor.ts#L145) [157](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/plugin/supervisor.ts#L157) | +| [packages/core/src/plugin/system-prompt.ts](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/plugin/system-prompt.ts) | Source | [38](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/src/plugin/system-prompt.ts#L38) | +| [packages/core/test/config/config.test.ts](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/test/config/config.test.ts) | Test | [79](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/core/test/config/config.test.ts#L79) | +| [packages/plugin/src/tui/context.ts](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/plugin/src/tui/context.ts) | Source | [179](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/plugin/src/tui/context.ts#L179) | +| [packages/theme/src/tui/defaults.ts](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/theme/src/tui/defaults.ts) | Source | [112](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/theme/src/tui/defaults.ts#L112) [335](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/theme/src/tui/defaults.ts#L335) | +| [packages/theme/src/tui/fallback.ts](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/theme/src/tui/fallback.ts) | Source | [11](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/theme/src/tui/fallback.ts#L11) | +| [packages/theme/src/tui/schema.ts](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/theme/src/tui/schema.ts) | Source | [109](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/theme/src/tui/schema.ts#L109) | +| [packages/theme/src/tui/types.ts](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/theme/src/tui/types.ts) | Source | [31](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/theme/src/tui/types.ts#L31) | +| [packages/tui/src/app.tsx](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/app.tsx) | Source | [192](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/app.tsx#L192) [219](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/app.tsx#L219) | +| [packages/tui/src/component/dialog-agent.tsx](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-agent.tsx) | Source | [14](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-agent.tsx#L14) | +| [packages/tui/src/component/dialog-integration.tsx](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-integration.tsx) | Source | [30](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-integration.tsx#L30) [51](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-integration.tsx#L51) [117](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-integration.tsx#L117) | +| [packages/tui/src/component/dialog-model.tsx](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx) | Source | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L1) [2](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L2) [11](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L11) [14](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L14) [21](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L21) [22](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L22) [23](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L23) [33](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L33) [64](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L64) [75](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L75) [91](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L91) [119](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L119) [130](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L130) [139](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L139) [140](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L140) [141](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L141) [161](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L161) [198](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L198) [273](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L273) [275](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L275) [276](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L276) [277](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/dialog-model.tsx#L277) | +| [packages/tui/src/component/prompt/autocomplete.tsx](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/prompt/autocomplete.tsx) | Source | [522](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/prompt/autocomplete.tsx#L522) [528](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/prompt/autocomplete.tsx#L528) | +| [packages/tui/src/component/prompt/index.tsx](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/prompt/index.tsx) | Source | [1559](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/component/prompt/index.tsx#L1559) | +| [packages/tui/src/context/local.tsx](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/context/local.tsx) | Source | [26](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/context/local.tsx#L26) [96](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/context/local.tsx#L96) | +| [packages/tui/src/context/runtime.tsx](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/context/runtime.tsx) | Source | [2](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/context/runtime.tsx#L2) [4](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/context/runtime.tsx#L4) [11](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/context/runtime.tsx#L11) [26](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/context/runtime.tsx#L26) [27](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/context/runtime.tsx#L27) | +| [packages/tui/src/feature-plugins/sidebar/context.tsx](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/feature-plugins/sidebar/context.tsx) | Source | [2](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/feature-plugins/sidebar/context.tsx#L2) [11](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/feature-plugins/sidebar/context.tsx#L11) [24](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/feature-plugins/sidebar/context.tsx#L24) | +| [packages/tui/src/plugin/api.tsx](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/plugin/api.tsx) | Source | [191](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/plugin/api.tsx#L191) | +| [packages/tui/src/routes/home.tsx](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/routes/home.tsx) | Source | [1](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/routes/home.tsx#L1) [86](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/routes/home.tsx#L86) | +| [packages/tui/src/routes/session/composer/subagents-tab.tsx](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/routes/session/composer/subagents-tab.tsx) | Source | [9](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/routes/session/composer/subagents-tab.tsx#L9) [25](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/routes/session/composer/subagents-tab.tsx#L25) [48](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/routes/session/composer/subagents-tab.tsx#L48) [50](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/routes/session/composer/subagents-tab.tsx#L50) | +| [packages/tui/src/routes/session/index.tsx](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/routes/session/index.tsx) | Source | [25](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/routes/session/index.tsx#L25) [167](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/routes/session/index.tsx#L167) [206](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/routes/session/index.tsx#L206) [2056](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/routes/session/index.tsx#L2056) [2078](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/routes/session/index.tsx#L2078) [2091](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/routes/session/index.tsx#L2091) [2093](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/routes/session/index.tsx#L2093) [2109](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/routes/session/index.tsx#L2109) [2122](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/routes/session/index.tsx#L2122) [3463](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/routes/session/index.tsx#L3463) [3493](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/routes/session/index.tsx#L3493) | +| [packages/tui/src/util/renderer.ts](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/util/renderer.ts) | Source | [3](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/util/renderer.ts#L3) [9](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/src/util/renderer.ts#L9) | +| [packages/tui/test/component/session-tabs-mouse.test.tsx](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/test/component/session-tabs-mouse.test.tsx) | Test | [70](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/test/component/session-tabs-mouse.test.tsx#L70) [135](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/test/component/session-tabs-mouse.test.tsx#L135) [163](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/test/component/session-tabs-mouse.test.tsx#L163) | +| [packages/tui/test/feature-plugins/sidebar-context.test.tsx](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/test/feature-plugins/sidebar-context.test.tsx) | Test | [11](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/test/feature-plugins/sidebar-context.test.tsx#L11) | +| [packages/tui/test/util/renderer.test.ts](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/test/util/renderer.test.ts) | Test | [2](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/test/util/renderer.test.ts#L2) [5](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/test/util/renderer.test.ts#L5) [28](https://github.com/Kilo-Org/kilocode/blob/6315d9da325e7201aab9d77547ef855ab03ba1e0/packages/tui/test/util/renderer.test.ts#L28) | + +## Exceptions and exclusions + +`package.json` is standard JSON, so inline comments would invalidate it. Its Kilo launch-script changes remain visible in the upstream diff. `bun.lock` is generated dependency metadata and is not hand-annotated. + +Ignored/untracked scratch files are outside this committed snapshot. Separate uncommitted live-smoke changes in the root manifest/lockfile and `script/test-live-smoke.ts` were not included in the annotation commit. + +The source comparison found no differences in Protocol, Server or generated Client relative to the actual upstream ancestor. The older first baseline recorded in technical notes predates consumed upstream changes and is not the correct sole base for attributing current Kilo edits. + +## Related reports + +- [Shared production-source footprint](v2-shared-source-footprint-2026-09-10.md) +- [Historical pre-cleanup inventory](kilo-override-marker-inventory.md) +- [Pinned v1 behavior assessment](v1-kilo-marker-port-assessment.md) diff --git a/migration-tracking/marker-audit/v2-shared-source-footprint-2026-09-10.md b/migration-tracking/marker-audit/v2-shared-source-footprint-2026-09-10.md new file mode 100644 index 000000000000..627d95b275c9 --- /dev/null +++ b/migration-tracking/marker-audit/v2-shared-source-footprint-2026-09-10.md @@ -0,0 +1,43 @@ +# Shared source footprint — September 10, 2026 + +Production-source footprint: **24 marked source files**. For the complete **54 shared-file** review (including four tests, branding, configuration and two metadata exceptions), see the [current shared-change inventory](v2-current-shared-change-inventory.md). Counts literal `kilocode_change` presence, not occurrences or changed lines. Excludes Kilo-owned packages and kilocode subtrees, tests, documentation and README translations. This is not proof that unmarked changes do not exist. + +- `packages/core/src/config.ts` +- `packages/core/src/mcp/index.ts` +- `packages/core/src/plugin/sdk.ts` +- `packages/core/src/plugin/supervisor.ts` +- `packages/core/src/plugin/system-prompt.ts` +- `packages/theme/src/tui/defaults.ts` +- `packages/theme/src/tui/fallback.ts` +- `packages/theme/src/tui/schema.ts` +- `packages/theme/src/tui/types.ts` +- `packages/plugin/src/tui/context.ts` +- `packages/tui/src/app.tsx` +- `packages/tui/src/component/dialog-agent.tsx` +- `packages/tui/src/component/dialog-integration.tsx` +- `packages/tui/src/component/dialog-model.tsx` +- `packages/tui/src/component/prompt/autocomplete.tsx` +- `packages/tui/src/component/prompt/index.tsx` +- `packages/tui/src/context/local.tsx` +- `packages/tui/src/context/runtime.tsx` +- `packages/tui/src/feature-plugins/sidebar/context.tsx` +- `packages/tui/src/plugin/api.tsx` +- `packages/tui/src/routes/home.tsx` +- `packages/tui/src/routes/session/composer/subagents-tab.tsx` +- `packages/tui/src/routes/session/index.tsx` +- `packages/tui/src/util/renderer.ts` + +Protocol and Server source contain no literal markers. Unmarked differences from the pinned baseline are not counted. Ten direct TypeScript modules exist under `packages/schema/src/kilocode/`; Gateway source has no literal `@opencode-ai/core` imports. + +## Annotation coverage follow-up + +The upstream-diff audit identified supporting hunks whose annotations were distant, plus an unmarked renderer regression test and `.gitignore` change. Added explanatory annotations at those change sites without altering emitted JavaScript. The production-source count remains **24**; **four** shared test files now carry annotations, and `.gitignore` is annotated as well. + +Two shared metadata files remain explicit exceptions: + +| File | Why there is no inline marker | Kilo difference to review | +|---|---|---| +| `package.json` | Standard JSON does not support comments. | Kilo extension/dev entrypoints and preserved upstream dev entrypoint; inspect the full diff. | +| `bun.lock` | Generated dependency metadata should not be hand-annotated. | Kilo workspace/dependency resolution; inspect the generated diff. | + +Marker presence does not replace an upstream diff review. Added imports and related supporting hunks belong to the same patch even when they are not wrapped in begin/end annotations. Review against the actual consumed upstream ancestor (`59b29de40966803e2c7cd734d439843fb773f6a6` for this checkpoint), rather than treating subsequent upstream evolution after the initial historical pin as Kilo changes. diff --git a/migration-tracking/plans/kilo-opencode-v2-execution-plan.md b/migration-tracking/plans/kilo-opencode-v2-execution-plan.md new file mode 100644 index 000000000000..904d5a083d84 --- /dev/null +++ b/migration-tracking/plans/kilo-opencode-v2-execution-plan.md @@ -0,0 +1,508 @@ +# Kilo v1 → v2 migration plan + +Finalized 2026-09-30. Base: opus-55. Merged in: upstream-resync rigor, capability handshake and validation rules from deepseek; VS Code blockers, sharing/remote steps, standing rules and metrics from kimi-k3. Status: not started. First actions: +- Team sign-off on Decisions 3, 4 and 5. +- Owners assigned for the upstream-sync lane (N1/N3) and the delta sweep (N4). +- N7 aligns the progress plan and issue bodies. + +Strategy: a target-shaped port on `kilo-v2`, following #13750's architecture: +- Kilo behavior lives in `kilo-*` packages and `src/kilocode/` seams. +- No `packages/opencode` and no `packages/llm`. +- `main` is never merged wholesale into `kilo-v2`. + +#12887 (strangler) is not the strategy; only its ratchet idea is reused. + +## Current state (verified 2026-09-29/30) + +| Item | Fact | +|---|---| +| Upstream branches | `dev` (`2fa3363c`, default branch) and `v2` (`74dbc509`) are **separate lines**. They diverged at `0e2dd4ad` (2026-06-26); `v2` is 3,956 commits ahead of `dev` and 1,326 behind. | +| Upstream releases | v2 is published on npm as **`@opencode/cli`**: 2.0.0 on 2026-09-11 (23:44 UTC), `latest` = 2.0.20 (2026-09-29). v2.0.x are git tags on `v2`; **there is no GitHub Release for v2**. v1 remains upstream's default line: GitHub "Latest" and npm `opencode-ai@latest` are v1.18.33, released weekly. | +| Upstream npm scope rename | On 2026-09-07 (#47852, `a5312e16`) upstream renamed its workspace packages from **`@opencode-ai/*` to `@opencode/*`**, e.g. `@opencode/cli`, `@opencode/core`, `@opencode/client`. Some packages kept the old scope (e.g. `@opencode-ai/pty`). `kilo-v2`'s base predates the rename. | +| `main` (Kilo v1) | `.opencode-version` v1.18.26. 2,645 non-merge commits since `f7115470` (2026-08-12), where the 816-file source assessment stops. No ratchet or manifest exists. | +| `kilo-v2` | Base `59b29de4` (2026-09-03, sole parent of the first Kilo commit `047ffd045f`). At least 1,013 commits behind upstream `v2` (measured 09-28). 14 non-merge Kilo commits: 7 squashed checkpoints plus green-gate fixes (#14310); #14594 is open. **Upstream has never been merged into `kilo-v2`.** The branch was created directly on an upstream commit, and the earlier `76dbaf20`-based branch was replaced, not merged. Docs still cite pin `76dbaf20`. | +| Parity | 31/43 broad capabilities. Files: 65 ported, 137 native, 233 partial, 163 needs-port, 68 deferred, 150 not needed (of 816). No row has E2E acceptance. | +| Known reds | `kilo-vscode` `typecheck` is a skip shim (`bun script/typecheck.ts`); the real gate `typecheck:port` (`tsgo --noEmit`) fails. The VS Code preview needs Restricted Mode (key conflict). Pristine upstream is red (see `pinned-v2-baseline.md`). | +| Shared footprint | 24 marked shared source files (5 Core, 4 theme, 1 plugin, 14 TUI), plus 4 tests and 2 metadata exceptions, measured against the old base. | +| Missing on `kilo-v2` | `kilo-jetbrains`, `kilo-docs`, `kilo-web-ui`. `kilo-console` is obsolete. | +| Tracking | #13750 → 9 subissues (#14016–#14024) → 62 children (#14371–#14432). All open and unassigned. The 09-21 P-labels are P0 #14017/#14020, P1 #14016/#14021, P2 #14018/#14019/#14022, P3 #14023/#14024. | +| Progress plan | `migration-tracking/plans/kilo-opencode-v2-plan-progress.md` on `kilo-v2`, updated 09-09 with status "Implementation paused". It conflicts with this plan; N7 aligns it. | +| Stale docs | `AGENTS.md` says the default branch is `v2`. The fork conventions name `johnnyeric/kilo-opencode-v2` and pin `76dbaf20`. | + +Root problem: the port loses ground at both ends. Upstream `v2` moves by about a thousand commits every few weeks and has never been merged in. Meanwhile, `main` adds v1-only behavior that nobody has assessed since 08-12. + +## Feature target and v1 assessment cutoff + +The v2 product must provide: +1. upstream v2 capabilities, plus +2. all Kilo v1 behavior and patches on `main`, minus items explicitly marked post-cutover or not needed. + +This is **feature** parity. Code parity is impossible because the v1 and v2 foundations are incompatible. + +`main` is a moving target, so the plan tracks a **v1 assessment cutoff**: the `main` commit up to which every Kilo change has a recorded disposition (ported, native, partial, needs port, deferred, not needed). The cutoff is currently `f7115470`. Each delta sweep (N4) assesses `cutoff..origin/main` and then moves the cutoff forward. The cutoff is a bookmark for assessment, not a code target. + +## Decisions + +1. **v1 soft freeze is an external dependency.** The v1 team owns it on a best-effort basis; security and critical patches continue on `main`. This plan does not implement any `main`-side enforcement. It depends on `main` only through the v1 assessment cutoff and the recurring v2-owned delta sweep (N4). +2. **Upstream intake by merge.** "Merge-forward" means merging upstream `v2` release tags **into** `kilo-v2` (a merge commit on our branch). Nothing is ever pushed to or merged into `anomalyco/opencode`. `kilo-v2` is never recreated or rebased, because it is shared history. + - One consolidation merge (N1) goes to the **latest upstream v2 release tag at execution start** (v2.0.20 as of 09-29). + - After that, merge every upstream v2 release tag, at least weekly. + - Pins are always release tags. +3. **Staged cutover:** G1 CLI/TUI → G2 VS Code (sidebar, tabs, Agent Manager, settings) → G3 JetBrains. Each surface has its own go/no-go. Until their gate passes, the IDEs stay on v1: + - VS Code ships from `main` and bundles the CLI built from the same commit. It does not pin a CLI version. + - JetBrains pins a CLI version. + + **G2 and G3 are independent client-surface tracks.** Their work is mainly Kilo-specific client code (`packages/kilo-vscode`, `kilo-ide-ui`, `kilo-ui`, `kilo-jetbrains`) that consumes the v2 core through public contracts. They do not block G1, and G1 does not wait for them. Each track can start once its prerequisites land (N1 for both; #14383 for VS Code; N6b for JetBrains) and can run in parallel with G1 under its own owner. + + Some client-track items touch core, server or protocol, and must be coordinated with core work: + - #14380, the message-deletion runtime operation; + - N9, the server side of the capability handshake; + - #14416, the FIM/next-edit producer and service contract; + - #14391, the Agent Manager control-plane contracts. + + These follow the standing rules: Kilo-owned seams first, then a counted shared patch. +4. **Storage:** + - Customer v2 `kilo` uses a distinct v2 data/config location: not preview `kilo2`, not the v1 `kilo` paths. + - v1 files are never moved or rewritten while any v1 surface ships. + - Import is opt-in, copies then migrates the copy, and is **re-runnable/idempotent**. Between G1 and G3, history is split between the v2 CLI and the v1 IDEs. + - Paths are consolidated only after G3. +5. **Post-cutover (non-gating):** voice/STT; image generation (experimental on v1); team sharing and incremental share sync; legacy TUI theme/binding extras. Features behind experimental flags on `main` also default to post-cutover unless explicitly made a gate (N4 keeps the list). KiloClaw is **not needed**, because it has already been removed from the client on `main`. **Individual snapshot share/unshare/fork-from-share and the public viewer for v2 messages are G1 gates**, because they are v1 CLI features. Team upload stays refused until team sharing ships. +6. **Hard gates:** everything else, including FIM/next-edit (G2, G3). Cloud agents (#14023) and Anaconda Desktop (#14024) gate whichever surface they consume, so their discovery runs first. +7. **Clients:** never mix v1 and v2 routes within one session. Clients detect the backend through an explicit version/capability handshake. +8. **Gates override P-labels for surface readiness.** P-labels set the global work order; gates define what a surface needs before it ships. The G1-gating parts of #14018/#14019 are scheduled with G1 despite their P2 labels. Relabel them rather than defer them. + +## Relation to prior proposals + +| Topic | #12887 (strangler) | #13750 (target-shaped port) | This plan | +|---|---|---|---| +| Where v2 work happens | Extract on `main` behind V1 adapters | Port on `kilo-v2` | Same as #13750 | +| `packages/opencode` on v2 | Kept as a temporary legacy host | Not reconstructed | Same as #13750 | +| Control of `main` | Ratchets and an ownership manifest (never built) | None | External best-effort soft freeze, plus a v2-owned recurring delta sweep | +| Upstream v2 intake | Merge regularly | Pinned SHA (in practice, recreated on new tips) | Merge every release tag into `kilo-v2` | +| Cutover | Single | Single (phase 6) | Staged: G1 → G2 → G3 | +| Storage | In-place migration; rollback TBD | `kilo2` preview, later take the `kilo` paths | Separate customer v2 store until v1 retires; re-runnable copy-import | +| Scope | No cuts | JetBrains/voice deferred | Explicit post-cutover list (Decision 5) | + +What changed since those proposals: +- Upstream now publishes v2 (`@opencode/cli`, 2.0.0–2.0.20) in parallel with v1. +- Upstream renamed its npm scope. +- `kilo-v2` has consumed no upstream change since 09-03. + +## Execution model + +- **The unit of execution is a GitHub issue.** This plan is the index: decisions, sequencing and the issue register. It changes only when a decision changes. +- **The progress plan holds live status per capability row.** After N7 aligns it, it receives only status and evidence updates. Every row links to its issue. +- **The working log records milestones only.** These are: each upstream tag merge, each N4 delta sweep, each gate go/no-go, each decision change, and notable incidents or reversals. Only the coordinator writes to it; individual agents do not. Per-change evidence goes in the PR, the issue, and that issue's progress-plan row. This keeps parallel PRs from conflicting on the single append-only log file. +- **Every issue body has these fields.** N7 adds any that are missing; the existing Outcome / Scope / Current position / Acceptance / Tracking sections stay. + - Gate: G1, G2, G3, post-cutover, or discovery. + - Blocked by: real GitHub issue links, not prose or draft numbers. + - Base: whether the issue must start after N1. + - Touches: packages and paths, so parallel agents don't edit the same files. + - Verification commands. + - Owner. +- **The `Gate` field is single-valued.** Units that run once per gate or per sweep get one child issue per run, with the parent kept as a tracker: + - N11 and #14414: one child each for G1, G2 and G3; + - N4: one child per sweep; + - N3: one child per upstream tag. + + Issues split across gates get one child issue per part: #14378, #14395, #14399, #14419. This keeps "0 open issues per gate before go/no-go" accurate. +- **Client tracks (G2, G3) run in parallel with G1** once their prerequisites close (Decision 3). +- **Parallel agents:** + - Take issues whose "Blocked by" items are all closed. + - Avoid overlapping "Touches". The TUI issues (#14375–#14379) all touch `packages/tui` and `packages/kilo-cli/src/tui-plugin`, so run them in series or split their files. + +## Standing rules (every change) + +- **Kilo behavior location.** Kilo behavior lives in `kilo-*` packages or `packages//src/kilocode/`. A shared-file patch is a last resort: marked `kilocode_change`, counted by the N2 ratchet, and justified by a stated missing seam. +- **Failures.** Compare every failing check against the baseline in `pinned-v2-baseline.md` before attributing it to a change. +- **`main` changes.** They reach v2 only through N4 dispositions (`not-applicable | already-fixed | port | defer`). Re-implement by default; cherry-pick only commits that are isolated and understood. +- **Native equivalence is verified, not inferred.** Before treating a native v2 service as covering Kilo behavior, check scope, errors, cancellation, persistence and presentation. A relocation is complete only when the consuming workflow preserves the required behavior. +- **Commit size.** Use normal PR-sized commits. No more squashed checkpoints. +- **Branches and commits.** Branch from `kilo-v2`, target `kilo-v2`, keep branch names short with no slashes, and use `type(scope): summary` commits. + +## Plan + +### Phase 0 — Prerequisite: create the execution issues + +This must finish before any N-unit starts. Existing issues with no blockers (discovery #14425/#14426/#14429/#14430, #14415) may start in parallel. + +P0. **Lift the implementation pause, after sign-off and before any implementation agent starts.** Both documents currently tell agents to stop: the working log's 2026-09-08 checkpoint says "Do not resume implementation or restart delegates without a new instruction", and the progress plan's status line says "Implementation paused". In one small docs PR to `kilo-v2`: +- add a dated milestone entry to `migration-tracking/plans/kilo-opencode-v2-working-log.md`: "Execution plan adopted (link); implementation resumes under it; supersedes the 2026-09-08 pause"; +- replace the working-log header, which calls the file a "local working copy" of #13750, with a statement that it is the tracked milestone history of the migration; +- set the status line of `kilo-opencode-v2-plan-progress.md` to "Active — see execution plan". The full realignment stays in N7. + +P1. **Create one GitHub issue for each of N1–N17** (see "New issues to create"), with N6 created as N6a and N6b. +- Parent each one as a native sub-issue of #13750, or of the epic named in the table. +- Create the per-run and per-part child issues listed in the Execution model: + - G1/G2/G3 children for N11 and #14414; + - part children for #14378, #14395, #14399 and #14419. + + Children for N4 sweeps and N3 upstream tags are created when each run happens. +- Use the existing body template (Outcome / Scope / Current position / Acceptance / Dependencies / Tracking) plus the required Execution-model fields: Gate, Blocked by, Base, Touches, Verification commands, Owner. +- Copy Scope and Acceptance from this plan's step for that unit. + +P2. **Link dependencies.** Record every "Blocked by" from the new-issues table and the dependency order as real GitHub issue links, both between new issues and to existing ones (e.g. N9 is blocked by N8 and #14387). + +P3. **Record the numbers.** Write each created issue number into the "Issue #" column of the new-issues table, and add it to #13750's issue list. From then on, cite the GitHub number; the N-ID is an alias only. + +P4. **Check completeness.** Every N-unit has an issue, a parent, a gate, and blocked-by links. No step in Phases A–G refers to work without an issue. + +### Phase A — External dependency: v1 soft freeze + +Owned by the v1 team, best effort, and not executed by this plan. The recommendations passed to that team: +- a policy note on `main`; +- `v2:*` disposition labels on `main` PRs; +- a growth ratchet on `packages/opencode/src/kilocode/**`; +- continued upstream v1.18.x merges. + +The v2 side stays correct without them, because N4 classifies every Kilo change on `main`, labelled or not. + +### Phase B — Consolidate `kilo-v2` onto upstream (first code work) + +1. **N1 — Consolidation merge.** This is the first upstream merge ever into `kilo-v2`. + 1. Add the fetch-only remote `upstream` = `https://github.com/anomalyco/opencode.git` and fetch the `v2` branch and its tags. This is the same remote and fetch as the original setup (`pinned-v2-baseline.md`); only the integration method changes, from recreate to merge. + 2. Select the latest v2 release tag and record its SHA. Confirm `59b29de4` is its ancestor; if not, stop and re-plan. + 3. In a dedicated worktree, on branch `v2-upstream-sync` from `kilo-v2`, `git merge ` as **one slice**. The topology changes are cross-cutting, and a partial merge leaves the tree broken. + 4. Resolve conflicts toward the v2 architecture, then re-apply Kilo invariants through owned seams. Preserve markers. Expect topology changes (e.g. `plugin-browser` arrives). + 5. **Scope-rename codemod `@opencode-ai/*` → `@opencode/*`.** Rewrite: + - import specifiers in all Kilo-owned code (`packages/kilo-*`, `packages/*/src/kilocode/**`, `packages/schema/src/kilocode/**`); + - `package.json` dependency names (`workspace:*`); + - tsconfig paths; + - scripts and docs that name packages (e.g. `@opencode-ai/core` in `AGENTS.md` and the migration-tracking notes). + + Rewrite only names that exist under `@opencode/*` at the tag; keep the packages upstream left on the old scope (e.g. `@opencode-ai/pty`). Kilo's own `@kilocode/*` names are unchanged. + 6. Run `bun run generate` in `packages/client`. Report generated and handwritten diffs separately. + 7. Run `bun migration-tracking/technical-notes/script/v2-baseline.ts --checks` on the pristine tag and on the merge result. Classify each new failure `not-applicable | already-fixed | port | defer`. Also validate the `kilo-*` packages and `bun run extension`. + 8. Re-verify every "Implemented" row in the progress plan with its focused tests, and downgrade any that regressed. + 9. Fix the docs: + - `pinned-v2-baseline.md`, `v2-fork-conventions.md` and `PINNED_V2` in `v2-baseline.ts` record the new tag and base; + - the pin policy becomes "release tags, merged into `kilo-v2`"; + - branch names follow `AGENTS.md`; + - `AGENTS.md` says the default branch is `kilo-v2` for v2 work and `main` for v1. +2. **N2 — `kilo-v2` ratchet.** A Kilo-owned script (under `migration-tracking/technical-notes/script/` or `packages/kilo-cli/script/`) with exact counts of: + - marked shared files; + - client→Core/Server imports; + - Kilo-package imports of upstream internals. + + The baseline is the post-N1 measurement, recorded in a new dated `marker-audit/` footprint file. Increases fail unless they state the missing seam. Then run #14381 (marker-disposition sweep) against the new base. +3. **N3 — Sync automation.** A Kilo-owned workflow that dry-run-merges each new upstream `v2.*` tag into `kilo-v2` and reports conflicts by owner, plus a real `e2e` lane for `kilo-v2`. Do not edit upstream's `test.yml`. + + Each new upstream tag is handled individually, as it arrives: + - the workflow (or the sync owner) opens one child issue per tag; + - the sync owner performs the real merge; + - acceptance is the N1 baseline diff, typecheck and `bun run generate` checks. +4. Start signing/notarization (#14411) now, because of its external lead time. + +### Phase C — Re-baseline (starts now, parallel with B) + +This phase writes files, so each unit owns fixed paths to avoid colliding with N1: +- N4 owns `marker-audit/`; +- N7 owns `plans/kilo-opencode-v2-plan-progress.md` and the test plans; +- N5 (data part) owns its fixture-data directory. + +N1 does not edit these paths, apart from the scope-rename codemod where they name packages. + +5. **N4 — `main` delta sweep (recurring).** + - Assess `cutoff..origin/main` over Kilo-owned and marked v1 files, and refresh `marker-audit/v1-kilo-marker-port-assessment.tsv`/`.md`. + - Also inventory the features behind experimental flags on `main`; these default to post-cutover (Decision 5). + - File new work under the owning epic, then advance the cutoff. + - Run it now, then before each gate's go/no-go. +6. **N5 — v1 reference fixtures.** Record v1 behavior at the cutoff for offline wait, overflow accounting, slow snapshot and snapshot retention, so that #14371–#14374 verify against pinned v1 behavior regardless of later upstream merges. It has two parts: + - **before N1:** capture the v1 reference data only, with no imports of v2 packages; + - **after N1:** land the v2 test harness that consumes the data, so it uses the renamed `@opencode/*` scope. +7. **N6 — Package decisions**, split into two issues: + - **N6a (no blocker):** assess `kilo-docs` (needed at G1) and `kilo-web-ui` (port or obsolete); record `kilo-console` as obsolete. + - **N6b (blocked by N1):** import `kilo-docs` and `kilo-jetbrains` (for G3). Importing after N1 avoids applying the scope rename twice. +8. **Discovery:** #14425 and #14426 (cloud agents) and #14429 and #14430 (Anaconda Desktop). Each one records which binary, protocol and surface it consumes, which sets the gate for #14427/#14428 and #14431/#14432. +9. **N7 — Alignment.** Align the progress plan and the issue bodies (see "Progress plan alignment" and the register). + +### Phase D — G1: CLI/TUI + +10. #14017 runtime: #14371–#14375, #14377, #14379, #14382. Verify against the N5 fixtures. #14378 is split: its tables and bindings are post-cutover, and alerts and sound need a G1 decision in N7. +11. **N8 — Customer v2 store identity** in the `kilo-cli` host: + - concrete paths per Decision 4; + - boot fails closed on v1 or OpenCode stores; + - assert that upstream `V1Migration` does nothing on an empty store; + - verify whether the v1 IDE and CLI currently share one DB/auth file, and if so document the split history for users; + - verify that v1 IDE extensions can never resolve the customer v2 `kilo` binary (via PATH, a global install, or a "latest" download). VS Code does not pin a CLI version. If an extension can resolve the v2 binary, G1 ships v2 under a name or channel that v1 IDEs don't resolve. +12. #14413 import: + - covers the DB, `auth.json` and Kilo `kilo.jsonc` keys; originals are untouched; + - fixtures come from released v1 versions, including the cutoff; + - a re-run imports only new sessions, never duplicating or overwriting v2-side changes (a test is required). +13. #14020 distribution: #14408, #14409, #14410, #14411, #14412. v1 and v2 CLIs install side by side. +13a. **N17 — v2 preview release train** (under #14020): + - regular (e.g. weekly) releases from `kilo-v2` under the isolated preview identity, for the team and early adopters; + - starts once N1, #14408, #14410 and #14412 land, so that G1 is not the first v2 release; + - preview findings feed #14414 and the progress plan. + + G1 is the bar for customers; the preview train is how the team learns when v2 is close enough to that bar. +14. #14018 sharing: #14396, #14397, #14398, #14401, and the fail-closed part of #14399. +15. #14019 remote: #14402–#14407, and the host-registration part of #14395. +16. Any #14427/#14428 or #14431/#14432 that discovery attaches to G1. +16a. **N12 — TUI picker, dialogs and status/footer**, covering progress-plan rows that have no issue: + - model picker inline preview and section/search interaction; + - provider-specific guidance, failure details and automatic integration-method presentation in the provider/integration dialogs; + - version/config guidance and onboarding in the status/footer (the presence part is #14395). + + It touches `packages/tui` and `packages/kilo-cli/src/tui-plugin`, so run it in series with #14375–#14379. +16b. **N13 — Runtime integration remainder**, covering progress-plan rows that have no issue: + - memory durable injection indicators; + - the remaining CLI/cloud indexing paths; + - sandbox cross-platform release validation; + - v1 sandbox policy variants (settings, toggle, inheritance semantics). +16c. **N11 — G1 acceptance run.** + - Execute every G1-tagged test-plan scenario end to end, including rows already marked "Implemented, full E2E pending": Gateway auth/org/catalog, memory, indexing, sandbox, telemetry, swarm, skills/agents, config, review, headless run, ACP, updater, model info, sidebar. + - Include external-gated verification where permitted: the deployed Gateway, and `kilo cloud` against the deployed service. + - Record results with SHA in the progress plan. +17. **G1 go/no-go (#14414-G1):** + - N11-G1 has passed; + - the canary operates in isolation; + - N4 has run and its G1 items are dispositioned; + - rollback is the v1 CLI on untouched v1 files. + +### Phase E — G2: VS Code + +18. #14016, in this order: + 1. #14383, the real typecheck. It comes first and depends on N1, because of the scope rename. + 2. The remaining VS Code issues: #14384–#14394, #14380 (message deletion, whose consumer is the VS Code client), and the VS Code visibility-reporting part of #14395. + 3. The Restricted Mode key conflict and desktop verification of the Stop fix, both tracked under #14384/#14387. +19. #14021: #14415 → #14416 (FIM, a hard gate; start alongside G1) and #14417. +20. **N9 — Handshake and extension switch:** + - the capability handshake (Decision 7); + - the extension bundles the v2 binary and uses the v2 store; + - opt-in import on first launch; + - verify the mapping of the v1 extension's globalState and secrets. +20a. **N14 — Original-extension consumers**, covering progress-plan rows that have no issue: + - memory UI acceptance; + - indexing write controls; + - sandbox controls; + - the telemetry capture/proxy surface. + + It sits under #14016 and touches `packages/kilo-vscode`, so coordinate it with #14386 (settings webview). +20b. **N15 — VS Code scenario set.** + - The current test plans defer VS Code and contain no IDE scenarios. Write G2 scenarios: sidebar, editor tabs and restore, Agent Manager and worktree routing, settings, terminals, reconnect and interruption, opt-in import, handshake, rollback to the previous extension. + - Owned by the VS Code track; it can start now because it does not depend on N1. +20c. Any #14427/#14428 or #14431/#14432 that discovery attaches to G2. +20d. **N11 — G2 acceptance run** of every G2-tagged scenario, including the N15 set, the permission/question UI callers and the policy variants. +21. **G2 go/no-go (#14414-G2):** N11-G2 has passed and N4 has run again. Rollback is the previous extension version on untouched v1 files. + +### Phase F — G3: JetBrains + +22. #14022: #14420 → #14421 → #14422 and #14423 (#14423 needs #14416) → #14424. Generate the client from the stable assembled OpenAPI contract, and follow the same import, handshake and rollback rules as G2. +22a. **N16 — JetBrains scenario set.** Write G3 scenarios for supported IDEs: startup and discovery, authentication, sessions and streaming, permissions, history, terminal and editor services, reconnect, restart and version mismatch. Owned by the JetBrains track; it can start now. +22b. **N11 — G3 acceptance run** of every G3-tagged scenario, including the N16 set, plus any #14427/#14428 or #14431/#14432 that discovery attaches to G3. +23. **G3 go/no-go (#14414-G3)** after N11-G3 passes. + +### Phase G — v1 retirement + +24. **N10:** + - rename `main` to a `v1` maintenance branch (security fixes only, announced end of life) and promote `kilo-v2` to `main`; + - move the v1 dirs aside with a backup and a one-time migration, then have v2 take the canonical `kilo` paths (there is no reverse migration); + - work through the post-cutover backlog. + +## Dependency order + +``` +Phase 0 (P1–P4: create N1–N17 and child issues) ──> every N-unit below +Start after Phase 0 (no other blockers): N4, N5 (data part), N6a, N7, N15, N16 +Start now (existing issues): #14425/#14426, #14429/#14430, #14415 +N1 ──> N2 ──> #14381 +N1 ──> N3 (then one child issue per upstream tag) +N1 ──> N5 (harness part), N6b, N8, and all parity issues below +N1 + #14411 started early +N1 + #14408, #14410, #14412 ──> N17 (preview release train) + +G1: #14371..#14375, #14377, #14379, #14382 (need N5) + #14396, #14397 ──> #14398 ──> #14401 + #14402 ──> #14403..#14406 ──> #14407 + N8 ──> #14413 + #14408, #14409, #14410, #14412, #14411 + N12 (after #14375..#14379, same files), N13 + #14427/#14428, #14431/#14432 if discovery assigns them to G1 + all of the above ──> N11-G1 ──> + N4 run ──> #14414-G1 + +G2 (client track, parallel with G1 after N1): + #14383 ──> #14384..#14394, #14380, N14 + #14415 ──> #14416, #14417 + N8 + #14387 ──> N9 + N15 + #14427/#14428, #14431/#14432 if assigned to G2 + all ──> N11-G2 ──> + N4 run ──> #14414-G2 + +G3 (client track, parallel with G1 after N1 and N6b): + #14420 ──> #14421 ──> #14422, #14423 (needs #14416) ──> #14424 + N16 + #14427/#14428, #14431/#14432 if assigned to G3 + all ──> N11-G3 ──> #14414-G3 ──> N10 +``` + +## Issue register + +Nothing in the tree is skipped. Every issue has a gate, is marked post-cutover with a reason, or gets its gate from discovery. + +| Issue | Title (short) | Placement | Reason / change needed (applied by N7) | +|---|---|---|---| +| **#14017** | Runtime and CLI/TUI parity | G1 epic | | +| #14371 | Offline network wait | G1 | Verify against the N5 fixtures instead of the stale "pinned v1 revision"; drop "before upstream merges move the engine" | +| #14372 | Overflow accounting | G1 | Same as #14371 | +| #14373 | Slow-snapshot interaction | G1 | Same as #14371 | +| #14374 | Snapshot retention/concurrency | G1 | Same as #14371 | +| #14375 | TUI notifications | G1 | | +| #14376 | Legacy theme catalogue | **Post-cutover** | Decision 5 (legacy TUI extras) | +| #14377 | Session scope switching | G1 | | +| #14378 | Alerts, tables, bindings, sound | **Split** (child per part) | Tables and bindings are post-cutover (Decision 5); alerts and sound get a G1 decision | +| #14379 | Location/event filtering | G1 | | +| #14380 | Message deletion | G2 | The consumer is the VS Code client; it touches core/server, so coordinate with core work (Decision 3) | +| #14381 | Marker-disposition sweep | Phase B, after N2 | Markers change with the merge | +| #14382 | Plan-to-Code handoff regression | G1 | | +| **#14018** | Sharing | G1 epic (partly) | | +| #14396 | Share transport | G1 | | +| #14397 | Share payload contract | G1 | | +| #14398 | Public viewer for v2 messages | G1 | Decision 5 | +| #14399 | Team sharing ownership | **Split** (child per part) | Fail-closed refusal gates G1; the ownership model is post-cutover | +| #14400 | Incremental sharing | **Post-cutover** | Decision 5 | +| #14401 | Deployed share contracts (external) | G1 | | +| **#14019** | Remote | G1 epic | `/remote` is a CLI feature | +| #14402–#14407 | Relay protocol, transcript, suggestions, detach/heartbeat, metadata, deployed acceptance | G1 | | +| **#14020** | Distribution and cutover | G1 epic, repeated per surface | | +| #14408 | CI/build hardening | G1 | | +| #14409 | Production payload format | G1 | | +| #14410 | Update/rollback with artifacts | G1 | | +| #14411 | Signing/notarization/hosting | G1 | Starts during Phase B | +| #14412 | Clean install | G1 | | +| #14413 | Opt-in import | G1 | Change to the customer v2 store (not `kilo2`), re-runnable/idempotent import, and cutoff fixtures | +| #14414 | Canary and runbook | Tracker; children G1, G2, G3 | One child issue per surface; replace the draft links "#03/#04/#06" with #14410, #14411, #14413 | +| **#14016** | VS Code parity | G2 epic | | +| #14383 | Real typecheck | G2, first | Blocked by N1 (scope rename) | +| #14384–#14394 | Sidebar, tabs, settings, connection, auth, generation, terminal, Agent Manager, notifications, removal, timeline | G2 | Restricted Mode and Stop verification go under #14384/#14387 | +| #14395 | Presence registry and relay | **Split** (child per part) | Host registration is G1 (with remote); VS Code visibility reporting is G2 | +| **#14021** | Editor services | G2 epic | | +| #14415 | Services inventory | G2 | Can start now | +| #14416 | FIM/next-edit | G2, G3 | Hard gate; starts alongside G1; the producer/service contract touches core, so coordinate with core work (Decision 3) | +| #14417 | Code actions/generation | G2 | | +| #14418 | Voice | **Post-cutover** | Decision 5 | +| #14419 | Image generation and KiloClaw | **Split** (child per part) | Image generation is post-cutover (experimental on v1). KiloClaw is not needed, because it was already removed from the client on `main` (Decision 5) | +| **#14022** | JetBrains | G3 epic | | +| #14420–#14424 | Inventory, startup/auth, sessions, terminal and editor services, IDE validation | G3 | #14423 needs #14416 | +| **#14023** | Cloud agents | Discovery | | +| #14425, #14426 | Identify and map consumers | Discovery, now | | +| #14427, #14428 | Implement and validate | Gate set by discovery | | +| **#14024** | Anaconda Desktop | Discovery | | +| #14429, #14430 | Discover and assess | Discovery, now | | +| #14431, #14432 | Adapt and validate | Gate set by discovery | | +| N1 | Consolidation merge and scope-rename codemod | Phase B | New, created in Phase 0 | +| N2 | `kilo-v2` ratchet | Phase B | New, created in Phase 0 | +| N3 | Sync automation and `e2e` lane | Phase B; tracker with a child per upstream tag | New, created in Phase 0 | +| N4 | Recurring `main` delta sweep | Phase C; tracker with a child per sweep | New, created in Phase 0 | +| N5 | v1 reference fixtures | Phase C (data before N1, harness after N1) | New, created in Phase 0 | +| N6a | Package assessment | Phase C | New, created in Phase 0 | +| N6b | Package imports | Phase C, after N1 | New, created in Phase 0 | +| N7 | Progress-plan and issue-body alignment | Phase C | New, created in Phase 0 | +| N8 | Customer v2 store identity | G1 | New, created in Phase 0 | +| N9 | Handshake and extension switch | G2 | New, created in Phase 0 | +| N10 | v1 retirement | Phase G | New, created in Phase 0 | +| N11 | Gate acceptance run | Tracker; children G1, G2, G3 | New, created in Phase 0; covers the "Implemented, E2E pending" rows | +| N12 | TUI picker, dialogs, status/footer | G1 | New, created in Phase 0; progress-plan rows that had no issue | +| N13 | Runtime integration remainder | G1 | New, created in Phase 0; progress-plan rows that had no issue | +| N14 | Original-extension consumers | G2 | New, created in Phase 0; progress-plan rows that had no issue | +| N15 | VS Code scenario set | G2 | New, created in Phase 0; the test plans have no IDE scenarios today | +| N16 | JetBrains scenario set | G3 | New, created in Phase 0; the test plans have no IDE scenarios today | +| N17 | v2 preview release train | G1 (pre-cutover) | New, created in Phase 0; regular releases before G1 | + +New issues to create in Phase 0. They are children of #13750 unless the Unit column names another parent. Fill in "Issue #" in P3. + +| ID | Issue # | Unit | Gate/Phase | Blocked by | Touches | +|---|---|---|---|---|---| +| N1 | TBD | Consolidation merge to the latest v2 tag, including the scope-rename codemod, baseline, doc fixes and re-verification | B | — | Whole repo, except the Phase C paths owned by N4, N5 and N7 (codemod only there) | +| N2 | TBD | `kilo-v2` ratchet and post-merge footprint | B | N1 | `migration-tracking/`, script dir | +| N3 | TBD | Upstream-tag dry-run workflow and `e2e` lane; one child issue per new upstream tag, merged by the sync owner | B | N1 | New `.github/workflows/kilo-*` files | +| N4 | TBD | Recurring `main` delta sweep from the cutoff, including the experimental-flag inventory; one child issue per sweep | C, each gate | — | `marker-audit/` | +| N5 | TBD | v1 reference fixtures for #14371–#14374: v1 data before N1, v2 harness after N1 | C | Harness part: N1 | Fixture data, then test harness | +| N6a | TBD | Package assessment: `kilo-docs`, `kilo-web-ui`; record `kilo-console` obsolete | C | — | `migration-tracking/` notes only | +| N6b | TBD | Package imports: `kilo-docs` (G1), `kilo-jetbrains` (G3) | C | N1, N6a | `packages/kilo-docs`, `packages/kilo-jetbrains` | +| N7 | TBD | Align the progress plan and sweep the issue bodies | C | — | `plans/kilo-opencode-v2-plan-progress.md`, test plans, GitHub | +| N8 | TBD | Customer v2 store identity (under #14020) | G1 | N1 | `packages/kilo-cli` host/paths | +| N9 | TBD | Capability handshake and extension v2 store/import switch (under #14016) | G2 | N8, #14387 | `packages/kilo-vscode`; server side of the handshake in `packages/kilo-cli` and `packages/schema/src/kilocode` (shared Server/Protocol files only as a counted patch) | +| N10 | TBD | v1 retirement and storage consolidation | G | #14414-G3 | Branches, `kilo-cli` paths | +| N11 | TBD | Gate acceptance run (tracker; children N11-G1/G2/G3): every gate-tagged test-plan scenario including "Implemented, E2E pending" rows, plus external-gated Gateway and `kilo cloud` verification where permitted; results with SHA | G1, G2, G3 | That gate's issues; N7 (test-plan gate tags) | Test plans, progress plan | +| N12 | TBD | TUI picker inline preview and search, provider/integration dialog guidance and failure details, status/footer guidance and onboarding (under #14017) | G1 | N1 | `packages/tui`, `packages/kilo-cli/src/tui-plugin` (in series with #14375–#14379) | +| N13 | TBD | Runtime integration remainder: memory injection indicators, CLI indexing paths, sandbox cross-platform validation and v1 policy variants (under #14017) | G1 | N1 | `packages/kilo-cli`, `packages/kilo-memory`, `packages/kilo-indexing` | +| N14 | TBD | Original-extension consumers: memory UI, indexing write controls, sandbox controls, telemetry capture/proxy (under #14016) | G2 | #14383 | `packages/kilo-vscode` (coordinate with #14386) | +| N15 | TBD | VS Code scenario set for N11-G2 (under #14016) | G2 | — | `test-plans/` (coordinate with N7) | +| N16 | TBD | JetBrains scenario set for N11-G3 (under #14022) | G3 | — | `test-plans/` (coordinate with N7) | +| N17 | TBD | v2 preview release train from `kilo-v2` under the preview identity (under #14020) | G1 (pre-cutover) | N1, #14408, #14410, #14412 | Release tooling, `packages/kilo-cli` packaging | + +## Progress plan alignment (N7 scope) + +`migration-tracking/plans/kilo-opencode-v2-plan-progress.md` needs one pass. After it, it receives only status updates. +- Replace the status "Implementation paused" and the 09-09 date. +- Map the phase table (0–6, single cutover) to G1/G2/G3 plus post-cutover. +- Add `Gate` and `Issue` columns to every inventory row. +- Mark the Decision-5 rows `Post-cutover`, and record a ship/defer/drop decision for every deferred row. +- Replace "Next steps" (currently VS Code first) with this plan's G1-first order. +- Rewrite the import and canary rows for the customer v2 store, re-runnable import, and per-surface canary. +- Add rows for N1 (scope rename), N4, N8 and N9. +- Record the v1 assessment cutoff and the adopted upstream tag. +- Tag every scenario in `test-plans/kilo-opencode-v2-test-plan*.md` with G1, G2, G3 or post-cutover, so that N11 can select scenarios by gate. +- Working log: after P0, record the realignment as a milestone entry. Leave historical entries as they are, since they describe their own checkpoints. +- Verify that every progress-plan row links to an issue. Rows without an issue must reach zero; the nine found on 09-30 are covered by N11–N14. + +## Validation + +- **Every change:** + - package-level `bun typecheck` (never `tsc`); + - tests from the package directory; + - `lint:effect-simplifications` with no new violations relative to the recorded baseline. +- **N1:** + - merge-result failures are a subset of pristine-tag failures; + - `bun install` resolves with the renamed scope; + - a search finds no `@opencode-ai/` references other than packages upstream kept on the old scope; + - `check:generated` passes in `packages/client` and `packages/www`; + - `packages/cli` and `kilo-cli` build and pass the service smoke test; + - `bun run extension` builds. +- **N2:** counts are at or below the recorded baseline in CI. +- **N4:** zero unclassified Kilo-owned or marked files in `cutoff..origin/main` at each gate. +- **Gates:** + - execute the gate rows in `test-plans/kilo-opencode-v2-test-plan*.md` against an isolated real host, with external networking denied where possible; + - record the SHA and test limits; + - never record a local pass as E2E. +- **Import/cutover:** + - schema diff of the v1 store (at the cutoff) against the migrator's expected tables, on real fixtures; + - credentials and supported config keys are preserved; + - the v1 binary still opens the original store afterwards. + +## Progress metrics (reported in the progress plan at each checkpoint) + +| Metric | What it tells you | How it is measured | Target | When | +|---|---|---|---|---| +| Upstream lag | How far `kilo-v2` trails upstream; growth means the sync lane stalled | Commits from `kilo-v2`'s upstream merge base to the latest upstream v2 tag | 0 after each tag merge; alarm after one missed tag | Each upstream tag | +| Unassessed `main` changes | v1 behavior whose v2 impact is unknown | Kilo-owned/marked files changed in `cutoff..origin/main` without a disposition | 0 at each gate's go/no-go | Each gate, and monthly | +| Shared-file patches | Future merge cost ("merge tax") | N2 count of marked upstream files | At or below the post-N1 baseline; every increase justified | Every PR (CI) | +| Gate readiness | How close each surface is to shipping | E2E-accepted progress-plan rows ÷ all rows for that gate | 100% before go/no-go | Weekly | +| Open work per gate | Remaining execution units | Open issues with that gate | 0 before go/no-go | Weekly | +| Source parity | Remaining v1 behavior to port | needs-port/partial counts in the assessment TSV | Decreasing; every remaining file is gated, post-cutover or not needed | After each N4 | +| Progress-plan rows without an issue | Work nobody owns | Progress-plan rows with an empty `Issue` column | 0 | Each N7/N4 pass | +| Canary health | Whether a surface is safe to widen | Crash rate, update success, import failures, v1-store access attempts (must be 0), from #14414 telemetry | Thresholds in the #14414 runbook | Daily during canary | + +## Risks + +| Risk | Mitigation | +|---|---| +| Skipped tag merges reopen the gap | N3 dry-run workflow plus a named sync owner | +| The scope rename breaks every Kilo import | N1 codemod, with a search check and `bun install`/typecheck validation | +| First-ever upstream merge, large and cross-cutting | Dedicated worktree, one slice, feature work held until green | +| `main` keeps changing without labels | N4 sweep runs before every gate, independent of the v1 team's labels | +| Split history between G1 and G3 | Re-runnable import (#14413); document it if v1 IDE and CLI share storage | +| A v1 IDE extension picks up the customer v2 `kilo` binary at G1 (VS Code does not pin a CLI version) | N8 check; if needed, G1 ships under a name or channel that v1 IDEs don't resolve | +| G1 is the first v2 release anyone uses | N17 preview release train starts right after N1 and the build issues | +| Client tracks drift from core contracts | Decision 3 lists the client items that touch core/server; they follow the standing rules and are coordinated with core work | +| Parallel agents collide | "Touches" field on every issue; the TUI issues run in series | +| Stale issue bodies mislead agents | N7 sweep of all 62 bodies | +| FIM is the largest IDE gate | Starts alongside G1 | +| Deferred-scope creep | A ship/defer/drop decision per deferred row before each gate | +| Import fixtures miss newer v1 schemas | Fixtures at the current cutoff, refreshed by each N4 | + +## Open (non-blocking) + +- Exact customer v2 path names (N8). +- A G1 decision on #14378's alerts and sound. +- The v1 end-of-life date after G3. +- Owners for every issue, plus the sync (N1/N3) and sweep (N4) lanes. diff --git a/migration-tracking/plans/kilo-opencode-v2-plan-progress.md b/migration-tracking/plans/kilo-opencode-v2-plan-progress.md new file mode 100644 index 000000000000..5a9a4771f2c2 --- /dev/null +++ b/migration-tracking/plans/kilo-opencode-v2-plan-progress.md @@ -0,0 +1,245 @@ +# Phased Plan — Port Kilo onto OpenCode v2 + +Updated: September 9, 2026 +Related issue: Kilo-Org/kilocode#13750 +Status: Implementation paused; source parity assessment complete. + +## Summary + +Port Kilo onto the native OpenCode v2 runtime while preserving the existing Kilo CLI, terminal UI, and product-client workflows. The VS Code deliverable is the **original Kilo extension**, including its interface and interactions, backed by v2. + +The original capability checklist records **31 of 43 accepted capabilities (72.1%)**. This remains a historical baseline measure of broad capabilities, not an estimate of remaining effort or VS Code completion. The expanded inventory below separates those broad capabilities into smaller behaviors and exposes gaps identified by the completed source assessment. It deliberately does not assign a new percentage: its rows differ in size, overlap across clients, and have not all received equivalent runtime acceptance. + +The source assessment covers **816 v1 files and 6,128 change annotations**, with no files left unclassified. It identifies both successful reuse and substantial remaining work. Where a detailed finding reveals a missing requirement inside a previously accepted capability, that acceptance must be revisited. + +The original extension now builds and opens from `bun run extension`. Local adapters cover multiple workflows, but the full extension typecheck and complete original-UI acceptance remain unfinished. The latest response-completion and Stop correction passed focused real-host tests; final desktop verification remains pending. + +## What this is not + +- A replacement of the Kilo interface with the upstream application's interface. +- A second legacy runtime running alongside v2 inside the same session. +- Automatic migration of a live v1 database or configuration on startup. +- A claim that source presence, compilation, or fewer upstream patches establishes behavioral parity. +- A production release or a fully verified cross-platform distribution. + +## Placement + +| Responsibility | Preferred destination | Boundary | +|---|---|---| +| Gateway authentication, catalog and request policy | `packages/kilo-gateway` | Use native provider/integration and plugin interfaces. | +| Product identity, daemon, CLI commands and composition | `packages/kilo-cli` | Own product startup and registration; reuse native runtime services. | +| Memory, indexing and sandbox engines | Corresponding `packages/kilo-*` packages | Preserve domain behavior through explicit runtime integration. | +| Original VS Code host and backend adapters | `packages/kilo-vscode` | Use public v2 client APIs and Kilo RPCs; no private Core calls from the client. | +| Original browser UI and shared Kilo components | `packages/kilo-vscode/webview-ui`, `packages/kilo-ui`, `packages/kilo-ide-ui`, `packages/kilo-i18n` | Preserve original product behavior in owned source. | +| Browser-safe Kilo RPC definitions | `packages/schema/src/kilocode` | Share one typed contract between host and clients. | +| Generic execution and persistence | Native v2 Core, Protocol, Server and Client | Extend only when an existing public interface cannot carry the requirement. | +| Shared UI/runtime extension points | Narrow upstream-facing hooks | Keep policy in owned packages and document unavoidable shared changes. | + +## Fork boundary and upstream updates + +Prefer an existing plugin interface, then a host composition interface, before changing shared upstream files. Kilo-owned copies and new Kilo packages do not need inherited patch annotations solely because their source originated upstream. Shared modifications still need clear ownership and a reason they cannot live behind an existing interface. + +A relocation is complete only when the consuming workflow preserves its required behavior. Native equivalents must be checked for scope, errors, cancellation, persistence, and presentation rather than inferred from names or file presence. + +## Store and config + +The v2 preview has separate configuration and storage from stable v1. Imports are opt-in and preserve the original source. Supported configuration mappings are explicit; unsupported values are refused rather than silently discarded. + +Settings writes use the existing configuration authority with profile/project scope and revision checks. Explicit refresh reloads configuration without disposing active sessions or terminal processes. This is distinct from supporting every v1 setting: collection semantics, feature controls, and each original client's consumption still require verification. + +## Phases + +| Phase | Deliverable | Exit criteria | Current position | +|---|---|---|---| +| 0 — Isolated identity | Separate preview binary, branding, configuration and data. | v1 and v2 run side by side; boot leaves the original store untouched. | Foundation implemented. | +| 1 — Kilo API integration | Kilo operations through native client and owned plugin/RPC interfaces. | Real Kilo operations work end to end without private runtime dependencies in clients. | Foundation implemented; service-specific gates remain. | +| 2 — Native conversation | Streaming, tools, permissions, interrupt, resume and reconnect. | A native conversation reaches idle and survives supported lifecycle transitions. | Runtime paths implemented; product-client acceptance remains. | +| 3 — Runtime integrations | Gateway, memory, indexing, sandbox, telemetry, agents and skills. | Required behavior is implemented or explicitly superseded, with consumer evidence. | Broad coverage; detailed parity gaps remain. | +| 4 — CLI / TUI remainder | Commands, settings, presentation, remote, cloud and packaging. | CLI/TUI requirements pass their behavior-specific acceptance. | Many broad capabilities accepted; audit findings require follow-up. | +| 5 — Product clients | Original VS Code extension, Agent Manager and JetBrains. | Original workflows operate through v2 without mixing v1 and v2 session routes. | Original VS Code port in progress. | +| 6 — Import / canary / cutover | Explicit migration and isolated rollout. | Imports preserve originals; distribution and rollout gates pass. | Import foundations implemented; release gates open. | + +## Inventory and progress + +### Reading the inventory + +> **End-to-end acceptance is incomplete.** Implementation describes code coverage, not confirmation that the full capability works end to end. Verification applies only to the tested scope; no row below claims complete E2E acceptance. + +- **Implemented**: the stated slice has an implementation and recorded local verification; this is not a release-wide guarantee. +- **Native reuse**: v2 supplies the stated generic behavior; Kilo-specific differences are listed separately. +- **Partial**: working behavior exists, with concrete missing scope or acceptance. +- **Needs port**: required behavior has no established complete implementation. +- **Deferred**: identified scope awaits a later implementation or external dependency. +- **Not needed**: the original item is obsolete in the target design. + +Verification: **Local checks passed** means recorded focused tests or isolated integration checks for the stated slice. **Pending** means acceptance remains open. Partial rows may have passing tests for individual paths; their scope notes describe the limits. + +### Identity, Gateway and model policy + +| Capability | Destination | Phase | Implementation | Verification | Remaining scope / acceptance | +|---|---|---|---|---|---| +| Kilo branding and isolated preview identity | Kilo CLI and theme integration | 0 | Implemented | Local checks passed; full E2E pending | Full legacy theme catalogue is a separate requirement. | +| Separate configuration/data directories | Kilo CLI host | 0 | Implemented | Local checks passed; full E2E pending | Preserve isolation during release and import rollout. | +| Local daemon, serve and attach | Kilo CLI host | 1 | Implemented | Local checks passed; full E2E pending | Each client must verify its connection lifecycle. | +| Native generated client | v2 Client / Protocol | 1 | Native reuse | Kilo integration E2E pending | Keep Kilo client adapters on public contracts. | +| Gateway device authentication and profile | Kilo Gateway | 1 | Implemented | Local checks passed; full E2E pending | Original extension authentication variants remain partial. | +| Organization selection and scoped requests | Kilo Gateway and clients | 1 | Implemented | Local checks passed; full E2E pending | Complete original-client acceptance and external service verification. | +| Catalog, BYOK and model routing | Kilo Gateway | 1, 4 | Implemented | Local checks passed; full E2E pending | Preserve provider-specific behavior in original UI adapters. | +| Catalog system-prompt selectors | Kilo CLI model policy | 1, 4 | Implemented | Local checks passed; full E2E pending | All eight selectors have source-backed adaptations and local wire coverage. | +| Training/privacy request policy | Kilo Gateway and settings | 3, 4 | Implemented | Local checks passed; full E2E pending | Verify remaining original settings consumers. | +| Startup catalog snapshot reuse | Kilo Gateway | 1 | Implemented | Local checks passed; full E2E pending | Identity/age checks and retention on transport failure are locally covered. | +| Model information, pricing and context tiers | Kilo CLI model-info UI | 4 | Implemented | Local checks passed; full E2E pending | Original picker-attached presentation is a separate partial item. | +| Account balance, Pass and routed-model display | Kilo CLI sidebar | 4 | Implemented | Local checks passed; full E2E pending | Local renderer coverage does not verify deployed account services. | + +### Conversation runtime and behavior + +| Capability | Destination | Phase | Implementation | Verification | Remaining scope / acceptance | +|---|---|---|---|---|---| +| Durable prompt admission and execution | Native v2 sessions | 2 | Native reuse | Kilo integration E2E pending | Preserve client event translation and lifecycle boundaries. | +| Streaming conversation, history and resume | Kilo CLI over native client | 2 | Implemented | Local checks passed; full E2E pending | Original VS Code end-to-end acceptance remains partial. | +| Permissions and questions/forms | Native v2 with Kilo adapters | 2 | Implemented | Local checks passed; full E2E pending | Validate each original UI caller and policy variant. | +| Response completion and Stop in original extension | VS Code session adapter | 2, 5 | Partial | Full E2E pending; see scope notes | Focused host regression passes; final desktop verification pending. | +| Session fork, compaction and snapshots | Native v2 runtime | 2 | Native reuse | Kilo integration E2E pending | Generic functionality does not cover all Kilo snapshot/overflow rules below. | +| Offline wait and network recovery interaction | Kilo session policy / client integration | 2, 4, 5 | Needs port | Pending | Restore detection, user reply/reject, cancellation and visible waiting lifecycle. | +| Kilo overflow accounting and proactive thresholds | Kilo policy over native compaction | 2 | Partial | Full E2E pending; see scope notes | Native overflow rebuild exists; exact token/economic threshold behavior is unproven. | +| Slow snapshot interaction | Snapshot policy and UI | 2, 4, 5 | Partial | Full E2E pending; see scope notes | Timeout question, persisted disable choice and initialization feedback remain missing. | +| Snapshot retention, concurrency and detailed restore/diff behavior | Snapshot runtime and client adapters | 2, 5 | Partial | Full E2E pending; see scope notes | Audit identifies pruning, locking, validation and editor detail gaps. | +| Native session transfer and explicit foreign-format import | Kilo CLI | 4, 6 | Implemented | Local checks passed; full E2E pending | Preserve source data and verify supported formats independently. | +| Session message deletion for original client | Public runtime operation and VS Code adapter | 5 | Needs port | Pending | No established complete replacement for the original caller. | + +### Runtime integrations, settings and CLI + +| Capability | Destination | Phase | Implementation | Verification | Remaining scope / acceptance | +|---|---|---|---|---|---| +| Memory engine and CLI/TUI operations | Kilo memory and CLI plugin | 3, 4 | Implemented | Local checks passed; full E2E pending | Durable injection indicators and detailed original-client parity remain separate. | +| Original extension memory adapter | VS Code backend and shared Memory RPC | 5 | Implemented | Local checks passed; full E2E pending | Full UI acceptance remains pending. | +| Codebase indexing runtime | Kilo indexing integration | 3 | Implemented | Local checks passed; full E2E pending | Original extension write controls and remaining cloud/indexing paths are incomplete. | +| Shell sandbox confinement | Kilo sandbox / CLI | 3 | Implemented | Local checks passed; full E2E pending | Cross-platform release validation remains open. | +| Local MCP spawn confinement | Kilo host policy and native MCP hook | 3 | Implemented | Local checks passed; full E2E pending | Lifecycle and fail-closed behavior have focused local coverage. | +| Interactive PTY refusal under sandbox | Kilo host decorators | 3 | Implemented | Local checks passed; full E2E pending | Covers normal and persistent terminal creation. | +| Original sandbox controls and v1 policy variants | Kilo settings and VS Code adapter | 3, 5 | Partial | Full E2E pending; see scope notes | Settings, toggle and inheritance semantics need full parity. | +| Telemetry integration | Kilo runtime integration | 3 | Implemented | Local checks passed; full E2E pending | Original extension capture/proxy surface remains separate. | +| Swarm orchestration | Kilo plugin and host policy | 3 | Implemented | Local checks passed; full E2E pending | Does not complete Agent Manager or notebook control planes. | +| Skills and custom agents | Native services with Kilo discovery | 3 | Implemented | Local checks passed; full E2E pending | Original removal/management callers remain incomplete. | +| Project configuration discovery | Kilo CLI host | 0, 3 | Implemented | Local checks passed; full E2E pending | Keep native configuration folding authoritative. | +| Profile/project scalar settings | Kilo settings store and RPC | 4 | Implemented | Local checks passed; full E2E pending | Supported fields are explicit; no general v1 settings parity claim. | +| Provider, agent, permission and policy collections | Kilo settings store and adapters | 4, 5 | Implemented | Local checks passed; full E2E pending | Complete all original editor flows and collection semantics. | +| Configuration diagnostics and live refresh | Kilo settings RPC / native reload | 4, 5 | Implemented | Local checks passed; full E2E pending | Real-host regression preserves session and PTY identity. | +| Generic review command | Native command plugin | 4 | Native reuse | Kilo integration E2E pending | Kilo review policy is maintained separately. | +| Kilo review policy and Plan workflow | Kilo CLI plugins | 4 | Implemented | Local checks passed; full E2E pending | Remaining presentation differences follow the source audit. | +| Headless prompt, resume and explicit auto mode | Kilo CLI | 2, 4 | Implemented | Local checks passed; full E2E pending | No implicit approval policy changes. | +| Piped input, local attachments and final JSON | Kilo CLI | 4 | Implemented | Local checks passed; full E2E pending | Keep per-command contract verification. | +| CLI model/agent inventory | Kilo CLI | 4 | Implemented | Local checks passed; full E2E pending | Provider-specific UI selection differences remain separate. | +| ACP host surface | Kilo CLI over native ACP | 4 | Implemented | Local checks passed; full E2E pending | Generic native ACP alone is not the product launcher. | + +### Terminal UI details + +| Capability | Destination | Phase | Implementation | Verification | Remaining scope / acceptance | +|---|---|---|---|---|---| +| Kilo sidebar and process lifecycle | Kilo TUI plugin | 4 | Implemented | Local checks passed; full E2E pending | Local real-host restart and live-PID tests cover the stated process behavior. | +| Persistent appearance/input settings | Kilo host and native TUI settings | 4 | Implemented | Local checks passed; full E2E pending | Does not imply all legacy bindings or themes are present. | +| Picker policy and model details | Kilo model policy and native picker | 4 | Partial | Full E2E pending; see scope notes | Exact inline wide preview and section/search interaction differ. | +| Provider/integration dialogs | Native integration UI with Kilo policy | 4 | Partial | Full E2E pending; see scope notes | Provider-specific guidance, failure details and automatic method presentation remain. | +| Legacy Kilo theme catalogue and fallback | Owned theme assets / TUI integration | 4 | Partial | Full E2E pending; see scope notes | Complete selectable Kilo, Kilo-v1 and colorblind themes are not established. | +| Completion, abort and timeout notifications | TUI notification policy | 4 | Partial | Full E2E pending; see scope notes | Interrupted/subagent completion and friendly timeout behavior differ from v1. | +| Session scope switching and list actions | TUI session UI | 4 | Needs port | Pending | Preserve audited scope switching, empty-state actions and export choices. | +| Location-filtered direct event subscriptions | TUI event integration | 4 | Partial | Full E2E pending; see scope notes | Native location metadata exists; foreign-project filtering regression remains. | +| Multiline alerts and fixed-width Markdown tables | TUI presentation | 4 | Needs port | Pending | Exact legacy rendering behavior is not established. | +| Legacy feedback/Vim/news bindings and default sound | TUI configuration and actions | 4 | Needs port | Pending | Native primitives do not establish the original bindings and defaults. | +| Status/footer guidance and presence display | Kilo TUI presentation | 4 | Partial | Full E2E pending; see scope notes | Version/config guidance, onboarding and remote presence details remain. | + +### Remote, sharing and distribution + +| Capability | Destination | Phase | Implementation | Verification | Remaining scope / acceptance | +|---|---|---|---|---|---| +| Remote transcript, queue and interaction translation | Kilo remote adapter | 4 | Partial | Full E2E pending; see scope notes | Local coverage exists; deployed relay/consumer acceptance remains open. | +| Remote inline attachments and capability advertisement | Kilo remote adapter | 4 | Implemented | Local checks passed; full E2E pending | External attachment workflow acceptance remains pending. | +| Remote session detach/exit | Kilo remote adapter | 4 | Implemented | Local checks passed; full E2E pending | Local ownership, cancellation and heartbeat fence are covered. | +| Remote suggestions | Kilo suggestion tool and adapter | 4 | Implemented | Local checks passed; full E2E pending | Local subscription, settlement, replay and cancellation covered; not original IDE suggestion parity. | +| Share/unshare/fork client operations | Kilo Gateway and client UI | 1, 5 | Partial | Full E2E pending; see scope notes | Local paths exist; full backend/viewer acceptance remains open. | +| Public shared-transcript viewer | Cloud viewer | 1, 5 | Needs port | Pending | Verify rendering of self-contained v2 messages. | +| Team sharing ownership | Gateway and sharing backend | 1, 5 | Deferred | Pending | Keep team upload refusal until backend acceptance is established. | +| Incremental sharing and deletion/revert semantics | Sharing backend and synchronization | 1, 5 | Needs port | Pending | Settle tombstone or replacement contract before enabling synchronization. | +| Cloud CLI client | Kilo CLI / Gateway | 4 | Implemented | Local checks passed; full E2E pending | External integration is not established by local fixtures. | +| Viewer presence registry and relay | Kilo presence service and RPC | 3, 5 | Partial | Full E2E pending; see scope notes | Complete host registration, consumers and real-host integration; relay verified locally only. | +| Local directory update and rollback | Kilo updater | 4 | Implemented | Local checks passed; full E2E pending | Real artifact staging, integrity and pointer switching locally verified. | +| Archive update transport | Kilo packaging/updater | 4 | Deferred | Pending | Safe extraction tooling remains gated. | +| Signing, hosted updates and platform delivery | Release tooling | 4, 6 | Deferred | Pending | Signing/notarization, distribution and full platform acceptance remain open. | +| Kilo CI and build hardening | Repository workflows/tooling | 6 | Partial | Full E2E pending; see scope notes | Audit identifies remaining cache, native-build and platform workflow requirements. | + +### Original VS Code and remaining clients + +| Capability | Destination | Phase | Implementation | Verification | Remaining scope / acceptance | +|---|---|---|---|---|---| +| Original sidebar chat | Kilo VS Code host and webview | 5 | Partial | Full E2E pending; see scope notes | Original UI opens; finish contracts and native end-to-end acceptance. | +| Original editor tabs and restore | Kilo VS Code panels | 5 | Partial | Full E2E pending; see scope notes | Verify streaming, restore and independent panel session routing in original UI. | +| Original settings webview | Kilo VS Code and settings RPC | 5 | Partial | Full E2E pending; see scope notes | Backend slices work; complete all original controls and persistence acceptance. | +| Automatic local server connection | Kilo VS Code connection/startup | 5 | Implemented | Local checks passed; full E2E pending | Complete lifecycle acceptance alongside remaining original workflows. | +| Models, accounts and authentication adapter | Kilo VS Code backend | 5 | Partial | Full E2E pending; see scope notes | Tested basic flows; custom-provider and authentication variants remain. | +| Enhance prompt / commit / branch generation | Kilo generation RPC and VS Code adapter | 5 | Partial | Full E2E pending; see scope notes | Stateless backend verified; complete original editor/SCM consumers. | +| Terminal lifecycle and script terminals | Kilo VS Code adapter and ticket bridge | 5 | Partial | Full E2E pending; see scope notes | Real host/WS tests pass; complete native original-panel acceptance. | +| Agent Manager and notebook workflows | Original webviews with v2 services | 5 | Partial | Full E2E pending; see scope notes | UI builds; control-plane behavior is incomplete. | +| Original notifications and suggestions | Kilo VS Code backend/UI | 5 | Needs port | Pending | Complete the original request/reply and lifecycle contracts. | +| Agent and skill removal | Kilo VS Code management adapters | 5 | Needs port | Pending | Preserve original management semantics through public authority. | +| Task timeline and editor diff viewer | Original VS Code UI and adapters | 5 | Partial | Full E2E pending; see scope notes | Source availability does not establish full projection/detail behavior. | +| Inline autocomplete/FIM and next-edit | Editor services and Gateway | 5 | Needs port | Pending | Original editor consumers and service contracts remain. | +| Voice / speech-to-text | Original capture UI and transcription service | 5 | Deferred | Pending | Client integration and external provider acceptance remain. | +| Image generation and Claw | Feature-specific client/backend integrations | 5 | Needs port | Pending | Complete feature service contracts and consumers. | +| JetBrains plugin | Kilo v2 client integration | 5 | Deferred | Pending | Not accepted as ported. | +| Kilo Console capability in original checklist | — | — | Not needed | Not applicable | Recorded as obsolete in the baseline inventory. | + +### Import and cutover + +| Capability | Destination | Phase | Implementation | Verification | Remaining scope / acceptance | +|---|---|---|---|---|---| +| Explicit credential import | Kilo CLI import | 6 | Implemented | Local checks passed; full E2E pending | Supported mappings only; preserve source credentials. | +| Kilo configuration key mapping | Kilo CLI import | 6 | Implemented | Local checks passed; full E2E pending | Unsupported-feature refusals are import safety, not feature completion. | +| Schema comparison and fixture migration | Kilo import tooling | 6 | Implemented | Local checks passed; full E2E pending | Continue coverage as supported mappings evolve. | +| Canary identity and release cutover | Product/release integration | 6 | Partial | Full E2E pending; see scope notes | Preview isolation exists; production rollout acceptance remains. | + +## Check v2 overlap before porting v1 work + +Reuse native permissions, MCP, compaction, session fork, plugins, subagents, daemon transport and other generic services. Compare the exact Kilo policy before treating those services as equivalent. For example, native retry does not implement the original offline-wait interaction, and native snapshot capture does not supply the original slow-snapshot question. + +The complete file assessment has the following dispositions: + +| Source disposition | Files | +|---|---:| +| Already ported | 65 | +| Native equivalent | 137 | +| Partial | 233 | +| Needs port | 163 | +| Deferred | 68 | +| Not needed | 150 | +| **Total** | **816** | + +These file counts are not interchangeable with the inventory rows above. They include source, tests, configuration and tooling; a single capability can span many files, and one file can contain multiple behaviors. + +## Current implementation and validation + +Recorded verification includes focused package tests, isolated real-host execution, loopback service contracts, rendered UI fixtures, terminal round trips, and real local artifact apply/rollback. Earlier successful runs apply to their checkpoints and are not evidence that every current package is green. + +The original extension and its six webviews/two workers build. The latest lifecycle regression verifies completed responses return to idle and interruption succeeds even without cached local activity. Full extension typechecking still fails on incomplete port surfaces, so the preview package's `typecheck` script now skips with a pointer to #14016 while `typecheck:port` retains the real check. Final desktop verification of that correction remains unrun. + +No new runtime acceptance is claimed by this document update or by completion of the source audit. External service verification, production distribution, and full platform validation remain open. + +The `packages/kilo-cli` test harness is now self-sufficient: interactive/TUI proofs skip with a named reason when `dist/interactive` is absent, and a package-only `test:interactive` script runs them after `bun run build:tui` (see `kilo-opencode-v2-working-log.md`, 2026-09-24). + +## Next steps + +1. Resolve the original extension's missing contracts and typecheck failures, then run original-UI acceptance for chat, Stop, reconnect, panels, settings and terminals. +2. Prioritize audited behavior gaps and revisit broad accepted capabilities wherever the detailed requirements are not met. +3. Complete remaining remote/sharing contracts and client services, followed by external verification when permitted. +4. Finish packaging, platform and canary/cutover acceptance before release. + +## Subissues + +- [#14016 — Complete original Kilo VS Code client parity](https://github.com/Kilo-Org/kilocode/issues/14016) +- [#14017 — Complete remaining runtime and CLI/TUI parity](https://github.com/Kilo-Org/kilocode/issues/14017) +- [#14018 — Complete v2 session sharing compatibility and acceptance](https://github.com/Kilo-Org/kilocode/issues/14018) +- [#14019 — Complete remote session consumer acceptance](https://github.com/Kilo-Org/kilocode/issues/14019) +- [#14020 — Complete distribution and safe migration cutover](https://github.com/Kilo-Org/kilocode/issues/14020) +- [#14021 — Complete remaining editor services](https://github.com/Kilo-Org/kilocode/issues/14021) +- [#14022 — Port the JetBrains plugin to v2](https://github.com/Kilo-Org/kilocode/issues/14022) +- [#14023 — Assess and integrate cloud agent consumers with v2](https://github.com/Kilo-Org/kilocode/issues/14023) +- [#14024 — Assess and integrate Anaconda Desktop and related clients with v2](https://github.com/Kilo-Org/kilocode/issues/14024) diff --git a/migration-tracking/plans/kilo-opencode-v2-working-log.md b/migration-tracking/plans/kilo-opencode-v2-working-log.md new file mode 100644 index 000000000000..e52e10068374 --- /dev/null +++ b/migration-tracking/plans/kilo-opencode-v2-working-log.md @@ -0,0 +1,1889 @@ +# Phased Plan - Port Kilo onto OpenCode V2 + +> [!IMPORTANT] +> Local working copy of +> [Kilo-Org/kilocode#13750](https://github.com/Kilo-Org/kilocode/issues/13750). +> Originally captured on 2026-09-04 from the issue version updated +> 2026-09-04T13:55:36Z; local progress and corrections updated 2026-09-05. +> Johnny will publish these changes to GitHub manually. Continue maintaining +> this file locally; local edits do not imply the GitHub issue has been updated. + +> Work In Progress + +The separate [scenario test plan](kilo-opencode-v2-test-plan.md) tracks detailed +CLI/TUI and runtime checks. Manual test results are recorded separately from +this implementation inventory; writing a checklist earns no completion credit. + +## Active continuation target + +Read-only audit references: [pre-cleanup v2 marker inventory](kilo-override-marker-inventory.md) and [v1-to-v2 marker assessment](v1-kilo-marker-port-assessment.md). All 816 marker-bearing files (6,128 occurrences) now have source-assessment dispositions: 65 already ported, 137 native equivalent, 233 partial, 163 needs port, 68 deferred, and 150 not needed. This completed read-only inventory does not add implementation or runtime acceptance credit; implementation remains paused. + +### Paused checkpoint — 2026-09-08 (GitHub #13750 / local task JOH-7) + +**Implementation is paused at Johnny's request after the bounded response/Stop +fix below. Do not resume implementation or restart delegates without a new +instruction.** This is the local copy of GitHub issue #13750; no GitHub update +or publication was performed. + +- The default `bun run extension` entry now builds and opens the **original Kilo + extension UI**, not the superseded white upstream-app wrapper. Original sidebar, + editor and agent-manager webviews are built from the transplanted local sources. + The development launcher uses an isolated short profile/extensions directory, + removes inherited VS Code launcher variables, and focuses the Kilo sidebar. + The empty drag-and-drop import warning was removed. No desktop automation was + resumed for these bounded fixes. +- Original-client adapters have local coverage for sessions/prompts/history, + permissions/questions, models/accounts, settings/collections/refresh, memory, + stateless generation, and ticket-based terminals. These are implemented slices, + **not full VS Code parity or complete UI acceptance**. Johnny reports that some + workflows now work. The stale shared daemon was restarted with explicit approval; + the previously missing settings `collectionGet` method was then verified live. +- **Response remained thinking / Stop did nothing:** the event adapter listened + for legacy `session.status` but did not translate native + `session.execution.started/succeeded/failed/interrupted`. It now emits the + original UI's busy/status and turn-close messages, with completion after final + message projection. Shutdown is not reported as a completed user turn. Successful + Stop now acknowledges the native interrupt even when local active tracking is + absent, allowing the provider to clear stale UI state. +- Verification: `packages/kilo-vscode/test/backend-session.test.ts` passes against + an isolated real host with external networking denied. Its child fixture checks + busy → completed → idle, Stop on an already-idle session with no cached activity, + and Stop on an active permission-blocked request, including the interrupted event + and removal from native active sessions. No live model request was sent. The + changed lifecycle files have no typecheck diagnostics; the full extension + typecheck still fails on incomplete port surfaces. The original extension and + all six webviews/two workers rebuilt successfully; reload the development + extension window to use the new bundle. Desktop verification remains unrun. +- **Remaining before full port:** missing original backend consumers (including + sandbox controls, message deletion, agent/skill removal, notifications, + suggestions, agent-manager/notebook control planes and provider/auth gaps), + feature-specific services (FIM/next-edit, speech, image generation, Claw and + remaining cloud/indexing paths), presence integration handback, and full native + end-to-end acceptance. Existing logs still contain unrelated missing-method + errors. Review the detailed [existing-port evidence](../kilocode/baseline/existing-vscode-port.md) + and task-board handbacks before resuming; do not replace missing methods with + successful no-ops. + +Whole-row baseline coverage remains **31/43 (72.1%)**, not a percentage of the +VS Code port. No additional row is closed by this checkpoint. Local-only work +and the prohibition on external-host verification remain in force. + +### Existing VS Code extension port — scope correction 2026-09-08 + +Johnny clarified that phase 5 must preserve the existing Kilo VS Code extension, +including its UI and workflows, while replacing the backend with v2. The earlier +upstream-app embedding does not satisfy that requirement. Its connection, terminal, +packaging, and native-test evidence remains useful infrastructure, not acceptance +of the existing extension port. Sidebar/editor/settings rows are reopened; current +whole-row coverage is **31/43 (72.1%)** until the real surfaces pass acceptance. +Source: local Kilo commit `d99662338e3ddbd2613ab41fc0369837a6eb4be9`. + +### Earlier app-wrapper preview acceptance — superseded scope + +The latest goal is to implement the VS Code extension. Sidebar chat and editor +conversation rows now pass acceptance through a locally installed VSIX in the +actual VS Code workbench. Historical preview coverage was **34/43 (79.1%)**, meeting the earlier +79% target without changing the denominator or crediting partial rows. +All staging and acceptance are local-only; external deployment remains unverified. + +The native run covers sidebar streaming, editor permission approval and model +continuation, independent editor sessions, reconnect, full window reload with +restored conversations, settings persistence to the profile file, New Chat, +and Disconnect. The installed artifact is self-contained; terminal ticket +transport has separate real-host acceptance and Node runtime checks. +See [IDE evidence](../kilocode/baseline/ide-preview-integration.md). + +### Earlier Goal 79% continuation — 2026-09-07 + +Remote heartbeats now advertise the implemented attachment capability, enabling +the consumer's attachment UI without advertising unsupported session cloning. +Remote suggestions now support accept/dismiss, reconnect replay, and one +follow-up admission, with viewer eligibility and interruption cleanup. The +focused remote suites pass (30 tests, 374 assertions). CLI and Gateway +typechecks pass; portable preview `build-r6q70z` builds and launches. + +The updater now has real local directory staging and CLI check/apply/rollback. +Parent acceptance through the rebuilt bundled command copied the actual +718,102,380-byte portable artifact (67,140 entries), ran its Kilo launcher, +and rolled back while retaining both builds. +Unit coverage and independent review pass for local staging/rollback; this does +not establish other-platform packaging, archive distribution, or release gates. +See [updater evidence](../kilocode/baseline/updater-local-parity.md). +Sharing's viewer compatibility and post-share deletion/revert contracts remain +open; no external deployment has been accessed or credited. +An isolated cached-runtime attempt stopped during bundling on missing +`pg@8.20.0`; the actual sharing service has not passed a local boot check. +No missing dependency was downloaded. + +The [remaining-scope audit](../kilocode/baseline/goal79-scope-boundary.md) +classifies the three cross-client rows as editor-dependent work; host-only +ports cannot close them. Even closing remote and packaging would yield 33/43. +Following the user's "Continue the goal" instruction after that scope question, +local IDE implementation is reopened. External hosts remain prohibited. + +### IDE settings accepted — 2026-09-08 + +The VS Code settings webview row is accepted using the existing Kilo settings +RPC, with no shared App/Core/Server/Protocol changes. The extension command +opens the dedicated Kilo-owned screen; all 18 managed fields use source-backed +controls and profile/project revisions. Real browser acceptance proves +Save/reopen/Reset, project isolation, stale-edit refusal, explicit boolean +drafts, and a narrow viewport. Native extension command activation passes; +VS Code main-process shutdown requires separately reported forced cleanup. +The existing CLI settings suite passes (21 tests, 155 assertions), package +typechecks/build pass, and independent composition/UI reviews find no blockers. +See [IDE evidence](../kilocode/baseline/ide-preview-integration.md). +Sidebar and editor conversation acceptance was subsequently completed through +the installed artifact, as recorded above. + +### Goal 70% accepted — 2026-09-07 + +Accepted coverage is **31/43 (72.1%)**, meeting the requested 70% target. +This batch closes **`kilo.jsonc` key mapping**, **Gateway catalog/BYOK/org +routing**, **Sandbox PTY/MCP/git spawn policy**, and **CLI TUI remainder**. +The denominator stays 43; partial/external-gated rows receive no new credit. + +The importer now maps v1 top-level `subagent_depth` to the native v2 +`experimental.subagent_depth`, preserving the pinned v1 runtime's top-level +precedence and the identical nonnegative-integer constraint. Authored policies +and other experimental leaves survive. Evidence: **49 importer tests / 216 +assertions**, **34 settings/request-policy/model-picker tests**, clean package +typecheck, and parent source review of both actual depth consumers. The new +real isolated-host check reads the imported value through public `config.get`. +See [mapping evidence](../kilocode/baseline/config-mapping-v2-gap.md). + +Gateway closure combines the existing native protocol/BYOK/routing and actual +personal/team scope acceptance with all eight source-adapted prompt selectors. +The final prompt checks preserve native composition and user constraints +(**2 tests / 62 assertions**); the complete Gateway package passes **72 tests / +764 assertions**. The new real TUI test shows no routed-model row for an +ordinary response, then shows the actual response-selected model after Auto +execution. Catalog choices and response metadata remain distinct. The +independent prompt review found no blockers. Compiled distribution and deployed +backend verification are separate capabilities and receive no credit here. +See [prompt parity](../kilocode/baseline/model-prompt-policy-v2-parity.md) and +[local scope acceptance](../kilocode/baseline/non-ide-closure-checkpoint.md). + +Sandbox closure: the connection-scoped MCP hook confines every local spawn, +including cold add and disabled direct connect. Host decorators refuse both +ordinary and persistent terminal creation before process spawn; the latter is +the native TUI path and uses the existing typed 503 error. Other terminal +operations and daemon infrastructure remain intact. Native local evidence: +**5 PTY tests**, including stock positive controls and both sandbox refusals, +plus **20 shell/MCP regressions**, with clean CLI typecheck and independent +review. Model-requested git is confined by the shell policy; host VCS/snapshot +operations were outside the pinned v1 model-shell policy and remain trusted. +Remote MCP runs on its own host and is not claimed locally confined. No new +Core/Protocol edits were needed for either terminal decorator; the approved +shared-hook count remains eight. See +[sandbox evidence](../kilocode/baseline/sandbox-spawn-next-slice.md). + +CLI/TUI closure adds persistent session-scoped memory activity, full source-backed +model details in the relocated dialog, and real-renderer evidence for funded +account rows, actual routed models, per-model usage expansion and indexing +progress/completion. Parent final usage/indexing run: **3 tests / 6 assertions**; +memory sidebar: **4 tests / 11 assertions**; delegate model detail acceptance: +**4 scenarios**, including zero/high context tiers, short viewport and metadata +failure; account/routed: **2 tests**. Existing PR/process restart and agent/Plan +acceptance is retained. The source-reviewed sync-failure toast is not claimed as +fault-injected. Manual UI-081/082/083 statuses remain NOT RUN; real-host automated +equivalents satisfy this implementation row. Native throughput is reused, the +relocated dialog is an intentional v2 layout, absent process description/ports +are not invented, and the shared native branch-watcher limitation is disclosed. +See [CLI/TUI acceptance](../kilocode/baseline/cli-tui-closure-verification.md). + +Final portable build **`build-AbricZ`** includes the last tier-threshold fix. +Isolated loopback smoke passes for bundled TUI, daemon start/reuse (same PID), +and default attach/clean exit with the daemon preserved. Measured fixture times: +TUI 7,660 ms; daemon start 722 ms / reuse 205 ms; default attach 1,432 ms. +These are local smoke timings, not an apples-to-apples product benchmark. +Final CLI and Gateway package typechecks, changed-source formatting and diff +checks pass. No external deployment, production updater payload, sharing backend +or remote relay acceptance is claimed. Those rows remain open. + +This closes safe import, not every refused legacy feature. Unsupported keys +remain explicit refusals; selected supported subsets require the existing +explicit opt-in. Cross-surface presentation imports and unimplemented IDE +capabilities receive no feature credit. All verification is local-only. + + +### Goal 62% acceptance — 2026-09-07 + +Accepted capability coverage is now **27/43 (62.8%)**. The newly accepted row +is **Settings scopes and remaining Kilo-only fields**. The denominator remains +43 (the obsolete Console row is excluded); earlier 26/43 checkpoints below +are historical and are not rewritten as new verification. + +Closure evidence: the Profile/Project settings adapter manages 17 native +consumer-backed fields and the Kilo training-model toggle, with native +presentation and domain controls reused. The new real-TUI project scenario +edits and resets an image-width override, verifies profile fallback, and boots +a second isolated host to prove persisted project configuration loads in the +correct order. Parent validation: **2 UI scenarios passed**, **38 focused +contract tests passed / 208 assertions**, package typecheck and diff check +clean. Two delegates independently reconciled the field/scope criteria; the +review's test condition is fulfilled by the parent run. Details and field +sources: [settings parity](../kilocode/baseline/settings-v2-parity.md). + +Root acceptance decisions, consistent with the recorded plan scope: + +- Reuse native reasoning and shell-output presentation controls without + claiming identical v1 semantics. Cross-surface import remains in the open + `kilo.jsonc` mapping row; it earns no migration credit here. +- Keep IDE-only display and commit-generation settings with their separate + phase-5 capabilities. Explicit unsupported-key reports are not counted as + implemented features. +- Retain the scalar settings adapter and existing per-domain controls. The + open community proposal's unified collection editor is not required by the + recorded decision to avoid porting its entire dialog, and is not claimed + implemented. Host plugin composition remains fixed. + +The CLI/TUI remainder stays open: the parallel audit found remaining visual +coverage, durable memory-marker and model-detail differences. Sandbox PTY/git, +remote, sharing, updater/distribution and config-import rows also remain open. +No external host or deployment verification was used for this acceptance. + +MCP hook continuation — 2026-09-07: after the concrete spawn-hook patch was +offered for approval, Johnny requested continuation using the shared Task board. +Proceeding with that bounded exception: one shared production file, +`packages/core/src/mcp/index.ts`, adds a host-configured, connection-scoped +spawn hook. Sandbox policy remains Kilo-owned; no PTY or protocol expansion. +Core typecheck and native MCP lifecycle regressions pass. Independent review +found no blockers; the delegate verified 13 MCP and 7 shell sandbox tests on +the native host. Parent sandboxed verification reports 7 pass / 13 skip, not +additional native acceptance. Portable build `build-udBYOF` passes isolated +loopback TUI, daemon reuse and default attach smoke checks. CLI typecheck +passes after explicit local-config narrowing in the tests. Coordination +uses the shared board inbox and acknowledgments; explicitly stopped agents +remain stopped. This does not add whole-row capability credit. + +Continuation toward 34/43 — 2026-09-07: Johnny explicitly requested sustained +work to the non-IDE target and authorized up to ten concurrent delegates. +Active acceptance lanes are settings/config, Gateway prompt selectors, sharing +metadata, remote consumer contracts, native sandbox verification, a concrete +sandbox gate proposal, local updater mechanics and process-sidebar restart +coverage. Parent owns CLI/TUI completion and integration. The implementation +and verification lanes preserve existing work and use isolated fixtures; +deployment, publishing and explicitly gated shared-hook decisions are tracked +separately. Do not grant whole-row credit from a delegate's bounded result. + +Local-only constraint — 2026-09-07: Johnny specified no external-host access. +Use local source and isolated loopback fixtures only. Skip deployed sharing, +remote relay, publication and other external checks; keep their acceptance gates +open rather than substituting fixture results. The staging-target question is +resolved as local only. + +Local integration follow-up — 2026-09-07: parent ran the combined model-info, +settings UI, prompt-policy, remote-session and updater checks (24 pass, 0 fail), +the process-sidebar UI/unit checks including actual client/server restart +(8 pass, 0 fail), and sharing/selector/cache checks (17 pass, 0 fail). A new +Gateway regression reproduced cross-account snapshot deletion on failed refresh; +the correction retains snapshots on failed reads and scopes authoritative +deletion to the matching identity, with the 60s lifetime unchanged (36 focused +Gateway tests pass, 0 fail). CLI and Gateway typechecks passed at these checkpoints. +The follow-up portable build is `build-0nz3pB`: bundled TUI smoke, repeated +service start with the same daemon PID, real terminal attach/clean exit, and +daemon survival after client exit all passed against isolated loopback fixtures. +The corrected updater fixture suite also passed (14 tests, 0 failures). +Gateway selectors now apply six verified/adapted assets (`anthropic`, `trinity`, +`anthropic_without_todo`, `codex`, `gemini`, `ling`). Gemini/Ling use actual v2 +tool parameters and respect configured permission outcomes. Final delegate +wire/unit checks passed (2 tests, 48 assertions). `gpt55` and `beast` remain +deferred, so this does not close the Gateway row. +Updater CLI exposure remains deferred: its working JSON payload is fixture-only, +and archive extraction is disabled. No whole-row acceptance credit is added. + +Local-only validation correction: the first new cache-regression attempt used +`backend.url.origin` although that fixture returns a string. This may have +attempted the default Gateway with a dummy fixture key; it failed before the +snapshot assertion and was disclosed to Johnny. The corrected test uses the +explicit loopback string and reproduces the cache bug locally. Inspection also +found the portable TUI smoke's Gateway origin was implicit; it now explicitly +uses its loopback server before the bundled smoke is run. + +The latest reliability checkpoint found two regressions in a 539-test run +(528 pass, 9 skip, 2 fail). Both are fixed and independently retested: signed-out +Gateway provider withdrawal, and sidebar claims registered at plugin activation +instead of inside a remountable app contribution. Final bundle/terminal smoke +passed. See `kilocode/baseline/reliability-checkpoint-2026-09-07.md`; this is not +a clean full-suite result or a new whole-row acceptance count. + +Johnny clarified that Agent Manager stays with VS Code and requested the +remaining non-IDE work using delegates (2026-09-06). The current target is +**34/43 (79.1%)**, the theoretical ceiling after nine IDE-related exclusions, +not the earlier 35-row target. Eight more whole rows must be accepted beyond +the current 26. Exclusions remain excluded, not completed; the denominator +stays 43. This target is not a claim that every remaining gate can close now. + +The next delegated lanes are portable packaging/updater, sharing/remote +contracts, and sandbox spawn coverage. Preserve the isolated `kilo2` preview +identity while developing packaging; publishing releases, replacing an +installed CLI, live-account writes, and new upstream patch exceptions require +their specific decisions. Implement source-backed local slices in parallel; +do not substitute fixture evidence for explicitly deployed acceptance gates. + +Latest integrated verification (2026-09-06): with all batch writers frozen, +root ran `dist/interactive/bun run script/test.ts` from `packages/kilo-cli`. +The fresh-artifact wrapper passed **501 tests / 2 skipped / 0 failures / +3,130 assertions across 86 files** in 473.71 seconds. The two skips are the +deliberately gated MCP-confinement host scenarios. This includes the portable +launcher/PTY and accepted R1/R2 work, and supersedes the earlier 481-test +snapshot. It precedes the final R4 consumer-visible streaming correction: +passing integration tests do not override that review blocker. This is not +deployed verification or evidence that remaining whole capability rows are +complete. Accepted coverage stays **26/43**. + +Current follow-up review gates (2026-09-06): remote permission/question +translation is accepted as a bounded slice after root independently reproduced +9 tests / 211 assertions and clean package typecheck, and final independent +review found no remaining blockers. Root-ID child replies, eight-parent-edge +ownership, and independent optional-domain live/replay behavior are covered. +Bounded R4 transcript translation is also accepted after root reproduced +11 tests / 257 assertions across the four remote suites and clean typecheck, +and the independent reviewer checked the settled formatted files. A held +model stream proves early assistant info and visible partial text before +completion; terminal reconciliation, worktree-root attribution, ownership +before translation I/O and defect recovery are covered. History lookup remains +bounded to the newest 200 messages. Deployed relay/viewer acceptance and the +remaining remote contracts are still open. The full-suite snapshot above +predates this final R4 correction; focused evidence does not replace it. +The bounded portable internal artifact is accepted after the corrected +no-install dependency copy, safe output creation, canonical link checks, and +single-Solid reactive client fix. Root's fresh combined relocation run passed +both native Auto wire tests and the mounted Kilo TUI model-picker/prompt/reply, +resize and exit checks; Luna confirmed the delta. Its default regression test +is enabled. The follow-up actual relocated `kilo2 serve` readiness/stop and +persistent-PTY websocket round-trip are also accepted: root and an independent +reviewer each reproduced 2 tests / 6 assertions and clean package typecheck. +Success and induced post-create failure both verify owned terminal/daemon +process death. The separate session-terminal PTY is now bounded-accepted too: +root and independent review each reproduced the five-child portable test +(2 tests / 6 assertions), including public websocket I/O, native `stty size` +after resize, and child exit on success and induced failure. The input-echo +marker alone is not command-output proof; the native size response supplies +that evidence. This is macOS arm64 evidence, not other-platform, updater, +release-channel or deployment acceptance. Current package-wide typecheck is +being re-established after the concurrent remote-status delta; portable files +had no diagnostics in the review run. +MCP registration was withdrawn after a +reproduced cold `mcp.add` activation bypass; Linux argv construction is fixed, +but an effective pre-spawn gate and disabled-server direct-connect coverage +are required before re-registration. A narrow Core MCP hook exception has +been requested, not approved. These are work-in-progress slices, not additions +to the accepted count or a new full-suite green claim. + +## Summary + +Memory save-pulse bounded acceptance (2026-09-07): user confirmed the +five-second green bullet beside `Enabled`, with no saving label or spinner. +Root independently ran 39 tests (180 assertions) across the event and CLI +memory suites; independent source review approved the producer/session/Location +attribution and lifecycle. After formatting, the affected pulse tests passed +again (5 tests / 18 assertions); package typechecks and all eight changed TS +files' formatting are clean. Durable injection markers remain pending, and +same-root/foreign-Location rejection is source-reviewed only. No whole-row +credit is added; 26/43 is unchanged. + +Fresh integration checkpoint (2026-09-07): the canonical fresh-artifact +wrapper passed **518 tests / 2 gated skips / 0 failures**, 3,212 assertions +across 89 files (491.56 seconds), including the accepted Terminal Bench +sidebar and corrected indexing renderer assertion. The two skipped MCP +sandbox activation scenarios retain their existing approval gate. This +checkpoint precedes the model-info dialog and memory save-pulse work; it +does not grant either pending slice acceptance or change the 26/43 count. + +Model-info bounded acceptance (2026-09-07): the Kilo-owned `/model-info` +dialog is production-wired and independently source-reviewed. The final +test tightening requires the exact inspected model in both scenarios; +root reran 2 tests / 4 wrapper assertions successfully (11.84 seconds), +with clean CLI typecheck and formatting. Public session state remains +unchanged while inspecting a different model. Native picker-attached +layout, full legacy detail parity, cold model-sync error handling and +empty-catalog feedback remain documented follow-ups. No whole-row credit. + +Terminal Bench sidebar bounded acceptance (2026-09-07): root reproduced +Gateway 24 tests / 329 assertions and the real-renderer sidebar's 4 tests / +8 wrapper assertions, with clean CLI typecheck. Independent review verified +the tolerant metadata decode, pinned units, durable selected-model lookup and +muted row colors. The ineffective held-response test was removed rather than +counted as evidence; identity clearing is source-reviewed defensive behavior, +not an empirically established no-stale-intermediate guarantee. The separate +model-info-panel benchmark surface, durable memory-marker tone and remaining manual +CLI/TUI checks stay pending. No whole-row credit is added. + +Post-R3 full-run result (2026-09-07): the canonical fresh-artifact wrapper +completed with **513 passed / 2 gated skips / 1 failure**, 3,203 assertions +across 88 files (496.06 seconds). `sidebar-indexing-ui.test.tsx` timed out +waiting for a renderer frame. Its immediate isolated rerun passed (1 test / +2 assertions, 3.10 seconds). A later focused run reproduced the timeout after +resize with both labels present: the right-hand scrollbar glyph between the +heading and newline defeated the whitespace-only test predicate. The fixture +now tolerates that chrome while still requiring the Disabled state on the +next non-blank line; independent review confirmed other states still fail. +The affected indexing/bench/usage/memory group passes (9 tests / +18 assertions, 20.19 seconds). This fixes the reproduced assertion failure; +the historical full-run result remains red until a fresh full run succeeds. + +Remote R3 bounded acceptance (2026-09-07): root independently reviewed the +replacement queue implementation and ran all five remote suites: **23 tests / +317 assertions, zero failures**, with clean package typecheck and formatting. +Every queue snapshot reads the authoritative pending inbox; no truncated local +queue is presented as complete. Tests cover 65-item FIFO, overtaken reads, +subscription invalidation, another client's admission/cancellation and +cursor-paginated descendant ownership beyond 32 siblings. This focused result +postdates the full-suite snapshot above and does not imply a fresh full run. + +### Explicitly pending for later + +- Remote: deployed relay/viewer verification, history/ingest authority, + suggestions and remaining attach/detach/exit/clone contracts, and the + aggregate inline-attachment budget decision. The pinned consumer discards + renamed titles; emitting that field is not delivered rename support. +- Documented remote limits: eight parent edges for descendant operations; + transcript history searches the newest 200 messages; location validation is + per node, not a transactional tree snapshot. Queue snapshots reflect pending + state, so items delivered immediately may never appear as queued. +- Packaging: platforms beyond tested macOS arm64, release identity/channels, + updater/publishing and real-account/backend acceptance. +- Other whole-row gates: six Gateway prompt selectors, remaining required + settings/config behavior, CLI/TUI parity checks, explicit approval and + validation of the MCP activation hook, and sharing's cloud/deployment gates. + +The detailed boundaries remain in `kilocode/baseline/remote-v2-parity.md`, +`packaging-correction-plan.md` and `non-ide-closure-checkpoint.md`. Deferred +items receive no completion credit; accepted whole-row coverage remains 26/43. + +Gateway scope acceptance (2026-09-07): the new real-host loopback fixture is +bounded-accepted after root's run and independent source review plus three +stable repeats (1 test / 2 wrapper assertions; detailed assertions live in the +child fixture). Two Locations exercise personal/team/personal eligibility, +metadata RPCs, organization headers, response-derived routed model identity, +stale selected-model rejection without a Gateway request, and unavailable +catalog drain/recovery. This satisfies the local "real scopes" behavior +criterion, not deployed-account verification. Six legacy prompt selectors +remain unported, so the Gateway row and 26/43 total remain unchanged. + +Migrate Kilo onto the OpenCode v2 architecture as a **target-shaped port**, not +a merge of `main` into `v2`, and not a second `packages/opencode` host. + +- Branch: `kilo-v2`, tracking a **pinned** `upstream/v2` SHA (record in the repo + baseline doc). Merge forward once history is shared; perpetual rebase is not + the standing policy. +- `main` stays the V1 product line. +- Related (different approach): + [#12887](https://github.com/Kilo-Org/kilocode/issues/12887) + +## What this is not + +- Wholesale `main` merge into the v2 branch +- Keeping `packages/opencode` as a legacy host on v2 (that package is gone on + `origin/v2`) +- Dual-running V1 and V2 against the same SQLite file or the same live + `kilo.jsonc` +- Ambient V1→V2 migration on preview startup +- Implementing the whole product as a stack of plugins (plugins are owner 3. + Identity, SQLite path, daemon topology, and IDE backends are host. + Protocol/Schema/Server edits are the exception, not the default.) + +## Placement + +Each Kilo capability maps to **one** owner: + +1. **Catalog / provider** — `catalog.transform`, or a native `packages/ai` + provider, for model/protocol only. +2. **Host** — product binary and daemon topology. Current path is a from-scratch + `packages/kilo-cli` (upstream `packages/cli` does not export a wrap surface + for commands, service manager, updater, or ACP). Cost: re-own `kilo serve` / + daemon / attach, updater, ACP. Alternative not taken: build the upstream CLI + under Kilo identity (`OPENCODE_CLI_NAME` / channel defines) plus a narrow + app-name hook. VS Code, JetBrains, and Agent Manager are Protocol clients of + the local server — not OpenCode's `sdks/vscode`. +3. **Plugin** — Location-scoped `kilo-*` packages on upstream seams: + `catalog.transform` / integration transforms, `Tool.Service` registration + (executable tools; later transform wins on name), `session.context` + (prompt/tool metadata only — that hook cannot invent executables), + `tool.execute.before` / `.after`, `permission.evaluate`. Plugins do not add + Protocol endpoints or become the host. + +Clients depend on Schema and Protocol only. Core and Server stay behind HTTP. +Do not call private Core from Gateway or UI. + +Prefer, in order: `kilo-*` plugin seams → host `ServerOptions` / identity → +Protocol / Schema / Server only when an upstream endpoint's shape must change → +shared-file patch (marked, counted). + +## Fork boundary and upstream updates + +The dependency graph is the protection; markers are leftover debt. + +Prefer, in order: + +1. `kilo-*` packages (`kilo-gateway`, `kilo-memory`, `kilo-indexing`, + `kilo-sandbox`, VS Code, JetBrains). These are Kilo-owned additions, not + upstream patches. +2. Upstream v2 seams: `catalog.transform` or native `packages/ai`; + Location-scoped plugin hooks; `ServerOptions` on the host; Kilo-owned + Protocol / Schema / Server modules only when the public contract needs a + Kilo field. +3. Kilo-prefixed paths inside packages we wrap (`packages/cli`, Protocol / + Schema / Server). Do not create a new Kilo subtree inside + `packages/core/src/session/runner`. +4. Shared-file patches only when no seam exists. Mark each one with the existing + Kilo marker convention and keep an exact count. The count must not grow + without an explicit exception. + +**Current approved shared-patch count on `kilo-v2`: 8** (`AGENTS.md`; +`packages/plugin/src/tui/context.ts` `home.logo` slot; +`packages/tui/src/app.tsx` pluginDirectories; +`packages/tui/src/routes/home.tsx` Slot `home.logo`; +`packages/core/src/plugin/sdk.ts` host-enforced post registration; +`packages/core/src/plugin/supervisor.ts` post-config activation; +`packages/core/src/plugin/system-prompt.ts` public exports of its existing +Anthropic/Trinity prompt strings only; `packages/core/src/mcp/index.ts` +connection-scoped host spawn hook). The MCP exception follows the concrete +patch and continuation recorded above. The prompt-export exception is the +narrow option A explicitly offered by Johnny and selected on 2026-09-05: +reuse maintained assets without private raw-file imports or copied prompts. +It does not authorize changing native prompt dispatch or other Core files. +The bounded selector handoff is implemented and independently reviewed below. +The two-file +Core exception was explicitly approved on 2026-09-04 for fail-closed Gateway +routing. Any other shared worktree diff remains outside the approved count and +must be removed or separately approved before landing. + +Take `upstream/v2` in controlled steps. Do not merge `main` wholesale into +`kilo-v2`. Avoid Core forks for the migrator, runner, and private APIs; those +are the highest merge tax. + +## Store and config + +V1 session DB and live `kilo.jsonc` are not compatible with this port. Sharing +them corrupts data. + +OpenCode v2's `V1Migration.transformSession` rewrites the SQLite file it opened +(Bun only; Node/workerd is a no-op). OpenCode maps mixed v1/v2 config in memory +and does not rewrite the file. + +**Rules** + +- **Preview identity is not a customer SKU.** Internally the preview uses a + separate application identity (`kilo2`) so paths cannot collide with stable + `kilo` or with OpenCode. Do not ship `kilo2` to customers. The product name + stays `kilo`. When v2 becomes the default `kilo` binary, move v1 data/config + dirs aside first, then take the `kilo` paths. +- Same project directory is Location, not the DB file. One SQLite per + application graph. +- **Boot:** create/open only the `kilo2` file (empty if missing). No `session` + table means the migrator does not run. If the resolved path is the live Kilo + or OpenCode store, refuse to serve. Assert/test that upstream + `V1Migration.layer` is a no-op on empty `kilo2` (no `session` table). The + ambient fiber may still start; that is upstream debt to watch, not a Core + fork. +- **Import (phase 6):** explicit and opt-in. Copy the live V1 DB, migrate the + copy, never the original. Copy selected config keys into a new file; never + rewrite live `kilo.jsonc`. The host (`packages/cli` / `kilo-cli`) owns + detection, prompt, and copy-import — not a plugin. Do not ship the prompt + until the importer exists. Precondition: a read-only schema diff of Kilo + `main`'s store against the migrator's expected `session` / `session_message` / + `event` tables, tested on a real Kilo `main` fixture. Note: upstream migrator + also imports a sibling `opencode-next.db` when present. Also import + `auth.json` → v2 credentials and map Kilo-only `kilo.jsonc` keys (upstream + in-memory migrate only knows upstream v1 keys). +- **Rollback:** run the V1 `kilo` binary on the original files. There is no + reverse migration. Leftover V1 tables on a migrated copy are not a way back. +- Do not fork `V1Migration` to take a destination path. Separate path is host + identity (upstream already has `OPENCODE_DB` and channel DBs). + +## Phases + +Completeness is the inventory below, not extra phases. Flip Status on a row when +a slice lands. + +### 0 — Isolated identity (internal preview, current) + +Internal preview binary: own XDG, empty DB, branding, fail-closed. Not a +customer release. + +**Done when:** stable `kilo` and the internal preview run side by side; two +stores; boot does not migrate. + +### 1 — Kilo API seam + +One real Kilo-owned operation end-to-end through the generated client and a +`kilo-*` plugin/host path, without importing Core. Upstream +`@opencode-ai/client` already covers generic session/prompt/events — that alone +is not this gate. Protocol / Schema / Server edits only when an upstream +endpoint's shape must change; record each exception. + +**Done when:** e.g. gateway auth or org switch works through the client + plugin +path with no Core import; any Protocol change is listed. + +### 2 — One native conversation + +Stream, tools, permissions, interrupt, resume, restart through that contract. + +**Done when:** one headless path goes prompt → idle and survives reconnect. + +### 3 — Runtime + `kilo-*` packages + +Memory, indexing, sandbox, telemetry, gateway policy, skills/agents. Reuse +packages; change registration. + +**Done when:** those inventory rows are done or obsolete. + +### 4 — CLI / TUI remainder + +`/review`, privacy, themes, `kilo run --auto`, export, resume-claude/codex, +`/remote`, `kilo cloud`. + +**Done when:** Phase 4 inventory rows are done or obsolete. + +### 5 — Product clients + +VS Code sidebar, editor tabs, Agent Manager, settings webview, JetBrains. +Capability handshake. Never mix V1 and V2 routes in one session. + +**Done when:** Phase 5 inventory rows are done or obsolete. + +### 6 — Import / canary / cutover + +Host prompt + copy-then-migrate-the-copy. Originals untouched. Canary identity +(`kilo2` vs `kilo` + channel) is an explicit gate before shipping the importer — +not deferred silently. + +**Done when:** schema-diff + fixture pass; opt-in import works including +credentials and Kilo config keys; V1 still opens the original store; canary is +isolated. + +## Inventory (Kilo-Org/kilocode) + +Status: `not-started` · `in-progress` · `done` · `started` · `unknown` · +`obsolete` · `upstream-equivalent` + +The local statuses below record implementation and validation progress. They +do not imply the work has been committed, merged, deployed, or released. + +The completion denominator is frozen at **43 active capability rows** (44 +including the obsolete Console row). Count only `done` and verified +`upstream-equivalent`; partial implementations receive no completion credit. +The previous requested target of 70% required **31 covered rows** (72.1%). +Johnny's latest direction (2026-09-05) is to maximize verified completion outside +VS Code and JetBrains, while auditing existing ports and reducing upstream +patches in parallel. Keep the 43-row denominator: exclusions are not completed +work, and partial rows do not gain credit. The earlier 60% target required 26 +rows and remains historical below. + +### Current execution scope + +- Continue CLI/TUI, host, Gateway, runtime and explicit import work through + existing v2 extension seams. Local IDE work is reopened by the continuation + instruction after the Goal 79 scope audit; begin with VS Code integration. + JetBrains work remains later in the client sequence. + Classify the remaining cross-client Phase 5 capabilities individually before + claiming they are excluded or covered by a CLI feature. +- Use the BB Delegates plugin for bounded implementation, parity verification + and cleanup audits, primarily through cheaper Kilo models. Preserve exact file + ownership and coordinate full-suite validation after writers settle. +- Recheck accepted ports against the recorded current-main source, including + Johnny's reported memory right-sidebar display discrepancy. A completed engine + or modal does not prove sidebar presentation parity; reopen a row if its + acceptance scope is contradicted by verified evidence. +- Prefer reusable upstream behavior and Kilo-owned extensions. Audit the actual + shared-file patch inventory against the approved allowance before landing; + this direction does not approve new upstream patches or blanket refactors. +- Keep deployment, unsupported-platform and product-decision gates explicit. + Maximum completion means verified acceptance, not bypassing those gates. + +Plan save regression (2026-09-05): the native write tool authorizes +Location-relative resources, while the Kilo policy incorrectly allowed an +absolute plan path. The policy now allows `.kilo/plans/*.md` without granting +external-directory access. Its prompt restores the native save question before +writing and the separate `plan_exit` implementation choice afterward. A real +host regression begins without a plan directory and exercises question → write +→ completion; cancellation, outside-plan denial and configured denials remain +covered. Parent verification: 14 tests / 60 assertions and package typecheck +pass using bundled Bun 1.4. Save consent is model instruction, not a new +deterministic write-approval service. The broader CLI/TUI row remains open. + +Follow-up validation (2026-09-05): the settings fixture now waits for native +focus, processed filter text and the selected row before Enter, rather than +matching any visible option text. The delegate recorded three consecutive +settings UI passes; parent independently ran settings UI and memory-sidebar +targets (**5 pass / 0 fail**). Remote catalog/inline-attachment tests also pass +independently (**5 tests / 60 assertions**), as do remote RPC and the updated +wrapped limitation disclosure (**2 tests / 21 assertions**). Remote deployed +transcript compatibility remains open. The former AISDK-compatible adapter +bypassed supplied HTTP middleware and carried account metadata for prompts +and titles. A Kilo-owned native compatible route now replaces that alias; +parent's focused rerun passes **9 tests / 98 assertions**, including a positive +all-wire credential regression. Opus follow-up review confirms the native +delta closes the leak without a protocol/auth/reasoning regression. The stable +snapshot passed the full CLI fresh-artifact wrapper: **443 pass / 0 fail, +2,741 assertions across 77 files** (359.21s, bundled Bun 1.4). Log: +`/Users/johnnyamancio/.bb/thread-storage/thr_qr9z7p3cqc/native-route-full-cli.log`. +Post-snapshot cleanup shares the two native routes' parser/isolation helpers +inside `packages/ai/src/kilocode/routed.ts`, preserves dialect option handling +and explicit `extraBody`, and strengthens the real server-value sentinel. +Parent independently reran **10 AI tests / 82 assertions** and **9 CLI tests / +98 assertions**, all passing, plus AI typechecks. The full 443-test result +above predates this cleanup; it is not relabeled as a post-cleanup full run. +Independent review retracted the title-specific explanation: +title generation uses normal request preparation and native-route titles are +covered. Compiled Auto package loading is a separate open gate: isolated Bun +1.4 probes confirm runtime `import.meta.resolve`/dynamic loading fails for the +new route in a compiled executable. The current compiled headless artifact +does not include that plugin, while the interactive preview is source-mode; +this is not evidence that today's source preview cannot load Auto. No compiled +Auto end-to-end flow has passed. A build-time rewrite of Core's builtins would +still be an upstream override and has not been approved. +These focused results neither close +those rows nor replace a coordinated full-suite run. + +Independent remote review subsequently found an uncovered catalog-alias bug: +wire selections must use catalog `id`, not provider-route `modelID`. The +identity fix now uses `source.id` for wire identities and the configured +default. Final parent rerun: **7 remote tests / 90 assertions pass**. The +regression derives the selection from the advertised catalog, creates the +session through the relay, checks the persisted/current identity and admits +the prompt through relay `send_message`. Admission does not prove model +execution. A separate remote aggregate +attachment-budget concern remains under review; no new arbitrary payload +limits are implied by the existing Core per-file limit. + +| Capability | Owner | Phase | Status | +|---|---|---|---| +| Kilo logo / branding | 4 shared patch or upstream hook | 0 | done | +| Isolated `kilo2` identity + config/data dirs | 2 host | 0 | done | +| Gateway device auth / profile | 3 plugin (`integration.transform`) | 0–1 | done | +| Organization / team (`/teams`) | 3 plugin (+ TUI); Protocol only if shape must change | 0–1 | done | +| Gateway catalog, BYOK, org routing (`kilo-gateway`) | 3 plugin (`catalog.transform`) + approved host-policy seam; local scope, response metadata and all eight prompt selectors accepted | 1, 4 | done | +| `kilo serve` / daemon / attach | 2 host | 1 | done | +| Generated JS SDK | upstream `@opencode-ai/client`; Kilo = plugin/RPC typings only if published | 1 | upstream-equivalent | +| Session share / unshare / fork-from-share | 3 plugin + Kilo backend; Protocol only if fork-from-share needs it | 1 | in-progress | +| Memory (`/memory`) | 3 + reused `kilo-memory`; current-main UI/engine gate reconciled below | 3 | done | +| Codebase indexing | 3 + `kilo-indexing` | 3 | done | +| Sandbox tool shells (macOS/Linux) | 3 + `kilo-sandbox` (`shell.hook` / tool path) | 3 | done | +| Sandbox PTY / MCP / git spawn policy | 2 host decorators + approved MCP spawn hook; model git covered by shell confinement | 3 | done | +| OpenTelemetry / telemetry | 2 or 3 | 3 | done | +| Kilo Swarm | 3 plugin + narrow host permission callback; persistent root/descendant board | 3 | done | +| Skills, custom agents, `.kilo/agents` | 3 | 3 | done | +| Project config discovery (`.kilo/`, `kilo.jsonc` vs `.opencode`) | 2 host seam or accept upstream names | 0–1 | done | +| TUI `/settings` appearance / input controls | upstream TUI; reuse existing dialog | 4 | upstream-equivalent | +| Persistent preview TUI presentation settings | 2 host through upstream config interface | 0–1, 4 | done | +| Settings scopes and remaining Kilo-only fields | 2 host + Kilo UI seams; scoped comparison and acceptance recorded above | 4 | done | +| Generic `/review` command | upstream command plugin; Kilo host reuse | 4 | upstream-equivalent | +| Kilo review scope/policy extensions | 3 command plugin delta | 4 | done | +| CLI TUI remainder (Kilo sidebar, built-in agents and custom Plan workflow) | 2 host + 3 TUI/agent/plan policy extensions; automated local equivalents and v2 layout decisions accepted | 4 | done | +| Headless text run/resume, explicit per-run `--auto` | 2 host using upstream client/execution | 2, 4 | done | +| Headless piped input / local attachments / final JSON result | 2 host using upstream prompt attachments | 4 | done | +| CLI model / visible-agent selection inventory | 2 host using upstream inventory APIs | 4 | done | +| Local v2 session listing / export / fresh-copy import | 2 host using upstream transfer/client | 4 | done | +| Resume Claude/Codex sessions | 2 host; format-specific import required | 4 | done | +| `/remote` | 2 + Gateway | 4 | started | +| `kilo cloud` CLI client | 2 + Gateway | 4 | done | +| Updater / update channel / packaging | 2 host (consequence of kilo-cli path) | 4 | in-progress | +| ACP | 2 host (upstream in `packages/cli`, not exported) | 4 | done | +| Credential import (`auth.json` → v2) | 2 host; explicit opt-in, source-proven mappings and fail-closed refusals | 6 | done | +| `kilo.jsonc` key mapping (Kilo-only keys) | 2 host; safe mappings and explicit unsupported-key refusals | 6 | done | +| V1 schema-diff + fixture import test | 2 host | 6 | done | +| VS Code sidebar chat | 2 client; preserve existing Kilo UI, migrate backend; wrapper acceptance insufficient | 5 | started | +| VS Code editor tabs | 2 client; preserve existing Kilo panels and restoration | 5 | started | +| Agent Manager | 2 client | 5 | not-started | +| VS Code settings webview | 2 client; preserve existing settings UI; reuse verified RPC authority | 5 | started | +| Inline autocomplete / FIM | 2 client; deferred editor consumer, classified in scope audit | 5 | not-started | +| Code actions, enhance prompt, git commit generation | 2 client; deferred editor/SCM consumers, classified in scope audit | 5 | not-started | +| Task timeline, diff viewer | 2 client | 5 | not-started | +| Voice / speech-to-text | 2 client + transcription provider; deferred capture surface | 5 | not-started | +| JetBrains plugin | 2 client | 5 | not-started | +| Kilo Console | — | — | obsolete | + +**Overlap with upstream v2** (do not rebuild): permissions, MCP, snapshots, +compaction, session fork, ACP, plugins, subagents, `webfetch` / `websearch`, +daemon HTTP+SSE. Port Kilo policy on these, not a second engine. + +This list describes reusable engines and UI primitives, not complete Kilo +product parity. In particular, upstream ACP/daemon code does not supply the +missing `kilo-cli` command/attach surface, and a TUI feature does not complete +its VS Code or JetBrains inventory row. + +The [extension-led surface audit](../kilocode/baseline/extension-surface-inventory.md) +expands these coarse rows into cross-client acceptance checks. Distinguish +source-present, UI-observed, local-workflow-tested and live-contract-verified +evidence. The frozen row percentage is not a percentage of all Kilo user +workflows. New gaps in completed scope reopen its row; genuinely unassigned +scope candidates stay visible pending an explicit inventory revision. + +## Check v2 overlap before porting v1 work + +Before implementing any inventory item, recent v1 addition, or relevant open +community PR, compare the intended behavior with the actual v2 implementation. +Existing Kilo steering/review skills were written for v1: retain their useful +working principles, but verify architecture, paths, APIs, and validation commands +against this checkout rather than treating v1-specific instructions as v2 facts. +Record the v1 PR/commit and its state separately from the port decision. An open +proposal is a candidate to evaluate, not a shipped-v1 parity requirement. + +Classify each capability as: + +- **Reuse:** v2 already supplies the behavior. Use its engine, component, + endpoint, or command; do not copy the v1 implementation. +- **Delta only:** v2 supplies the base, but Kilo still needs policy, fields, + branding, config compatibility, persistence, or host/client wiring. Name that + difference explicitly and implement only it. +- **Port:** a required behavior is absent. Re-implement against v2's seams. +- **Unverified / deferred:** evidence or a product decision is still missing. + Do not infer equivalence from a matching command name or silently discard a + v1 capability. + +Check command reachability, saved versus process-local state, project/global +scope, server versus client policy, headless/IDE behavior, data coverage, and +restart behavior. A registered placeholder is not implementation; an upstream +implementation that the Kilo host disables or does not wire is not delivered +parity. Preserve regression cases that describe the required behavior even +when the v1 patch itself becomes unnecessary. + +Repeat this check when v1 PRs merge and when the consumed v2 snapshot changes. + +Current-HEAD upstream `/review` plumbing was verified before implementing its +Kilo delta. The first isolated host test passed 20 assertions with the generic +upstream template; that historical result did not prove Kilo policy parity. + +The current Kilo-owned `src/review-policy.ts` registers through the public command +plugin seam. It adds explicit staged/unpushed/Agent Manager worktree scope rules, +base-ref validation and quoting guidance, effort selection, untrusted-data rules, +review-phase no-edit guidance, and Kilo review criteria/output. Deprecated +`local-review` aliases fail with replacement instructions before model execution. +Explicit user-configured `/review` commands still take precedence. No Core command +implementation or shared hook was changed for this extension. + +`test/review.test.ts` and `test/review-policy.test.ts` now pass 4 tests / 45 +assertions: public inventory, literal guidance (including `$&`), attachments, +bare/default input, queued delivery, settled model output, alias refusal, and +user override precedence. This proves policy delivery and command behavior, +not a model-quality evaluation or enforced filesystem sandbox. Git scope/no-edit +rules remain advisory model instructions, as in the v1 policy. The generic +upstream config-command string substitution still interprets `$&` specially; +the Kilo policy preserves it literally without changing upstream config code. +Keep the existing dev-fix triage lane for individual bug fixes. Neither lane +authorizes cherry-picking wholesale or increasing the shared-patch count. + +### Initial overlap audit — 2026-09-05 + +Source inspection uses Kilo v1 `origin/main` +`1536aef0fbe96c4575d23e9de2d50ba897f6b8ac` and the actual checked-out v2 HEAD +`59b29de40966803e2c7cd734d439843fb773f6a6`. The baseline document still records +`76dbaf20adbd43fd208a00ef3cda4a51e125a234`; `/settings` was also verified at +that older pin. Do not attribute newer capabilities to that pin: for example, +the current `/stats` module is absent there. Reconcile the baseline record with +the consumed snapshot separately; this documentation audit changes no refs. +PR states below were checked through GitHub; behavior classifications are +source evidence, not fresh runtime parity tests. This is an initial audit, not +an exhaustive list of v1 additions. + +| V1 capability / proposal | V1 state | V2 evidence | Port decision | +|---|---|---|---| +| In-app `/settings`, [#12502](https://github.com/Kilo-Org/kilocode/pull/12502) | Open, not merged | `packages/tui/src/app.tsx` opens `DialogConfig`; `component/dialog-config.tsx` edits theme, input, transcript, diffs, and attention settings | Reuse presentation controls; delta only for durable Kilo settings, scopes, and uncovered product fields. Do not port the entire v1 dialog. | +| Provider/model/agent/plugin controls included in #12502 | Open proposal | Existing `/connect`, `/models`, `/agents`, `/plugins`; `component/dialog-integration.tsx`, `component/dialog-model.tsx`, `component/dialog-agent.tsx`, `feature-plugins/system/plugins.tsx` | Reuse existing controls where behavior matches; compare disable/re-enable, persistence, and Kilo host plugin restrictions individually. A collection of commands is not full parity with the proposed unified settings page. | +| Saved auto-approve and its slash command, [#12444](https://github.com/Kilo-Org/kilocode/pull/12444) | Merged | `app.tsx` provides `permission.mode`; `context/permission.tsx` holds local mode, and `routes/session/index.tsx` replies to visible permission requests | Delta only. V1 saves a server-side global rule shared with IDE/headless clients; v2's TUI toggle is not equivalent. Preserve scope/policy requirements; reuse permission machinery. | +| Last-commit diff view, [#13257](https://github.com/Kilo-Org/kilocode/pull/13257) | Merged | `feature-plugins/system/diff-viewer.tsx` exists; `packages/schema/src/vcs.ts` modes are `working`, `branch`, `committed` | Reuse the viewer; retain the exact `HEAD~1` to `HEAD` behavior as a delta to verify/port. Branch-commits-only is not automatically last-commit parity. | +| Copy/export transcript versus generated summary, [#11504](https://github.com/Kilo-Org/kilocode/pull/11504) | Summary proposal open | `routes/session/index.tsx` implements `/copy` and `/export`; `packages/core/src/session/transfer.ts` exports/imports projected transcripts | Reuse transcript transfer and UI. A non-destructive model-generated summary is a separate candidate, not fulfilled by copying text or compacting a session. CLI wrappers and IDE surfaces remain separate work. | +| Themes, reasoning visibility, paste behavior, attention controls in #12502 | Open proposal; existing v1 controls also used | `component/dialog-config.tsx`, `config/index.tsx`, and `feature-plugins/system/notifications.ts` already own common presentation/notification behavior | Reuse; only Kilo defaults, identity, extra controls, persistence, and event-policy differences need work. Preview currently disables attention by default. | +| Privacy mode, [#12442](https://github.com/Kilo-Org/kilocode/pull/12442) | Merged | JSON export supports sanitization, but the inspected v2 TUI settings have no equivalent `privacy_mode` control | Keep screen-level PII privacy as an explicit CLI/TUI delta; sanitized export does not provide live-screen privacy. | +| Plugin loading and lifecycle | Present in inspected v1 source | `packages/core/src/plugin.ts`, `packages/core/src/plugin/host.ts`, and `packages/cli/src/commands/handlers/plugin/` provide lifecycle and host/CLI machinery | Reuse the v2 engine. Kilo built-ins, legacy hook compatibility, and exposure through the Kilo host remain deltas; do not transplant the v1 loader. | +| Skills discovery and model guidance | Present in inspected v1 source | `packages/core/src/skill.ts`, `packages/core/src/skill/discovery.ts`, and `packages/core/src/skill/instructions.ts` supply permission filtering, remote discovery/cache, and delta-aware guidance | Reuse discovery/instruction machinery. Compare Kilo trust provenance, `.kilocode`/`.kilo` roots, built-ins, project boundaries, and trusted `!cmd` expansion separately; do not drop those behaviors based on generic skill support. | +| MCP tools, prompts, resources, and OAuth | Present in inspected v1 source | `packages/core/src/mcp/index.ts`, `packages/core/src/config/plugin/mcp.ts`, and `packages/server/src/handlers/mcp.ts` supply location-scoped MCP operations and configuration | Reuse core MCP. Kilo MCP Apps and Kilo CLI/UI wiring remain deltas; generic MCP support does not establish Apps parity. | +| Theme engine and chooser | Present in inspected v1 source | `packages/theme/src/tui/schema.ts` and `packages/tui/src/component/dialog-theme-list.tsx` provide semantic light/dark themes and selection | Reuse the v2 schema/chooser. Translate Kilo palettes and branding, and wire persistence through the host; v1 theme JSON is not assumed drop-in compatible. | +| Usage/context statistics and poster | Present in inspected v1 source; surfaces differ | Current HEAD has `packages/core/src/session/stats.ts` and `packages/tui/src/feature-plugins/system/stats.tsx`; the recorded pin does not | Partial reuse. Aggregate session statistics and the upstream token poster do not establish parity with Kilo's compact usage display, context accounting, or shareable poster. Compare fields, aggregation windows, and host exposure before porting adapters. | + +### Settings-specific acceptance + +The community settings PR #12502 includes provider/model/agent/plugin controls, +project/global scope switching, immediate saves, permissions, and Kilo-specific +fields. The existing v2 `/settings` dialog covers presentation preferences; +it does not establish equivalence for every proposed field or workflow. + +The initial audit found `packages/kilo-cli/src/tui.ts` supplying +`config.get/update` from an in-memory object. The host now uses +`src/tui-config.ts` through that same upstream interface to persist presentation +preferences in the isolated profile's `tui.json`. New config instances restore +saved values; atomic writes preserve the file on failure. Host-owned plugins +remain fixed, and provider/model config is not persisted through this interface. +Malformed files report an error instead of being replaced with defaults; +preflight rejects symlink/hardlink storage. These cases have focused tests. + +This closes global preview presentation persistence, not the open community +PR's full scope. The subsequent scoped adapter and per-field comparison are now accepted in +the Goal 62% checkpoint above. Profile/Project edits, consumer-backed scalar +fields and existing native controls satisfy this row; full unified collection +editing and cross-surface v1 import are not claimed. No v1 config server or +second settings engine was imported. + +## Current implementation and validation — 2026-09-05 + +### User smoke-test follow-up — model and agent parity reopened + +Johnny confirmed that models appeared after selecting the Kilo team. Do not +continue treating this as an unresolved login failure. **Favorites already +exist** in the native picker; this profile has none saved. Preserve native +favorites, recents, search and selection persistence; neither seed favorites +nor silently import the v1 preference store. + +The Gateway and Memory rows were reopened for the parity gaps below. That +reopening reduced coverage to **24/43 (55.8%)**. The subsequently verified +credential-import and Memory gates bring current coverage to **26/43 (60.5%)**. Earlier +checkpoints do not establish that the reopened gaps are complete. Keep the denominator +at 43. Kilo built-in agents and custom Plan behavior belong to the existing +in-progress CLI TUI remainder row. The completed custom-agent discovery and +CLI inventory-listing rows do not establish built-in Kilo mode parity. + +#### Memory parity correction — 2026-09-05 user smoke test + +Johnny still observes duplicate memory entries and a less polished dialog. +Remote `refs/heads/main` was checked read-only with `git ls-remote` and still +equals local `ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`; this is not newly +landed UI after our reference snapshot. Current main's +`packages/opencode/src/kilocode/cli/cmd/tui/component/dialog-memory.tsx` +already has a selectable command menu, structured status/source/item panels, +auto-save activity, refresh, and scroll/page controls. At reopening, our +`tui-plugin/memory.tsx` was a simplified alert-based replacement. The current +batch now reuses the engine and supplies a native selectable menu plus +structured, refreshable Status/Show panels through existing public UI seams. + +- [x] Replace plain help/status/show alerts with source-backed current-main + interaction parity through v2 UI seams; do not fabricate unsupported fields. +- [x] Prove one actionable memory entry in both slash autocomplete and command + palette. The real-renderer regression now counts actionable rows in both; + `suggested: false` removes the extra Suggested entry while retaining the + command. No stale-process assumption is used to dismiss the report. +- [x] Reconcile the full memory feature contract before restoring row credit: + the real current-main digest/consolidation engine, durable autosave/injection + statistics and opted-in execution grouping are now ported. The integrated + snapshot reader passes a real canonical Git/model fixture (`+1/-0`), while + missing bounds remain unavailable. Independent source review confirms v1 + never supplies a dedicated memory-model setting and its consolidation cost + is hardcoded zero; absent v2 provider usage is not a lost shipped gate. + The focused engine suite has 171 passing tests; host/diff/UI has 31. V2 + execution-drain grouping remains explicitly different from v1 per-turn + events, with opt-in baseline, steering, interruption and cancellation proofs. + +See [memory boundary and correction](../kilocode/baseline/memory-v2-parity.md). +Previously passing tests remain evidence for their bounded implementation, +not proof of latest-main product parity. The subsequent runtime/UI work is +documented in [Memory UI evidence](../kilocode/baseline/memory-ui-v2-parity.md). + +Source comparison uses local v1 `origin/main` at +`ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7` and the current v2 checkout based on +`59b29de40966803e2c7cd734d439843fb773f6a6`, not the older pinned snapshot. +This is source evidence, not a fresh deployed Gateway response capture. + +- [x] **Gateway-first presentation:** make Kilo Gateway the primary presented + provider in the Kilo picker/connect flow. Native + `packages/tui/src/component/dialog-model.tsx` now accepts an optional host + presentation policy; Kilo supplies its priority, upstream keeps `opencode` + first. Preserve explicit user model choices and other connected providers; + presentation priority must not silently switch a session's model. +- [x] **Recommended models:** trace Gateway recommendation metadata through + the v2 catalog and render the Kilo recommendation ordering/group. V1 + `packages/kilo-gateway/src/api/models.ts` maps `preferredIndex` to + `recommendedIndex`; its VS Code selector consumes this field. That is + evidence of Kilo product behavior, not proof the old CLI had the identical + layout. Use actual catalog metadata, not a hardcoded popular-model list. + Audit remaining Kilo model groups/badges and record reuse versus missing + deltas individually; a generic provider list is not full presentation parity. + Source correction: v1 CLI uses Favorites and Recent, matching the native v2 + selection-history surface. **Most Used** is a separate VS Code count/timestamp + feature, not a CLI acceptance gate; it remains in the product-client inventory. + The scoped catalog now carries real recommendation order; source BYOK and + training-disclosure flags appear in native rows, including favorites/recents. + Missing flags are not privacy guarantees. A real team-switch fixture proves + personal labels are cleared. This is not the full v1 model-info panel. +- [x] **Picker first-render consistency:** user confirms correct final groups + but sees the native ungrouped list first for about a second. Current + `DialogModel` initializes/clears groups before awaiting the Kilo metadata + request; `createModelPicker` fetches on each dialog open with no cache. + Avoid rendering an actionable list that changes order underneath selection. + Prefer account-scoped metadata ready before display, with an honest loading + state for a cold request and usable failure fallback. Test delayed responses, + reopening, keyboard selection and invalidation on account/location changes; + never reuse another account's recommendations. Cold loading, failure fallback, + ready-empty search and confirmed close/reopen are now tested (two fresh RPCs, + no cache); the real renderer additionally proves a personal-to-team switch + replaces inventory and waits for fresh scoped metadata before selection. +- [ ] **Kilo auto models (remaining acceptance):** real `kilo-auto/*` catalog + IDs and `autoRouting` now pass through discovery, grouping, selection and the + source-selected native transport. OpenRouter Auto uses a Kilo-owned route that + captures a response-selected model; explicit compatible Auto keeps the + existing compatible extractor. This does not close the row: verify eligible + catalogs and unavailable selections across real scopes, and show a routed + model only when response metadata actually supplies it. + V1's model adapter carries `autoRouting`; its constants reference + `kilo-auto/free`, and its tests cover `kilo-auto/efficient`. These are source + examples, not a guaranteed current availability list. +- [ ] **Kilo built-in agents:** compare v1 + `packages/opencode/src/kilocode/agent/index.ts` with native v2 agents and + register only missing policy/prompt deltas through supported extensions. + Cover Code (`build` compatibility), Ask, Debug, Plan and Explore behavior, + primary versus delegated visibility, permissions, explicit agent selection + and custom-agent precedence. V1 marks Orchestrator deprecated: record its + compatibility decision separately rather than restoring it as an active + default merely because it exists in the source. Do not duplicate v2's + agent registry or subagent execution engine. +- [x] **Build → Code display rename:** Kilo's built-in coding agent is named + **Code**, not Build. Preserve the native v2 `build` ID in sessions/API calls + for compatibility; do not rewrite stored sessions or create a second coding + agent merely to change its label. Picker/composer, assistant footer, + agent-switch notices and subagent labels must use the registered display + name rather than title-casing the ID. The post policy supplies Code; native + UI name lookup also respects other registered/custom names and falls back + to the ID only when no agent metadata is available. Real TUI coverage checks + Code in picker/composer/history and switching while assistant records retain + `agent: build`. Headless `run --agent code` now resolves native `build` only + when no real custom `code` agent is registered; create and resume are both + host-tested with paginated history. Native default resolution already handles + `default_agent: code` by falling back to `build` when no real `code` agent + exists; a local-model host test proves that resolved assistant identity, + custom `code` precedence and explicit `ask` selection without a config + override. Session creation alone defers agent resolution. Explicit generic + API `agent: code` remains unsupported; clients must use advertised IDs. +- [x] **Kilo custom Plan workflow:** compare v1 `native-plan-prompt.txt`, + `tool/plan.ts`, `plan-file.ts` and `plan-followup.ts` under + `packages/opencode/src/kilocode/` against v2's `packages/core/src/plugin/plan.ts`. + Port the missing context-first, one-question-at-a-time planning guidance, + plan-file-only writes and execution permission boundaries, saved-plan + validation, completion/handoff and explicit transition to implementation. + A renamed native Plan agent or copied prompt alone is insufficient. Verify + user rejection/cancellation cannot begin implementation and custom agents + do not accidentally inherit built-in-only restrictions. + Nine real-host tests now cover native Form approval, rejection/cancellation, + refinement, invalid/free-form choices, saved paths, custom-agent precedence, + configured denials, and model-preserving new-session handoff. The Kilo-owned + post replacement reuses the bound native question executor and changes no + shared Core path. See [Plan evidence](../kilocode/baseline/plan-v2-parity.md). + +#### Right-sidebar parity — user smoke-test follow-up + +The current `packages/kilo-cli/src/tui-plugin/tui.tsx` now registers additive +memory and account-credit views through native sidebar slots. The remaining +sections and broader validation below are still open. Track this work under +the existing CLI TUI remainder row; current coarse coverage, including the +completed credential and Memory gates, is **26/43 (60.5%)**. + +- [ ] **Memory:** show actual project/session-scoped state, distinguishing + loading, unavailable, disabled and enabled; trace activity indicators to real + memory events/results. Reuse the current Memory RPC and consent model, never + enable memory as a side effect of showing the sidebar. V1 reference: + `kilocode/plugins/memory-status.tsx` and + `kilocode/cli/cmd/tui/component/memory-status.tsx` under `packages/opencode/src`. +- [ ] **Account credits and Kilo Pass:** restore personal/team credit scope, + available balance, and applicable personal Pass usage/base allotment, bonus + and renewal information from verified response fields. V1 reference: + `packages/opencode/src/kilocode/plugins/sidebar-footer.tsx`. Missing data is + not zero; zero balance is valid. Keep account balance distinct from session + cost, respect privacy masking, clear stale account data on team/account + changes, and verify refresh/cancellation against v2 events rather than + copying v1 event names or polling assumptions. +- [ ] **Full section audit, not just memory/balance:** compare v1's registered + `sidebar-indexing`, `sidebar-background-processes`, `sidebar-pr` and + `sidebar-usage` plugins with current v2 consumers. Inventory indexing state, + background jobs, PR information, input/output/reasoning/cache token usage, + cache rate, session/per-model cost and steps, throughput and conditional + model benchmark metadata. Record each field's source, scope, refresh trigger, + reuse/missing status and availability gate before implementing it. Do not + fabricate metrics or imply all legacy sources already have v2 equivalents. +- [ ] **Reuse native sidebar sections:** v2 already provides context tokens/ + percentage and session spend, MCP status and directory/branch through + `packages/tui/src/feature-plugins/sidebar/`. Preserve those controls and avoid + duplicated totals. Add Kilo-owned views through the existing + `sidebar.content` / `sidebar.footer` slots in + `packages/tui/src/routes/session/sidebar.tsx`; no sidebar fork is needed for + additive sections. Use semantic theme tokens and existing privacy settings. +- [ ] **Validation:** real host/TUI fixtures covering memory consent changes, + account/team switching, zero versus unavailable balances, masked data, + stale/out-of-order refreshes, session navigation, indexing/job updates, + narrow/wide terminals and sidebar scrolling. Capture or verify each external + field contract before claiming live-data parity; use local fixtures for this + implementation batch, with no live account calls implicitly authorized. + +#### Post-checkpoint request-policy slice — 2026-09-05 + +Checkpoint `61a8707c03` remains the saved native-Gateway/CLI stage. The subsequent +bounded `hide_prompt_training_models` request-policy slice is accepted locally: +the existing Gateway HTTP hook adds `provider.data_collection: "deny"` for the +effective explicit opt-in, using the same raw settings fold as the model picker. +No new shared upstream patch, dependency, or credential migration was needed. +The picker reads live per open; request policy is cached until host startup, +account refresh, or `config.updated`. Default file watching is off, so the UI +continues to instruct restart. This is a request to the Gateway, not evidence +of deployed provider retention behavior. + +Independent review follow-up distinguishes a stored null from an absent raw +Kilo-only key, with a fixed invalid notice and the existing cross-scope fallback. +A missing file substitution can invalidate native profile configuration while +separately parsed Kilo-only values remain effective; the scope message, docs, +and tests now state that boundary explicitly. Invalid native fields alone do +not establish whole-document rejection. + +Validation provenance: the delegate's canonical bundled-Bun wrapper run recorded +**447 pass / 0 fail / 2,756 assertions / 78 files** before the null-handling +follow-up. Root independently verified **14 request-policy/picker/wire tests** +and **22 Gateway plugin tests** at that snapshot, then **27 settings/policy/UI +tests / 174 assertions** plus CLI typecheck after the follow-up. The latter +focused result is not a new full-suite claim. The manual runtime plan includes +policy precedence and refresh-timing scenarios, still **NOT RUN**. + +The unused CLI declaration of `@ai-sdk/openai-compatible` was also removed; +Core retains its real dependency and version patch. Neither this cleanup nor +the request-policy slice closes the compiled interactive Auto packaging gate. +Coarse accepted coverage remains **26/43 (60.5%)**; broader Settings/Gateway +acceptance stays open. + +#### Authored provider-policy import — 2026-09-06 + +Accepted one additional config mapping: pinned v1 `experimental.policies` +maps exactly to native v2 provider-use policies. Authored policies follow +generated enabled/disabled-provider policies, preserving last-match precedence. +Strict boundary validation rejects excess or invalid fields without echoing +their contents. A real isolated host verifies the imported deny removes the +target provider while retaining an allowed provider. + +Root and independent Luna review each reproduced **45 importer tests / 201 +assertions**; the reviewer also ran CLI typecheck clean. No new upstream patch +or dependency. Other unsupported Kilo-only keys remain required gaps where +applicable; this single mapping does not close the config row or increase +the **26/43** count. See `kilocode/baseline/config-mapping-v2-gap.md`. + +#### Bounded catalog prompt selectors — 2026-09-06 + +Accepted the `anthropic` and `trinity` catalog selectors through the existing +post context hook and maintained v2 prompt assets. The exact catalog tag is +decoded tolerantly, never inferred from `family` or written into settings. +Location/workspace-scoped readers access the existing Gateway cache, clear +with failed/revoked scopes, and are removed with identity-checked finalizers. +Custom agent systems win; other system parts remain intact. Unknown tags, +including inherited object-property names, select no asset. + +Root reproduced **2 CLI prompt tests / 21 assertions**, full Gateway +**52 tests / 460 assertions**, and Core typecheck. Independent Luna review +also passed focused prompt/selector tests and CLI/Gateway typechecks with no +P1/P2 findings. Production launch tests use actual loopback catalog metadata, +not an injected selector map. This is source-runtime fixture evidence, not +compiled interactive packaging or deployed-model verification. + +Only the separately approved two-asset exports touch shared Core. Other +legacy prompt selectors remain unresolved; the Gateway capability row and +**26/43** count stay open/unchanged. Details: +`kilocode/baseline/model-prompt-policy-v2-parity.md`. + +#### Explore shell-policy slice — 2026-09-06 + +Accepted the pinned v1 Explore command policy through existing SDK phases: +the default-phase transform adds allowances after native AgentPlugin but +before ConfigAgentPlugin; the existing enforced post policy applies a shell +permission ceiling. Configured denies remain in their original order. The +rejected count-budget workaround and its false no-pre-seam rationale were +removed; no rule-provenance inference or new shared patch remains. + +Root verified CLI typecheck and **5 production-host tests / 78 assertions**: +safe commands execute, unsafe commands are denied, user denies survive broad +allows, and attempts to disable either policy ID cannot bypass the enforced +ceiling. The redirect regression proves no output file was written. This is +the v1 command-policy port, not an OS sandbox guarantee. Ask/Debug/Code behavior +remains covered. See `kilocode/baseline/explore-policy-v2-parity.md`. + +The larger CLI/TUI remainder row still has sidebar and other acceptance gaps; +coarse coverage remains **26/43**, with no full-row credit for this sub-slice. + +#### PR sidebar slice — 2026-09-06 + +Additional bounded running-shell display accepted on 2026-09-06: root wired +`installProcessSidebar` through the existing Kilo slot and independently ran +**6 tests / 8 assertions** (typed join plus two real-renderer lifecycle/tab +scenarios). It joins viewed-session durable shell messages to running registry +entries, reconciles stale caches on revisit, and disposes subscriptions on +unmount. Missing descriptions/ports/readiness and v1 process supervision are +not fabricated or claimed. Manual case UI-083 remains NOT RUN. This later +slice is not covered by the earlier full 481 snapshot; row credit is unchanged. + +Accepted the Kilo-owned PR sidebar through the existing UI slot. It uses the +viewed session's location, the v1 `gh` view/branch/SHA/fork fallback chain, +exact head matching for SHA-based candidates, validated output, and scoped +lookup cancellation. A single lookup deadline drives termination with a +bounded kill grace; the retained escalation timer is cleared on completion. +No shared UI change or dependency was added. + +After review fixes, root reproduced **19 tests / 46 assertions** covering +eight real-renderer scenarios and then **12 unit tests / 34 assertions** plus +CLI typecheck after the final timer cleanup. The delegate reran the complete +PR targets after that cleanup (**20 pass**). The later coordinated full CLI +wrapper run passed **481/0**, as recorded under the active target above. +Tests use isolated fixture executables, not live GitHub accounts. + +The measured same-location branch-refresh miss on this macOS host remains an +upstream VCS producer limitation, shared with the native footer. No universal +watcher fix is claimed. Manual PR scenarios `UI-081`/`UI-082` remain **NOT RUN**. +See `kilocode/baseline/sidebar-pr-v2-parity.md`; broader CLI/TUI acceptance and +the **26/43** count remain unchanged. + +#### Prior integrated implementation checkpoint — 2026-09-05 + +Integrated validation after checkpoint `047ffd045f`: **420 CLI tests, +0 failures, 2615 assertions across 73 files** (354.22s), including the compiled +preview/ACP build and real local host/TUI fixtures. Gateway: **43 pass / 325 +assertions**. Reused Memory engine: **171 pass / 648 assertions**. CLI, Gateway, +Memory, TUI and Schema package typechecks pass. These are local fixture checks, +not deployment or release validation; no live account or paid inference was +used and nothing was pushed. + +#### Gateway protocol follow-up — 2026-09-05 + +The current Gateway batch has **47 passing direct Gateway tests / 355 +assertions**, clean Gateway typecheck, clean AI typecheck, and a passing +launched-host protocol fixture. The fixture makes two requests through each +catalog-selected protocol, checks the Kilo Bearer/team headers and stateless +Responses reasoning replay, then verifies a same-ID team-to-personal protocol +change. It uses loopback only; it is not a deployed Gateway canary. + +The batch does not close the Gateway inventory row or alter the frozen +**26/43 (60.5%)** count: prompt-selector policy remains open and the remaining +Auto catalog acceptance remains listed above. A first full CLI run ended +**421 pass / 1 fail** on a settings TUI frame wait; the correct-runtime isolated +settings test passed. The follow-up run recording **416 pass / 6 fail** on +compiled/packaged startup tests is **not valid full acceptance evidence**: it +bypassed `script/test.ts` (no `KILO_CLI_TEST_ARTIFACT_DIR`), so compiled-mode +tests silently fell back to a stale `dist/kilo2` built by unsupported +Bun 1.3.14 — not a same-command confirmation. The six compiled/packaged +startup failures are attributed to that stale artifact's `requireRuntime` +guard (evidence: the same binary passes `paths`/`--help` and fails +`serve`/`init` with the Bun version error), not to this Gateway batch, and the +Gateway protocol fixture and settings UI passed within the run. A coordinated +proper-wrapper confirmation rerun (`dist/interactive/bun run script/test.ts`) +is still required before any overall green claim. + +The current batch also adds durable **Session family usage** through a +Kilo-owned read-only host RPC and the existing scrollable sidebar slot. It +aggregates settled assistant records across project-bounded parent descendants, +not native session/global counters, and excludes fork provenance and auxiliary +records. Tests cover nonzero costs/cache/reasoning, foreign-project exclusion, +in-flight omission, privacy, completion refresh and native narrow/wide sidebar +behavior. See [usage evidence](../kilocode/baseline/sidebar-usage-v2-parity.md). +This does not close the entire sidebar row: v1 background-process supervision, +PR metadata, throughput and model benchmarks remain separately unported or +unverified. Generic Persistent PTY is not a substitute for that process engine. + +The 70% batch closes the local credential-import and reconciled Memory rows. +For credentials, API keys and metadata, +source-proven Kilo/OpenAI/Copilot/xAI OAuth mappings, and well-known origin/env +credentials all use the actual isolated host writer; unsupported mappings and +conflicts are explicitly refused. The current count is **26/43 (60.5%)**; +the 70% target is still **31/43**, not rounded-up partial credit. This is not +provider authorization or deployment verification and does not close config +mapping or the overall Phase 6 cutover gate. See +[credential evidence](../kilocode/baseline/credential-import-v2-parity.md). + +Kilo `privacy_mode` now imports as the same boolean consumed by the isolated +PrivacyStore (both enabled and disabled are tested); invalid values are refused +without echoing content. Other unmapped Kilo-only configuration remains partial. +Settings gained stale-dialog path/revision checks and native v2 automatic +compaction, token-buffer and retained-token controls (zero is supported by the +native budget schema). Seventeen settings/real-TUI tests pass. No v1 context +percentage or pruning behavior is inferred from those controls; the broader +settings row is still open. + +The current local batch adds [real indexing status in the sidebar](../kilocode/baseline/sidebar-indexing-v2-parity.md) +through existing slots and a read-only scoped RPC. The disabled fixture proves +that merely displaying it does not start indexing or admit model work. This +does not close the remaining sidebar sections or increase coarse-row credit. +The Memory sidebar additionally reads real persisted injection/save activity +and reconciles asynchronous saves while enabled; its isolated renderer test +proves an engine injection appears without model work and survives resizing. + +`/remote` now has a bounded, explicitly confirmed control adapter through the +validated Gateway account. Normal-host tests prove default OFF, no-credential +refusal, explicit enable/disable and credential-change cleanup; real TUI tests +prove cancellation makes no relay request. Reconnect, directory listing, +command preflight and owned inbox cancellation are locally tested. Legacy +transcript forwarding and broader relay compatibility remain open, so this +does not close the Remote row. See [Remote evidence](../kilocode/baseline/remote-v2-parity.md). + +[Model/agent/sidebar batch evidence](../kilocode/baseline/model-sidebar-v2-parity.md) +records the tested subset: Gateway-first presentation, actual-metadata +Recommended/Auto grouping, native favorites retained, Code/Ask/Debug policy, +memory status and privacy-aware credits/Pass panels. This does **not** close +the full Recommended/Auto acceptance items: Most Used, complete dynamic +catalog eligibility and routed-model execution metadata remain unverified. +The custom Plan file/exit/implementation handoff is explicitly not ported. +No coarse inventory row gains completion credit from these partial results. + +Final local validation: bundled Bun 1.4 full CLI suite **373 pass / 0 fail** +(2,413 assertions); full Gateway **38 pass / 0 fail**; focused native TUI +model/preference/autocomplete tests **23 pass / 0 fail**. CLI, Gateway, Client, +Schema and TUI typechecks pass. The source launcher reads these changes on +restart; no distribution release, commit, push or live account probe occurred. + +Implementation order: trace model metadata and the native picker seam first; +then Gateway presentation/recommendations/auto; then built-in agents and Plan. +Sidebar memory and native-data sections can proceed independently; account +credits depend on the verified Gateway data contract. +Keep policy in Kilo-owned modules and reuse native preferences and controls. +If no public picker seam suffices, document the smallest shared hook for review +instead of cloning the picker or creating another preference store. Validate +with isolated host/TUI fixtures and loopback model responses, including empty +favorites, favorite persistence, team switching and denied Plan writes. No +live account calls, paid inference, GitHub changes or product decisions are +authorized by this plan-only update. + +### Historical completed local checkpoint — 26/43 (60.5%) + +At this checkpoint the requested capability target was reached and validated. The fixed +denominator remains 43; no partial row was split or removed to reach it. +The supported-runtime rebuilt-package suite passes **366 tests, zero failures, +2,368 assertions across 58 files** (275.48s). Final Kilo CLI typechecking passes; +post-cleanup skill tests pass **15/15**, and project-config/settings regressions +pass **16/16**. Gateway/indexing package typechecks and CLI import-boundary +tests (**4/4**) also pass. The source TUI/ACP launcher rebuild and version smoke +check pass. No commits, pushes, live cloud/account tests or additional shared +OpenCode patches were made in this batch. This is local implementation +coverage, not release readiness. The remaining 17 active rows retain their +honest partial/unstarted/unknown statuses. + +The skill/custom-agent row now combines native markdown/discovery reuse with +host-owned provenance, native shell/external-directory preflight, mandatory +human Form approval and native shell execution. **15 tests / 64 assertions** +pass independently for expansion, actual inlined output, rejection/deny, +headless auto cancellation, literal user activation, source/symlink boundaries +and bounds. The project-root filter plus settings regressions also pass +**16 tests / 118 assertions**. Untrusted sources do not execute; no ambient v1 +skill stores are scanned. See the +[skill-shell ledger](../kilocode/baseline/skill-shell-parity.md). + +Execution-policy validation reopened the memory and indexing rows: their tool +visibility flags hid explicit denies but did not perform the native approval +assertion at execution. Both v1 tools asked for that approval. Host-owned +native authorization now precedes memory access and semantic search; absent +host authorization fails closed. Independent bundled-runtime validation passed +**33 tests** across memory and indexing, including real model-tool ask, reject, +allow and deny paths. Both rows regain completion credit. Previously recorded +coverage was 22/43 after reopening them and completing cloud; partial fixes do +not count toward the 26/43 target. + +Swarm is now covered by **7 tests / 47 assertions**, independently rerun: +real native subagent descendants, completed board output, approval/feedback, +idempotent admission, participant discovery, activity notices, cross-root and +moved-location rejection, and history after a host process restart. The +non-interactive notice guard also proves configured deny wins over saved +allow, ask does not prompt, and public permission hooks are respected. The +host test caught and fixed undefined cursor metadata on an empty board; +conversation forks are correctly treated as independent roots. See the +[Swarm ledger](../kilocode/baseline/swarm-v2-parity.md). + +The Cloud Agent CLI row now passes **55 tests / 185 assertions** across seven +files, independently rerun under bundled Bun 1.4. Start/send/status/result and +start-only `--stream` use the resolved account/team. Tests cover actual CLI +JSONL, provided stream URL origin pinning, ticket fallback, non-fatal stream +failure, cancellation, and no bearer/ticket leakage in admission diagnostics. +Transport tests stay on loopback; this is the source-backed CLI port, not a +claim about deployed backend availability. See the +[cloud ledger](../kilocode/baseline/cloud-cli-v2-parity.md). + +The schema-diff/fixture-validation row is now complete: an actual Kilo-main +writer at `d99662338e3ddbd2613ab41fc0369837a6eb4be9` generated the disposable +source, and the normal suite imports its recorded SQL dump without needing a +sibling checkout. The real v2 migrator runs only on a fresh copy; database/WAL +hashes prove the source unchanged. The portable test passes **30 assertions**; +the explicit live writer roundtrip passes **35 assertions**. Text, completed +tool content and compaction survive; metadata drops and finish normalization +are asserted, not hidden. This closes the test row, **not** a production store +importer or the phase-6 preservation/host-boot gates. See the exact refs, +artifact hash, transforms and historical-store limitations in the +[fixture audit](../kilocode/baseline/v1-fixture-generation.md). + +The preceding 22/43 checkpoint completed these three rows: + +Historical integration run while Swarm was still being implemented: +`packages/kilo-cli` supported-runtime `script/test.ts` rebuilt the artifacts +and reported **340 pass, 1 fail, 2,230 assertions across 54 files** (268.11s). +The failure was the in-progress Swarm host acceptance test, not attributed to +upstream or waived. Its replacement and fix now pass the focused suite above; +a fresh full integration run now passes as recorded above. The earlier completed checkpoint was +303 pass / zero failures. Kilo gateway/client package typechecks also pass. + +Three further rows now pass their real host/CLI acceptance paths: + +- Indexing reuses the actual Kilo engine, LanceDB and parser assets. An explicit + `--indexing-config` is required; disabled/unconfigured hosts register no search + tool and send no code. Actual model-tool tests prove location-specific search, + ignore rules, line ranges and isolated storage. Engine: **501 pass, 9 existing + skips**; adapter/input: **17 pass**. The engine includes 118 byte-identical v1 + source/test copies, separately recorded from new adapter code in the + [indexing ledger](../kilocode/baseline/indexing-v2-parity.md). +- External sessions: explicit Claude/Codex source-directory discovery and fresh + import preserve supported text, reasoning and tool records, reject unsupported + lossy forms, and resume through the real host. CLI listing/import/export tests + use only synthetic sources; no ambient user history is scanned or modified. + See [external-session evidence](../kilocode/baseline/external-resume-parity.md). +- ACP: Kilo-owned build entry bundles the existing upstream ACP implementation; + `kilo2 acp` launches the isolated host, streams JSON-RPC and exits on EOF. + **9 tests** include the actual CLI, cancellation, host ownership, Kilo identity, + secret-free protocol output and a bundle graph excluding Core/Server/Standalone. + No upstream export or runtime private-package import was added. + +Settings scope editing and persistent profile privacy are integrated, including +real TUI edits consumed by the host and cross-location privacy refresh. Their +shared profile-write queue prevents one editor from losing another's fields. +The fixed row still includes remaining Kilo-only fields, so it remains +`started`, with no completion credit. Remote transcript relay remains +incomplete; tested partial adapters do not close that row. Trusted skill shell +expansion is now validated as recorded above. +Cloud CLI actual launch/CLI/stream validation is now complete as recorded above. + +The following four gates were completed at the preceding 19/43 checkpoint: + +The fixed 60% target is still **26/43**. Four additional capability gates are +locally verified; in-progress adapters below receive no credit. This is local +implementation coverage, not release readiness or exact historical v1 parity. + +- Branding: real renderer tests verify a separate quarter-tint depth layer in + light/dark modes, original artwork, Unicode fallback and narrow/wide layouts + (**14 tests / 243 assertions**). Existing theme tests also passed **10 / 38** + for semantic defaults/custom fallback. This closes the missing-depth + acceptance issue; it does not authorize additional shared patches for landing. +- Memory: explicit project operations/RPC/TUI, read-only context injection and + persisted default-off automatic consolidation are exercised through the real + host with a local model fixture (**19 tests**). Both memory and auto mode must + be enabled before auxiliary generation. The v2 boundary is a successful + coalesced execution, not v1 turn-close; it captures the latest completed pair. + Injection statistics and personal scope are not invented. See + [memory parity](../kilocode/baseline/memory-v2-parity.md). +- Shell sandbox: macOS **7 tests / 26 assertions** including actual model-tool + execution; real Linux Bubblewrap probe passes workspace/outside/protected + writes and network allow/deny controls. See + [Linux evidence and reproduction](../kilocode/baseline/sandbox-linux-validation.md). + PTY/MCP/independent git remain a separate open row; Linux future-name rules, + read confinement and proxy networking are not claimed. +- Telemetry: persistent isolated consent plus eleven emitted registry/lifecycle + names, payload-free OTLP logs, default OFF. `telemetry status|enable|disable` + is wired; enable/disable requires the host's exclusive lease, and foreground + plus daemon startup resolve consent while holding it. Saved decline wins over + env opt-in. Adapter/persistence tests **31 pass**; actual CLI/foreground/daemon + tests **2 pass / 31 assertions**. This deliberately does not restore v1's + default-on PostHog payload pipeline or claim all 27 v1 event semantics. + +Combined affected checkpoint: **86 pass / 638 assertions / 10 files** under +bundled Bun 1.4 (logo, memory, external imports/discovery, project config, +telemetry adapters/persistence, ACP bridge). Gateway **31 pass / 252 assertions** +and typecheck pass, including the new in-process validated-account extension; +the account resolver never becomes a credential-export RPC. Full-suite and +CLI typechecks will be rerun after concurrent settings/indexing/remote work +settles; transient in-progress errors are not called upstream failures. + +Full Kilo CLI build/test checkpoint after these integrations: **303 pass, +0 fail, 2021 assertions across 47 files** in 229.36s, using bundled Bun 1.4. +This includes the compiled admission-only preview and the source-backed +interactive/ACP CLI; it does not turn the compiled preview into an interactive +release artifact. The later credential/privacy additions require fresh checks. + +Settings remaining fields, cloud-agent CLI and remote relay remain under integration. +Cloud-agent tRPC and the `/remote` session relay are distinct services; neither +is tested against a deployment. No user histories, real cloud sessions, or +paid test inference are involved. + +Provenance: this checkout remains HEAD `59b29de40966803e2c7cd734d439843fb773f6a6`. +Current local Kilo-main reference inspected by this batch is +`ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`; earlier audits used `1536aef0`. +These are source-comparison refs, not an upstream v2 baseline bump. Shared +patch allowance remains six; all new code in this batch is Kilo-owned. + +### Previous overnight extension checkpoint (historical, 15/43) + +Verified coverage is **15/43 active rows (34.9%)** after isolated identity, +review policy, managed lifecycle/attach, and project discovery closed their +bounded acceptance gates. The requested 60% target remains **26/43**, not an +estimate that includes partially implemented adapters. + +- Project config: explicit `--project-config` on the foreground TUI, `serve`, + or `run` reads Kilo JSON/JSONC within the canonical project boundary. Native + config normalization and skills entries are reused. `.kilocode` precedes + `.kilo` in priority; JSONC precedes JSON. Default discovery stays off and + project executable plugin declarations/directories stay disabled. The + isolated host has file watching disabled, so config edits require restart. + Markdown `.kilo/agents`, v1 trust provenance, and trusted skill `!cmd` behavior + remain gaps in the separate skills/agents row. Configuration opt-in is a + trust decision: provider packages, MCP commands and permissions can be + configured, so this flag is not itself a sandbox. +- Explicit v1 auth/config import: `import-v1 --auth --config ` + previews without opening a store; `--apply` uses public key-connection APIs + and an atomic private config write. Reports omit secrets/config values; + sources remain unchanged. OAuth/well-known credentials, credential metadata, + conflicting target keys, target JSONC comments, and unmapped Kilo fields are + refused by the CLI. Nested dropped settings are reported rather than treated + as preserved. These two inventory rows remain partial, not full cutover. +- Telemetry: an OFF-by-default host-owned OTLP activity exporter subscribes to + public events, emits only four allowlisted update-event names, and ignores + event data. The global logs/traces exporter is deliberately not attached. + Environment opt-in requires `KILO_TELEMETRY_ENABLED=1` and an endpoint. + Local collector and real host tests pass; v1 product-event parity and durable + consent/UI policy are not implemented, so the row remains partial. +- Sandbox: explicit `--sandbox` on foreground TUI/serve/run registers an OS + shell hook through the existing SDK post-policy seam. macOS standalone and + real model-shell host tests pass (7 tests / 26 assertions), including outside + write/network denial and scoped launcher cleanup. Linux Bubblewrap remains + source-only/unverified here; it defaults `denyNames` to an empty list and + refuses nonempty basename restrictions rather than claiming protection for + newly created matching paths. No PTY, MCP, filesystem-read, or independent git + spawn protection is claimed. Managed daemon/attach reject this CLI flag. +- Memory: local project storage/commands/save/recall are implemented with + opt-in state and isolated paths. Automatic injection, capture/consolidation, + digests, personal memory, retention and cloud sync are not implemented. The + direct public RPC surface returns results immediately without session inbox, + message, or model admission; two-project isolation and restart are tested. + Real TUI status/enable/remember/show dialogs also pass without creating a + session. The UI imports only the pure command parser and RPC schema, not + filesystem/plugin implementation. This row remains partial. +- External transcripts: `import-external --model provider/model --agent + name [--directory path]` imports a fresh v2 session from local Claude/Codex + JSONL through the public client, without starting model execution. Stdout + contains only `{sessionID}`. The supported subset preserves + text, readable reasoning and paired tool output, records source provenance, + and refuses unsupported structures/unpaired tools. Provider reasoning + signatures are metadata, not replay state. The bundled-runtime host import/ + export suite passes 7 tests / 34 assertions; the real CLI also round-trips + imported content through export. Interactive discovery/resume and + broad real-world format compatibility remain open; this is not full parity. +- V1 session schema: the early [source audit](../kilocode/baseline/v1-session-schema-audit.md) + incorrectly described v1 diff counts as optional. The current fixture audit + retracts that claim for all three inspected Kilo-main refs; both writers and + the v2 decoder require counts. At this historical checkpoint, the tested + candidate utility only inspects and makes a private no-clobber SQLite copy; + it is not wired into the CLI or active store. Six synthetic-fixture tests + cover WAL contents, collisions, source DB/WAL change detection, and private + staging/publication. Read-only SQLite may update SHM read marks. No real + user store was opened and no migration was run. + +Final combined checkpoint (packaged Bun 1.4.0, from package directories): + +- `packages/kilo-cli`: `./dist/interactive/bun run script/test.ts` rebuilt the + isolated compiled artifact and passed **180 tests / 1,355 assertions across + 29 files**, zero failures (162.57s). This includes real TUI attach and memory, + daemon ownership/restart, macOS shell enforcement, public-client imports, + permissions/cancellation, and source/compiled API boundaries. Fake model and + collector endpoints were local; sharing tests do not verify deployments. +- `packages/kilo-gateway`: bundled `bun test` passed **30 tests / 232 assertions**, + zero failures. `kilo-client` has no standalone test files; its real consumer + paths are exercised by the CLI host/TUI/RPC tests. +- Package `bun run typecheck` passed for Kilo CLI, Gateway and Client. Selected + changed source/test formatting and `git diff --check` pass. Root-configured + lint on 17 integrated source files reports **10 warnings, zero errors** + (schema/client type assertions, intentional DTO spreads, and the memory + token estimate), not a warning-free claim. +- After a formatting-only fixture cleanup and explicit `return undefined` + parser lint cleanup, memory/RPC/TUI plus database-copy checks passed again: + **18 tests / 117 assertions**, zero failures. + +Earlier concurrent checkpoints had a sandbox fixture timeout and a transient +in-progress memory schema failure during attach. Explicit fixture location, +permission setup and bounded cleanup fixed the former; completed memory schema +integration removed the latter. Both pass in the final combined run; neither +is being dismissed as a pristine upstream failure. + +No new shared OpenCode override was added in this batch. `jsonc-parser` 3.3.1 +is a direct Kilo CLI dependency, reusing the parser already used upstream +instead of maintaining another parser. Work remains local/uncommitted; +the user's existing dirty files were preserved. No user-store migration, +public transcript upload, GitHub edit, or distribution install was performed. + +At this earlier checkpoint, the 60% target was not yet met. Remaining implementation work must not be confused +with decisions or external validation gates: + +- Safe local continuation candidates: broaden source-fixture coverage for + external imports, add markdown-agent discovery using supported schemas, + and extend Kilo presentation/CLI deltas after checking native equivalents. + These require implementation and verification, not automatic completion credit. +- Compatibility gates: v1 session transcript preservation; real Linux sandbox + behavior; full memory/skills/telemetry parity beyond the explicit local subset. +- Authority/product gates: extra shared OpenCode seams (including an exported + ACP entrypoint), public sharing deployment/token/viewer and team ownership + contracts, cloud-client account operations, distribution/update policy, + and undefined Swarm/voice/FIM scope. Do not guess these while Johnny is away. +- The IDE client rows remain substantial unimplemented work. Generic v2 TUI, + diff, statistics or subagent engines do not complete VS Code/JetBrains rows. + +### Earlier slices and evidence + +Phase 0 on `kilo-v2` (internal): identity, empty store, branding (shared patch / +hook), fail-closed. Not shipped to customers. + +Current-batch verification: rebuilt the isolated preview and ran +`script/coexistence.ts` with installed stable Kilo, both pointed at temporary +HOME/XDG/project paths. Two stores coexist; stable DB/WAL/SHM bytes are preserved; +preview contains no legacy table or session and shuts down cleanly. This meets +the internal identity row's Phase 0 gate, not customer migration/cutover or visual +logo acceptance. An earlier readiness failure had no diagnostic output; the +script now includes startup stderr and the rebuilt rerun passed, without claiming +a proven cause for that first failure. + +Phase 1 seam gate met: gateway auth + catalog run as owner-3 plugins through the +canonical plugin RPC client path without Core imports; generated client remains +upstream-equivalent. The conversation launcher now exposes a foreground +authenticated execution `serve` command; the earlier lifecycle entrypoint's +`serve` remains admission-only. The current batch adds Kilo-owned managed +daemon/attach adapters using the public service client with an explicit isolated +registration file. Five daemon tests (73 assertions) verify authenticated +discovery, idempotence, restart persistence, contended stores, dead-owner recovery, +and refusal to signal unverified live PIDs or read foreign/symlinked metadata. +CLI start/status/stop omit credentials. A real terminal fixture attaches twice +to the same session, verifies history and new prompts, and proves that TUI exit +does not stop the daemon. One-shot headless commands still own a private execution +host; they do not automatically attach or approve another client's permissions. +Share / unshare / fork-from-share now has a Kilo-owned RPC and +client path with fresh-ID v2 imports; production backend compatibility and +post-share incremental ingest remain before that inventory row is done. + +The 2026-09-05 [sharing contract audit](../kilocode/session-sharing.md) corrected +the initial fixture's flat message export to the cloud's nested envelope and +confirmed the newer `share_token` contract. The newer public viewer still +expects v1 messages; team ownership and removal-aware synchronization also +remain. Keep this row in progress until those gates and deployed compatibility +are resolved. + +The Kilo TUI now wires `/share`, `/unshare`, and `/fork-from-share` through the +existing keymap seam, with explicit upload confirmation. Terminal fixtures +cover cancellation, transcript import/navigation, and revoked-link failure. +Team sharing fails before upload until ownership can be preserved. This adds +no shared upstream patch. + +Local validation for the sharing step: 30 gateway tests, one interactive-host +integration test, three sharing TUI cases, and three existing account/login/ +branding TUI cases pass (37 tests). Affected package typechecks, lint, and +formatting checks pass. The integration test covers a fork that continues in +another project directory. Imported forks receive fresh session/message IDs +and discard source ancestry, revert state, and unavailable snapshot references. +Disabling uploads still permits revoking an existing public link. + +### Current host CLI surface + +Use `packages/kilo-cli/dist/interactive/kilo2` from this development checkout +(the launcher packages Bun 1.4). These commands use the same isolated +`kilo2/interactive` profile as the TUI and acquire its exclusive store lock; +stop the other instance first. They do not attach to a running daemon. +One-shot commands do not resume unrelated suspended executions when opening +the store. TUI and foreground `serve` retain startup recovery; a shutdown → +export → normal restart test verifies that export preserves the recovery claim. + +- `kilo2 serve`: authenticated foreground execution API on an ephemeral + loopback port. Prints the URL and password-file path, never the password. +- `kilo2 sessions --directory `: latest 50 top-level sessions for the + resolved location, as JSON. +- `kilo2 models [--directory ]` and `kilo2 agents [--directory ]`: + enabled models and visible agents, after location-scoped plugin activation. + Selection metadata only: no provider settings/headers or agent system prompts. + Custom agents from the isolated `kilo.jsonc` are supplied by v2, not a second + agent registry. This does not enable `.kilo/agents` or project config discovery. +- `kilo2 export [--sanitize]`: canonical settled v2 transcript + JSON on stdout, raw by default. `--sanitize` applies upstream content + redaction, **not complete path/identifier removal**: a local subprocess + test demonstrated that `info.subpath` can retain the project path after + `info.location.directory` is redacted. Inspect output before sharing. A + complete sanitizer needs its own audited fix; screen privacy is separate. +- `kilo2 import --directory `: schema-validated + fresh transcript copy through the public import API. Regenerates session + and message IDs; discards parent/fork/revert state and assistant snapshot + references absent from JSON. Preserves transcript content and metadata. + Returns the new session as JSON. Re-import creates another copy rather + than overwriting. This is not V1 DB migration, identity-preserving backup + restore, external-agent import, or URL/share import. +- `kilo2 run [prompt] [--session ] [--model ] + [--agent ] [--directory ] [--auto] [--file ...] + [--format text|json]`: creates or resumes a + same-location session through the public client. Waits for Kilo plugin + activation before selecting models, subscribes before admission, waits for + idle, and reconciles settled response text through paginated messages after + the admitted prompt. Final text goes to stdout and the session ID to stderr; + host error logs also use stderr. Explicit `--auto` replies `once` to this + session's permission requests; otherwise requested permissions are rejected. + It does not save a global allow rule. Questions/forms are cancelled with an + error rather than leaving an unattended run waiting. SIGINT interrupts the + session before host shutdown and exits 130. A continued run retains prior + conversation history but does not reprint earlier answers. + Already-running sessions are rejected before this adapter installs its + interruption or permission handlers. The command owns a private exclusive + host, not a shared-server attachment: before adding attach, design and test + concurrent admission, permission/form ownership, and response attribution. + Non-TTY stdin is appended to positional text with a newline; stdin alone is + accepted. Repeat `--file`/`-f` for local attachments, resolved against the + invocation directory even when `--directory` changes session placement. + Files must be regular and at most 10 MiB each. They are embedded in prompt + data URLs and sent to the selected model; UTF-8 source files use `text/plain`. + Empty input and invalid attachments fail before the host opens its store. + `--format json` writes one completed `{sessionID,text}` object, not upstream's + streaming JSON event format. Errors remain on stderr with nonzero exit status + and no result object. Model support for a particular media type still applies. + +The text-run slice does not yet include streamed terminal rendering/JSON event +formatting, automatic last-session selection, +remote attach, or external-agent transcript compatibility. Keep those in the +CLI remainder; the existence of this basic command does not close all v1 +headless parity. The local phase-2 path now covers native model streaming, +tools, permissions, interruption, idle, reconnect/restart, and explicit CLI +resume with deterministic local model servers; this is not a live production +Gateway/deployment validation. + +The transfer slice is validated against actual local HTTP hosts and the +conversation entrypoint: authentication, clean shutdown, machine-readable +listing, import/export across process restarts, repeated fresh-copy import, +partial sanitization, invalid input/missing-session failures, and continued +model execution from an imported transcript. No public Protocol/SDK generation +or additional shared upstream patch was needed. + +The next local headless batch adds piped input, file attachments, final JSON +results, and model/agent listings entirely in `packages/kilo-cli`. The packaged +Bun 1.4 suite passes **97 tests / 810 assertions**, including all three `/teams` +fixtures. This successful run does not establish that the previously observed +render timeout is fixed. Subprocess tests verify model-visible piped text and +attached source bytes, v2 attachment persistence, stdin-only custom-agent runs, +inventory filtering, and rejection before store creation. Input tests cover +MIME/byte preservation, missing/directory/oversized/FIFO rejection, and cancelled +stdin. Typecheck and targeted formatting pass; targeted lint has zero errors +and only the existing transfer assertion warning in `commands.ts`. +After adding the upstream-review reuse test, the final full repeat reported +**97 pass / 1 fail (826 assertions)** across 98 tests. The failure was the +existing conversation TUI fixture timing out after 600 frame passes with the +model dialog still visible; all new batch tests and `/teams` passed. No causal +link to this batch has been established, and no speculative upstream UI change +was made. Keep this distinct from the earlier `/teams` timeout. +Three consecutive isolated reruns of `test/tui.test.tsx` then passed both cases +each time (6 passes / 39 assertions in total). They narrow the reproduction but +do not prove the full-suite timeout fixed or pre-existing relative to this batch. +User evidence from the embedded BB terminal adds a separate UI follow-up: after +idle, missing text did not return when typing but did return after a resize. That +is consistent with a redraw/render-invalidation problem, not proven data loss or +a confirmed root cause. No Kilo redraw workaround or source fix is authorized +from this observation. Keep it separate from the current compiled-startup test +failures. + +Validation of this host/settings slice at +`59b29de40966803e2c7cd734d439843fb773f6a6` plus the local Kilo worktree changes: +bundled Bun 1.4 runs all 82 CLI tests successfully (753 assertions), including +source/compiled lifecycle hosts, TUI, sharing, and the new commands. All 30 +gateway tests also pass. After the final active-session guard, a full repeat +reported 81 pass / 1 fail: the `/teams` standard fixture timed out waiting for a +rendered frame. All new host/settings tests passed. An immediate focused +`teams.test.tsx` rerun passed all three cases (27 assertions). Record this +intermittent failure; it has not been proven pre-existing by a control or +resolved by the successful rerun. CLI `bun typecheck`, targeted Prettier checks, and +`git diff --check` pass. Targeted lint reports zero errors and three +type-assertion warnings within the new host adapters (readonly generated JSON +typing and undefined-key cleanup); these are not a claim of a clean repository- +wide lint baseline. No generated files, cloud checkout, or GitHub issue were +changed in this slice. + +Next unclosed host work: managed daemon/attach, isolated project configuration +and Kilo-only config compatibility, broader settings scopes, and remaining +headless input/output parity. Runtime inventory work (memory, indexing, +sandbox/telemetry/skills policy) remains separately open. Sharing is still +`in-progress` under the deployment/viewer/org/synchronization gates below; +none of the local host tests close those backend gates. + +## Session sharing — remaining acceptance gates + +Keep the sharing inventory row `in-progress` until all of these are resolved: + +1. **Cloud contract and deployment.** Target the newer purpose-bound JWT + `share_token` contract and nested `{ info, messages: [{ info, parts }] }` + export. The audited cloud `origin/main` is + `08c4887fa68738f19089284101084f404eb6c9b8` (2026-09-03). The older local + checkout returns UUID `public_id` and disables public reads; it must not be + mistaken for the current target. Neither revision proves deployed behavior. +2. **Public viewer compatibility (Kilo cloud/web).** The audited viewer reads + v1 `message.info.role` and separate parts. V2 messages use `type` and embed + their content, so the existing viewer drops them. Add explicit v2 support; + acceptance by the loose ingest schema is insufficient. Legacy shares with + separate parts remain rejected by the v2 importer until a deliberate + conversion preserves their content. +3. **Team ownership (Kilo plugin + backend).** Bootstrap creates personal + ownership. Model-routing headers and session metadata do not transfer team + scope. Wire the actual selected organization UUID and platform through the + supported `kilo_meta` contract and membership checks. Until then, reject team + or unavailable selections before bootstrap/upload; do not silently fall back + to personal ownership. +4. **Post-share synchronization (Kilo plugin + backend).** Ingest upserts IDs + and retains omitted messages; it has no per-item deletion or snapshot + replacement contract. Define how updates, reverts/removals, and revocation + behave before adding automatic uploads. `?v=2` controls lifecycle handling, + not transcript format or snapshot acknowledgement. Do not invent status or + lifecycle events to make a snapshot appear complete. +5. **End-to-end acceptance.** Use an intentionally shareable fixture against + the target deployment to verify rendering, ownership, updates, revocation, + and fork continuation. Current tests use local servers and publish no real + sessions. Public sharing requires explicit consent to upload the transcript, + tool output, attachments, and local paths; the current export is not redacted. + +These are completion criteria for the existing Phase 1 sharing row, not new +phases or permission to increase the approved shared-patch count. + +## Plan completion follow-up — 2026-09-10 + +Fixed the reported missing timestamp naming instruction and invisible new-session +handoff. Built-in Plan supplies a Unix-millisecond prefix for new filenames. +Successful new-session completion carries its destination in tool metadata; the +TUI preserves the Plan tab and opens/focuses a separate Code tab. The shared plugin +tab-open implementation now promotes explicitly opened tabs instead of replacing +a preview. The old Plan session remains Plan by design. + +Focused verification: 15 tests pass, including a real-host/rendered question-to-tab +handoff; CLI and TUI typechecks pass. Broader native tab coverage: 64 pass, two +shared-storage timeout failures. See +[Plan parity notes](../technical-notes/baseline/plan-v2-parity.md) for scope and +limitations. No capability percentage changed, no live session restart, no commit. + +## Self-sufficient CLI test harness — 2026-09-24 + +`packages/kilo-cli` `bun run test` now exits cleanly in a checkout without +`dist/interactive`: the live interactive/TUI proofs skip with a named reason +instead of hard-asserting, silently returning, or crashing `ENOENT`. + +Approach: skip + opt-in (plan "A refined"). `test/fixture.ts` gained +`interactiveArtifactDir()`, `interactiveBun()`, `interactiveKilo2()`, and +`interactiveSkipReason()`, resolving the artifact directory from +`KILO_CLI_INTERACTIVE_DIR` (default `dist/interactive`). Fourteen gated files now +use `test.skipIf(!binary)` (28 tests total); the two former `console.warn`+`return` +silent skips and the three hard `expect(...).toBe(true)` gates are gone. A +one-time banner names the missing prerequisite once per run. A new +`test/interactive-artifact-registry.test.ts` scans the tree and fails the default +run if the gated set drifts. A package-only `test:interactive` script verifies the +runtime and delegates to the unchanged `script/test.ts`; it fails loudly when the +runtime is absent and does not build it. + +Why this differs from v1: `main` has no `packages/kilo-cli`; v1's +`packages/opencode` harness auto-builds a single `Bun.build` cheaply (with a +`KILO_TEST_CLI_PATH` override). v2's `build-tui.ts` copies a Bun >= 1.4 runtime and +builds an ~800-closure portable bundle plus the ACP bridge, is platform-sensitive, +and throws on Windows, so it cannot be a mandatory default. `KILO_CLI_INTERACTIVE_DIR` +is the direct analog of v1's `KILO_TEST_CLI_PATH`. + +CI rationale: there is no `@kilocode/cli#test` entry in `turbo.json`, so +`@kilocode/cli` runs its package `bun run test` -> `script/test.ts`, which builds +only the admission-only artifact + ACP, never `dist/interactive`; the interactive +proofs skip in CI. Guardrails forbid editing `.github/workflows/*`/`turbo.json` and +Windows cannot build the launcher. Net: the interactive proofs run developer-local +only (`bun run build:tui` then `bun run test:interactive`) and skip in CI with a +named reason. This matches the spec, which lists CI live-proof coverage as out of +scope. + +Repoint audit (open question #2): all fourteen files are genuinely interactive; +none can be repointed to the admission-only `compiledBinary()` (that binary does not +execute conversations and lacks the cloud/TUI entrypoints). Recorded per-file: +interactive-runtime `bun` (agent-policy, privacy-ui, sidebar-account-ui, telemetry, +project-config, model-picker-ui, gateway-protocol, routed-model-integration, +routed-model-title-leak, settings-ui, gateway-scope-acceptance, model-prompt-policy) +and launcher `kilo2` (cloud-cli-subprocess, cloud-stream-cli). `cloud-cli.test.ts` +and the `seed(...)` paths spawn `cloud-fixture.ts` via `process.execPath` (Bun >= 1.4 +under the runner), so they are not gated. + +Verification: `bun typecheck` (tsgo --noEmit) passes. Focused run of the six +mixed/gated files reports 18 pass / 15 skip / 0 fail with the banner printed once. +Drift-guard fails when an unregistered gated file exists and passes after revert. +Full `bun run test` reports 577 pass / 28 skip (all 28 gated tests skip, zero +gated failures). Pre-existing, unrelated failures remain in `portable.test.ts` +(`portable-smoke-entry.js` `ReferenceError: settings is not defined` and a 28.7s +Gateway Auto timeout) and `sidebar-pr.test.tsx` (30s SIGTERM/SIGKILL grace timeout); +these are out of scope for this change. diff --git a/migration-tracking/technical-notes/baseline/agent-policy-v2-parity.md b/migration-tracking/technical-notes/baseline/agent-policy-v2-parity.md new file mode 100644 index 000000000000..17b991ae1c28 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/agent-policy-v2-parity.md @@ -0,0 +1,48 @@ +# Kilo agent policy v2 parity + +Source comparison: local Kilo v1 `origin/main` at +`ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`, and current v2 +`59b29de40966803e2c7cd734d439843fb773f6a6` plus local host extensions. +This is not a validation of the older `76dbaf2` pinned snapshot. + +`packages/kilo-cli/src/agent-policy.ts` is a host policy plugin. Register it with +`{ phase: agentPolicyPhase }` after native config discovery so a user-defined +agent ID wins over a Kilo compatibility default. + +| V1 behavior | V2 result | Decision | +|---|---|---| +| Build mode presented as Code | Native `build` keeps its ID and is named Code | Ported without changing the core default-agent ID. | +| Build label leaked through transcript IDs | Native display-name lookup now covers assistant footer, agent-switch notices and subagent presentation as well as picker/composer | Real TUI conversation checks Code labels while public history retains `agent: build`; absent metadata keeps the native fallback. | +| Build presentation collision | The public v2 agent editor has no source/provenance field | Rename only when the current name is native `Build`; a preconfigured presentation is left unchanged. | +| Ask mode is read-only | Missing `ask` is a primary agent with explicit deny rules for shell, edits, delegation, and all other tools | Ported through v2 permission rules; no shell command allowlist is added. | +| Debug mode | Missing `debug` is a primary evidence-led diagnostic agent | Ported through the public agent transform. | +| Explore and system/delegated agents | Existing v2 agents are not removed or rewritten | Preserved. | +| Orchestrator | Deprecated v1 agent | Intentionally not resurrected. | +| Custom agent collision | Existing `ask` or `debug` ID is left unchanged | User config takes precedence. | +| Plan prompt files and `plan_exit` lifecycle | A separate Kilo-owned post replacement reuses the bound native question tool, validates `.kilo/plans` files, and gates implementation handoff | Ported in `plan-policy.ts`; configured Plan agents remain untouched. See [Plan evidence](plan-v2-parity.md). | + +The focused test starts an isolated loopback host with the bundled Bun 1.4.0 runtime, +uses production `launch()` registration, and inspects agents through the public +client. It checks native permission evaluation and drives denied Ask shell and +Debug read requests with a loopback fake model. No live credentials or paid +inference are used. Existing custom Ask/Debug and Plan definitions remain intact. + +The rename is presentation, not a stored-ID migration. A headless compatibility +follow-up now resolves explicit `run --agent code` after location plugin +activation: an actual registered `code` agent wins; otherwise the command uses +native `build`. Both create and resume use that resolved ID. Two real-host tests +exercise each case with paginated history (12 assertions). No session rewrite +or generic API alias is introduced. Native `Agent.selectedDefault` already +falls back from absent configured `code` to `build`; a real custom `code` wins. +The new default-agent regression executes a loopback model and reads the +assistant projection, proving those two cases plus explicit `ask` selection. +Session creation alone defers that choice. This is upstream reuse, not a new +configuration rewrite. The complete agent-policy target passes 2 tests with +36 assertions after this addition. + +Rename follow-up validation (bundled Bun 1.4.0): `test/tui.test.tsx`, +`test/agent-policy.test.ts` and `test/model-picker-ui.test.tsx` — **4 pass, +0 fail, 42 assertions**. CLI and TUI typechecks pass; native model-presentation +regressions **6 pass / 0 fail**. The real TUI fixture requires both composer +and assistant footer labels, rejects any visible Build label, checks switches +in both directions, and verifies the stored assistant agent remains `build`. diff --git a/migration-tracking/technical-notes/baseline/auto-packaging-v2-probe.md b/migration-tracking/technical-notes/baseline/auto-packaging-v2-probe.md new file mode 100644 index 000000000000..946463e392a9 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/auto-packaging-v2-probe.md @@ -0,0 +1,75 @@ +# Auto packaging compile probe — 2026-09-05 + +Read-only verification probe for the routed Auto packaging question raised in review +(`import.meta.resolve("@opencode-ai/ai/kilocode/openrouter-routed")` under Bun `--compile`). +No repository source was modified; all artifacts live in an owned temp directory. This +document records measured facts only. It was produced by a verification delegate; the +parent thread owns any follow-up implementation and task state. + +## Scope and provenance + +- Runtime: bundled Bun **1.4.0** from `packages/kilo-cli/dist/interactive/bun` (system bun is + 1.3.14, below `src/runtime.ts`'s build gate; matches pinned `bun@1.4.0`). No installs, no + network, no accounts. +- Compile flags copied from `packages/kilo-cli/script/build.ts:34-40` (`target: "bun"`, + `minify`, `format: "esm"`, all `autoload*` disabled). +- Evidence directory (preserved): `/var/folders/pd/_rh0zzyx19ncnzldhjlt3mtc0000gp/T/kilo/vprobe` + (`source-a/`, `entry-b.ts`, `entry-d.ts`, `build-probe*.ts`, `iso/probe-a..e`, `realbuild/`). +- Real-graph probes imported the working-tree `packages/kilo-cli/src/routed-model-plugin.ts` + and `packages/core/src/provider.ts` by absolute path into a temp entry; no shared file was + edited, renamed, or swapped. + +## Measured matrix (source vs compiled, bun 1.4.0) + +| Path (mirrors the real code shape) | Source mode | Compiled binary | +| --- | --- | --- | +| `import.meta.resolve("@opencode-ai/ai/kilocode/openrouter-routed")` (`routed-model-plugin.ts:23`, unguarded) | resolves to the checkout file URL | **throws** `Cannot find package '@opencode-ai/ai' imported from /$bunfs/root/...` | +| `Provider.loadPackage(fileURL)` → `import(fileURL)` (`provider.ts:80` + `import.bun.ts`) | works | unreachable; the resolve throws first | +| `loadPackage(bare "@opencode-ai/ai/...")` pass-through → `import(variable)` | works | **fails even when the module is embedded** elsewhere in the graph | +| Core builtins literal closure (`provider.ts:36-74`) | works | **works**; loads from `/$bunfs`, proven after deleting the probe source tree entirely | +| Direct-literal dynamic import in a Kilo-owned module | works | works (embedded) | + +Commands and results (all under the evidence directory): + +```console +$ bun run source-a/entry.ts # source mode: [1]-[4] all ok +$ bun run build-probe.ts && iso/probe-a # error: Cannot find module '@probe/ai/...' from '/$bunfs/root/probe-a' +$ iso/probe-b # [1] resolve threw; [3] bare → LoadError; [4] builtin openrouter → model(): function +$ rm -rf source-a node_modules && iso/probe-c # [C2]/[C3] literal imports still work (embedded); [C1] bare variable import fails +$ bun run build-probe-d.ts && iso/probe-d # [D1] routed bare specifier via build-injected builtin → model(): function +$ iso/probe-e # negative control without injection → Provider.LoadError, exit 1 +$ cd packages/kilo-cli && dist/interactive/bun run script/build.ts $T/realbuild # builds; routed strings absent +``` + +## Current packaging state — the gap is latent + +- The headless compiled `dist/kilo2` (rebuilt 2026-09-05 18:48 from this worktree) does **not + contain the routed plugin at all**: `src/index.ts` → `host.ts` never reaches + `interactive-server.ts` (only `daemon.ts`/`tui-preview.ts` import it). The bundle contains + zero occurrences of `kilocode.routed-model`, `kilocode/openrouter-routed`, or + `openai-compatible/kilo`; builtin provider strings are present. +- The interactive entry `dist/interactive/kilo2` is a shell-script **source-mode launcher** + (runs `src/tui-preview.ts` from the checkout with the bundled bun). `import.meta.resolve` + therefore works there, and all routed tests are source mode (`test/routed-model.test.ts:181`). +- Source-preview behavior is correct and verified; no shipped artifact currently exercises the + routed path in compiled form, so the compile gap has no user-visible effect today. + +## Not claimed + +- No compiled end-to-end Auto flow (with or without a loopback fake Gateway) was run or is + claimed: no compiled artifact contains the plugin, so such a run is only possible after a + packaging seam lands. Mechanism evidence above is seam-level. +- No impossibility claim beyond tested paths: the table reflects measured behavior for bun + 1.4.0 with these flags; other Bun versions or loader APIs (`Bun.embeddedFiles` returned 0 + entries in 1.4.0) were not exhaustively explored. + +## Candidate seam — not accepted + +A build-time `onLoad` plugin in `packages/kilo-cli/script/build.ts` that injects +`["@opencode-ai/ai/kilocode/openrouter-routed", () => import("@opencode-ai/ai/kilocode/openrouter-routed")]` +into Core's builtins map text made the bare specifier load in a compiled binary (probe D), and +the negative control without injection failed with `Provider.LoadError`. This candidate is +**upstream-coupled and fragile** (keyed on a marker line in `packages/core/src/provider.ts`) +and was **rejected by the parent thread — not an approved extension**. It is recorded here as +measured evidence only. No Core rewrite, override, or shared patch was implemented, and the +measured source-mode behavior of the current implementation stands as delivered. diff --git a/migration-tracking/technical-notes/baseline/cli-fixture-storage-incident.md b/migration-tracking/technical-notes/baseline/cli-fixture-storage-incident.md new file mode 100644 index 000000000000..eb6c0c597136 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/cli-fixture-storage-incident.md @@ -0,0 +1,96 @@ +# CLI fixture-storage incident — 2026-09-07 + +The user reported test labels in `/connect` and Zen instead of anonymous Kilo +models. The user clarified that `/connect` itself did not throw an error. + +## Confirmed cause and impact + +Direct development invocations of `gateway-scope-acceptance-fixture.ts` and +`sidebar-bench-ui-fixture.tsx` bypassed their test wrappers. Both entries called +`layout("interactive")` and imported dummy credentials with the ambient storage +environment. Their wrapper processes supplied temporary HOME/XDG directories, +but direct invocations did not. A temporary project directory alone did not +isolate the credential database. The writing delegate confirmed these commands. + +Read-only inspection of the real interactive database found 39 credentials +labelled `Scope acceptance fixture` and 20 labelled `Bench fixture`; a Bench +fixture was active. The original `Kilo` credential was also present. Earlier +claims that all development runs were isolated are retracted. Wrapper test +success did not establish safety of direct fixture entrypoints. + +## Authorized recovery + +After the user's explicit approval, a SQLite online backup was created in a +mode-0700 private thread-storage directory, with the database file mode 0600. +The backup passed `PRAGMA quick_check` and contained the expected 60 records. + +Backup: `/Users/johnnyamancio/.bb/thread-storage/thr_qr9z7p3cqc/credential-recovery-20rbKX/before-cleanup.db`. +It contains credentials: do not publish, attach, or commit it. + +One transaction deleted exactly 59 records, requiring integration `kilo`, key +credential type, and both the exact fixture label and corresponding known dummy +key. The remaining original `Kilo` record was compared with the backup: its ID, +credential value, and active flag were unchanged. No original credential was +activated or replaced; no sessions or other tables were deleted. The deleted +records are recoverable from the backup. This is credential cleanup, not a claim +that fixture-created sessions or all other historical test state were removed. + +## Separate product defect + +The Gateway refresh path required a credential and profile before fetching a +catalog. Consequently there was no anonymous Kilo catalog, while the native Zen +catalog could remain selectable. Fixture cleanup does not by itself fix this. + +## Implemented correction + +`test/fixture.ts` supplies an explicit wrapper root and exports +`guardedFixtureLayout()`. The guard checks the actual resolved interactive layout +against that root, canonicalizing existing ancestors to reject symlink escapes. +It does not merely trust changed environment variables after module import. +All six fixture entrypoints using `importCredential`, plus the model-picker +fixture using the public credential-creation API, call it before host launch. +The new anonymous-host fixture uses the same guard. Direct entrypoints without +the marker refuse; normal test wrappers supply isolated paths. + +Gateway catalog reads now omit Authorization when there is no credential, skip +the authenticated profile path, and retain only records marked `isFree: true` +by the server. The public endpoint was read without credentials: all 371 records +carried `isFree`, of which 19 were true (18 also passed the existing tools-support +gate). This is deliberately stricter than v1, which listed paid records too. +Inference uses v1's literal anonymous key, without persisting a credential. +The metadata RPC supports the same anonymous catalog. Empty or failed anonymous +reads do not expose seed models or fall back to the upstream public Zen offer. +Authenticated Kilo account paths remain separate. + +Zen suppression recognizes upstream's public-key marker, not arbitrary provider +credentials. The real-host regression seeds the actual native provider plugin +and asserts its uncredentialed offer is absent; this catches a future change to +that upstream behavior. Configured-key and authenticated Zen cases have separate +unit coverage. No Core or shared TUI edits were needed. + +The authenticated scope/bench/model-info/picker fake gateways now return an empty +catalog only for the unauthenticated public startup request. Their exact Bearer +checks remain for authenticated and organization requests. + +## Verification and pending work + +- Gateway unit suite: 32 passed / 383 assertions; package typecheck clean. +- Real-host anonymous scenarios: available catalog with successful loopback + inference, empty catalog, and HTTP 503; all three passed. No paid inference. +- Guard coverage includes missing root, safe root, escaped paths, symlinks, and + actual direct invocation of all seven credential-writing entrypoints under + disposable HOME directories, asserting that no credential database is created. +- Final combined CLI regression: 27 passed / 0 failed / 79 assertions across + nine files (45.85 seconds), bundled Bun 1.4.0; package typecheck clean. +- All 16 changed/new TypeScript files in this incident passed Prettier checks. +- After verification, the real credential database still had only the original + `Kilo` record; ID, credential value, and active flag again matched the backup. +- No real-credential request or unrelated credential cleanup was performed. + +Pending for later: audit non-credential-writing direct host fixtures for the same +storage discipline; inspect/remove other historical fixture state only with an +explicitly scoped recovery request; rebuild/reverify portable artifacts before +distributing them. Exact anonymous Auto-Free default ordering is not imposed by +this correction: Kilo free models replace uncredentialed Zen, while native model +ordering remains. Explicit per-model/variant API-key configuration still wins +over provider-level settings. No whole-row parity credit is claimed. diff --git a/migration-tracking/technical-notes/baseline/cli-remainder-next-gaps.md b/migration-tracking/technical-notes/baseline/cli-remainder-next-gaps.md new file mode 100644 index 000000000000..f3efbc5e5eba --- /dev/null +++ b/migration-tracking/technical-notes/baseline/cli-remainder-next-gaps.md @@ -0,0 +1,237 @@ +# CLI TUI remainder — next gaps audit + +Bounded read-only acceptance audit of the CLI/TUI remainder after the completed +ports. Source comparison: Kilo v1 `origin/main` at +`ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7` versus this checkout +(`kilo-v2` at `61a8707c03` plus the dirty accepted request-policy slice, which +this audit leaves untouched). No implementation, no live accounts, no paid +inference, no engine replacement, no new upstream hooks. + +## Already ported — do not redo + +| Surface | Status | Record | +|---|---|---| +| Code label on native `build`, Ask read-only denies, Debug evidence-led agent, `run --agent code` | Ported and tested | [agent-policy](agent-policy-v2-parity.md) | +| Plan save permission, `plan_exit` completion, save-consent prompt | Fixed and tested | [plan](plan-v2-parity.md) | +| Memory engine, capture, commands, dialog, compact sidebar row | Ported and tested | [memory](memory-v2-parity.md), [memory-ui](memory-ui-v2-parity.md), [memory-sidebar](memory-sidebar-v2-parity.md) | +| Credits/Pass footer, model picker groups/favorites, duplicate `/memory` | Ported and tested | [model-sidebar batch](model-sidebar-v2-parity.md) | +| Indexing sidebar, session-family usage sidebar | Ported and tested | [sidebar-indexing](sidebar-indexing-v2-parity.md), [sidebar-usage](sidebar-usage-v2-parity.md) | + +## V1 sidebar sections still missing in v2 + +V1 registers six sidebar plugins at `ecccd1f` +(`packages/opencode/src/kilocode/plugins/`): `sidebar-pr` (order 50), +`sidebar-footer` (99, credits), `sidebar-usage` (150), `sidebar-indexing` (225), +`sidebar-background-processes` (250), `memory-status` (1000). V2 has the same +`sidebar.content` slot (`packages/plugin/src/tui/context.ts:184`) with footer, +usage, indexing, memory and routed-model claims installed from +`packages/kilo-cli/src/tui-plugin/tui.tsx:69-90`. PR and background processes +have no v2 claim. + +### Gap 1 — Explore read-only shell ceiling (implemented) + +V1 Explore is not merely "no bash". The actual v1 ceiling at `ecccd1f` +(`packages/opencode/src/kilocode/agent/index.ts`, `patchAgents` explore branch): + +``` +Permission.merge( + defaults, + fromConfig({ "*": "deny", grep, glob, list, skill, webfetch, websearch, + semantic_search, read, board?, external_directory ask+whitelist }), + user, + fromConfig({ bash: exploreBash }), // ceiling: user allows cannot widen shell + denies(user), // stricter user denies survive +) +``` + +`exploreBash` = `readOnlyBash` plus `"gh *": "deny"` and `"find *": "deny"` +(index.ts:127-133). `readOnlyBash` (index.ts:48-125) = `"*": "deny"`, the 28 +readable allows (`cat head tail less ls tree pwd echo wc which type file diff du +df date uname whoami printenv man grep rg ag sort uniq cut tr jq`), the 20 +read-only git allows (`log show diff status blame rev-parse rev-list ls-files +ls-tree ls-remote shortlog describe cat-file name-rev stash-list tag -l branch +--list/-a/-r remote -v`), then defense-in-depth denies: chain and redirection +operators (`*\n*`, `*<(*`, `*|*`, `*;*`, `*&*`, `*$(*`, `` *`* ``, `*>*`), `sort` +output/compress/files0-from flags, `rg --pre`, `rg --hostname-bin`, `ag --pager`, +`man -P/--pager/-H`. (Counts verified against the pinned source; this audit +earlier wrote "27 / 11", which was inaccurate.) V1 also appends a description +suffix: "Bash is limited to an allowlist of read-only commands…" and keeps the +same upstream explore prompt the native agent already uses, so no prompt delta +exists. + +V2's actual native ceiling (`packages/core/src/plugin/agent.ts:105-137`) denies +everything except grep, glob, webfetch, websearch, read (with .env asks), +subagent-deny and `external_directory` ask. **There is no `bash`/`shell` allow at +all** — every shell command is denied through the `*` deny. The prior +agent-policy decision ("existing v2 agents are not removed or rewritten") kept +this narrower agent; the user-visible delta is real: v1 Explore could run +read-only shell commands, v2 Explore cannot run any. + +Producer evidence for a faithful v2 port, rule by rule: + +- V2 evaluates permissions with `findLast` + `Wildcard.match` + (`packages/core/src/permission.ts:87-91`), so append order in a post policy + reproduces v1's merge order exactly. +- The v2 shell tool is `name = "shell"` + (`packages/core/src/tool/plugin/shell.ts:21`) and asserts + `action: name` with per-parsed-command `resources` + (`shell.ts:145-147`), so v1's command patterns map to + `{ action: "shell", resource: "", effect }` rules unchanged. +- The `skill` action exists (`packages/core/src/skill.ts` evaluates + `"skill"` against skill IDs); v1's `skill: allow` maps directly. +- `semantic_search` is a real v2 Kilo tool when indexing is configured + (`packages/kilo-cli/src/indexing.ts:166-173`); v1 allowed it for Explore. +- V2 has **no `list` tool** (tool set: + `packages/core/src/tool/plugin/`: edit, file-diff, glob, grep, opencode, + patch, question, read, shell, skill, subagent, webfetch, websearch, write) — + v1's `list: allow` is not-applicable. No board tools exist; v1's + `board_read/board_post` was flag-gated and is not-applicable. + +**Implemented** as a post/pre split inside `packages/kilo-cli/src/agent-policy.ts` +— no new engine, no shared Core patch, no separate command-filter module. The +audit's original step (c) ("re-append every present deny last") was rejected: it +would reinstate the native `*`/`subagent` catch-all after the allowlist and seal +all shell again, and any provenance filter is unsafe. The accepted design: + +1. `createExplorePolicy()` — registered in the **default** phase, so its + `ctx.agent.transform` folds after the native AgentPlugin and before + ConfigAgentPlugin (supervisor pre = `[internal.pre, sdk.all(), instance.all()]`, + post = `[internal.post, sdk.allPost()]`). It appends the verbatim `exploreBash` + table as `action: "shell"` rules (28 readable allows, 20 git allows, `gh *` and + `find *` denies, chain/redirection denies, exec-flag denies) plus `skill` and + `semantic_search` allows, so the allowlist wins over the native wildcard deny. + Because config is appended afterward, an explicit user deny (including a + wildcard identical to the native one) wins by last-match with no re-append. +2. `createAgentPolicy()` — registered in the **post** phase, owns the + `permission.evaluate` ceiling hook: scoped to `agent = "explore"` and + `action = "shell"`, it forces `deny` for any resource the v1 table alone + denies. It runs after the configured-deny short-circuit in + `Permission.evaluateInput`, so user denies are never reordered, while a broad + config `shell` allow cannot reopen table-denied commands (v1: user allows + cannot make Explore's shell writable). The post plugin is host-enforced, so a + config `plugins: ["-kilocode.agent-policy"]` remove does not disable it. +3. The description suffix is appended to the native Explore description, leaving + a custom description unchanged (same guard as the Code rename). A configured + Explore keeps its presentation and still receives the ceiling. +4. The v2 external_directory posture (`ask` default plus preserved allows) is + untouched: v1's whitelisted-dirs re-application enumerates v1 global paths + that do not exist in v2's Location model. Recorded as a deliberate v2 delta, + not a silent drop. + +Test evidence in `packages/kilo-cli/test/agent-policy.test.ts` (isolated loopback +host, bundled Bun 1.4.0, production `launch()` registering the policy itself, +loopback fake model, no live inference): the native ruleset allows `rg`/`git +log`/`git status`/`cat` and denies `git push`/`gh`/`find`/`sort +-o/--compress-program`/`rg --pre`/chain/pipe/substitution/backtick/redirect via +last-match; real host runs execute the allowed commands and deny the rest before +execution; an explicit configured `git status` deny still wins; a broad global +plus agent `shell` allow does not reopen table-denied commands; disabling the +default-phase policy via `plugins: ["-kilocode.explore-policy"]` plus a broad +allow still denies the unsafe commands through the enforced post ceiling and the +redirect writes no file, and an attempted `plugins: ["-kilocode.agent-policy"]` +disable does not remove the ceiling; a configured Explore keeps its own +description/system. Focused run: 5 tests, 0 failures, 78 assertions, clean +`tsgo --noEmit`. See [explore-policy](explore-policy-v2-parity.md). + +### Gap 2 — PR sidebar section + +V1 `sidebar-pr.tsx` (order 50) renders one line `PR #N - title` from +`api.state.vcs?.branch` plus `api.state.path.directory`, probing local `gh` +(5-minute `Bun.which` TTL) and matching the PR only when `headRefOid` equals the +local HEAD, with 1-second command timeouts inside a 20-second deadline. It shows +nothing without a branch, directory, `gh` binary, or matching PR, and mounts a +stable unconditional root box. + +V2 producer seams, all public and verified: + +- `ctx.location.vcs.info(location)` → `VcsInfo { branch: { current?, default? } }` + (`packages/plugin/src/tui/context.ts:123-127`; + `packages/client/src/promise/generated/types.ts:1636,427`), synced from the + public `vcs.get` API (`packages/client/src/solid/data.ts:1238`) with a + `vcs.branch.updated` event (`data.ts:1148`). +- `ctx.location` provides the directory; the TUI plugin process may spawn + local `gh`/`git` exactly as v1 did (client-side plugin, no server RPC). +- The native sidebar footer already shows `directory:branch` + (`packages/tui/src/feature-plugins/sidebar/footer.tsx:19-20`) but nothing + renders PR info anywhere — the line is genuinely missing. + +Bounded slice: new Kilo-owned `packages/kilo-cli/src/tui-plugin/sidebar-pr.tsx` +plus one install line in `tui.tsx`. Keep v1's semantics: resource key on +directory+branch, SHA-exact match, silent absence, stable root. No purchase, +network credential, or account surface involved. + +Test acceptance following `packages/kilo-cli/test/sidebar-indexing-ui.test.tsx`: +real isolated host and child TUI; a fixture repository with a real commit; a +PATH-shimmed `gh` executable script (a real process, not a mock object) +returning `{"number":2,"title":"…"}` for the fixture branch — assert the +rendered `PR #2 - …` line at 160 columns; a second run without the shim on PATH +asserts a stable empty root and clean exit; assert no session message, inbox +item, or model request is created; exercise the 100→140 column auto-sidebar +restore like the indexing fixture. + +### Gap 3 — Background Processes sidebar section (next in line) + +V1 `sidebar-background-processes.tsx` (order 250) lists only active processes +(`starting|running|ready|stopping`) from the v1 BackgroundProcess subsystem +(`packages/opencode/src/kilocode/background-process/index.ts`), each with tone +dot, description-or-command label, command line, PID, and PORTS, collapsing +above two entries. + +V2's real producer is different and thinner, and the audit refuses to fabricate +the missing fields: `ShellInfo` is `{ id, status: running|exited|timeout|killed, +command, cwd, shell, file, pid?, exit?, metadata, time }` +(`packages/client/src/promise/generated/types.ts:166`); there is **no +`description` and no `ports` field anywhere in the v2 producer** +(`packages/core/src/shell.ts:262-282` builds `metadata` as a free-form record). +Public seams: plugin `ctx.data.shell.list(location)` +(`context.ts:113-118`, synced from `api().shell.list` +in `data.ts:1240`), typed `session.shell.started`/`ended` events carrying +`sessionID` (published by the public `session.shell` path, +`packages/core/src/session/session.ts:195-224`; projected at +`packages/core/src/session/message-updater.ts:167-190`), the +`metadata.background === true` convention the client store already reads +(`data.ts:773`), and the public `shell`/`session.shell` APIs for tests. The +native composer shell-tab lists running shells +(`packages/tui/src/routes/session/composer/shell-tab.tsx:18`), so the sidebar +section is a distinct surface, not a duplicate. + +Bounded slice: new Kilo-owned `packages/kilo-cli/src/tui-plugin/sidebar-processes.tsx` +rendering running session shells only (status `running`, tone success, command +truncated v1-style, PID when present), hidden when empty, session-scoped via the +typed events with a `shell.list` seed. Explicit reductions to record: no +description or PORTS lines (no producer), running-only tone mapping, session +scoping via events instead of v1's state accessor. + +Test acceptance: real host and child TUI; start a real long-running shell +through the public `session.shell` API (no model work), assert the section +appears with command and PID, then after termination assert it disappears; +assert no session message or inbox work beyond the shell's own synthetic +notification path. + +### Deferred — memory Enabled active tone + +The compact Memory row honestly keeps `Enabled` muted because v2 has no +per-session save evidence reaching the TUI: injection is request-scoped +ephemeral context and no engine event reaches the TUI bus +([memory-sidebar](memory-sidebar-v2-parity.md) honest gaps). The smaller honest +variant would persist a `lastSave: { at, sessionID }` fact in the Kilo-owned +engine state, expose it on `MemoryRpc.status`, and pulse the existing 5-second +poll into a success tone for five seconds — Kilo-owned files only +(`packages/kilo-memory`, `packages/kilo-cli/src/memory-plugin.ts`, +`memory-rpc.ts`, `tui-plugin/sidebar-memory.tsx`), but it is a larger slice than +either gap above and touches the engine state schema, so it stays deferred +behind root's acceptance of the v1-pulse-only semantic (v1's durable +message-marker half has no v2 seam without Core changes). + +## Rejected approaches + +- No replacement of native sidebar/agent/permission engines; every slice + consumes the existing public plugin transform, slot, data-store, and RPC + seams. +- No new upstream hook and no speculative framework; each slice is one + Kilo-owned file plus at most one registration line in an already-Kilo file. +- No shell-policy shortcut for Explore: the v1 `readOnlyBash`/`exploreBash` + rule list is ported verbatim as permission rules; no separate command-filter + module and no narrowed subset is invented. +- No claim that `description`, `ports`, `list`, or board tools exist on v2 — + each was checked against the producer and is absent or not-applicable. diff --git a/migration-tracking/technical-notes/baseline/cli-tui-closure-verification.md b/migration-tracking/technical-notes/baseline/cli-tui-closure-verification.md new file mode 100644 index 000000000000..662b6dfc7a67 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/cli-tui-closure-verification.md @@ -0,0 +1,184 @@ +# CLI/TUI closure verification — row "CLI TUI remainder" — 2026-09-07 + +## Goal 70 superseding acceptance evidence — 2026-09-07 + +All newly identified local implementation and automated acceptance gaps are +resolved. The earlier findings below describe the earlier checkpoint. + +- Memory has a persisted session/root-bound fact for nonempty context injection, + plus the existing five-second save pulse. Identity changes reset that pulse. + Parent: 4 sidebar tests / 11 assertions, 30 memory contracts / 160 assertions. +- Model details use the accepted relocated public dialog, retaining the native + picker. Description, reasoning, family, cached Free and actual tier quotes + render from public records. Zero and 128K thresholds, a short viewport and + unavailable metadata have real renderer coverage: 4 scenarios pass. The + local-server sync-error toast remains source-reviewed, not fault-injected. +- Funded personal Credits/Pass/Bonus/Renews, privacy and team balances, plus the + actual routed model and ordinary-response absence: 2 renderer suites pass. +- Usage expansion/collapse now has real mouse interaction and per-model counts; + indexing runs the real engine through In Progress and Complete. Parent: + 3 tests / 6 wrapper assertions. The usage cleanup hang was fixed by returning + focus to the composer before typing `/exit`; no usage-production fix was needed. +- Agent/Plan/import parent integration: 68 tests / 354 assertions. Previously + accepted PR and process lifecycle/restart evidence is retained, not rerun or + relabeled as new validation. + +Root accepts automated real-host equivalents for the implementation inventory. +UI-081/082/083 manual statuses remain NOT RUN. The process restart equivalent +restarts the isolated host against its existing layout and verifies real process +truth; it is not claimed as a manual daemon-restart run. The measured same-commit +branch watcher limitation, shared with the native footer, remains disclosed. +Native throughput is reused; no absent process description/port data is invented. +These are explicit v2 scope/layout decisions, not claims to reproduce every v1 +visual arrangement. The canonical plan owns the final count and bundle evidence. + + +Bounded whole-row closure verification, not a general audit. Provenance: this +checkout at the frozen accepted batch; Kilo v1 source `origin/main` at +`ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`; canonical row and scenario text +read from `plans/kilo-opencode-v2-issue-13750.md` and +`plans/kilo-opencode-v2-test-plan-ui.md` (both root-owned, untouched). Bundled +Bun 1.4.0 (`packages/kilo-cli/dist/interactive/bun`) for every run. All other +writers frozen; no production write was made or is proposed without root's +delta approval; manual scenario statuses stay untouched (an automated +equivalent never marks a manual case RUN). + +## Row checklist — verified items + +Each item below was re-verified in this session against pinned v1 source and +current v2 code, with a green run where listed. + +- [x] **Built-in agents (Code/Ask/Debug/Explore + Plan workflow).** Explore + shell ceiling accepted: 5 tests / 78 assertions real-host + ([explore-policy-v2-parity.md](explore-policy-v2-parity.md)); Code/Ask/Debug + and Plan workflow accepted earlier. Orchestrator: **not a required gap** — + v1 registers it only as `deprecated: true` (`kilocode/agent/index.ts:588-624` + at the pin), v1's own migration doc calls it "Redundant" + (`kilocode/docs/migration.md:110`), the canonical row text records the + decision ("record its compatibility decision separately rather than restoring + it as an active default"), and + [agent-policy-v2-parity.md](agent-policy-v2-parity.md) records + "Intentionally not resurrected" with the real-renderer picker asserting its + omission (`test/model-picker-ui-fixture.tsx`). +- [x] **PR sidebar (UI-081/082 automatable steps).** Fixture-backed equivalent + run today on the real renderer, isolated host, fixture `gh` shim only: + `test/sidebar-pr.test.tsx` (12 tests) and `test/sidebar-pr-ui.test.tsx` + (8 scenarios, 33.9s) — **20 pass / 0 fail**. Step mapping: tracking/branch + lookup, local-SHA and fork-parent/fork-SHA chains (`sha`, `parent`, + `parent-sha`), wrong-head rejection (`sha-wrong`), malformed/null fail-closed + (unit), no-`gh` (`nogh`), long-title wrap at narrow/wide with + hide/show-single-relookup and width-only no-duplicate lookup (`found`), + in-flight supersede with no late render and no retry (`stale`), clean abort + of a lookup in flight across `/exit` (found + unit kill-grace/escalation + tests). +- [x] **Running-shell sidebar (UI-083 automatable steps).** Equivalent run + today: `test/sidebar-processes.test.tsx` (4) and + `test/sidebar-processes-ui.test.tsx` (2 scenarios, 13.3s) — **6 pass / 0 + fail**. Step mapping: real background shell through the public + `session.shell` API with command + real PID row; session isolation on tab + switch; durable re-sync keeps running truth; shell ending while another + session is focused reconciles on revisit; narrow/wide resize; exit cleanup; + ended/missing entries never render (unit). +- [x] **Memory row states.** Loading/Unavailable/Disabled/Enabled distinguished + from the real Memory RPC with consent-model respect and real refresh + triggers; suite green today (`test/sidebar-memory.test.tsx`, + `test/sidebar-memory-ui.test.tsx`, 5 tests / 10 assertions across the three + files including usage). +- [x] **Credits/Pass, indexing, session-family usage, routed-model section, + native sections preserved.** Accepted ledgers + ([model-sidebar](model-sidebar-v2-parity.md), + [sidebar-indexing](sidebar-indexing-v2-parity.md), + [sidebar-usage](sidebar-usage-v2-parity.md)); usage suite green today; + `sidebar-routed-model.tsx` renders the v1 routed-model-meta equivalent as + its own accepted section. + +## Row checklist — blocking remainder (each with exact source/consumer) + +### B1 — Terminal Bench 2.0 sidebar: accepted 2026-09-07; model-info surface pending + +Root accepted the bounded sidebar and metadata delta after independent review +and the final cleanup: Gateway 24 tests / 329 assertions, sidebar UI 4 tests / +8 wrapper assertions, CLI typecheck clean. No whole-row credit is claimed. + +The v1 benchmark data is **catalog metadata, not a usage-RPC field** — the +existing reduction record's stated reason ("the durable RPC does not provide +one") is correct for throughput but does not cover bench. Verified: + +- v1 producer: the Gateway models response itself carries it — + `packages/kilo-gateway/src/api/models.ts:48` (zod schema) and `:307` + (passed through per model record), tested at v1 + `packages/kilo-gateway/test/api/models.test.ts:37,76,219` + (`{ overallScore, avgAttemptCostUsd }`). +- v1 consumer: `packages/kilocode/plugins/sidebar-usage.tsx:71-75` reads + `provider?.models[current.modelID]?.terminalBench` and renders the + "Terminal Bench 2.0" collapsible (Completion score, Cost/attempt) at + `:256-272`; also `kilocode/components/model-info-panel.tsx:131`. +- Before B1, v2 dropped `terminalBench`. The accepted delta now carries it + through Gateway metadata and the KiloModels Entry to the sidebar; the + model-info surface remains absent. + +Implemented within the approved bounds; see +[sidebar-bench-v2-parity.md](sidebar-bench-v2-parity.md) for the resolved +contract (producer wire shape + consumer path), the landed files, and the +regression coverage (raw decode + four real-renderer scenarios including +model/account switches and narrow/wide). The v1 model-info panel surface +remains recorded, not implemented. + +### B2 — Throughput row: recorded reduction, capability exists natively + +v1's throughput is **not** a durable field: it is computed client-side from +live stream samples with a `source === "computed"` gate +(`sidebar-usage.tsx:55,105-112`), shown only when samples exist. V2's durable +usage RPC carries tokens/cost/steps but no rate, the native session footer +already renders live `tok/s` when enabled +(`packages/tui/src/routes/session/index.tsx:2064-2066`, `session.tps` +config), and [sidebar-usage-v2-parity.md](sidebar-usage-v2-parity.md) records +the reduction. Classification: genuinely equivalent capability on a different +surface; whether the recorded reduction satisfies the row's throughput +acceptance was resolved by root: accept the native live throughput surface +instead of adding a duplicate Kilo counter. This disposition does not close +the broader CLI/TUI row. + +### B3 — Memory Enabled active tone: save pulse accepted; durable markers pending + +V1 semantics at the pin +(`kilocode/cli/cmd/tui/component/memory-status.tsx:13-25`): Enabled renders +success when the viewed session has memory injection markers or within 5s of a +session save pulse. V2 keeps Enabled muted, documented +(`src/tui-plugin/sidebar-memory.tsx:16-20`). Verified seams: v2's injection is +request-scoped ephemeral and admits no durable synthetic message +(`src/memory-plugin.ts:573`); the dormant +`packages/kilo-memory/src/marker-meta.ts` helper has no consumer, so the +marker half genuinely has no v2 producer without engine changes. The +five-second save pulse is now accepted after user confirmation and independent +review: existing engine saved events cross the public Memory RPC and affect +only the attributed session's Enabled bullet. No new persisted `lastSave` +record or polling-based pulse was needed. See +[memory-activity-next-slice.md](memory-activity-next-slice.md) for the 39-test +root verification and explicit coverage limits. The durable-marker half +remains pending; the broad row stays open. + +### B4 — Manual scenario statuses stay NOT RUN + +`UI-081`/`UI-082`/`UI-083` remain **NOT RUN** in the root-owned +[UI test plan](../plans/kilo-opencode-v2-test-plan-ui.md). The automated +equivalents above ran green today on the real renderer isolated host, which is +**not** a manual-run mark. The genuinely unautomated steps are recorded, not +fabricated: UI-083's "record behavior after restarting the client/server" +(no automated equivalent exists and none is claimed), and UI-082's +same-location Git-branch watcher behavior — the measured producer limitation +on this host, recorded in +[sidebar-pr-v2-parity.md](sidebar-pr-v2-parity.md) and shared with the native +footer, per-host rather than component-owned. + +## Discipline + +- No production writes: the only artifacts of this verification are this doc + and the test runs above; no existing file was modified, no new test was + needed (the existing suites already exercise every automatable scenario + step; the unautomatable steps are recorded rather than simulated). +- No percentage speculation: the row remains open in the canonical plan. + B1 sidebar and B2 native throughput are accepted; the model-info surface, + B3 memory activity and the remaining manual cases stay pending. +- No upstream change, no install, no live account; loopback and fixture-only + processes; bundled Bun for every run (31 sidebar-related tests green today). diff --git a/migration-tracking/technical-notes/baseline/cloud-agent-consumers.md b/migration-tracking/technical-notes/baseline/cloud-agent-consumers.md new file mode 100644 index 000000000000..3b2ade0bae7b --- /dev/null +++ b/migration-tracking/technical-notes/baseline/cloud-agent-consumers.md @@ -0,0 +1,187 @@ +# Cloud agent consumers — inventory and migration scope + +Discovery for Kilo-Org/kilocode#14425 (parent epic #14023). Discovery only: no +source changes in any repository. + +## Header + +| Item | Value | +|---|---| +| Date | 2026-10-02 | +| Updated | 2026-10-07 — applied PR #14758 review; added `webhook-agent-ingest`; recorded EOL dispositions (Gastown unidirectional, no tracking; KiloClaw removal signal); owner column set to `cloud` | +| Cloud repo | `Kilo-Org/cloud` at `3de933dd85070e9d15c41d8ff3af6c91ca856fd4` (read-only clone) | +| This repo, `origin/main` (v1) | `622ed1f5ae5af2d864908a0bb759b0c9121d406c` | +| This repo, `kilo-v2` (v2) | `a2e6c1f69b36a0215c30a80f9d13da9f530073df` | + +Citation conventions: `cloud@3de933dd :` means the cloud repo at +`3de933dd85070e9d15c41d8ff3af6c91ca856fd4`. Bare `path:line` means this repo at +the SHA above. + +Both v1 and v2 publish the CLI package as **`@kilocode/cli`** (v1 +`packages/opencode/package.json:2-3` on `origin/main` is `7.8.3`; the v2 +`packages/kilo-cli/package.json:2-3` on `kilo-v2` is `0.0.0-internal` and +`private`). Every consumer below resolves `@kilocode/cli` from npm, so the v1/v2 +distinction is a package/channel question, not a package-name question. + +Do not confuse the SDK's `/v2` subpath with the Kilo v2 product. The +`@kilocode/sdk` package is v1's Kilo SDK, published from +`origin/main:packages/sdk/js/package.json` (name `@kilocode/sdk`, version `7.8.3`) +and already exposing a `./v2` subpath (`src/v2/…`). The `@kilocode/sdk/v2` +imports in cloud-agent-next refer to that second-generation client subpath, not +to the Kilo v2 runtime. On `kilo-v2` there is no `@kilocode/sdk` package yet: +`packages/sdk/package.json` is upstream `@opencode-ai/sdk`, with Kilo helpers in +`packages/kilo-client`. Contract mapping under #14426 must keep the two distinct. + +Already tracked elsewhere, not re-assessed here: the `kilo cloud` CLI client +(`origin/main:packages/opencode/src/kilocode/cloud/` and +`packages/kilo-cli/src/cloud/`); the `/remote` relay contract consumed by +`cloud-agent-sdk` (#14019); the VS Code Agent Manager (#14016). + +## Inventory + +Scope is **proposed, pending approval by the cloud repo's owners** (no +`CODEOWNERS` exists in the clone, so owners are recorded as the `cloud` team +rather than individuals), except the Gastown and KiloClaw EOL dispositions, which +a cloud owner supplied as review comments on PR #14758 on 2026-10-05. Those two +are owner-reviewed; the rest is still proposed. + +| Consumer | Repo + path | Owner | Kilo dependency | How obtained and pinned | Deployed runtime | Update / rollout | In scope? | Rationale | Recommended gate | Evidence | +|---|---|---|---|---|---|---|---|---|---|---| +| cloud-agent-next (hosted cloud agent; all `agent_*`/`workspace_*` sessions, incl. PR-review/auto-fix bot sandboxes) | `Kilo-Org/cloud` `services/cloud-agent-next/` | cloud | Binary + SDK + HTTP: wrapper spawns `kilo serve` and imports `@kilocode/sdk` and `@kilocode/sdk/v2` | `@kilocode/cli@7.8.1` npm-global in sandbox image (Dockerfile `ARG KILOCODE_CLI_VERSION`); `KILOCODE_CLI_VERSION` in `wrangler.jsonc` `image_vars`/`build_vars` for every container class; `@kilocode/sdk@7.8.1` in `package.json` and `wrapper/package.json`; single source `src/shared/kilo-cli-version.ts` | 7.8.1 | Bump `KILO_CLI_VERSION` + manifests; `wrangler deploy` rebuilds Cloudflare Containers (changed-worker matrix in `deploy-workers.yml`; `deploy-production.yml`/`deploy-staging.yml`); `rollout_active_grace_period 1800` | yes | Primary hosted runtime; embeds the v1 CLI server + SDK; every hosted agent flow executes here. Owner-confirmed (PR #14758): the v1↔v2 message/API differs significantly, so this is a required adaptation, not a version bump alone; the concrete deltas are #14426 | G1 for adaptation; independent cutover (pinned, own go/no-go) | cloud@3de933dd `services/cloud-agent-next/Dockerfile:6,64`; `src/shared/kilo-cli-version.ts:1`; `wrangler.jsonc:182,277`; `wrapper/package.json:11`; `wrapper/src/control-plane/kilo-runtime.ts:184`; `wrapper/src/control/worktree-runtime.ts:379` | +| auto-routing-benchmark (decider benchmark runner) | `services/auto-routing-benchmark/container/` | cloud | Binary: spawns `kilo run --format json` | `npm install -g @kilocode/cli@latest` resolved at image build; no version pin | latest at last image build (7.8.x as of the assessed SHA; exact build unverified) | `wrangler deploy` builds/pushes the container image; each deploy re-pins to that day's `latest` | yes | Floats on `@kilocode/cli@latest`, so a v2 `latest` reaches it automatically | G1 | cloud@3de933dd `services/auto-routing-benchmark/container/Dockerfile:12`; `container/server.mjs:64`; `wrangler.jsonc:32-39` | +| Gastown (agent orchestration via Durable Objects + container) | `services/gastown/container/` | cloud | Binary + SDK: spawns `kilo serve` via `createKilo()`; ships `@kilocode/plugin` for plugin discovery | `@kilocode/cli@7.2.14` (+ `cli-linux-x64`, `cli-linux-x64-musl`, `@kilocode/plugin@7.2.14`) in Dockerfile; `@kilocode/sdk@7.2.14`, `@kilocode/plugin@7.2.52` in `container/package.json` | 7.2.14 | `pnpm --filter cloudflare-gastown deploy:prod` → `container:prepare` + container build + `wrangler deploy`; `max_instances 500` | no (EOL) | Owner disposition on PR #14758 (pandemicsyn, 2026-10-05; reconfirmed 2026-10-07): Gastown is EOL'd; do not invest effort updating it. The dependency is unidirectional — Gastown pins a `@kilocode/cli` version and Kilo has no connection to it — so no v2 work and no removal tracking are required. Runtime facts recorded for completeness only | n/a (EOL; no v2 work) | cloud@3de933dd `services/gastown/container/Dockerfile:80-81`; `container/package.json:15-16`; `container/src/process-manager.ts:9,664`; PR #14758 review comment | +| KiloClaw (per-user OpenClaw runtimes on Fly.io) | `services/kiloclaw/` | cloud | Binary: ships `@kilocode/cli` in the Fly image and spawns `kilo run --auto`; controller also configures the `kilo` provider | Baked `@kilocode/cli@7.2.31` in Dockerfile; controller runs background `npm install -g @kilocode/cli@latest` 3h after boot when `KILOCLAW_KILO_CLI=true` | baked 7.2.31, then per-instance self-upgrade to `latest` | `deploy-kiloclaw.yml` builds/pushes the Fly image; each machine self-upgrades at runtime | no (EOL; slated for removal) | Owner disposition on PR #14758 (pandemicsyn, 2026-10-05): KiloClaw is EOL'd and slated for removal; take it wholly out of v2 support if possible. Kilo only appears as an optional CLI users may invoke from OpenClaw and as a rescue/doctor admin mechanic, which the owner flags as the most likely to break. Residual risk: the runtime `@latest` self-upgrade can still pull a v2 release into EOL machines without an image rebuild; accepted because no v2 support is planned. The doctor route today runs `openclaw doctor`, not the Kilo CLI. Client-side removal/disposition is recorded by kilocode #14419 (its acceptance allows a product-decision removal) and inventoried by #14415; execution-plan Decision 5 already drops the client KiloClaw port. Cloud-service removal is not yet tracked — see "EOL disposition signal" | n/a (EOL; no v2 work) | cloud@3de933dd `services/kiloclaw/Dockerfile:93`; `controller/src/index.ts:638-646`; `controller/src/routes/kilo-cli-run.ts:120,159`; `controller/src/routes/doctor.ts:420-421`; `docs/instance-features.md:29`; PR #14758 review comment | +| MCP catalog generator (CI) | `.github/workflows/kilo-mcp-catalog.yml` + `apps/web/src/scripts/mcp-catalog/catalog.ts` | cloud | Binary: `kilo run --model … --variant … --format json` | `npm install -g @kilocode/cli` (unpinned) inside the workflow | workflow-run `latest` | Runs on every PR and main push; a required `catalog (PR)` check | yes (CI consumer executing the runtime) | Floats on `@latest`; generates summaries used by `services/kilo-mcp` | G1 | cloud@3de933dd `.github/workflows/kilo-mcp-catalog.yml:115,371`; `apps/web/src/scripts/mcp-catalog/catalog.ts:714,722` | +| code-review-infra | `services/code-review-infra/` | cloud | HTTP only: calls cloud-agent-next `prepareSession`/`initiate` and `interruptSession` | n/a (no Kilo package) | n/a | n/a | no | Indirect caller: no runtime embed; cloud-agent-next owns execution and the response contract | n/a | cloud@3de933dd `services/code-review-infra/src/code-review-orchestrator.ts:1243,1262` | +| auto-fix-infra | `services/auto-fix-infra/` | cloud | HTTP only: calls cloud-agent-next prepare/initiate | n/a | n/a | n/a | no | Same as code-review-infra | n/a | cloud@3de933dd `services/auto-fix-infra/src/fix-orchestrator.ts:259,270` | +| auto-triage-infra | `services/auto-triage-infra/` | cloud | HTTP only: calls cloud-agent-next prepare/initiate + callback | n/a | n/a | n/a | no | Same as code-review-infra | n/a | cloud@3de933dd `services/auto-triage-infra/src/triage-orchestrator.ts:378,387` | +| security-auto-analysis (remediation) | `services/security-auto-analysis/` | cloud | HTTP only: calls cloud-agent-next `prepareSession`/`interruptSession` | n/a | n/a | n/a | no | Same as code-review-infra | n/a | cloud@3de933dd `services/security-auto-analysis/src/remediation.ts:1202,1091` | +| webhook-agent-ingest | `services/webhook-agent-ingest/` | cloud | HTTP only: queue consumer calls cloud-agent-next `prepareSession` and `initiateFromKilocodeSessionV2`; KiloClaw trigger type posts to kilo-chat | n/a (no Kilo package) | n/a | n/a | no | Indirect caller: no runtime embed; cloud-agent-next owns execution and the response contract | n/a | cloud@3de933dd `services/webhook-agent-ingest/src/queue-consumer.ts:470,558`; `services/webhook-agent-ingest/package.json:24-27` | +| app-builder | `services/app-builder/` | cloud | None: `cloudflare/sandbox` for app preview/build | n/a | n/a | n/a | no | Sandbox product with no Kilo CLI/SDK/server usage | n/a | cloud@3de933dd `services/app-builder/Dockerfile:1-2` | +| cloud-agent-sdk + web/mobile clients | `packages/cloud-agent-sdk/`, `apps/web`, `apps/mobile` | cloud | HTTP/relay only: consumes cloud-agent-next tRPC and the `/remote` relay | n/a | n/a | n/a | no | Client-side SDK and the web/mobile apps; `apps/web` also hosts the cloud-agent-next callers (routers + `lib/cloud-agent-next/*`). Already tracked in #14019. The "kilocode-backends prepare-session endpoint" in cloud-agent-next's README is historical naming for that web/api layer, not a separate service in this clone | n/a | cloud@3de933dd `packages/cloud-agent-sdk/src/cli-live-transport.ts:1-4`; `packages/cloud-agent-sdk/package.json:5`; `services/cloud-agent-next/README.md:299` | +| session-ingest | `services/session-ingest/` | cloud | Contract-coupled, no runtime: receives CLI session events/ingest and backs `kilo import`; shares `@kilocode/session-ingest-contracts` with cloud-agent-next and cloud-agent-sdk | n/a (service, not the CLI) | n/a | n/a | yes (contract-coupled; does not execute the runtime) | Owner notes the CLI and session-ingest are closely tied (PR #14758, 2026-10-05): the CLI ships to session-ingest, and cloud agents, the session feature and others tap in. The CLI↔session-ingest wire contract is therefore a G1 producer concern even though session-ingest embeds no runtime | G1 (producer-side contract) | cloud@3de933dd `services/session-ingest/src/remote-session-notifications.ts:82`; `services/cloud-agent-next/wrangler.jsonc:59`; `services/cloud-agent-next/package.json:50`; `packages/cloud-agent-sdk/package.json:29`; `services/cloud-agent-next/src/kilo/client.ts:112`; PR #14758 review comment | +| kilo-ops, wasteland, mcp-gateway, images-mcp, kilo-mcp | `services//` | cloud | None | n/a | n/a | n/a | no | No Kilo CLI/SDK/server spawn or image inclusion found | n/a | — | + +## Proposed migration scope + +**Proposed, pending approval by the cloud repo's owners.** + +### In scope + +- **cloud-agent-next** — the central hosted runtime. It embeds the v1 CLI server + and `@kilocode/sdk`, and it is the execution path for hosted sessions and the + PR-review/auto-fix/auto-triage/security bot flows. Pinned to `7.8.1`, so it + cuts over on its own pin bump after G1 stabilises the v2 CLI/SDK/server + contract. An owner confirmed the v1↔v2 API differs significantly, so this is a + confirmed adaptation, not a no-change consumer; the concrete deltas and the + approach are #14426/#14427. +- **auto-routing-benchmark** — executes `kilo run` from a container image built + against `@kilocode/cli@latest`. +- **MCP catalog generator (CI)** — executes `kilo run` with an unpinned + `@kilocode/cli` install. +- **session-ingest** — no runtime executor, but contract-coupled to the CLI: the + v2 CLI ships session events/ingest to it and `kilo import` uses it, and + cloud-agent-next and cloud-agent-sdk share `@kilocode/session-ingest-contracts` + with it (owner-confirmed coupling). Its wire contract must be mapped under + #14426 as part of the G1 producer surface. + +Three of the four execute the **CLI/headless runtime surface**, not the VS Code +or JetBrains surfaces; the fourth is the CLI's session-ingest contract. +**Recommended placement: #14427 and #14428 belong to G1.** The shared adaptation +work is the v2 CLI/server/SDK/session-ingest contract; the floating consumers +must be ready or protected before v2 is published to the `latest` channel they +track. cloud-agent-next is pinned and contributes a cross-repo dependency to +#14427 but performs its own cutover under its own go/no-go, exactly like +JetBrains. + +### Out of scope + +- **Gastown** — owner disposition: EOL'd; do not invest effort updating it. + The dependency is unidirectional — Gastown pins a `@kilocode/cli` version and + Kilo has no connection to it — so no v2 work and no removal tracking are + required. The runtime facts are retained in the inventory for completeness only. +- **KiloClaw** — owner disposition: EOL'd and slated for removal; take it wholly + out of v2 support if possible. The optional CLI use and the rescue/doctor admin + mechanic are the only Kilo touchpoints; the owner flags the doctor mechanic as + the most likely to break. Its runtime `@latest` self-upgrade can still pull v2 + into EOL machines without an image rebuild — recorded as an accepted residual + risk, not a support obligation. Client-side disposition is handled by #14419 + (and inventoried by #14415); cloud-service removal is not yet tracked (see + "EOL disposition signal"). +- **code-review-infra, auto-fix-infra, auto-triage-infra, security-auto-analysis, + webhook-agent-ingest** — orchestration callers of cloud-agent-next's tRPC API + (`prepareSession`/`initiate*`). They embed no Kilo runtime; any + cloud-agent-next response-shape change is covered by cloud-agent-next's G1 + work. No separate gate. +- **apps/web** — hosts the cloud-agent-next callers (routers and + `lib/cloud-agent-next/*`) but embeds no runtime; it is a web/client and + orchestration surface, not a consumer gate. The "kilocode-backends + prepare-session endpoint" referenced in cloud-agent-next's README is + historical naming for that layer, not a separate service in this clone. +- **app-builder** — sandbox app host with no Kilo runtime. +- **cloud-agent-sdk and mobile** — client consumers of cloud-agent-next and the + `/remote` relay; tracked in #14019. (`apps/web` is covered above.) +- **VS Code extension / Agent Manager** — tracked in #14016. +- **kilo-ops, wasteland, mcp-gateway, images-mcp, kilo-mcp** — no runtime + embedding found. +- The **`kilo cloud` CLI client** in this repo — tracked separately. + +## EOL disposition signal + +KiloClaw is end-of-life and slated for removal; recorded here so downstream tasks +stop building v2 compatibility for it and the removal work can be scheduled. +Gastown is also EOL but is excluded: its Kilo dependency is unidirectional (it +pins a `@kilocode/cli` version and Kilo has no connection to it), so it needs no +v2 work and no removal tracking. This is the decision record; any removal issues +belong in `Kilo-Org/cloud`. + +| Consumer | Decision | Existing downstream task | Removal tracking | +|---|---|---|---| +| KiloClaw | EOL; remove; not needed for v2 | kilocode #14419 records the client-side product-decision removal (its acceptance allows it) and #14415 inventories it; execution-plan Decision 5 already drops the client KiloClaw port | Missing: cloud-service removal of `services/kiloclaw` (worker/controller/container) and its satellites (`kiloclaw-billing`, `kiloclaw-inbound-email`, `kilo-chat`, the `kiloclaw_chat` path in `webhook-agent-ingest`, the `apps/web` instance UI, DB tables, Fly.io app/DNS) is untracked | + +Cascade note: confirm whether `kilo-chat` has consumers outside KiloClaw before +including it in the KiloClaw removal; `webhook-agent-ingest` posts to it today. + +Action: create the removal issue in `Kilo-Org/cloud` (owner to confirm). Gastown +requires no action. Until then this decision is the only record. + +## Owner-confirmed findings (PR #14758 review, 2026-10-06) + +| Finding | Impact | Evidence | +|---|---|---| +| The v1↔v2 message/API differs significantly | cloud-agent-next is a confirmed adaptation, not a no-change consumer. The concrete contract deltas and the adaptation approach remain #14426/#14427 work | PR #14758 review (eshurakov question; fpliger reply: "Yes, it's significantly different") | +| cloud-agent-next deploys build containers from the Dockerfiles, then Cloudflare rolls out the changes | Confirms the Cloudflare-container rollout path; the `@kilocode/cli` pin is baked at image build | PR #14758 review (eshurakov) | +| The Vercel Sandbox path exists in code but is unenrolled in the assessed config | `parseVercelSandboxEnrollment` disables it when `VERCEL_SANDBOX_ORG_IDS` is empty and `wrangler.jsonc` sets it to `""`; only the enrollment question remains open | cloud@3de933dd `services/cloud-agent-next/src/agent-sandbox/vercel/vercel-runtime-config.ts:131-133`; `wrangler.jsonc:69,483`; `scripts/vercel-snapshot.ts` | +| `@kilocode/sdk` and `@kilocode/cli` must be the same version; divergence is a bug | Recorded as a release invariant. It holds on v1 (both `7.8.x`). v2 ships no `@kilocode/sdk` package yet, so #14426/#14427 must establish the v2 lockstep pair (Kilo SDK vs `@opencode-ai/sdk`) | PR #14758 review (eshurakov); `origin/main:packages/sdk/js/package.json` (`@kilocode/sdk@7.8.3`); `kilo-v2:packages/sdk/package.json` (`@opencode-ai/sdk@1.18.4`) | + +## Remaining unknowns + +| Unknown | Needed to resolve | Who can answer | +|---|---|---| +| Per-service owner names (the team-level owner is recorded as `cloud`) | Name owners per service, or add a `CODEOWNERS` file | cloud repo owners / maintainers | +| Exact currently deployed versions in dev and prod (repo pins may lag deployed images; floating consumers unverified) | Query the deployed workers/containers or read deploy artifacts | cloud repo owners / release operators | +| v2 release package name and dist-tag; whether v2 replaces `@kilocode/cli@latest` or ships under a separate name/channel | Release-channel decision for the v2 CLI | Kilo release owner / plan coordinator | +| Residual CLI exposure for the EOL KiloClaw service (its `@latest` self-upgrade can pull a v2 release into EOL machines) | Confirm no support obligation; decide whether to pin or disable the self-upgrade | cloud repo owners | +| CLI↔session-ingest wire contract (session event/ingest payloads and `kilo import`) under v2 | Map the producer/consumer contract in #14426 against the v2 CLI | session-ingest owner / v2 CLI owner | +| Whether the Vercel Sandbox path is enrolled in any environment (code exists but `VERCEL_SANDBOX_ORG_IDS` is empty in the assessed `wrangler.jsonc`) | Confirm dev/prod enrollment; if unused, mark the package-time Vercel install inactive | cloud-agent-next owner | +| Other repositories or deployment configs (secrets, separate infra) outside the read-only clone that consume the runtime | Full org-level inventory | cloud repo owners | + +## Proposed progress-plan row changes (for N7; do not apply here) + +File: `migration-tracking/plans/kilo-opencode-v2-plan-progress.md`, section +"Remote, sharing and distribution". + +1. Add a row: + `| Cloud agent runtime consumers (hosted) | Kilo CLI / Gateway | 4, 6 | Partial | Evidence recorded; contract mapping pending #14426 | Four consumers: cloud-agent-next pinned (independent cutover) and a confirmed v2 adaptation (v1/v2 API differs); auto-routing-benchmark and the MCP catalog CI float on @kilocode/cli latest (G1); session-ingest is contract-coupled (G1). Gastown and KiloClaw EOL, out of scope (owner disposition, PR #14758). Indirect callers (incl. webhook-agent-ingest) need no gate. See technical-notes/baseline/cloud-agent-consumers.md. |` +2. Amend the existing `Cloud CLI client` row's remaining scope to point at the + inventory and state that local `kilo cloud` fixtures do not establish + cloud-hosted consumer compatibility. +3. Link this baseline document from the #14023 subissue list area so the epic's + evidence is one hop from the plan. +4. Do not add v2 rows for the EOL consumers. Gastown is unidirectional (pinned + Kilo version; no Kilo connection) and needs no tracking. KiloClaw is a + product-decision removal: client side in kilocode #14419/#14415, cloud-service + removal owned by the cloud team (see "EOL disposition signal"). + +Evidence links for the epic: #14425 (this discovery), #14426 (contracts), +#14427 (adaptation), #14428 (validation). diff --git a/migration-tracking/technical-notes/baseline/cloud-agent-contracts.md b/migration-tracking/technical-notes/baseline/cloud-agent-contracts.md new file mode 100644 index 000000000000..71318b3d2218 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/cloud-agent-contracts.md @@ -0,0 +1,392 @@ +# Cloud agent consumer contracts — mapping and dispositions + +Discovery for Kilo-Org/kilocode#14426 (parent epic #14023), building on #14425. +Discovery only: no source changes in any repository. + +## Header + +| Item | Value | +|---|---| +| Date | 2026-10-07 (amended; first pass 2026-10-05) | +| Cloud repo | `Kilo-Org/cloud` at `3de933dd85070e9d15c41d8ff3af6c91ca856fd4` (read-only clone `/Users/fpliger/dev/kiloverse/cloud`) | +| This repo, `origin/main` (v1) | `62f674d4a91969ac077e4a7b6ceeb8a99de80e47` | +| This repo, `kilo-v2` (v2) | `782263326df5be50081517ddedd937f1080a89cf` | +| Upstream opencode, `origin/v2` | `f20f5b68ee318721c453fb19d20e46d2b9225bc0` (clone `/Users/fpliger/dev/opencode`) | +| #14425 inventory | `migration-tracking/technical-notes/baseline/cloud-agent-consumers.md` | + +#14425 assessed the cloud repo at the same SHA; its this-repo SHAs were older +(`origin/main` `622ed1f5`, `kilo-v2` `a2e6c1f6`, both ancestors of the SHAs +above). Owners, pinning and recommended gates below are reused from #14425 and +not re-derived. + +## How to read this document + +This is the second pass. The first pass (2026-10-05) mapped the cloud consumers +against **kilo2's current surface** and framed most problems as "v2 must build +this." That framing was incomplete: `packages/kilo-cli` on `kilo-v2` is a +**thin, diverged fork** of upstream opencode's v2 `packages/cli`, and upstream +`origin/v2` already contains much of what the consumers need (a headless +`serve` with host/port, a streaming `run --format json`, `session import` from +a file or URL, and a v1→v2 session-history migration). Where kilo2 differs from +upstream, the divergence is called out explicitly and the fix is usually "sync +kilo-v2 forward," not "design something new." + +A second correction: the terms are overloaded. The **v1 SDK's `v2` subpath** is +the second-generation SDK API that talks to the **v1** runtime over v1 routes; +it is unrelated to the **v2 runtime** on `kilo-v2`. Citations say +`@kilocode/sdk/v2` when they mean the former and `kilo-v2:` when they mean +the latter. + +### Owner decisions recorded (2026-10-07) + +1. **Release channel:** v2 ships on a new pre-release channel called **`next`**; + `@kilocode/cli@latest` stays on the v1 line for the migration. (No `next` + channel exists yet; see G2.) +2. **Binary contract:** keep the **`kilo2`** binary for the duration of the + migration, then switch later if warranted. Cloud consumers adapt to `kilo2`. +3. **Credentials:** if v1 and v2 do not share auth, an import tool for users + (which includes auth) is acceptable. They do not share auth automatically, + but both upstream and Kilo already ship import paths (§1, §5, G6). +4. **Session migration goal:** migrate existing v1 users' sessions to v2; use + what upstream opencode has. Confirmed: upstream `origin/v2` has a v1→v2 + session-history migration; kilo-v2 already vendors an older copy (§5, G11). +5. **Restore:** resolve from both kilo-v2 and upstream v2 (§4, G8). +6. **Deployed versions:** no v2 consumers are deployed and versions are pinned, + so this is not an active concern; the `next` channel removes the residual + risk from the two floating consumers. + +These decisions supersede the corresponding "open questions" from the first +pass; the remaining open questions are at the end. + +## Citation conventions + +- `cloud@3de933dd :` — the cloud repo at the SHA above. +- `origin/main::` — v1 runtime surface in this repo at the SHA above. +- `kilo-v2::` — v2 runtime surface (kilo2 fork) at the SHA above. +- `origin/v2::` — upstream opencode v2 surface at the SHA above. +- A bare `#14425` reference means the inventory document, not a GitHub issue. + +## Scope and boundaries + +In scope from #14425: **cloud-agent-next**, **auto-routing-benchmark**, **MCP +catalog generator (CI)**, **session-ingest** (contract-coupled). + +Out of scope and not re-assessed: Gastown/KiloClaw (EOL by owner disposition), +code-review/auto-fix/auto-triage/security orchestration callers (no runtime), +app-builder, cloud-agent-sdk/web/mobile and the `/remote` relay (#14019), VS +Code Agent Manager (#14016), the `kilo cloud` CLI client. None of the four +in-scope consumers depend on the `/remote` relay; session-ingest is a distinct +HTTP/WS contract. + +The cloud repo pins the v1 CLI/SDK at **7.8.1**. v2 publishes the CLI package as +`@kilocode/cli` too (`kilo-v2:packages/kilo-cli/package.json:2`), but it is +`private` with **no `bin`** and version `0.0.0-internal`, so "v2 reaches a +consumer" is a packaging + channel question, not a name question. Because v2 +goes to `next` (not `latest`), the two floating consumers keep resolving the v1 +CLI until they opt in. + +## 1. cloud-agent-next + +Primary hosted runtime; wrapper spawns `kilo serve`, imports `@kilocode/sdk` and +`@kilocode/sdk/v2`, and pins `KILOCODE_CLI_VERSION=7.8.1` +(`cloud@3de933dd services/cloud-agent-next/Dockerfile:6`; +`src/shared/kilo-cli-version.ts:1`; +`wrapper/package.json:11`). + +| Area | Current behavior + evidence | v2 behavior + evidence | Breaks? | Change needed | Lands in | +|---|---|---|---|---|---| +| Startup | Two paths. Legacy wrapper calls `createKilo()`; the SDK spawns `kilo serve --hostname --port` and waits for stdout `kilo server listening` (`origin/main:packages/sdk/js/src/server.ts:62-90`; `origin/main:packages/opencode/src/cli/cmd/serve.ts:24`). Control-plane wrapper spawns `kilo serve --hostname=127.0.0.1 --port=0` and matches `/^kilo server listening on (http:\/\/127\.0\.0\.1:\d+)/` (`cloud@3de933dd wrapper/src/control-plane/kilo-runtime.ts:21,184`; `wrapper/src/main.ts:5-9,54,349-353`). | Built artifact is `kilo2` (Kilo decision; `kilo-v2:packages/kilo-cli/script/build.ts:35`; `script/build-tui.ts:29-41`), so the wrapper spawns `kilo2` and readiness must change: the interactive host prints `URL:` / `Password file:`, not a listening banner (`kilo-v2:packages/kilo-cli/src/tui-preview.ts:179-180`). kilo2's `serve` currently takes no `--hostname`/`--port` (`packages/kilo-cli/src/commands.ts:262-282`) and the headless `src/index.ts serve` is admission-only (`packages/kilo-cli/src/index.ts:11-19,52-57`). **Upstream already has the missing shape:** `serve --hostname --port --cors --service --stdio` (`origin/v2:packages/cli/src/commands/commands.ts`), so the fix is to align kilo2's serve with upstream, not invent one. | yes | Align kilo2's serve with upstream v2 (host/port, startup banner/readiness, auth) or define an explicit headless-spawn contract; cloud switches spawn to `kilo2` and the new banner/auth. | both (v2 contract first, then cloud) | +| Session admission | Wrapper calls `session.create`, `session.get`, `kilocode.sessionImport.session`, then `session.promptAsync` (async) with `parts`, `model.providerID=kilo`, `tools`, `agent` (`cloud@3de933dd wrapper/src/kilo-api.ts:380-384,389-395,407-449,456-459,343-368`). Session IDs are `ses_` (`src/utils/kilo-session-id.ts:26-27`). Wrapper HTTP admission routes `/job/prompt`,`/job/command`,`/job/answer-*` (`wrapper/src/server.ts:1235-1251`); control-plane frames `session.prepare/prompt/abort/answer` (`src/shared/control-plane-protocol.ts:611-669`). | v2 admits a prompt durably: `POST /api/session`, `POST /api/session/:sessionID/prompt` returning a `SessionInbox.User` enqueue, with execution scheduled by `SessionExecution.wake` unless `resume:false` (`kilo-v2:packages/protocol/src/groups/session.ts:170,338`; `packages/core/src/session/session.ts:154-187`; `packages/core/src/session/inbox.ts:169-202,277-336`). There is no `prompt_async`; `session.prompt` is the async admission. v2 has `POST /api/session/import` for v2 transcripts, not the v1 `kilocode.sessionImport.session` shape (`packages/protocol/src/groups/session.ts:189`). | yes | Adapt the wrapper to v2 create/prompt/import routes and payloads; expose the capability handshake so the wrapper can select v1 vs v2 (N9). | both | +| Streaming | Subscribes via `v2Client.global.event` → `GET /global/event` (`cloud@3de933dd wrapper/src/kilo-api.ts:373-377`; `origin/main:packages/sdk/js/src/v2/gen/sdk.gen.ts:1586`), including a raw SSE topology (`wrapper/src/global-feed.ts:236-243`). Depends on events `message.updated`, `message.part.updated`, `session.idle`, `session.status`, `session.error`, `permission.asked`, `question.asked`, `session.turn.open/close`, `session.network.asked` (`wrapper/src/connection.ts:219-221,105-106,1173,1193,1232,1269`; `origin/main:packages/opencode/src/kilocode/session/event.ts`). Worker ingests by entity key `message.updated`→`message/{id}`, `message.part.updated`→`part/{msg}/{id}` (`src/session/ingest-handlers/entity-id.ts:12-27`). | v2 stream is `GET /api/event` SSE, volatile by contract (overflow fails the stream), 15 s heartbeats (`kilo-v2:packages/protocol/src/groups/event.ts:38-55`; `packages/server/src/handlers/event.ts:12-33`). Current events are `session.text.delta`, `session.tool.*`, `session.step.*`, `session.inbox.*`, `session.execution.*`, `session.status`; `session.idle` is deprecated; `message.updated`/`message.part.*` are V1-only (`packages/schema/src/session-event.ts:189,196,234,383,455-509`; `packages/schema/src/session-status-event.ts:35-51`; `packages/schema/src/v1/session.ts:597,613`). | yes | Map every consumed event to the v2 vocabulary (or add a compatibility projection); adapt the worker ingest entity keys to v2 events/messages. | both | +| Tools | Passes a permission map and MCP servers through `KILO_CONFIG_CONTENT` (`cloud@3de933dd src/session-service.ts:1334-1357,1404-1424,1467-1470`); MCP types `{type:local|remote}` (`src/mcp-config.ts:7-21`). Reads tool parts (`part.type==='tool'`) and replies via `permission.reply/list`, `question.reply/reject/list` (`wrapper/src/kilo-api.ts:546-591`); auto-approves or rejects permissions (`wrapper/src/connection.ts:1171-1196`). | v2 registers tools through `Tool.Service` (no authorization in the registry) and enforces in `Permission.Service` with a persisted `permission` SQLite table (`kilo-v2:packages/core/src/tool.ts:157-183,220-271`; `packages/core/src/permission.ts:214-310`; `packages/core/src/permission/sql.ts:7`). MCP is config-driven (`packages/core/src/config/plugin/mcp.ts:39-56`). v2 reads `OPENCODE_CONFIG_CONTENT` (`packages/core/src/config.ts:206-210`) but **not** `KILO_CONFIG_CONTENT` as an input (it is only an internal constant, `packages/kilo-cli/src/skill-policy.ts:12`). | yes | Confirm v2 reads the permission/provider/MCP payload the wrapper actually injects (or adapt the injected config key); adapt permission/question reply calls. | both | +| Credentials | Injects `KILOCODE_TOKEN` (opaque capability redeemed at the outbound proxy), `KILO_AUTH_CONTENT` and writes `.local/share/kilo/auth.json` `{kilo:{type:'api',key}}`; sets `provider.kilo.options.apiKey/kilocodeToken/baseURL` (`cloud@3de933dd src/session-service.ts:1277-1311,814-828,1404-1410`; `wrapper/src/session-bootstrap.ts:286-287,680-688`). v1 reads these (`origin/main:packages/opencode/src/auth/index.ts:61-63`; provider `packages/core/src/plugin/provider/kilo.ts:18-19`). Runtime credential proxy on the worker (`src/server.ts:273,341-344,448`). | v2 does **not** read `KILO_AUTH_CONTENT`, `KILOCODE_TOKEN`, or `KILO_API_KEY`/`KILO_ORG_ID`; credentials are rows in the isolated `credential` table, resolved from the gateway account (`kilo-v2:packages/core/src/credential.ts:99-134`; `packages/core/src/credential/sql.ts:5-14`). `KILO_API_URL` selects the gateway only (`packages/kilo-cli/src/tui-preview.ts:141`). Two import paths exist: upstream's DB migration imports `/auth.json` into `credential` (`origin/v2:packages/core/src/database/migration/20260805200742_import_legacy_credentials.ts`), and Kilo's `kilo2 import-v1 --auth --apply [--gateway-server]` seeds the isolated profile (`packages/kilo-cli/src/commands.ts:216-238`; `packages/kilo-cli/src/import-v1-config.ts`). Note the public HTTP API intentionally has no credential-import RPC (`packaging-next-slice.md:168-169`), so a hosted consumer must seed via file/CLI or a new RPC. | yes (env path only) | For hosted consumers that inject env, add a v2 credential-injection path (a new env/RPC), or have the cloud write an `auth.json` into the v2 data dir / run `import-v1 --auth` before `run`. | both | +| Persistence | v1 identity is `kilo`: data `~/.local/share/kilo`, config `~/.config/kilo` (`origin/main:packages/core/src/global.ts:10-40`). Wrapper writes `$XDG_DATA_HOME/kilo/storage/session_share/.json` `{id,ingestPath}` and runs `kilo import ` after downloading `GET /api/session/:id/export` (`cloud@3de933dd wrapper/src/restore-session.ts:169-195,1069,1196-1207`). | v2 identity is `kilo2`: `~/.local/share/kilo2/...`, DB `kilo2.db` (`kilo-v2:packages/kilo-cli/src/paths.ts:6,26`), and it refuses the v1 store (`packages/kilo-cli/src/storage.ts:19-22`). v2 never reads `session_share/.json`. v2 `import` decodes `SessionTransfer.Data {info,messages}` and rejects URLs (`packages/kilo-cli/src/commands.ts:353-370`; `packages/schema/src/session-transfer.ts:5-9`), but upstream v2 `session import` accepts a file **or URL** (`origin/v2:packages/cli/src/commands/handlers/session/import.ts`), and a v1→v2 history migration exists (§5). | yes | Move restore to the v2 transcript/import path (upstream `session import` accepts URLs); add a v1-export→`SessionTransfer.Data` converter reusing the migration's transform (§5, G8). | both | +| Cancellation | `v2Client.session.abort` → `POST /session/:id/abort` (`cloud@3de933dd wrapper/src/kilo-api.ts:468-472`; `origin/main:packages/sdk/js/src/v2/gen/sdk.gen.ts:4872`); wrapper `/job/abort` sends an `interrupted` ingest event (`wrapper/src/server.ts:1247,831-845`); control-plane `session.abort` (`wrapper/src/control-plane/turn.ts:859-866`); worker `markAsInterrupted`/`interruptExecution` (`src/router/handlers/session-management.ts:267-272`). | v2 route is `POST /api/session/:sessionID/interrupt`, returning `{interrupted:boolean}`; interruption targets the process-local ownership chain, idle is a no-op, unknown sessions are rejected (`kilo-v2:packages/protocol/src/groups/session.ts:665-685`; `packages/core/src/session/execution.ts:27-33,148-165`; `packages/core/src/session/run-coordinator.ts:142-162`). | yes | Adapt to the v2 interrupt route/semantics; keep the `interrupted` ingest signal. | both | + +**Disposition: v2 client adaptation + runtime packaging change (both).** +cloud-agent-next is pinned at 7.8.1, so it keeps working until its own pin bump; +its cutover is independent with its own go/no-go (#14425). Nothing here is +"no change": binary, serve contract, routes, events, credentials, persistence and +cancellation all differ. + +## 2. auto-routing-benchmark + +Container installs `@kilocode/cli@latest` unpinned (`cloud@3de933dd +services/auto-routing-benchmark/container/Dockerfile:9-16`) and spawns `kilo run +--format json --auto -m kilo/ [--variant ] ` (`container/server.mjs:55-79`). + +| Area | Current behavior + evidence | v2 behavior + evidence | Breaks? | Change needed | Lands in | +|---|---|---|---|---|---| +| Startup | Image build runs `npm install -g @kilocode/cli@latest`; worker spawns `kilo` in a temp cwd (`cloud@3de933dd services/auto-routing-benchmark/container/Dockerfile:12`; `container/server.mjs:46,55-79`). | v2 `@kilocode/cli` is private with no `bin` and builds `kilo2` (`kilo-v2:packages/kilo-cli/package.json:2-4`; `script/build.ts:35`). Because v2 ships to `next`, `@latest` keeps resolving v1 until this consumer opts in. | yes (on opt-in) | Publish v2 CLI with an installable `bin`; adapt spawn to `kilo2`; pin to `next` explicitly when adopting. | both | +| Session admission | One fresh `kilo run` per case; no session ids/continuation; runs serialized because the CLI store is not concurrency-safe (`container/server.mjs:37-41`; `src/run.ts:1258`). | v2 `run` accepts positional prompt, `-s/--session`, `-m/--model`, `--agent`, `--auto`, `--file`, `--directory`, `--format text|json` (`kilo-v2:packages/kilo-cli/src/commands.ts:283-352`). There is no standalone `--variant` flag, but upstream v2 encodes the variant in the model ref as `provider/model#variant` (`origin/v2:packages/cli/src/commands/commands.ts`, `run` model help). | yes | Pass the variant as `-m kilo/#`, or add a `--variant` compatibility flag. | both | +| Streaming | Parses one JSON event per stdout line: completed `type:text` parts and `step_finish` cost (`cloud@3de933dd src/kilo-events.ts:52-85`; emitted by `origin/main:packages/opencode/src/cli/cmd/run.ts:876-891`). | kilo2's `run --format json` prints one completed `{sessionID,text}` object (`kilo-v2:packages/kilo-cli/src/tui-preview.ts:231`), but **upstream v2 preserves the streaming event log**: `noninteractive.ts` emits `{type,timestamp,sessionID,part}` for `text`/`step_start`/`step_finish`/`tool_use`/`reasoning` and `{type,error}` for errors (`origin/v2:packages/cli/src/run/noninteractive.ts`). The parser works against upstream v2; it fails only against kilo2 today. | yes (kilo2 only) | Align kilo2's `run` with upstream v2's non-interactive path (preferred), or adapt the parser to the single-object result. | this repo | +| Tools | None; `--auto` only. | `--auto` exists; no tool/permission flags passed. No break. | no | none | — | +| Credentials | Injects `KILO_AUTH_CONTENT {kilo:{type:api,key,organizationId}}`, `KILO_API_URL`, `KILO_ORG_ID`, `KILO_DISABLE_SESSION_INGEST=1` (`cloud@3de933dd container/server.mjs:66-79`). v1 reads them (`origin/main:packages/core/src/plugin/provider/kilo.ts:18-19`; `packages/opencode/src/auth/index.ts:61`). | v2 reads none of `KILO_AUTH_CONTENT`/`KILO_ORG_ID`/`KILO_DISABLE_SESSION_INGEST`; only `KILO_API_URL` as the gateway (`kilo-v2:packages/kilo-cli/src/tui-preview.ts:141`). Credentials can be seeded with `import-v1 --auth` or by writing an `auth.json` the DB migration imports (§5, G6). | yes | Seed v2 credentials via `import-v1 --auth`/`auth.json` before `run`, or add a v2 env path. | both | +| Persistence | Only a temp cwd, removed after each run (`cloud@3de933dd container/server.mjs:46,119`); no CLI home/config read. | v2 uses isolated `kilo2` paths (`kilo-v2:packages/kilo-cli/src/paths.ts:6,26`); a fresh temp cwd still works. | no | none (v2 store is created in-container). | — | +| Cancellation | 180 s timeout, kills the whole process group (`cloud@3de933dd container/server.mjs:59-63,84-95`; `src/cli-runner.ts:15`). | v2 `run` installs abort handling and interrupts its session (`kilo-v2:packages/kilo-cli/src/run.ts:78,269-270`); SIGKILL of the group still applies. | no | none | — | + +**Disposition: runtime packaging change + v2 client adaptation (both).** It +floats `@kilocode/cli@latest`, but the `next` channel keeps `latest` on v1, so +there is no forced break: this consumer adapts only when it opts into `next` +(or when v2 is eventually promoted to `latest`). Adaptation (binary, variant +syntax, streaming output, credentials) must land before that opt-in. + +## 3. MCP catalog generator (CI) + +Workflow installs the CLI unpinned (`npm install -g @kilocode/cli`) and shells +out to `kilo run` (`cloud@3de933dd .github/workflows/kilo-mcp-catalog.yml:112-115,368-371`; +`apps/web/src/scripts/mcp-catalog/catalog.ts:721-732`). + +| Area | Current behavior + evidence | v2 behavior + evidence | Breaks? | Change needed | Lands in | +|---|---|---|---|---|---| +| Startup | `npm install -g @kilocode/cli` in both jobs (`cloud@3de933dd .github/workflows/kilo-mcp-catalog.yml:115,371`). | v2 package is private/no-`bin`, artifact `kilo2` (`kilo-v2:packages/kilo-cli/package.json:2-4`; `script/build.ts:35`); `next` channel keeps the unpinned install on v1 until the workflow pins `next`. | yes (on opt-in) | Publish installable v2 CLI; adapt install/spawn; pin `next` when adopting. | both | +| Session admission | One independent `spawnSync('kilo',['run','--model',M,'--variant',V,'--format','json'])`, prompt on stdin, cwd `tmpdir()` (`cloud@3de933dd apps/web/src/scripts/mcp-catalog/catalog.ts:721-732`). | v2 `run` has `-m/--model` and `--format` but no `--variant`; upstream v2 encodes variant as `provider/model#variant` (`kilo-v2:packages/kilo-cli/src/commands.ts:283-352`; `origin/v2:packages/cli/src/commands/commands.ts`). | yes | Move the variant into the model ref, or add a `--variant` flag. | both | +| Streaming | Parses completed `type:text` parts from stdout lines (`cloud@3de933dd catalog.ts:684-707,758`). | Upstream v2 keeps the streaming `type:text`/`step_finish` log (`origin/v2:packages/cli/src/run/noninteractive.ts`); kilo2 currently emits a single `{sessionID,text}` object (`kilo-v2:packages/kilo-cli/src/tui-preview.ts:231`). | yes (kilo2 only) | Align kilo2's `run` with upstream, or adapt the parser. | this repo | +| Tools | None. | None passed; no break. | no | none | — | +| Credentials | Mints a service token, exports `KILO_API_KEY`/`KILO_ORG_ID`; otherwise ambient `kilo auth login` (`cloud@3de933dd .github/workflows/kilo-mcp-catalog.yml:155-173,397-415`). v1 reads `KILO_API_KEY`/`KILO_ORG_ID` (`origin/main:packages/core/src/plugin/provider/kilo.ts:18-19`). | v2 reads neither env. Seed via `import-v1 --auth`, or write an `auth.json` for the upstream DB migration (`origin/v2:.../20260805200742_import_legacy_credentials.ts`). | yes | Seed v2 credentials, or add a v2 env path. | both | +| Persistence | Runs in `tmpdir()` so project config is not loaded (`cloud@3de933dd catalog.ts:713-715,728`); writes only `catalog.json`. | Isolated `kilo2` store in the runner home; temp cwd still avoids project config. | no | none | — | +| Cancellation | 10 min `spawnSync` timeout; 20 min job timeout (`cloud@3de933dd catalog.ts:84,729`; `kilo-mcp-catalog.yml:66,324`). | v2 `run` handles abort/interrupt; process kill still applies. | no | none | — | + +**Disposition: runtime packaging change + v2 client adaptation (both), G1.** +Same floating exposure as the benchmark, neutralized by the `next` channel; the +required `catalog (PR)` check only breaks if/when the workflow opts into v2. + +## 4. session-ingest + +No runtime executor; contract-coupled to the CLI's session shipping and to +`kilo import` (#14425, owner-confirmed). The wire contract is +`@kilocode/session-ingest-contracts` and the server's `SessionItemSchema` union +(`cloud@3de933dd services/session-ingest/src/types/session-sync.ts:12-99`; +`packages/session-ingest-contracts/src/rpc-contract.ts:4`). + +| Area | Current behavior + evidence | v2 behavior + evidence | Breaks? | Change needed | Lands in | +|---|---|---|---|---|---| +| Startup | CLI-side producer is `KiloSessions`; server base is `KILO_SESSION_INGEST_URL` default `https://ingest.kilosessions.ai` (`origin/main:packages/opencode/src/kilo-sessions/kilo-sessions.ts:253,987`). Registration `POST /api/session` returns `{id,ingestPath}` (`cloud@3de933dd services/session-ingest/src/routes/api.ts:215,279-285`). | v2 producer is `@kilocode/gateway` `registerSessions` (`kilo-v2:packages/kilo-gateway/src/session.ts:104-115`), default base `KILO_SESSION_INGEST_URL ?? https://ingest.kilosessions.ai` (`:402`), registration `POST /api/session` (`:267-276`). | partial | Keep the same base/registration; map the reduced v2 producer. | this repo | +| Session admission | Continuous ingest: register once, then stream `message`/`part`/`session_diff`/`model`/`session_open`/`session_close`/`session_status`/`agent_notification`/`session_pr_link` items (`cloud@3de933dd session-sync.ts:12-99`; emitted by v1 `kilo-sessions.ts:718,812,929,1745`). | v2 only uploads on an explicit `share` RPC, as a reduced batch `kilo_meta` + `session` + self-contained `message` items (`kilo-v2:packages/kilo-gateway/src/session.ts:64-97,279-300`). No `part`/`session_diff`/`session_open`/`session_close`/`session_status`/`agent_notification`/`session_pr_link`; no continuous stream. | yes | Decide whether session-ingest must consume v2 self-contained messages, and add a continuous producer (or an explicit-sync equivalent) for hosted sessions. | both (v2 producer first; cloud validates) | +| Streaming | WS frame `{type:'ingest',sessionId,data:[…]}` and HTTP `POST /api/session/:id/ingest?v=N` with root `{data:[…]}` (`cloud@3de933dd services/session-ingest/src/types/ws-protocol.ts:5-9`; `src/routes/api.ts:431`; limits `util/ingest-limits.ts:11-12`). | v2 posts `{data: batch}` to `?v=2` over HTTP only (`kilo-v2:packages/kilo-gateway/src/session.ts:279-300,409-411`); no WS producer. | partial | Confirm reduced HTTP batch is accepted; WS continuity not provided. | this repo/cloud | +| Tools | n/a (no tools in the payload). | n/a. | no | none | — | +| Credentials | Bearer Kilo JWT via `KILOCODE_TOKEN`/auth.json (`cloud@3de933dd services/session-ingest/src/middleware/kilo-jwt-auth.ts:52-101`; wrapper writes `.local/share/kilo/auth.json`). | v2 resolves the active Kilo credential from the isolated store (`kilo-v2:packages/kilo-gateway/src/session.ts:386-397`); bearer still used on the wire. | partial | Confirm v2 credential resolution yields the same JWT audience; seed credentials for headless. | this repo/cloud | +| Persistence | Wrapper bridges by writing `$XDG_DATA_HOME/kilo/storage/session_share/.json` `{id,ingestPath}`; v1 CLI reads it (`cloud@3de933dd wrapper/src/restore-session.ts:169-195`; `origin/main:.../kilo-sessions.ts:1562,1603`). Restore downloads `GET /api/session/:id/export` then `kilo import ` (`wrapper/src/restore-session.ts:1069,1196-1207`). | v2 stores bootstrap under host storage key `session:`, not the `session_share` file (`kilo-v2:packages/kilo-gateway/src/session.ts:210-225,466-468`); v2 `import` accepts only a local v2 `SessionTransfer.Data` transcript and rejects URLs (`packages/kilo-cli/src/commands.ts:353-370`). Upstream v2 `session import` accepts a URL and the export shape is convertible via the migration's transform (§5). | yes | Add a v1-export→`SessionTransfer.Data` converter (reuse §5), or have session-ingest export v2 transfer data. | both | +| Cancellation | n/a; `session_close` reason `completed|error|interrupted` is data, not a cancel call (`cloud@3de933dd session-sync.ts:53-55`). | n/a; v2 has no `session_close` item. | partial | Carry terminal outcome another way if consumers rely on `session_close`. | this repo | + +**Disposition: v2 contract change (producer) with cloud coordination.** The v2 +producer exists but is reduced and explicit-share-only; continuous session +shipping and the `session_share`/import bridge are absent. session-ingest's +server code likely needs no schema change (the union is permissive), but its +message projection and the `session_close`/diff/part semantics must be validated +with v2 self-contained messages. + +## 5. v1 → v2 session history migration + +This is the load-bearing piece for the "migrate existing v1 users' sessions" +goal. The owner asked whether upstream already solves it — it does, and kilo-v2 +already vendors an older copy of the same file. + +| Item | Evidence | +|---|---| +| Upstream migration engine | `origin/v2:packages/core/src/database/v1-migration.bun.ts` — migrates the legacy local SQLite store in place: `session`→`session_v2`, and `message`+`part`→`session_message` via `transformSession`, plus importing a previous-channel `opencode-next.db`. | +| Upstream history | Introduced by `0fbe489e35 feat(core): migrate v1 data to v2 (#40723)`; refined by `3facbe1dd3` (older previous-channel DBs, #43142), `facd7ff452` (simplify effects, #45685), `2c369a21c9` (renamed legacy tools notice, #50188). | +| Platform variants | `origin/v2:packages/core/src/database/v1-migration.ts` selects a platform build; `v1-migration.noop.ts` is a no-op (`status: "completed"`) for hosts without Bun SQLite. Cloud-agent-next runs the Bun binary, so the Bun path applies. | +| Wiring | Auto-runs in the server graph via `V1Migration.layer` (`origin/v2:packages/server/src/routes.ts`; kilo-v2 `packages/server/src/routes.ts:185`); exposed as `GET /api/experimental/migration/v1` (`kilo-v2:packages/protocol/src/groups/migration.ts:18`). | +| Kilo's copy | `kilo-v2:packages/core/src/database/v1-migration.bun.ts` is an older revision (missing the renamed-tool notice; its project insert lacks upstream's `time_active`) and still uses the pre-rename scope `@opencode-ai/*`, while upstream v2 uses `@opencode/*`. | +| Local-store copy | `kilo-v2:packages/kilo-cli/src/import-v1-session.ts` copies the v1 store into the isolated `kilo2` layout (read-only source, WAL-consistent, no-clobber), but explicitly leaves running `V1Migration` against the copy to "the CLI command slice," which is not wired — kilo2 has no `migrate`/import-v1-sessions command (`packages/kilo-cli/src/commands.ts`). | +| Config/credential migration | `origin/v2:packages/cli/src/config/migrate.ts` (`ConfigMigrateV1`); `kilo-v2:packages/kilo-cli/src/import-v1-config.ts`; upstream credential import `origin/v2:.../migration/20260805200742_import_legacy_credentials.ts`. | + +**Consequence:** the migration algorithm exists and is proven (upstream, with +tests and follow-up fixes); there is no need to design one. The work is (a) +sync kilo-v2's copy up to upstream `origin/v2`, (b) account for the npm-scope +rename when syncing, and (c) wire the user-facing flow (discover v1 store → +copy into `kilo2` via `import-v1-session` → run `V1Migration`). None of this +covers the cloud session-ingest wire, which is a separate contract (§4). + +## Disposition summary + +| Consumer | Disposition | Lands in | Pinning (#14425) | Recommended gate (#14425) | Evidence | +|---|---|---|---|---|---| +| cloud-agent-next | v2 client adaptation + runtime packaging change | both (v2 contract/packaging first) | pinned 7.8.1; independent cutover | G1 for adaptation; independent go/no-go | §1; `wrapper/src/control-plane/kilo-runtime.ts:21,184`; `packages/protocol/src/groups/session.ts:170,338,666` | +| auto-routing-benchmark | runtime packaging change + v2 client adaptation | both; adapt on `next` opt-in | floats `@kilocode/cli@latest` (safe while v2 is on `next`) | G1 | §2; `container/server.mjs:55-79`; `src/kilo-events.ts:52-85`; `origin/v2:packages/cli/src/run/noninteractive.ts` | +| MCP catalog generator (CI) | runtime packaging change + v2 client adaptation | both; adapt on `next` opt-in | unpinned `npm install -g @kilocode/cli` (safe while v2 is on `next`) | G1 | §3; `catalog.ts:721-732`; `kilo-mcp-catalog.yml:115,371` | +| session-ingest | v2 contract change (producer) + cloud coordination | both (v2 producer first) | contract-coupled, no runtime | G1 (producer-side) | §4; `packages/kilo-gateway/src/session.ts:64-97,279-300`; `session-sync.ts:12-99` | + +No in-scope consumer is "no change". Every disposition includes a v2 +contract/packaging change in this repo; cloud-agent-next additionally owns a +client adaptation. Because v2 ships on `next`, the two floating consumers are +not forced to adapt until they opt in. + +## Gaps for #14427 + +Ordering rule: v2 contract/packaging items land in this repo first; cloud client +adaptations follow. The floating consumers adapt only when they move to `next`. + +### G1 — Spawnable kilo2 serve contract +- Repo: this repo. +- Problem: the cloud wrapper spawns `kilo serve --hostname/--port` and waits for + `kilo server listening`; kilo2 is named `kilo2`, prints `URL:`/`Password file:`, + and its `serve` lacks `--hostname`/`--port`. +- Solution: align kilo2's `serve` with upstream v2's (`--hostname`, `--port`, + `--cors`, `--service`, `--stdio`) and publish a documented readiness/auth + contract, or define an explicit headless-spawn contract. +- Touches: `packages/kilo-cli/src/{index.ts,commands.ts,tui-preview.ts,interactive-server.ts}`, `packages/server/src/{routes.ts,fetch.ts,handlers/health.ts}`, `packages/protocol/src/groups/{health.ts,server.ts}`. +- Dependencies: G2 (binary), G3 (handshake). +- Cloud coordination: yes — cloud-agent-next wrapper is the reference consumer. + +### G2 — Packaging + release channel +- Repo: this repo. +- Problem: `@kilocode/cli` on v2 is private, has no `bin`, and there is no + pre-release channel, so no consumer can install v2 and the floating + consumers would break if v2 took `latest`. +- Solution: add `bin` (keep the `kilo2` name per owner decision), drop `private` + for the pre-release channel, publish to a new **`next`** dist-tag, and keep + `latest` on v1 for the migration. Add a release guard/test that asserts + `latest` still resolves the v1 CLI. +- Touches: `packages/kilo-cli/package.json`, `packages/kilo-cli/script/{build.ts,build-tui.ts}`, release tooling (`.github/workflows/publish.yml`, `packages/script/src/index.ts`). +- Dependencies: none; blocks every consumer. +- Cloud coordination: yes — release-channel naming/ownership. + +### G3 — Version/capability handshake (N9) +- Repo: this repo. +- Problem: v2 health returns only `{healthy,version,pid}`, so a client cannot + safely select v1 vs v2 routes in one session. +- Solution: add a capability/version handshake. Record what each consumer needs: + cloud-agent-next needs backend generation + feature flags; benchmark/MCP + catalog need an argv/output contract marker. +- Touches: `packages/protocol/src/groups/health.ts`, `packages/server/src/handlers/health.ts`, `packages/client/src/service-version.ts`, `packages/kilo-client/src`. +- Dependencies: G1. +- Cloud coordination: yes. + +### G4 — Consumer-facing v2 SDK surface +- Repo: this repo. +- Problem: the cloud imports `@kilocode/sdk` and `@kilocode/sdk/v2`; v2 publishes + no `@kilocode/sdk` (only `@opencode-ai/sdk`). +- Solution: publish a v2 `@kilocode/sdk` (or document `@kilocode/client` as the + replacement) and provide a migration path for the two SDK import sites. +- Touches: `packages/sdk`, `packages/kilo-client` (`@kilocode/client`), `packages/client`. +- Dependencies: G3. +- Cloud coordination: yes. + +### G5 — cloud-agent-next client adaptation +- Repo: cloud. +- Problem: routes, event vocabulary and payloads all differ (§1). +- Solution: adapt wrapper to `/api/session`, `/api/session/:id/prompt`, + `/api/event`, `/api/session/:id/interrupt`; map events + (`session.text.delta`, `session.tool.*`, `session.inbox.*`, + `session.execution.*`); seed credentials. +- Touches: `services/cloud-agent-next/wrapper/src/{main.ts,kilo-api.ts,connection.ts,global-feed.ts}`, `wrapper/src/control-plane/{kilo-runtime.ts,turn.ts}`, `src/session/ingest-handlers/entity-id.ts`, `src/router/handlers/session-management.ts`. +- Dependencies: G1, G3, G4. +- Cloud coordination: yes — cloud team owns the change. + +### G6 — v2 credential seeding for hosted/floating consumers +- Repo: this repo (runtime), cloud (usage). +- Problem: cloud-agent-next injects `KILOCODE_TOKEN`/`KILO_AUTH_CONTENT`/auth.json + and the benchmark/MCP CI inject `KILO_AUTH_CONTENT` or + `KILO_API_KEY`/`KILO_ORG_ID`; v2 reads none of these. v2 credentials are rows + in the isolated store, and the HTTP API intentionally has no credential-import + RPC. +- Solution: an explicit v2 seeding path. Short term, seed via `kilo2 import-v1 + --auth --apply` or by writing an `auth.json` the upstream DB migration imports; + long term, a small env/RPC for hosted consumers. Owner confirmed an import + tool (including auth) is acceptable. +- Touches: `packages/kilo-cli/src/{credential-import.ts,import-v1-config.ts,commands.ts,tui-preview.ts}`, `packages/core/src/credential.ts`, `packages/kilo-gateway/src/account.ts`, `packages/core/src/database/migration/20260805200742_import_legacy_credentials.ts`. +- Dependencies: G2. +- Cloud coordination: yes. + +### G7 — `run` argv/output compatibility for headless consumers +- Repo: this repo (align with upstream), cloud (`#variant`). +- Problem: consumers pass `--variant` and parse a streaming JSON event log. + kilo2 has no `--variant` and prints one `{sessionID,text}` object, **but + upstream v2 already preserves the streaming log** and encodes the variant as + `provider/model#variant`. +- Solution: port upstream's `run/noninteractive.ts` path into kilo2 (preferred); + change consumers to `-m provider/model#variant` or keep a `--variant` shim. +- Touches: `packages/kilo-cli/src/{commands.ts,run.ts,tui-preview.ts}` plus the upstream `run/` port; cloud `services/auto-routing-benchmark/{container/server.mjs,src/kilo-events.ts}`, `apps/web/src/scripts/mcp-catalog/catalog.ts`. +- Dependencies: G2; needed before the consumers move to `next`. +- Cloud coordination: yes. + +### G8 — Restore/import compatibility +- Repo: this repo, cloud. +- Problem: cloud-agent-next runs `kilo import ` on the + session-ingest **v1** export (`{info, messages:[{info, parts}], sessionDiff}`), + while v2 `import` expects `SessionTransfer.Data` (self-contained v2 messages). +- Solution: add a v1-export → `SessionTransfer.Data` converter that reuses + `V1Migration.transformSession`'s part→content mapping (preferred), or have + session-ingest export v2 transfer data. Upstream `session import` already + accepts a file or URL to target. +- Touches: `packages/kilo-cli/src/{commands.ts,import-v1-session.ts}`, `packages/schema/src/session-transfer.ts`, `packages/core/src/database/v1-migration.bun.ts`, `packages/kilo-gateway/src/session.ts`, cloud `services/cloud-agent-next/wrapper/src/restore-session.ts`. +- Dependencies: G11 (reuse its transform); cross-repo order: v2 converter first, cloud restore second. +- Cloud coordination: yes. + +### G9 — session-ingest producer continuity +- Repo: this repo (producer), cloud (validation). +- Problem: v2 emits only `kilo_meta`/`session`/`message` on explicit share, with + no continuous stream and no `part`/`session_diff`/`session_open`/`session_close`/ + `session_status`/`agent_notification`/`session_pr_link`; bootstrap is stored + under `session:`, not the `session_share` bridge file. +- Solution: decide whether session-ingest consumes v2 self-contained messages + and add a continuous (or explicit-sync) producer; map terminal outcome in + place of `session_close`. +- Touches: `packages/kilo-gateway/src/session.ts`, `packages/schema/src/session-event.ts`, `packages/core/src/session/*`. +- Dependencies: G3; cloud validates its projection against v2 self-contained messages. +- Cloud coordination: yes. + +### G10 — Event/entity projection mapping for cloud ingest +- Repo: this repo, cloud. +- Problem: the worker keys entities on `message.updated`/`message.part.updated`; + v2 uses `session.message.content.updated`/`session.step.*`/`session.tool.*` and + self-contained messages. +- Solution: map v2 events/messages to the worker's entity model, or add a + projection. +- Touches: cloud `services/cloud-agent-next/src/session/ingest-handlers/*`, `src/websocket/ingest.ts`; v2 `packages/schema/src/session-event.ts`. +- Dependencies: G5. +- Cloud coordination: yes. + +### G11 — Sync + wire the v1→v2 session migration +- Repo: this repo. +- Problem: upstream `origin/v2` has a proven migration + (`v1-migration.bun.ts`, #40723 + follow-ups); kilo-v2 vendors an older copy and + never wires the user-facing flow; `import-v1-session.ts` exists but has no + command. +- Solution: sync kilo-v2's copy up to upstream `origin/v2` (including the + renamed-tool notice and `time_active`), reconcile the `@opencode-ai`→`@opencode` + scope rename, and wire the CLI flow (discover v1 store → `import-v1-session` + copy → run `V1Migration`), surfacing progress via + `/api/experimental/migration/v1`. +- Touches: `packages/core/src/database/{v1-migration.bun.ts,v1-migration.noop.ts,v1-migration.ts}`, `packages/kilo-cli/src/{import-v1-session.ts,import-v1-config.ts,commands.ts}`, `packages/server/src/routes.ts`. +- Dependencies: none for the engine; G2 for a user-installable binary. +- Cloud coordination: limited — this is local-user migration; confirm no cloud path assumes the v1 store. + +## Proposed progress-plan row changes (for N7; do not apply here) + +File: `migration-tracking/plans/kilo-opencode-v2-plan-progress.md`, section +"Remote, sharing and distribution". + +1. Add a row: + `| Cloud agent consumer contracts | Kilo CLI / Gateway | 4, 6 | Partial | Contract mapping and dispositions complete; implementation gaps open in #14427 | Four in-scope consumers mapped across startup, admission, streaming, tools, credentials, persistence, cancellation. v2 ships on a new next channel so floating consumers are not forced to break. Upstream v2 already provides serve, streaming run, session import and the v1→v2 migration; kilo-v2 needs to converge. See technical-notes/baseline/cloud-agent-contracts.md. |` +2. Amend the #14425-added "Cloud agent runtime consumers (hosted)" row's + remaining scope to say contract mapping is complete and point at + `technical-notes/baseline/cloud-agent-contracts.md`. +3. Amend the `Cloud CLI client` row to note that local `kilo cloud` fixtures and + the `/remote` relay do not establish cloud-hosted consumer compatibility + (already asserted by #14425; keep the two baselines linked). +4. Add a row for the v1→v2 session history migration (upstream-sourced, sync + + CLI wiring) so it is tracked independently of the cloud consumers. + +Evidence links for the epic: #14425 (inventory), #14426 (contracts, this file), +#14427 (adaptation), #14428 (validation). + +## Open questions for the cloud owners + +1. `next` channel mechanics: confirm where the `@kilocode/cli` dist-tag is set + (the in-repo `publish.yml` is the upstream `anomalyco/opencode` skeleton) and + that `latest` will hold the v1 line for the whole migration (G2). +2. Hosted credential injection: is writing an `auth.json` / running + `import-v1 --auth` acceptable in the sandbox image, or does v2 need a + first-class env/RPC for `KILOCODE_TOKEN`/`KILO_API_KEY` (G6)? +3. Session-ingest semantics: can session-ingest consume v2 self-contained + `message` items without `part` rows, and how should `session_close`/diff/ + status/notifications be carried (G9)? +4. Restore: is a v1-export→`SessionTransfer.Data` converter (reusing the + migration transform) acceptable, or should session-ingest export v2 transfer + data (G8)? +5. Migration scope: should the kilo2 CLI expose the migration as an explicit + command, or (like upstream) run it automatically on first open of a copied v1 + store (G11)? diff --git a/migration-tracking/technical-notes/baseline/cloud-cli-v2-parity.md b/migration-tracking/technical-notes/baseline/cloud-cli-v2-parity.md new file mode 100644 index 000000000000..2944fc445758 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/cloud-cli-v2-parity.md @@ -0,0 +1,204 @@ +# Cloud CLI v2 parity + +Scope: port the v1 (`origin/main`) cloud-client pure protocol surface into the v2 +`packages/kilo-cli` tree as Kilo-owned modules, behind an explicit-credential +adapter and a host-side Gateway RPC extension. This document is the parity and +verification ledger for that slice. + +## File ownership and provenance + +All files are Kilo-owned and new; none edit shared upstream code. Sources are +ported from `origin/main:packages/opencode/src/kilocode/cloud/` (v1, 14 files) at +pinned SHA `ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7` (the local `origin/main` +ref at port time). "Byte-port" means the file body is the v1 source with only +import-path rewriting. + +| v2 path | v1 source | Port kind | +|---|---|---| +| `src/cloud/contracts.ts` | `cloud/contracts.ts` | Byte-port (zod schemas, `projectStatus`, `resultExitCode`). | +| `src/cloud/errors.ts` | `cloud/errors.ts` | Byte-port. | +| `src/cloud/http.ts` | `cloud/http.ts` | Byte-port. | +| `src/cloud/message-id.ts` | `cloud/message-id.ts` | Byte-port. | +| `src/cloud/origin.ts` | `cloud/origin.ts` | Byte-port of `parseServiceOrigin` and the two origin constants. **Dropped** `resolveCloudAgentOrigin`/`resolveWebAppOrigin`: those read `process.env`, which is host-owned wiring. Origins are injected explicitly instead. | +| `src/cloud/response-json.ts` | `cloud/response-json.ts` | Byte-port. | +| `src/cloud/trpc.ts` | `cloud/trpc.ts` | Byte-port (`createTrpcClient`, `createAgentClient`, `createCloudAgentClient`). | +| `src/cloud/stream-ticket.ts` | `cloud/stream-ticket.ts` | Byte-port. | +| `src/cloud/websocket-stream.ts` | `cloud/websocket-stream.ts` | Byte-port. | +| `src/cloud/client.ts` | — | New. Explicit-credential adapter (`createCloudAdapter`): takes `{token, organizationId?, agentOrigin, webAppOrigin}`; never reads a credential store, env var, or global. Composes the tRPC client, status/result projection + exit-code mapping, and the stream-ticket/WebSocket stream path. | +| `src/cloud-rpc.ts` | — | New. Public Effect RPC wire contract (`CloudRpc.Definition`, id `kilocode.cloud`). Reuses the canonical zod schemas as Standard Schema validators directly; `start`/`send` narrow the message to prompt-only and structurally omit the org override; `stream.prepare` is a small zod pair. | +| `src/cloud-plugin.ts` | — | New. `registerCloud` GatewayExtension; registers the RPC handlers inside the host plugin scope, resolving the account in-host. | +| `test/cloud-transport.test.ts` | `test/kilocode/cloud/transport.test.ts` | Ported; loopback `Bun.serve` tRPC stub. | +| `test/cloud-stream-ticket.test.ts` | `test/kilocode/cloud/stream-ticket.test.ts` | Ported; mock `fetch`. | +| `test/cloud-websocket-stream.test.ts` | `test/kilocode/cloud/websocket-stream.test.ts` | Ported plus new pre-abort/live-abort coverage; injected mock `WebSocket`. | +| `test/cloud-adapter.test.ts` | — | New; adapter seam against loopback stubs. | +| `test/cloud-cli.test.ts` | — | New; real-launch public-RPC tests via `test/cloud-fixture.ts` and `createClient`. | +| `test/cloud-cli-subprocess.test.ts` | — | New; `tui-preview` cloud verb subprocesses via the packaged launcher. | +| `test/cloud-stream-cli.test.ts` | — | New; real loopback WebSocket `cloud start --stream` tests (provided URL, ticket fallback, non-fatal error, SIGINT abort). | +| `test/cloud-fixture.ts` | — | New; launch fixture wiring the cloud extension to loopback stubs via dev origin overrides. | + +## Deliberately not ported (host/Auth/Core seams owned by the parent) + +| v1 source | Why out of scope | +|---|---| +| `cloud/auth.ts` | Reads the v1 `Auth.Service` credential store and `KILO_API_KEY`. v2 resolves credentials in-host via the Gateway account Effect; the token never crosses RPC. | +| `cloud/defaults.ts` | Depends on v1 `Agent.Service`, `Config.Service`, `KilocodeModelState`, and `CloudCatalog` to pick mode/model. Model/mode resolution is parent-owned host wiring. | +| `cloud/catalog.ts` | Imports `@kilocode/kilo-gateway` header/url helpers for the model catalog. Catalog routing is parent-owned. | +| `cloud/repository.ts` | `CloudRepository.resolve` uses the v1 `Git.Service` for worktree/remote discovery. Repository discovery is host wiring; the wire `RepositoryInput` contract is carried in `contracts.ts`. | +| `cloud/commands.ts` | Effect CLI orchestration over `process.stdout`/`process.exitCode`. The parent owns the CLI command file (`cloud-command.ts`). | + +## Operations: source-backed surface + +| Operation | v1 source | v2 surface | +|---|---|---| +| `start` | `agent.start` mutation → `POST /trpc/start` | `CloudRpc.start` → adapter → tRPC. | +| `send` | `agent.send` mutation → `POST /trpc/send` | `CloudRpc.send` → adapter → tRPC. | +| `status` | `getMessageResult` query + `projectStatus` | `CloudRpc.status` → adapter (assistant projected away). | +| `result` | `getMessageResult` query + `resultExitCode` | `CloudRpc.result` → adapter (`{result, exitCode}`). | +| `stream` | `--stream` on `start`: print safe admission, then `streamAgentEvents` in-process using the admission `streamUrl` or a fetched session ticket | `CloudRpc.start` preserves `streamUrl` for the private consumer; `CloudRpc["stream.prepare"]` validates/pins a provided URL or fetches a ticket; the CLI runs `streamAgentEvents` itself. | +| `cancel` | **absent in v1 source** | **Not implemented — source gap.** | + +**Cancel is a real source gap, not an omission.** `origin/main`'s cloud module and +`cli/cmd/cloud.ts` expose no cancel/interrupt procedure or CLI verb. No cancel +endpoint was invented here; adding one requires a backend contract that does not +exist in the inspected source. + +## Wire contract (exact envelopes) + +- `start`/`send`: `POST /trpc/{start,send}`, JSON body = zod-validated + `AgentStartRequest`/`AgentSendRequest`. Client generates `message.id` + (`msg_<12hex-ms><14 base62>`) when absent and correlates the response + `messageId` (and `cloudAgentSessionId` for send) — a mismatch throws + `ambiguousAdmissionError` ("outcome is unknown; do not retry automatically"). +- `getMessageResult`: `GET /trpc/getMessageResult?input=`. +- Success envelope: `{ result: { data: ... } }`, strict-parsed; `result.data` is + decoded by the per-method zod schema. +- Stream ticket (when the admission carries no `streamUrl`): + `POST {webAppOrigin}/api/cloud-agent-next/sessions/stream-ticket` with + `{cloudAgentSessionId, organizationId?}`; 403/404 retried up to 10×/1s; the + returned ticket builds `/stream?cloudAgentSessionId=…&ticket=…`. +- WebSocket stream: relative or absolute URL pinned to the agent origin + (`ws`/`wss` only); newline-delimited events to a sink; completes on + `{streamEventType:"complete"}` after a 3s grace; idle timeout and an 8 MiB + queued-output bound. + +## Lifecycle / status / error semantics + +- Message status: `queued | running | completed | failed | interrupted`. +- `GetMessageResultOutputSchema` enforces lifecycle invariants: `queued` cannot + carry `acceptedAt`; non-terminal cannot carry `terminalAt`/`completionSource`; + `failure` only on `failed`/`interrupted`; `gateResult`/`assistant` only on + `completed`. +- Exit codes (`resultExitCode`): `completed=0`, `queued`/`running=2`, + `failed=3`, `interrupted=4`. +- HTTP status mapping (tRPC): 401 auth, 402 balance, 403 denied, 404 + session/message not found, 5xx unavailable; redirects are errors. +- RPC error surface is fixed and generic (`kilocode.cloud` / + `kilocode.cloud_unavailable`); schema and upstream API error text — which can + embed prompt or repository-token content — is never echoed across the + boundary. + +## Security / credential handling + +- The adapter takes an explicit resolved `{token, organizationId?, origins}` and + never reads a credential store, `process.env`, or a global. Credential/profile/ + selection resolution lives in the host Gateway account Effect (`registerCloud` + receives it as an argument). +- The RPC `start` input **structurally omits** `options.kilocodeOrganizationId`; + the resolved selected org is injected host-side. A caller cannot override org + selection through the wire (verified by `cloud-host.test.ts`). Personal (null) + selections omit the org field entirely. +- The bearer token is header-only on admission and never crosses the RPC + boundary; tests assert it never appears in a request body, URL, RPC response, + or CLI stdout/stderr. The expiring, session-scoped stream ticket in an + admission's `streamUrl` may cross the authenticated local RPC to its real CLI + consumer (for the source `--stream` flow); it is never the account token, and + the parent sanitizes CLI stdout. +- `allowHttpLoopback` is an explicit opt-in flag propagated to the origin guard; + it is never inferred from ambient environment. Production defaults are the + source origin constants (`https://cloud-agent-next.kilosessions.ai`, + `https://kilo.ai`); explicit dev overrides allow only loopback HTTP via the + existing `parseServiceOrigin` guard. + +## Stream over RPC + +v1's `cloud start --stream` prints the safe admission (with `streamUrl` elided +from stdout), then opens the WebSocket stream in-process via +`streamAgentEvents`, resolving the stream URL from the admission's +`streamUrl` when present or from a fetched session stream ticket otherwise. + +The v2 RPC framework **does** expose a real event channel (`Rpc.define` +`events` plus the `ctx.rpc.register` result's `events.emit`); an earlier draft +of this section claimed otherwise and was wrong. That channel is **not** used +for cloud streaming: routing raw JSONL stream events through a new RPC event +broker would be an invented abstraction with no existing consumer. + +The implemented seam is `stream.prepare({cloudAgentSessionId, streamUrl?})` → +`{origin, streamUrl}`: + +- A caller-provided `streamUrl` is validated and pinned to the configured agent + origin (`resolveWebSocketUrl`) **before any ticket fetch**; a cross-origin URL + is rejected, never followed. The ticket is therefore never forwarded to an + arbitrary origin. +- When `streamUrl` is absent, `createStreamTicketClient` fetches a scoped ticket + under the current resolved account and builds `/stream?…&ticket=…`. +- The expiring, session-scoped ticket crosses the authenticated local RPC to its + real CLI consumer; the account bearer token never does. The parent sanitizes + CLI stdout. +- The CLI then opens the existing `streamAgentEvents` WebSocket transport itself + (`cloud start --stream`), printing the safe admission as one + JSON line followed by raw event lines; a transport failure is caught as a + non-fatal `{streamEventType:"error"}` notice, matching v1. + +### Port delta: AbortSignal on `streamAgentEvents` + +v1's `streamAgentEvents` has no external cancellation — only the idle, +complete-grace, and close timers. v2 adds an optional `signal?: AbortSignal` to +`StreamAgentEventsOptions` so the CLI can cancel a live stream: a pre-aborted +signal rejects **before any WebSocket is opened**; a live abort closes the +socket, clears every timer and the abort listener, and rejects with +`"WebSocket stream aborted"`. The listener is registered before timers are +armed and before any frame can arrive, with a post-registration aborted re-check +to close the setup race. This is the only intentional behavioural delta from the +v1 transport and is exercised by dedicated unit tests and a real SIGINT CLI +test. + +## Verification + +Run from `packages/kilo-cli` (tests cannot run from the repo root): + +| Check | Command | Result | +|---|---|---| +| Typecheck | `bun run typecheck` | pass (0 errors; `tsgo --noEmit`). | +| Cloud unit tests | `./dist/interactive/bun test test/cloud-transport.test.ts test/cloud-stream-ticket.test.ts test/cloud-websocket-stream.test.ts test/cloud-adapter.test.ts` | in-process contract tests (Bun 1.4.0). | +| Cloud real-launch RPC | `./dist/interactive/bun test test/cloud-cli.test.ts` | 5 pass — spawns the real host via `test/cloud-fixture.ts`, seeds a credential through real device-auth against a loopback gateway, drives `CloudRpc` over authenticated HTTP via the public `createClient`. | +| Cloud CLI subprocess | `./dist/interactive/bun test test/cloud-cli-subprocess.test.ts` | 7 pass — seeds a credential, then runs the packaged `dist/interactive/kilo2` `cloud start/send/status/result` subprocesses against loopback stubs via the dev origin overrides. | +| Cloud streaming CLI | `./dist/interactive/bun test test/cloud-stream-cli.test.ts` | 4 pass — real loopback WebSocket `cloud start --stream`: provided-URL pin, ticket fallback, non-fatal error notice, SIGINT abort. | + +Test isolation: every test uses an in-process loopback `Bun.serve` stub or an +injected mock `WebSocket`/`fetch`. **No live cloud endpoint, no cloud task, and +no paid model call is contacted.** Credentials are fixture-seeded through the +real device-auth flow against a loopback gateway; no real account is used. The +bundled Bun 1.4 runtime (`dist/interactive/bun`, and the `dist/interactive/kilo2` +launcher for subprocesses) is used per the package's runtime requirement. + +## Deployed verification gaps + +These are honest limits of the loopback-stub verification; none are covered by +the tests above and each needs a deployed-environment pass: + +1. **Real tRPC envelope drift.** Tests assert the exact `{result:{data}}` envelope + against a stub that returns it. The live cloud-agent service has not been + contacted, so envelope drift between the pinned source contract and the + deployed service is unverified. +2. **Stream-ticket retry cadence against the real web app.** The 403/404 → retry + behavior is stub-verified; real propagation latency and terminal behavior are + not. +3. **WebSocket stream against a live session.** Real loopback sockets drive + event sequencing, provided-URL pinning, ticket fallback, abnormal close, and + SIGINT abort locally; live reconnect, ordering under real load, and the + deployed `streamUrl` shape remain unverified against the real service. +4. **Credential/profile resolution end to end.** `cloud-host.test.ts` builds the + real account Effect from a persisted fixture credential against a stub + profile backend. A live sign-in and org switch (`/teams`) against the real + gateway is the parent's integration scope. +5. **Cancel.** Absent from the v1 source contract; there is nothing to verify. diff --git a/migration-tracking/technical-notes/baseline/config-mapping-v2-gap.md b/migration-tracking/technical-notes/baseline/config-mapping-v2-gap.md new file mode 100644 index 000000000000..dff8c3ddfcd5 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/config-mapping-v2-gap.md @@ -0,0 +1,154 @@ +# V1 to v2 config mapping gap audit + +Companion to `kilocode/baseline/pinned-v2-baseline.md` and +`kilocode/baseline/credential-import-v2-parity.md`. Audits every Kilo-only v1 +config key at the pinned v1 source against the v2 migration engine, the v2 +config schema, and the v2 runtime consumers, and records what the v1 import +(`packages/kilo-cli/src/import-v1-config.ts`) does with each. + +## Sources + +| Source | Reference | +|---|---| +| v1 Kilo config schema | `origin/main` `ecccd1f54b` (`packages/core/src/v1/config/config.ts`, `packages/core/src/config/experimental.ts`, `packages/core/src/policy.ts`, `packages/core/src/catalog.ts`) | +| v1 retained schema (this tree) | `packages/core/src/v1/config/config.ts` | +| v1 migration engine (this tree) | `packages/core/src/v1/config/migrate.ts` | +| v2 target schema | `packages/schema/src/config.ts` and `packages/schema/src/config/*.ts` | +| v2 runtime consumption | `packages/core/src/config/normalize.ts`, `packages/core/src/config/plugin/*.ts`, `packages/core/src/catalog.ts` | +| Import implementation | `packages/kilo-cli/src/import-v1-config.ts` | +| Import tests | `packages/kilo-cli/test/import-v1-config.test.ts` | + +The Kilo-only key list was derived by diffing the top-level fields of the v1 +`ConfigV1.Info` schema at the pin against this tree's retained v1 schema, plus +the Kilo-only nested fields marked `kilocode_change` in the pinned file. + +## What the native migration already carries (not duplicated) + +`ConfigMigrateV1.migrate` in this tree already maps these v1 keys; the import +does not re-implement them: + +- `autoshare: true` -> `share: "auto"` (and `share` verbatim) +- `tools` -> `permissions` rules (with `write`/`patch` -> `edit`, `task` -> + `subagent`, `bash` -> `shell` renames); `permission` merges on top +- `compaction.auto`, `compaction.preserve_recent_tokens` -> `keep.tokens`, + `compaction.reserved` -> `buffer` +- `experimental.mcp_timeout` -> `mcp.timeout` (catalog and execution) +- `enabled_providers` / `disabled_providers` -> generated + `experimental.policies` (`provider.use` allow/deny statements) +- `experimental.subagent_depth`, `mode`/`agent` -> `agents`, + `small_model` -> `agents.title.model`, `model`, `default_agent`, `$schema`, + `shell`, `username`, `enterprise`, `snapshot` -> `snapshots`, `watcher`, + `formatter`, `lsp`, `attachment` -> `media`, `tool_output`, `mcp`, + `skills`, `command` -> `commands`, `instructions`, + `references`/`reference`, `plugin` -> `plugins`, `provider` -> `providers`, + `autoupdate` -> `update` + +Kilo v1 `model` / `small_model` / `default_agent` are `NullOr(string)` where +the retained v1 schema only accepts strings; the import treats an explicit +`null` as "unset" and drops it before decoding instead of failing the file. + +## Carried by the Kilo-owned import seam + +These keys are validated at the raw level and patched into the written profile +config. They are intentionally not added to the shared upstream v1 schema or +the migration engine. + +| v1 key | v2 target | Why the mapping is exact | +|---|---|---| +| `privacy_mode` | `privacy_mode` (verbatim boolean) | Consumed by the isolated CLI privacy store (`packages/kilo-cli/src/privacy-settings.ts`) under the same key | +| `hide_prompt_training_models` | `hide_prompt_training_models` (verbatim boolean) | Consumed by the CLI settings store and gateway model routing (`packages/kilo-cli/src/settings.ts`, `settings-rpc.ts`, `interactive-server.ts`) under the same key | +| `subagent_depth` (top-level) | `experimental.subagent_depth` | The pinned v1 runtime consumed only the top-level key (`origin/main` `packages/opencode/src/tool/task.ts:135`), and the pinned experimental struct has no such field; the upstream migration engine reads only `experimental.subagent_depth` (`migrate.ts:89`), so it silently dropped the top-level form. The import validates the top-level value through the retained v1 schema's identical `NonNegativeInt` range and patches it into `experimental.subagent_depth` with top-level precedence over any migrated experimental value. The v2 runtime consumes the leaf (`packages/core/src/tool/plugin/subagent.ts:129`, last-wins `Config.latest`, default 1), and the Kilo settings dialog manages the same leaf. A real isolated host loads the patched value through its config pipeline. | +| `experimental.policies` | `experimental.policies` | The pinned v1 policy shape (`ConfigExperimental.Policy` = `PolicyV2.Info` fields with `action` narrowed to `Catalog.PolicyActions`, exactly `["provider.use"]` at the pin) is field-for-field the v2 `ConfigPolicy.Info` (`{action: "provider.use", resource, effect: "allow"\|"deny"}`). The v2 runtime consumes it: `packages/core/src/config/plugin/policy.ts` removes denied providers from the catalog, and `normalize.ts` places authored policies after generated ones with last-match-wins evaluation. The import validates each statement against the v2 contract (excess properties rejected) and appends authored statements after the migration-generated ones, preserving that precedence. | + +## Refused: Kilo-only keys with no safe v2 seam + +Every remaining Kilo-only key is reported unsupported by the import (blocking +the import by default; `--allow-unmapped` imports the mapped subset). None of +these dispositions is a capability claim; they are gaps recorded here, not row +completions. "Not silently retired" means the key is refused loudly, matching +the no-swallow rule. + +Reconciled against the current source on 2026-09-07: each refused key below was +re-searched for a v2 runtime consumer, and none has gained one. The sandbox +feature configures only through the `--sandbox` launch flag with host-typed +options (`packages/kilo-cli/src/commands.ts:164`, +`packages/kilo-cli/src/interactive-server.ts:153`), never through profile +config, so `sandbox`-family config keys still have nothing to map into. +Telemetry consent is deliberately a separate owner-only opt-in file +(`packages/kilo-cli/src/telemetry-settings.ts`), so the v1 default-true +`experimental.openTelemetry` cannot map without inventing consent. The +`experimental.subagent_depth` and `media.image` v2 fields are native (carried by +the migration or authored fresh), not v1 mappings, and are now surfaced through +the Kilo settings dialog (see `settings-v2-parity.md`). + +| v1 key | Why no mapping is safe | +|---|---| +| `web_search` (boolean) | v1 `web_search === true` force-enables the WebSearchTool while the provider is chosen by the model provider and env keys (`packages/opencode/src/tool/registry.ts` at the pin); v1 `false` means "not force-enabled", so provider-native search can still apply. The v2 target `websearch` is a provider selection (`false \| {provider: "random" \| WebSearch.ID}`) consumed by `config/plugin/websearch.ts`, where `false` forces search off. Mapping `true` to `"random"` invents behavior; mapping `false` to `false` changes meaning. Neither direction is equivalent. | +| `subagent_model`, `subagent_variant`, `subagent_variant_overrides` | No v2 target. v2 has per-agent `model` selections only; there is no "default task-subagent model" concept (`packages/schema/src/config/agent.ts`). Mapping to a named agent such as `general` would invent an equivalent. | +| `indexing` | v2 consumes indexing configuration only through the explicit opt-in `--indexing-config` launch input (`packages/kilo-cli/src/indexing-input.ts`), never from profile config. Mapping it into the written config would store values no runtime reads. Not mapped per instruction. | +| `sandbox` and `experimental.sandbox` / `sandbox_restrict_network` / `sandbox_writable_paths` | No v2 config consumer exists. Refused loudly, not silently retired. | +| `remote_control` | No v2 target field or consumer. | +| `auto_collapse_reasoning` | Native TUI `session.thinking` (`show`/`hide`) is consumed by the session view and exposed by `/settings` and `/thinking`. Import remains refused: mapping a profile key into `tui.json` crosses configuration surfaces, and hiding by default differs from v1 collapsing after completion. | +| `console` (`context_sidebar_width`, `diff_style`) | No v2 target field or consumer (Kilo Console UI state). | +| `terminal_command_display`, `code_edit_display`, `mcp_tool_display` | No v2 target field or consumer (VS Code chat UI state). | +| `commit_message` | No v2 target field or consumer. | +| `compaction.threshold_percent` | Kilo-only v1 extension; no v2 field. | +| `compaction.tail_turns`, `compaction.prune` | The v2 normalize explicitly reports both as unsupported (`normalize.ts`); the migration emits `prune` only for the v2 schema to drop it. | +| `experimental.openTelemetry` (v1 default-true telemetry opt-out) | v2 normalize lists `openTelemetry` as unsupported experimental; there is no v2 target. The v1 decoding default (`true`) is not reproduced anywhere in v2, and inventing a telemetry mapping would be a behavior claim, not a config mapping. | +| `experimental.image_generation`, `image_generation_model`, `native_notebook_tools`, `task_model_selection`, `speech_to_text_model`, `shared_agent_board` | v2 normalize lists the shared ones as unsupported experimental; the rest have no v2 target field. | +| `experimental.disable_paste_summary`, `batch_tool`, `primary_tools`, `continue_loop_on_deny` | v2 normalize lists them as unsupported experimental. | +| `logLevel`, `server`, `layout` | v2 normalize lists them as unsupported top level with no v2 target field or consumer (`layout` is deprecated in v1 itself). Top-level `subagent_depth` was reconciled out of this row on 2026-09-07: it now maps to the consumed `experimental.subagent_depth` leaf (see the carried table). | + +## Validation + +All commands used the package-local bundled Bun 1.4.0 +(`packages/kilo-cli/dist/interactive/bun`) with `--cwd packages/kilo-cli`: + +| Check | Result | +|---|---| +| `bun test test/import-v1-config.test.ts` | 49 pass, 0 fail (216 assertions), including the 5 policy tests below and the 4 subagent_depth tests (plan/readback precedence, top-level-only, real-host config-pipeline load, range/type refusal); re-run 2026-09-07 with the packaged Bun 1.4 | +| `bun typecheck` (package `@kilocode/cli`) | clean on 2026-09-07, including the settings and importer modules (an earlier run failed only on another slice's untracked `src/tui-plugin/sidebar-pr.tsx`; that file now typechecks) | + +New tests (all in `packages/kilo-cli/test/import-v1-config.test.ts`): + +- carries authored v1 experimental policies into the migrated v2 config (plan + shape plus on-disk readback of the isolated profile config) +- orders authored policies after generated provider policies so authored + statements win (v1 `disabled_providers` + authored allow for the same + resource lands deny-then-allow) +- refuses authored policies that do not match the v2 policy contract without + echoing them (unknown action, unknown effect, missing field, excess field, + non-array; fixed generic diagnostic) +- reports v1 `web_search` (both `true` and `false`) and the subagent model + keys as unsupported instead of inventing mappings +- applies imported provider policies through the native v2 catalog: imports a + v1 config declaring two providers plus a deny policy for one, launches the + isolated profile server with `models: false`, and asserts through the public + `provider.list` client API that the denied provider is absent and the other + is present + +Subagent-depth tests (2026-09-07): top-level value wins over a migrated +experimental value while authored policies and unrelated keys survive (plan and +readback); top-level-only input writes the patched leaf; a real isolated host +loads `experimental.subagent_depth` through its public `config.get` pipeline; +`-1`, `1.5`, `"2"`, and `null` are refused with the fixed content-free schema +diagnostic. + +## Honest acceptance status + +- The authored `experimental.policies` mapping is implemented, tested at the + plan, readback, precedence, refusal, and native-consumer levels. +- The top-level `subagent_depth` mapping (2026-09-07) closes the one refused + key that had a proven, consumer-verified v2 target: plan/readback with + top-level-over-experimental precedence, real-host pipeline load, and + range/type refusals. No shared upstream file changed. +- The previously accepted `privacy_mode` / `hide_prompt_training_models` + behavior is unchanged; its tests still pass. +- All other Kilo-only keys remain gaps, reported as unsupported by the import. + That is evidence of an honest refusal surface, not capability completion: + the table above records each gap with its source-backed reason. +- 2026-09-07 reconciliation: no refused key gained a v2 consumer, so no new + mapping was added. The settings-dialog surface, not the importer, gained the + native `media.image` and `experimental` leaf controls; the two surfaces stay + separate because the importer maps v1 keys while the dialog edits native v2 + fields. diff --git a/migration-tracking/technical-notes/baseline/credential-import-v2-parity.md b/migration-tracking/technical-notes/baseline/credential-import-v2-parity.md new file mode 100644 index 000000000000..1977cb5d8001 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/credential-import-v2-parity.md @@ -0,0 +1,91 @@ +# V1 credential import parity boundary + +The v2 import path is intentionally host-local. It reads supplied local v1 +files, builds an in-memory plan, and reports only source facts and +non-sensitive decisions; neither credentials nor a credential writer are +available over the public HTTP/RPC client. + +## Source evidence + +- V1 auth records are API, OAuth, or well-known entries. The import decoder in + `packages/kilo-cli/src/import-v1-config.ts` accepts exactly those source + shapes. +- The v2 credential schema supports a key plus arbitrary metadata and an OAuth + value with method ID, refresh/access tokens, expiry, and arbitrary metadata: + `packages/schema/src/credential.ts`. +- V1 well-known credentials store their source origin as the auth record key, + the manifest environment name as `key`, and its value as `token`. V2's + public `integration.wellknown.add` discovers that origin; its WellKnown + config loader resolves the discovered `manifest.auth.env` from the selected + key credential for the same normalized origin. The v2 legacy credential + migration uses this same origin-plus-key mapping. +- V1 Kilo uses OAuth `accountId` only as its selected organization. Its getter + returns it only for OAuth + (`ecccd1f:packages/kilo-gateway/src/server/handlers.ts:getOrganizationId`), + and the Kilo organization setter writes selected `organizationId` back to + that OAuth field + (`ecccd1f:packages/opencode/src/kilocode/server/httpapi/handlers/kilo-gateway.ts:344-356`). +- The v2 Kilo Gateway registers the OAuth method ID `device` and reads + `metadata.server` plus `metadata.organizationID` + (`packages/kilo-gateway/src/gateway.ts`, `plugin.ts`). +- The v2 OpenAI provider registers `chatgpt-browser` and reads + `metadata.accountID`; its v1 Codex equivalent stored and used `accountId` + for the same request header (`packages/core/src/plugin/provider/openai.ts`, + `ecccd1f:packages/opencode/src/plugin/openai/codex.ts`). +- The v2 and v1 GitHub Copilot providers both use `device` OAuth and + `enterpriseUrl` to select the enterprise endpoint + (`packages/core/src/plugin/provider/github-copilot.ts`, + `ecccd1f:packages/opencode/src/plugin/github-copilot/copilot.ts`). +- The v2 xAI provider registers `device`; the v1 xAI flow stores only OAuth + token fields, so no legacy metadata is carried + (`packages/core/src/plugin/provider/xai.ts`, + `ecccd1f:packages/opencode/src/plugin/xai.ts`). + +## Implemented mapping + +- Plain v1 API keys retain the pre-existing public-client key-connect path. +- An API key with v1 string metadata requires the in-process + `CredentialWriter`; it maps to `Credential.Key` preserving that metadata. + The public key-connect API has no metadata field, so it is not used here. +- A Kilo OAuth entry is accepted only for normalized integration ID `kilo`, + explicit `gatewayServer`, and a UUID `accountId` when present. It maps + refresh/access/expiry unchanged to `Credential.OAuth`, exact method ID + `device`, the validated explicit server, and `accountId` to + `organizationID`. An absent v1 account ID becomes explicit personal + `organizationID: null`. +- A v1 well-known entry is accepted only when its explicitly supplied origin + currently exposes the exact saved environment key. The importer first uses + the public discovery endpoint at the selected Location, then uses the + in-process writer to store the token as that normalized origin's key + credential. Discovery is reported without the token; if a later credential + write fails, the report records the discovered origin because the public API + has no add-and-write transaction or rollback operation. +- Proven v1-to-v2 OAuth mappings are deliberately finite: OpenAI uses + `chatgpt-browser` with `accountId` renamed to `metadata.accountID`; GitHub + Copilot uses `device` with only `enterpriseUrl` metadata; xAI uses `device` + with token fields only. Each still requires the exact registered OAuth method + in the public inventory before the host writer runs. +- `createCredentialImporter` uses the host's real `Credential.Service.create`; + that service atomically selects the created credential. The writer is an + in-process closure returned by the host and is not an API endpoint. +- Before writing, the public integration inventory must contain the target + integration and its matching key method or exact Kilo OAuth/device method. + The import receives the same public Location used for plugin activation so + location-scoped Kilo methods are visible to that inventory. + Existing credential conflicts still require explicit overwrite permission. + +## Deliberate gaps + +- No custom v1 Kilo server can be recovered from auth.json: v1 derived it from + process `KILO_API_URL` or a default. The import refuses Kilo OAuth without an + explicit validated `gatewayServer`; it never infers a server or maps + `enterpriseUrl`. +- Generic OAuth, the OAuth dummy API-key sentinel, malformed entries, and + well-known entries whose current manifest does not retain the saved + environment key remain refused. `opencode` OAuth is also refused because + v2 needs `server` and `orgID` metadata that v1's generic `accountId` and + `enterpriseUrl` cannot recover. No generic provider-to-method-ID table is + used. Unsupported provider metadata is refused rather than silently dropped. +- The implementation does not claim provider authorization validity or deployed + credential compatibility. Tests use isolated profiles and fake/in-memory + credential values only. diff --git a/migration-tracking/technical-notes/baseline/editor-services-inventory.md b/migration-tracking/technical-notes/baseline/editor-services-inventory.md new file mode 100644 index 000000000000..a078430b8b4f --- /dev/null +++ b/migration-tracking/technical-notes/baseline/editor-services-inventory.md @@ -0,0 +1,52 @@ +# Remaining editor services inventory — 2026-10-02 + +## Header and method + +- Issue: Kilo-Org/kilocode#14415 (parent epic #14021, gate G2). +- Governing plan: PR #14704 (`origin/v2-execution-plan:migration-tracking/plans/kilo-opencode-v2-execution-plan.md`). Decision 5 and the hard-gate list are cited where they set a disposition. +- `origin/main` (v1) inspected at `622ed1f5ae5af2d864908a0bb759b0c9121d406c`. +- `kilo-v2` (v2) inspected at `a2e6c1f69b36a0215c30a80f9d13da9f530073df`. +- Method: enumerate v1 consumers under `packages/kilo-vscode/src/` and `packages/kilo-jetbrains`, the matching v1 backend under `packages/opencode/src/kilocode/` and `packages/kilo-gateway/src/`, then locate the v2 destination and observe the current v2 tree. No source changes; read-only. +- Owner is `unassigned` everywhere: no assignee is recorded on #14415, #14021, or any child, and no team owner is stated in the plan. +- All v1 paths are `origin/main:` unless marked; all v2 paths are the `kilo-v2` working tree. + +## Main inventory + +| Service | Original consumer (UI/workflow + v1 path) | v1 backend | v2 destination | Current kilo-v2 state | Gate | Owner | Disposition | Evidence | +|---|---|---|---|---|---|---|---|---| +| Classic inline autocomplete (FIM) | VS Code editor ghost text; inline provider registered in `packages/kilo-vscode/src/services/autocomplete/AutocompleteServiceManager.ts:244`, generation in `.../classic-auto-complete/FillInTheMiddle.ts:72`, settings `webview-ui/src/components/settings/AutocompleteTab.tsx`, command `package.json:218` | Kilo Gateway `/kilo/fim` (`packages/opencode/src/kilocode/server/httpapi/groups/kilo-gateway.ts:238`, handler `.../handlers/kilo-gateway.ts:114`) via `packages/kilo-gateway/src/fim.ts` + `mistral-fim-endpoint.ts` + `server/fim.ts` | #14416 producer/service contract; consumer stays `packages/kilo-vscode/src/services/autocomplete/` (schema-RPC/plugin pattern of `packages/schema/src/kilocode/generation.ts` + `packages/kilo-cli/src/generation-plugin.ts`) | Snapshot present and registered (`extension.ts:574`), but `services/autocomplete/fim.ts:40` calls `client.kilo.fim`; no v2 route or SDK method exists (`packages/kilo-gateway/src/` has no `fim.ts`), so it is non-functional | G2, G3 (hard gate; Decision 6) | unassigned | implement under #14416 | v2 `fim.ts:40`; v1 `groups/kilo-gateway.ts:238`; v2 `packages/kilo-gateway/src/` listing | +| Chat textarea autocomplete | VS Code chat input ghost text; `packages/kilo-vscode/src/services/autocomplete/chat-autocomplete/ChatTextAreaAutocomplete.ts:105`, wired at `KiloProvider.ts:1395` | Reuses FIM: `generateFim` (`.../services/autocomplete/fim.ts:19`) | #14416 producer (same as FIM); consumer stays `.../chat-autocomplete/` | Wired in v2 (`KiloProvider.ts:1395`) but shares the missing FIM backend, so it cannot complete | G2 | unassigned | implement under #14416 | v2 `KiloProvider.ts:1395`; v2 `chat-autocomplete/ChatTextAreaAutocomplete.ts:110` | +| Next-edit (Mercury/NES) | VS Code off-cursor inline suggestion with Tab jump/accept and Esc dismiss; `.../next-edit/NextEditInlineCompletionProvider.ts:48`, commands/keybindings `package.json:228,233,882,887` | Kilo Gateway `/kilo/edit` (`groups/kilo-gateway.ts` `EditBody`, handler `handlers/kilo-gateway.ts:190`) via `packages/kilo-gateway/src/edit.ts` + `edit-prompt.ts` + `server/edit.ts` | #14416 producer/service contract; consumer stays `.../next-edit/` (`MercuryEditProvider.ts`) | Snapshot present, `.../next-edit/MercuryEditProvider.ts:41` calls `client.kilo.edit`; no v2 route or `edit.ts` in `packages/kilo-gateway/src/`, so it is non-functional | G2, G3 (hard gate; Decision 6) | unassigned | implement under #14416 | v2 `MercuryEditProvider.ts:41`; v1 assessment row `packages/opencode/test/kilocode/server/httpapi-kilo-edit.test.ts:33` | +| Editor code actions / quick fixes | VS Code editor context menu and lightbulb; `.../code-actions/code-action-provider.ts:3`, `register-code-actions.ts:7`, contributions `package.json:412–427,601–625` | None: local prompt templates (`.../code-actions/support-prompt.ts`) posted into the chat/session | #14417 verification; host plumbing (chat panel, focus/reveal) belongs to #14016 | Snapshot present and registered (`extension.ts:585–598`) but older than v1: `register-code-actions.ts` lacks `injectedTitle`, `lastFocusedChat`, and current `ADD_TO_CONTEXT` handling; not workflow-verified | G2 | unassigned | implement/verify under #14417 | v1↔v2 diff of `services/code-actions/register-code-actions.ts`; v2 `extension.ts:585` | +| Terminal context actions | VS Code terminal context menu; `.../code-actions/register-terminal-actions.ts:7` (fix/explain/add selection), contributions `package.json:432–442,625–641` | None: reads terminal buffer via `.../services/terminal/context.ts`, posts a chat prompt | #14417 verification; terminal lifecycle/tickets belong to #14016 | Snapshot present and registered (`extension.ts:586`) but older than v1 (missing `injectedTitle`); not workflow-verified | G2 | unassigned | implement/verify under #14417 | v1↔v2 diff of `register-terminal-actions.ts`; v2 `extension.ts:586` | +| Generate terminal command | VS Code command palette; v1 `extension.ts:635`, v2 `extension.ts:445` | None: injects `Generate a terminal command: …` into chat | #14417 verification | Wired in v2 (`extension.ts:445`); depends on chat parity, not an editor producer | G2 | unassigned | implement/verify under #14417 | v1 `extension.ts:635`; v2 `extension.ts:445` | +| Enhance prompt / commit message / branch-name generation | Enhance: chat prompt input `webview-ui/src/components/chat/PromptInput.tsx:1509` → `KiloProvider.ts:1679`; commit: SCM title `.../services/commit-message/index.ts:37` (`package.json:403,560`); branch: Agent Manager worktree naming `.../agent-manager/branch-naming.ts:171` | `/enhance-prompt` (`packages/opencode/src/kilocode/enhance-prompt.ts:32`), `/commit-message` (`.../commit-message/generate.ts:158`), `/session/:sessionID/branch-name` (`.../kilocode/branch-name.ts`) | `packages/schema/src/kilocode/generation.ts` (`GenerationRpc`) + `packages/kilo-cli/src/generation-plugin.ts` + client `packages/kilo-vscode/src/backend/generation.ts:234` | Implemented client-side against the v2 client: `enhancePrompt` (`KiloProvider.ts:1522`), `commitMessage` (`generation.ts:283`), `branchName` (`generation.ts:340`, consumed at `branch-naming.ts:171`). Stateless backend verified; original editor/SCM consumer acceptance still pending | G2 (G3: enhance/branch consumed on JetBrains) | unassigned | implement/verify consumers under #14417 | v2 `backend/generation.ts:234,283,340`; v2 `KiloProvider.ts:1522`; v1 `branch-naming.ts:171` | +| Speech-to-text | VS Code chat composer mic and model picker; `webview-ui/src/components/speech-to-text/SpeechToTextButton.tsx`, `useSpeechToText.ts`, capture in `src/speech-to-text/handler.ts:64` | Kilo Gateway `/kilo/audio/transcriptions` (`.../speech-to-text/transcribe.ts:6`, handler `handlers/kilo-gateway.ts`) and `/kilo/models/transcriptions` | `packages/kilo-vscode/src/speech-to-text/` + a Kilo Gateway transcription producer (post-cutover) | Client snapshot present (`KiloProvider.ts:2730`, `speech-to-text/catalog.ts`), but v2 `packages/kilo-gateway/src/` has no audio-transcription route and v1 `source.ts` is absent; non-functional | post-cutover (Decision 5, voice/STT; #14418) | unassigned | post-cutover | v2 `catalog.ts` (`/kilo/models/transcriptions`), `transcribe.ts` (`/kilo/audio/transcriptions`); v2 gateway listing | +| Image generation | Experimental chat tool invoked by the model (`generate_image`) plus experimental model picker; `webview-ui/src/components/settings/ExperimentalTab.tsx:147`, `context/image-models.tsx`, save path `KiloProvider.ts:1229` | `packages/opencode/src/kilocode/tool/generate-image.ts:150` gated by `.../tool/registry.ts:337` (`experimental.image_generation`); gateway `/kilo/models/images` | Core/runtime image tool + gateway image-model producer + `packages/kilo-vscode/src/image-generation/` (post-cutover) | Client model fetch present (`KiloProvider.ts:2718`, `image-generation/models.ts`) but no v2 `generate-image` tool and no gateway image route; non-functional | post-cutover (Decision 5, image gen experimental on v1; #14419) | unassigned | post-cutover | assessment TSV `packages/opencode/src/kilocode/tool/generate-image.ts` (needs port); v2 `KiloProvider.ts:2718` | +| KiloClaw | VS Code separate editor panel; `packages/kilo-vscode/src/kiloclaw/KiloClawProvider.ts`, wired `extension.ts:5,158,368,391` | None in v1: removed from `origin/main` (0 claw paths in the repo) | none — remove the stale v2 tree and wiring | 40 claw paths still present in `kilo-v2` (`src/kiloclaw/`, `webview-ui/kiloclaw/`, 6 manifest commands) from an older snapshot; still imported and registered | not needed (Decision 5, #14419) | unassigned | not needed; removal candidate | v1 `origin/main` has 0 claw paths; v2 `git ls-files` has 40; v2 `extension.ts:5,158,368,391` | + +## Handed to sibling epics + +| Item | Observed here | Why it is not a #14021 feature service | Sibling epic | +|---|---|---|---| +| Notebook host contracts | `packages/kilo-vscode/src/services/notebook/` (`adapter.ts`, `bridge.ts`, `output.ts`) and the notebook cell selector in `AutocompleteServiceManager.ts:249` | Notebook open/edit/execute is basic client parity, not a feature-specific producer | #14016 | +| Terminal lifecycle and ticket bridge | `packages/kilo-vscode/src/services/terminal/context.ts`, `truncate.ts`; terminal ticket/relay work | Host terminal plumbing shared by many consumers; terminal context actions are inventoried above under #14417 | #14016 | +| Chat prompt/message plumbing for enhance and speech | `webview-ui/src/components/chat/PromptInput.tsx`, message types under `webview-ui/src/types/messages/` | Host request/reply wiring is client parity; the producers are inventoried above | #14016 | +| Attention/notifications, marketplace, browser automation, git service | `services/attention/`, `services/marketplace/`, `services/browser-automation/`, `services/git/` | General client services already tracked by the parity epic | #14016 | +| FIM/next-edit for JetBrains | JetBrains v1 consumes none of FIM/next-edit today (`packages/kilo-jetbrains` has no inline-completion provider; only `LegacyMigrationConverters.kt:146` migrates autocomplete settings) but #14423 depends on #14416 | G3 integration and validation, not G2 inventory | #14022 | +| JetBrains enhance prompt and branch name | Consumed on v1: `KiloBackendChatManager.kt:150` (`/enhance-prompt`), `KiloWorkspaceRpcApiImpl.kt:319` (branch name) | Needs the v2 generation contract after G3 import; VS Code completion does not close G3 | #14022 | +| Cloud agent consumers | No cloud-agent consumer of FIM, next-edit, speech, image generation, commit, or code actions was found in v1 or v2 | Outside the editor-service boundary | #14023 | + +Notes: v1 JetBrains has no speech, image-generation, commit-message, or code-action consumer in production sources (grep of `packages/kilo-jetbrains/{backend,frontend}/src/main`), so those rows are G2-only. JetBrains does have an `EditorContextDto` (`KiloCliDataParser.kt:30,937`) for selection context, which is chat parity for #14022. + +## Proposed progress-plan row changes (for N7 to apply; do not edit the plan here) + +Current rows under "Original VS Code and remaining clients" in `migration-tracking/plans/kilo-opencode-v2-plan-progress.md`: + +| Existing row | Proposed change | +|---|---| +| `Inline autocomplete/FIM and next-edit` (Needs port / Pending) | Keep one row; update remaining scope to name the two producers and consumers: classic FIM and chat textarea autocomplete (`client.kilo.fim`) plus next-edit (`client.kilo.edit`), all under #14416, G2+G3 hard gate. Note the v2 snapshot is registered but has no Gateway/RPC routes, so it is non-functional. | +| `Enhance prompt / commit / branch generation` (Partial / Full E2E pending) | Update remaining scope: stateless generation now runs through the Kilo-owned `kilocode.generation` RPC (`backend/generation.ts`); remaining work is verifying the original editor/SCM consumers under #14417. Note JetBrains consumes enhance prompt and branch name on v1, so this is also G3-relevant. | +| `Voice / speech-to-text` (Deferred / Pending) | Mark post-cutover and cite Decision 5 (#14418); record that the v2 client snapshot exists but the `/kilo/audio/transcriptions` and `/kilo/models/transcriptions` producers are absent, so there is nothing to verify at G2. | +| `Image generation and Claw` (Needs port / Pending) | Split into two rows. `Image generation` → post-cutover (Decision 5, experimental on v1; #14419); note the v2 client model fetch exists but the tool and image route are missing. `KiloClaw` → not needed (Decision 5; removed from `origin/main`) with the stale `kilo-v2` claw tree recorded as a removal candidate. | +| (no row) | Add `Editor code actions and terminal context actions` → destination "VS Code editor/terminal menus + chat", phase 5, implementation `Needs port`, verification `Pending`, remaining scope "bring the stale v2 `services/code-actions` snapshot to v1 parity and verify fix/explain/improve/add-to-context and terminal actions in the real UI" under #14417. | diff --git a/migration-tracking/technical-notes/baseline/existing-vscode-port.md b/migration-tracking/technical-notes/baseline/existing-vscode-port.md new file mode 100644 index 000000000000..a7558271c516 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/existing-vscode-port.md @@ -0,0 +1,193 @@ +# Existing Kilo VS Code extension port — 2026-09-08 + +## Scope and source + +Johnny requires the existing Kilo extension UI and workflows ported onto the v2 +backend. The earlier upstream-app wrapper is not an acceptable substitute. +Source is the clean local Kilo commit `d99662338e3ddbd2613ab41fc0369837a6eb4be9` +(package `kilo-code` 7.4.20). No external source fetch was performed. + +Imported tracked files: existing host source/assets/audio, all six original +Solid webviews and their workers, Kilo UI components and translation package. +The original activation entry is temporarily `src/existing-extension.ts` while +its backend is adapted. The launch command has not switched to an incomplete +entry. No v1 server or SDK runtime is imported. + +The former SDK view contracts are a type-only referenced closure under +`packages/kilo-client/src/ide-types.ts`; this keeps shared UI independent of the +VS Code host package. `src/backend/` implements explicit native-client adapters. +The streaming adapter reuses public `@opencode-ai/client/solid` event folding, +then projects its native state into the existing IPC message/part format. + +## Current validation + +`packages/kilo-vscode/test/backend-session.test.ts` passes with bundled Bun 1.4: +real isolated native host, create/prompt/history/rename/fork/list/delete, +throwOnError behavior, native streaming projected into original UI events, +shell permission approval and continuation. The entire child process runs under +loopback-only network policy. This is adapter acceptance, not complete UI parity. + +Expanded acceptance also covers native question forms (single and multiple +answers, label/value translation, invalid answer counts, dismissal), whole-message +revert/clear, workspace file search/project identity, and tool-file attachment +projection. The adapter joins native coalesced session refreshes before projecting +events; this fixed an observed pending-refresh failure during host teardown. +Numeric, conditional, and external form controls and part-level revert remain open; +unsupported operations currently fail explicitly rather than changing their meaning. + +All six original webviews and both workers now build from tracked source in this +checkout. Their original UI dependencies live in Kilo-owned `kilo-ide-ui`; shared +upstream `packages/ui` is unchanged. Browser pricing guards now distinguish +unavailable quotes from genuine free models. Full host integration remains in +progress; a successful bundle is not original-UI runtime acceptance. + +The real-host session fixture also verifies opaque native history pagination, +MCP add/connect/disconnect/remove through a real SDK stdio server, and original +slash-command model selection, template expansion, and file attachments. MCP +pending status renders as connecting in the original settings screen. The PTY +create/update/remove/ticket adapter is composed into the facade. Original Agent +Manager terminal routing now obtains native single-use tickets through a local +relay; each panel registers its actual document origin through the existing ready +IPC message. Real-host acceptance covers terminal echo, fresh tickets, removal of +origin access, lifecycle event projection, and cleanup. The native-shaped script +terminal facade is now integrated: headless acceptance executes a real script, +observes its exit code, and removes its PTY. Native terminal rendering remains open. +Native command admission does not expose a caller-supplied message ID, so original +command retry/idempotency behavior still needs a separate disposition. + +The existing Remote RPC definition moved without a contract change to +`packages/schema/src/kilocode/remote.ts`, with a CLI re-export. The extension adapter +uses that same public definition; the existing real-host/loopback relay test passes +(1 test, 19 assertions) under external-network denial. Account/auth and terminal +adapters have landed; account review follow-ups and native UI acceptance continue. +The session-usage definition likewise moved unchanged into Schema with a CLI +re-export; original model-usage reads pass against a completed native model step, +and both existing CLI session-usage tests pass. + +The original session-process cleanup call now lists native shells by their +originating session metadata and removes only matching shells in the requested +location. Real-process acceptance verifies that the selected PID disappears, +another session keeps the same running PID, and cancellation reports failure +without stopping that other process (9 assertions). Native teardown is +asynchronous. The legacy parent-lifetime reassignment policy has no equivalent +native Shell field and remains part of the outstanding orchestration port. + +Original per-agent model-selection messages now use `kilocode.model-state` RPC +instead of the missing v1 `path.get` and extension-side filesystem writes. The +host serializes saves across panels and retains the existing `model.json` +preference fields. Real-host acceptance covers concurrent saves, host restart, +clear/reset, unchanged native preference fields, and malformed JSON refusal +(11 assertions). Native TUI per-agent selection remains in-memory today; this +slice does not claim that its selection behavior has been ported. Native TUI +preference writes still use their existing independent process-local queue. + +Memory now uses the shared `kilocode.memory` RPC, including original source-file +selection, stored-item strings, token counts, scope isolation, and explicit purge +confirmation. The composed provider facade exposes the original `provider.auth` +entry point over the native integration registry. Root headless checks pass for +memory (24 assertions) and account/auth (22 assertions). Agent mention positions +also survive native prompt admission and projected history; committed reverts +emit removal events for rows that disappear from that history. + +`script/build-existing-extension.ts` builds the original host, six webviews, and +two workers. The native activation runner invokes this build before staging, so +activation cannot silently use an older bundle. The current-source run verified +the original sidebar, editor-tab, and Agent Manager identities with exit code 0; +it did not prove streaming chat or complete workflow parity. + +The original UI packages now have local dependency directories with workspace +links resolving inside this checkout. Their utility imports use native +`@opencode-ai/util` path, encoding, and binary helpers, replacing the build's +copied utility shims. The indexing detector uses the existing source-backed +plugin-spec helper. The web build inspects esbuild input metadata and refuses +filesystem sources outside this checkout, except cached third-party dependencies +under `node_modules`. Native helper tests pass (19 tests, 37 assertions). + +Johnny reported disruption from repeated VS Code launches. All desktop acceptance +is paused until explicit authorization to resume. Continue headless local checks; +do not treat this pause or the earlier wrapper's tests as original-UI acceptance. +Project-scoped stateless generation now uses a Kilo-owned RPC. Headless acceptance +verifies successful enhancement, branch-name generation with session context, +and commit generation with the caller's diff, while preserving session count and +the full message history. No-change commit generation refuses explicitly. + +Explicit configuration refresh is being integrated through the existing settings +RPC. The host reuses the native serialized loader and rereads Kilo project files; +the original Reload command and marketplace refresh no longer request instance +disposal. Core's configuration regression suite passes (39 tests, 142 assertions) +and its package typecheck is clean. RPC and preserved-session/terminal acceptance +remain pending; this is not yet a completed refresh-flow claim. + +Package-wide compilation remains in progress while imported dependencies and +roughly 97 SDK methods plus raw routes are mapped. No package-wide pass, final +VSIX, or completed capability row is claimed for this port yet. + +## Remaining work + +- Integrate and exercise the original webview builds in the extension host. +- Complete native client facade, account/auth, model metadata and settings. +- Finish permission/forms and event projection, including non-chat messages. +- Preserve Agent Manager, terminals, timeline/diff, SCM and editor actions. +- Port missing feature-specific server seams through Kilo-owned RPCs where + possible; do not silently return successful empty responses for absent work. +- Wire the original activation/manifest and automatic server lifecycle. +- Validate original UI and actual VSIX in isolated VS Code; skip only genuinely + external checks under the user's local-only constraint and record them. + +The canonical plan reopens the three former wrapper rows: current coverage is +31/43, not a completed existing-extension port. + +### Autocomplete parser and packaged assets — 2026-09-08 + +The original autocomplete implementation now consumes the locally available +web-tree-sitter 0.25.10 named types and Query constructor. Its prior 0.24.7 +dependency disagreed with its Parser/Language runtime calls. The original host +build copies the runtime WASM, every cached grammar (all declared languages are +present), and the original query files into dist. Parsing and an original +TypeScript context query are verified both from source and in a separate Node +process with only the bundled module and assets in a temporary directory. + +The host bundle uses the cached esbuild Node/CommonJS build with the package's +CommonJS Tree-sitter entry. This preserves runtime __dirname asset discovery; +the prior Bun bundle embedded development source paths, which the isolated +packaged test reproduced. All six original webviews and two workers still build +from this checkout. This verifies parser/context infrastructure, not the still +unported FIM/next-edit backend or desktop autocomplete behavior. Desktop tests +remain paused at the user's request; no external hosts or downloads were used. + +### Original past-chat and first-use session listing — 2026-09-08 + +The original `experimental.session.list` callers now use native v2 session +pages. Worktree-family queries resolve the native project and its worktree +registry, while directory queries stay directory-scoped and the original +no-directory first-use check stays global. Root filtering is applied before +pagination; archived sessions are omitted unless requested, following pinned +v1 `KiloSession.listGlobal`. The adapter continues across native pages when +client-side archived filtering removes rows. Labels come from actual native +worktree roots rather than guessed branch names. + +A real temporary Git repository and linked worktree validate the unchanged +`handleSessionSearch` consumer, exclusion of unrelated projects/current chat, +worktree labels, title filtering, and the first-use one-session query (9 +assertions). No native public archive mutation exists yet, so archive creation +and archived-row pagination remain unverified via real-host acceptance. This +does not claim the separate archive workflow is ported. All work is headless +and local-only. + +### Default original UI and wrapper removal — 2026-09-08 + +At the user's explicit request, the upstream-app wrapper was deleted rather +than retained as another extension mode. `src/extension.ts` is now the original +Kilo activation entry, `script/build.ts` builds that entry plus all original +webviews, and the package manifest contributes the original Kilo sidebar and +commands. `bun run extension` uses this manifest in a dedicated development profile and +extension directory, preventing the installed stable extension from contributing +duplicate toolbar entries. A disable-extension flag alone was insufficient in +the user-observed development window. + +The preview package identity is unchanged, so its VSIX replaces the old wrapper. +Wrapper-only browser assets, Vite config, activation code, and acceptance tests +were removed. Connection/daemon and terminal transport code remains because the +original host consumes it. Packaging now requires original webview assets and +rejects the old `dist/web/` payload. No desktop was launched for this change; +remaining backend gaps still apply to the original UI. diff --git a/migration-tracking/technical-notes/baseline/explore-policy-v2-parity.md b/migration-tracking/technical-notes/baseline/explore-policy-v2-parity.md new file mode 100644 index 000000000000..3e6cd5b0151c --- /dev/null +++ b/migration-tracking/technical-notes/baseline/explore-policy-v2-parity.md @@ -0,0 +1,107 @@ +# V1 Explore read-only shell ceiling parity boundary + +At Kilo `origin/main` commit `ecccd1f`, Explore is not merely "no bash". The v1 +`patchAgents` explore branch (`packages/opencode/src/kilocode/agent/index.ts`) +merges `Permission.fromConfig({ bash: exploreBash })` after the user ruleset as a +ceiling, then re-appends `denies(user)` last, and appends a description suffix. +`exploreBash` is `readOnlyBash` plus `"gh *": "deny"` and `"find *": "deny"`. +`readOnlyBash` is `"*": "deny"`, the 28 readable allows (`cat head tail less ls +tree pwd echo wc which type file diff du df date uname whoami printenv man grep +rg ag sort uniq cut tr jq`), the 20 git read-only allows (`git log show diff +status blame rev-parse rev-list ls-files ls-tree ls-remote shortlog describe +cat-file name-rev stash-list tag -l branch --list/-a/-r remote -v`), then +defense-in-depth denies for chain/redirection operators and for flags that let +an "allowed" command write files or exec a program +(`sort -o/--output/--compress-program/--files0-from`, `rg --pre/--hostname-bin`, +`ag --pager`, `man -P/--pager/-H`). These counts are verified against the pinned +source; the earlier audit's "27 readable / 11 git" was inaccurate. + +V2's native Explore (`packages/core/src/plugin/agent.ts`) denies everything +through a bare wildcard deny with grep/glob/webfetch/websearch/read allows and +`.env` asks; there is no shell allow at all. This slice restores the v1 +read-only shell posture on v2's existing public seams, with no new engine, no +shared Core patch, and no OS-sandbox claim. + +## Implemented public seam + +`packages/kilo-cli/src/agent-policy.ts` splits the policy across the two public +phases. `createExplorePolicy()` is registered in the **default** phase (no +`phase` option) and owns only the `agent.transform` that appends the allowlist +ceiling — the exact "after native AgentPlugin, before ConfigAgentPlugin" seam. +`createAgentPolicy()` is registered in the **post** phase and owns the +`permission.evaluate` ceiling enforcement. Source-verified ordering: + +- The supervisor's pre list is `[internal.pre (AgentPlugin), sdk.all(), + instance.all()]` and its post list is `[internal.post (ConfigAgentPlugin), + sdk.allPost()]` (`packages/core/src/plugin/supervisor.ts`). A default-phase SDK + plugin joins `sdk.all()`, so its transform is folded after the native + AgentPlugin's and before ConfigAgentPlugin's. +- `Agent.Service` rebuilds by folding every registered transform in registration + order against one editor (`packages/core/src/state.ts`). +- Permission evaluation is last-match-wins (`Permission.evaluate` uses + `findLast` + `Wildcard.match`, `packages/core/src/permission.ts`). + +The transform appends the verbatim v1 `exploreBash` table as `action: "shell"` +rules plus `skill` and `semantic_search` allows, so the allowlist wins over the +native wildcard deny. Because config is appended afterward, explicit user rules +stay authoritative: + +- An explicit user deny (including a wildcard identical to the native one) lands + after the ceiling and wins by last-match. No re-append and no provenance + guessing is needed — the earlier count-budget workaround was removed. +- A broad config `shell` allow lands after the ceiling too, so pure ruleset + evaluation would reopen a table-denied command. The `permission.evaluate` + ceiling hook in the post `createAgentPolicy` forces `deny` for any shell + resource the v1 table alone denies. The hook runs in the real `assert` path + after the configured-deny short-circuit (`Permission.evaluateInput`), so user + denies are never reordered, while config allows cannot reopen the ceiling — + matching v1's "user allows cannot make Explore's shell writable". + +A configured Explore agent keeps its own system prompt, mode, and description; +the ceiling always applies. The description suffix is appended only when the +current description still equals the exact native string (the same guard shape +as the Code rename). + +`list`/`board` allows are not ported: V2 has no `list` tool and no board tools. +V1's whitelisted external directories stay unported per V2's existing +external_directory `ask` posture. These are deliberate V2 deltas, not silent +drops. + +## Test evidence + +`packages/kilo-cli/test/agent-policy.test.ts` and its fixture drive the real +production `launch()` host on a loopback fake model with the bundled Bun 1.4.0 +(no live accounts, no paid inference). The host registers `createExplorePolicy()` +itself in the default phase, so the tests exercise the production wiring +unchanged. The fixture detects Explore through its system prompt and echoes the +requested command back as a real shell tool call, so the real permission +pipeline decides each outcome. + +- Real last-match evaluation on the native ruleset: `cat`, `rg`, `git status`, + and `git log` allow; `git push`, `gh`, `find -delete`, + `sort -o/--compress-program`, `rg --pre`, and + chain/pipe/substitution/backtick/redirect forms deny; unknown commands stay + denied; `skill`/`semantic_search` allow; `edit` denies; the description suffix + is present on the native Explore. +- Real host runs: allowed `cat` and `git status` execute with no error part, + while every denied command surfaces a tool error (blocked before execution). +- An explicit configured `git status *` deny still wins over the allowlist in + both ruleset evaluation and real execution; untouched allowlist entries stay + allowed. +- A broad global `shell *:allow` plus an agent-level `shell *:allow` does not + reopen `git push`, `find`, `sort -o`, `sort --compress-program`, `rg --pre`, + or chain/pipe/substitution/backtick/redirect in real execution (the + evaluate-hook ceiling), while `cat` and `git status` still run. +- A configured Explore keeps its own description and system prompt yet still + receives the ceiling. +- With the default-phase explore policy disabled via config + `plugins: ["-kilocode.explore-policy"]` plus a broad shell allow, the enforced + post `kilocode.agent-policy` permission.evaluate ceiling still denies every + table-denied command in real execution, `cat`/`git status` still run, and the + redirect is blocked before any child write (`out.txt` is not created). An + attempted `plugins: ["-kilocode.agent-policy"]` disable does not remove the + ceiling: post SDK plugins are host-enforced against config remove operations. +- Ask and Debug are unchanged (the Code rename, Ask read-only denies, Debug, and + the default-agent fallback all still pass). + +Focused run: 5 tests, 0 failures, 78 assertions, plus a clean `tsgo --noEmit`. diff --git a/migration-tracking/technical-notes/baseline/extension-cleanup-audit.md b/migration-tracking/technical-notes/baseline/extension-cleanup-audit.md new file mode 100644 index 000000000000..9cff3c9b9b8d --- /dev/null +++ b/migration-tracking/technical-notes/baseline/extension-cleanup-audit.md @@ -0,0 +1,160 @@ +# Extension and Upstream Patch Cleanup Audit + +## 1. Overview and Provenance + +This audit evaluates the current v2 worktree against the consumed upstream snapshot and the approved fork plan allowance. It distinguishes Kilo-owned additions from shared upstream patches, audits the exact technical necessity of each shared modification, and determines whether proposed cleanups represent genuine removable duplication or required upstream extension seams. + +- **Consumed Upstream Snapshot Base SHA:** `59b29de40966803e2c7cd734d439843fb773f6a6` (`fix(core): detect new ecosystem config roots (#47026)`), distinct from the earlier historical pinned snapshot `76dbaf20adbd43fd208a00ef3cda4a51e125a234`. +- **Local Production Ref (`origin/main`) SHA:** `ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7` (`Merge pull request #13805 from Kilo-Org/fix/glob-search-timeout-20260904`). +- **Current Branch HEAD SHA:** `82040801cd253665d2785c290031d307a18a0d64`. +- **Canonical Fork Plan:** `plans/kilo-opencode-v2-issue-13750.md` (lines 84–92 define the approved shared-patch ceiling). + +## 2. Shared-File Accounting vs Fork Plan Allowance + +The canonical fork plan (`plans/kilo-opencode-v2-issue-13750.md`) authorizes exactly **6 approved shared-patch files**: +1. `AGENTS.md` (root pointer to Kilo fork rules) +2. `packages/plugin/src/tui/context.ts` (`home.logo` slot definition) +3. `packages/tui/src/app.tsx` (`pluginDirectories` pass-through) +4. `packages/tui/src/routes/home.tsx` (`Slot home.logo` rendering) +5. `packages/core/src/plugin/sdk.ts` (host-enforced post-registration hook) +6. `packages/core/src/plugin/supervisor.ts` (post-config activation hook) + +### Tracked Working-Tree Changes vs Base + +A complete diff of tracked files against base `59b29de40966803e2c7cd734d439843fb773f6a6` shows **466 total modified/added tracked files**: +- **444 Kilo-owned new tracked files** (`packages/kilo-*`, `kilocode/`, `plans/`, and Kilo-namespaced subtrees in upstream packages). +- **22 shared upstream files** (6 approved + 1 generated `bun.lock` + 15 handwritten unapproved/unresolved paths). + +*Scope Note:* These counts cover tracked working-tree files only. Untracked working-tree additions (such as AI wrapper modules in `packages/ai/src/kilocode/` and `packages/ai/test/kilocode/`, untracked baseline audit docs, and scratch directories like `.blume/` and `.kilo/`) are omitted from tracked diff accounting and are not claimed as exhaustive repository additions. + +### Inventory of All 22 Modified Shared Files + +| File | Status | Nature of Diff | Lines Diff | Technical Necessity Classification | +|---|---|---|---|---| +| `AGENTS.md` | Approved (1/6) | Root documentation pointer | 2 | Retain (approved) | +| `packages/plugin/src/tui/context.ts` | Approved (2/6) | `home.logo` plugin slot | 12 | Retain (approved) | +| `packages/tui/src/app.tsx` | Approved (3/6) | `pluginDirectories` pass-through | 11 | Retain (approved) | +| `packages/tui/src/routes/home.tsx` | Approved (4/6) | `Slot home.logo` placement | 13 | Retain (approved) | +| `packages/core/src/plugin/sdk.ts` | Approved (5/6) | Post-registration hook | 11 | Retain (approved) | +| `packages/core/src/plugin/supervisor.ts` | Approved (6/6) | Post-config activation hook | 10 | Retain (approved) | +| `bun.lock` | Unresolved (Generated) | Dependency & workspace lockfile | 148 | Generated package manager lockfile; reconcile on release | +| `packages/theme/src/tui/defaults.ts` | Unapproved (Handwritten) | `text.logo` light/dark defaults | 22 | Required by `AGENTS.md` theme token rule; no safe equivalent seam | +| `packages/theme/src/tui/fallback.ts` | Unapproved (Handwritten) | `text.logo` fallback resolution | 12 | Required by `AGENTS.md` theme token rule; no safe equivalent seam | +| `packages/theme/src/tui/schema.ts` | Unapproved (Handwritten) | `text.logo` optional token schema | 13 | Required by `AGENTS.md` theme token rule; no safe equivalent seam | +| `packages/theme/src/tui/types.ts` | Unapproved (Handwritten) | `text.logo: RGBA` type token | 13 | Required by `AGENTS.md` theme token rule; no safe equivalent seam | +| `packages/tui/src/component/dialog-agent.tsx` | Unapproved (Handwritten) | `title: item.name ?? item.id` | 13 | Missing upstream presentation seam; canonical IDs cannot be renamed | +| `packages/tui/src/component/dialog-integration.tsx` | Unapproved (Handwritten) | Hardcoded `kilo: -1` in `INTEGRATION_PRIORITY` | 44 | Missing upstream provider priority configuration seam | +| `packages/tui/src/component/dialog-model.tsx` | Unapproved (Handwritten) | Inlined grouping, loading state, disclosures | 211 | Async Gateway RPC metadata presentation; unproven to remove without upstream slot | +| `packages/tui/src/component/prompt/autocomplete.tsx` | Unapproved (Handwritten) | Local command shadowing in autocomplete | 19 | Dual registry collision; required to avoid duplicate `/memory` | +| `packages/tui/src/component/prompt/index.tsx` | Unapproved (Handwritten) | `agentLabel` name fallback | 13 | Missing upstream presentation seam; canonical IDs cannot be renamed | +| `packages/tui/src/context/local.tsx` | Unapproved (Handwritten) | Added `local.agent.name` helper | 23 | Centralized display name lookup helper | +| `packages/tui/src/context/runtime.tsx` | Unapproved (Handwritten) | Added `TuiModelGroup`/`TuiModelPicker` | 33 | Contract for host-injected model presentation | +| `packages/tui/src/routes/session/composer/subagents-tab.tsx` | Unapproved (Handwritten) | Agent display name resolution | 33 | Missing upstream presentation seam; canonical IDs cannot be renamed | +| `packages/tui/src/routes/session/index.tsx` | Unapproved (Handwritten) | Epilogue override + agent name callsites | 99 | Missing upstream presentation seams across session transcript views | +| `packages/tui/src/util/renderer.ts` | Unapproved (Handwritten) | `renderer.screenMode = "main-screen"` | 16 | Sets intended exit screen mode; claims of native destroy corruption unproven | +| `packages/tui/test/util/renderer.test.ts` | Unapproved (Handwritten) | Upstream test assertions for screen restoration | 50 | Unit test coverage for intended `destroyRenderer` call order | + +## 3. Inventory of Tracked Additions (Kilo-Owned vs Generated vs Shared) + +The 444 new tracked files are categorized strictly under Kilo-owned namespaces: + +| Category | File Count | Purpose | +|---|---|---| +| `packages/kilo-cli/` | 207 | Host CLI entrypoint, commands, RPC plugins, TUI plugins, tests | +| `packages/kilo-indexing/` | 121 | Vector indexing engine, parsers, embedders, tests | +| `packages/kilo-memory/` | 66 | Durable memory, capture, recall, storage, prompts | +| `kilocode/` | 26 | Baseline audits, fork conventions, parity ledgers, verification scripts | +| `packages/kilo-gateway/` | 13 | Gateway account resolution, model routing, session management | +| `packages/kilo-client/` | 3 | Typed RPC client wrapper for Kilo extensions | +| `plans/` | 2 | Kilo v2 port roadmap and issue plan (`13750`) | +| Kilo-namespaced subtrees in upstream packages | 6 | `packages/cli/src/kilocode/acp.ts`, `packages/schema/src/kilocode/*` (3 files), `packages/theme/test/kilocode/logo.test.ts`, `packages/tui/test/kilocode/model-presentation.test.tsx` | + +**Generated Surface Hygiene:** No generated files (`packages/client/src/generated*`, OpenAPI specs) have manual edits. All client generation continues to be driven through `packages/client/bun run generate`. + +## 4. In-Depth Evaluation of the 5 Candidate Cleanup Areas + +### Area 1: TUI Theme Overrides (`packages/theme/src/tui/{defaults,fallback,schema,types}.ts`) + +- **Source Evidence:** + - `packages/theme/src/tui/schema.ts:109` (`logo: Schema.optional(ColorValue)`) + - `packages/theme/src/tui/defaults.ts:112,335` (`logo: "#6f6500"`, dark default) + - `packages/theme/src/tui/fallback.ts:11` (`logo: DEFAULT_THEME[mode].text.logo`) + - `packages/theme/src/tui/types.ts:31` (`readonly logo: RGBA`) + - Consumer: `packages/kilo-cli/src/tui-plugin/tui.tsx:30` (`render: () => `). +- **AGENTS.md Constraint Analysis:** + Root `AGENTS.md` explicitly mandates under **TUI Theme Tokens**: + - *"Choose theme tokens by semantic role, not by their current color. Do not use raw `theme.hue` values or borrow an unrelated semantic token to achieve a preferred appearance."* + - *"If the theme does not expose a token for the required semantic role, extend the theme schema, defaults, resolution, and types with that role before using it in a component. Do not repurpose the nearest-looking existing token."* + - *"When changing the public theme token surface, verify the built-in light and dark defaults and the custom-theme fallback path in addition to the affected TUI component."* +- **Audit Finding:** + The 4 theme files were modified strictly in compliance with repo rules. Reverting this patch to borrow `ctx.theme.hue.yellow[400]` or `ctx.theme.text.default` is explicitly **forbidden** by `AGENTS.md`. Hardcoding hex strings inside the component breaks custom theme support and light/dark theme resolution. +- **Classification:** **No safe equivalent extension seam exists.** These 4 files represent an intentional upstream theme schema expansion that should be submitted upstream or formally proposed for plan approval. + +### Area 2: Model Picker Presentation Hook (`dialog-model.tsx`, `runtime.tsx`) + +- **Source Evidence:** + - `packages/schema/src/kilocode/models.ts` & `packages/kilo-cli/src/model-picker.ts`: `KiloModels.Definition.list` RPC provides `recommendedIndex`, `hasUserByokAvailable` ("BYOK"), `mayTrainOnYourPrompts` ("May train"), and routing classification (`isKiloAutoID`). + - `packages/tui/src/component/dialog-model.tsx:28–60, 82–89, 120–129, 184–228, 246–260`: Manages an async lifecycle with `AbortController`, signals `groupState` ("loading" \| "ready" \| "fallback"), locks filtering while loading, renders loading spinner `emptyView`, and shows a warning banner on RPC failure. +- **Audit Finding:** + This is not static catalog metadata or removable duplicate scaffolding. Model disclosures and recommendations are dynamic, location-scoped, and tied to authenticated Gateway account state. The native `Model` schema (`@opencode-ai/schema/model`) lacks BYOK, prompt-training, and recommendation fields. +- **Classification:** **Unproven to remove without an upstream extension slot.** While inlining 211 lines into `dialog-model.tsx` incurs merge tax, simply deleting the logic removes Kilo Auto and BYOK disclosures. The proper long-term solution is a narrow upstream slot or hook in `DialogModel` allowing host plugins to provide async metadata decorators. + +### Area 3: Agent Display Naming Across 5 Shared Files + +- **Source Evidence:** + - `packages/tui/src/component/dialog-agent.tsx:14` (`title: item.name ?? item.id`) + - `packages/tui/src/component/prompt/index.tsx:1559` (`agentLabel: agent ? (agent.name ?? Locale.titlecase(agent.id)) : undefined`) + - `packages/tui/src/context/local.tsx:94–98` (`name: (id: string) => string`) + - `packages/tui/src/routes/session/composer/subagents-tab.tsx:48` (`local.agent.name(session.agent, session.location)`) + - `packages/tui/src/routes/session/index.tsx:2056,2091,2093,2122,3493` (`AssistantFooter`, `SessionSwitchMessageV2`, `SessionNoticeMessageV2`, `Subagent`). +- **Audit Finding:** + Canonical agent IDs (`"general"`, `"code"`, `"architect"`, custom IDs) are durable identifiers used in execution, state storage, protocol events, and permissions. They **cannot** be mutated to display names without breaking routing and persistence. Upstream OpenCode hardcodes `Locale.titlecase(agent.id)` across all these components. +- **Classification:** **Functionally required upstream hooks.** Until upstream OpenCode provides a native display name resolver (e.g. updating upstream components to natively render `agent.name ?? Locale.titlecase(agent.id)`), these UI hooks cannot be removed without regressing custom agent presentation to raw uppercase IDs. + +### Area 4: Autocomplete Deduplication & Integration Priority (`autocomplete.tsx`, `dialog-integration.tsx`) + +- **Source Evidence:** + - `packages/tui/src/component/prompt/autocomplete.tsx:522–536`: Deduplicates `results` across two distinct registries: TUI keymap commands (`keymapCommands()`) and server-side commands (`data.location.command.list(location.current)`). + - `packages/tui/src/component/dialog-integration.tsx:30`: `kilo: -1` in `INTEGRATION_PRIORITY`. +- **Audit Finding:** + TUI client commands (like `/memory` and `/teams`) have client execution handlers, whereas server commands insert text. Without the client-side shadowing set in `autocomplete.tsx`, `/memory` appears twice with conflicting semantics. Server-side registration alone cannot deduplicate them because the client keymap commands are defined client-side in the TUI. In `dialog-integration.tsx`, upstream hardcodes priority in a static object literal with no configuration seam. +- **Classification:** **Functionally required client logic.** Deleting the autocomplete shadowing causes duplicate command entries with divergent handlers. + +### Area 5: Terminal Screen Mode Restoration in `destroyRenderer` (`renderer.ts`, `renderer.test.ts`) + +- **Source Evidence:** + - `packages/tui/src/util/renderer.ts:8` (`renderer.screenMode = "main-screen"`) + - `packages/tui/test/util/renderer.test.ts:7–38` (verifies screenMode transition prior to destruction) +- **Audit Finding:** + The patch in `renderer.ts` sets `renderer.screenMode = "main-screen"` before calling `renderer.destroy()`, and the test in `renderer.test.ts` exercises this intended call order (`title:`, `screen:main-screen`, `destroy`). + However, attributing this exit teardown to preventing BB idle redraw race conditions is explicitly **unproven**: terminal resize restores alternate screen mode during an active TUI session, whereas teardown occurs strictly on exit before the CLI epilogue prints. Furthermore, whether native OpenTUI `destroy()` causes terminal corruption without this assignment has not been demonstrated with empirical evidence. +- **Classification:** **Unproven necessity / intended exit ordering.** While the test asserts intended ordering on shutdown, claims of native destroy corruption or preventing redraw races remain unproven without empirical evidence. + +## 5. Verified Intentional Architectural Differences (Preserved) + +The following areas match upstream concepts by name but represent **intentional, non-removable architecture**: + +1. **`.kilo` and `.kilocode` Project Config Discovery (`packages/kilo-cli/src/project-config.ts`):** + - Native upstream directory entries expose and execute plugins (`DirectoryEntry`), which has different trust and execution characteristics. + - Kilo's isolated config loader parses project documents (`kilo.json`, `kilo.jsonc`) without executing arbitrary third-party plugins. + - Upstream native `.agents` discovery only feeds skills; it does not parse or normalize agent markdown. Agent normalization has no exported upstream decoder. +2. **Host Path Isolation (`packages/kilo-cli/src/paths.ts`):** + - The preview explicitly uses `kilo2` identity and paths to prevent collision with production `kilo` and upstream `opencode` data stores. +3. **Bounded Activity Telemetry Exporter (`packages/kilo-cli/src/telemetry.ts`):** + - Upstream uses ambient OpenTelemetry spans/logs that can expose session transcripts. Kilo's exporter is strictly opt-in, name-only, and backed by an isolated settings adapter. +4. **Explicit-Credential Cloud Adapter (`packages/kilo-cli/src/cloud/*`):** + - Resolves credentials in-host via the Gateway account Effect so sensitive tokens never cross the public RPC boundary. + +## 6. Summary and Recommendation + +The 22 shared upstream files in the tracked diff break down as follows: +- **6 Approved Files:** Authorized in `plans/kilo-opencode-v2-issue-13750.md`. +- **1 Generated Lockfile:** `bun.lock` (package manager state). +- **15 Handwritten Shared Code/Test Files:** + - 4 Theme files (`packages/theme/src/tui/*`): Required by `AGENTS.md` theme rules; no safe extension seam. + - 2 Renderer files (`packages/tui/src/util/renderer.ts`, `test`): Intended teardown sequence; corruption claims unproven. + - 5 Agent naming files (`dialog-agent.tsx`, `prompt/index.tsx`, `subagents-tab.tsx`, `session/index.tsx`, `local.tsx`): Required because canonical IDs cannot be renamed and upstream lacks a display name hook. + - 2 Autocomplete & Integration files (`autocomplete.tsx`, `dialog-integration.tsx`): Required for dual registry deduplication and provider ranking. + - 2 Model picker files (`dialog-model.tsx`, `runtime.tsx`): Required for async Gateway metadata presentation. + +**Recommendation:** Based on this bounded source audit, no safe, functionally equivalent cleanups were proven that would reduce shared files without violating repository constraints (such as `AGENTS.md` theme token rules) or breaking runtime behaviors. This verdict is conditional on the current source evidence and boundaries analyzed, not an absolute theorem. These 15 handwritten shared files represent structural extension seam gaps that require either upstream PRs or formal fork plan approval. diff --git a/migration-tracking/technical-notes/baseline/extension-surface-inventory.md b/migration-tracking/technical-notes/baseline/extension-surface-inventory.md new file mode 100644 index 000000000000..38bef397b5d9 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/extension-surface-inventory.md @@ -0,0 +1,114 @@ +# Extension-led product inventory audit + +## Evidence and scope — 2026-09-05 + +This audit expands the acceptance checklist behind the existing +[43-row port inventory](../../plans/kilo-opencode-v2-issue-13750.md#inventory-kilo-orgkilocode). +It does not count newly discovered UI controls as completed capabilities. + +- V1 source: local `origin/main` at `ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`; + `packages/kilo-vscode/package.json` declares version `7.5.14`. This is source + provenance, not the version of the running Extension Development Host. +- V2 comparison: `HEAD 59b29de40966803e2c7cd734d439843fb773f6a6` plus the existing + local Kilo implementation. No fetch, ref change, build or dependency install. +- A Kilo/Sol delegate (`thr_7bdux6wma9`) attempted desktop observation, but + window sizing prevented reliable menu navigation. Those observations are + provisional and do not satisfy the requested VS Code self-test method. +- After Johnny authorized setup, the delegate completed a framework-driven + audit in a separate disposable session using the older v1 checkout + `d99662338e3ddbd2613ab41fc0369837a6eb4be9`, manifest `7.4.20`, with existing + artifacts (`--mode dev --build false`). Artifact-to-HEAD correspondence is + unverified. Older-runtime absence is not current-v1 absence. +- No extension prompts, account/team edits, purchases, live API probes or + user-data mutations are part of this audit. No live credentials may be + imported into the fixture. Private transcript/account values are excluded. + +Use four evidence labels: **source-present**, **UI-observed**, +**local-workflow-tested**, and **live-contract-verified**. These are different +claims, not interchangeable completion levels. A command declaration proves +discoverability in source, not that its handler or backend works. + +## Source-discovered acceptance checklist + +Paths below are relative to `packages/kilo-vscode/` at the V1 SHA above unless +explicitly identified as current v2 paths. These rows are **source-present**; +none gains workflow-completion credit from this scan. + +| Surface and source | What the inventory must explicitly check | Existing owner / v2 treatment | +|---|---|---| +| Model picker: `webview-ui/src/components/shared/ModelSelector.tsx:272` | Favorites, Auto, Recommended, **Most Used**, provider groups, search and deduplication. Most Used derives from usage history; it is not native v2 Recents under another name. | Reopened Gateway row + CLI TUI remainder; preserve native favorites and inspect missing metadata/history seams. VS Code renderer stays Phase 5. | +| Model defaults: `webview-ui/src/components/settings/ModelsTab.tsx` | Default/small/subagent models, thinking variants and per-model variant overrides; specialized autocomplete and speech selections. | Settings scopes/Kilo fields; map each schema and precedence to v2 rather than copying v1 config keys. Specialized editor fields stay client-owned. | +| Agent behavior: `webview-ui/src/components/settings/AgentBehaviourTab.tsx` | Built-in/custom agent visibility, default selection, create/edit/import/remove controls, scope/provenance, MCP connection/auth/config, rule and skill sources. | Native agent/skill/MCP engines reusable; management UI and Kilo policy remain separate acceptance work from discovery. | +| Workflows: `webview-ui/src/components/settings/agent-behaviour/WorkflowsTab.tsx:17` | This UI edits `config.command` entries with model/variant selection. Audit custom commands, scoped overrides and dispatch. | Reuse native v2 commands/config; do not invent a second workflow engine from the tab title. Settings + CLI TUI remainder + VS Code settings. | +| Auto-approve: `webview-ui/src/components/settings/AutoApproveTab.tsx` | Permission rule editor and cost-limit control; distinguish persistent rules from per-run auto approval, agent ceilings, rejection and interruption behavior. | Native permission engine reuse; settings/VS Code UI parity remains open. Headless `--auto` does not complete these controls. | +| Context/memory: `webview-ui/src/components/settings/ContextTab.tsx` | Project memory status/storage inspection, separate automatic-save consent, compaction model/limit/pruning and watcher settings. | Existing memory implementation remains credited for its tested subset; sidebar and settings fields are additional UI work. Reuse native compaction; audit unmapped fields. | +| Checkpoints: `webview-ui/src/components/settings/CheckpointsTab.tsx` | Toggle maps to v1 `snapshot`; separately verify transcript/diff restore interactions, not merely this setting. | Reuse v2 snapshot engine; Task timeline/diff and settings rows own client semantics. No restoration test performed in this audit. | +| History/navigation: `webview-ui/src/components/history/HistoryView.tsx` and `chat/PromptRail.tsx` | Local/cloud/optional worktree history, cloud import, search and prompt navigation. | VS Code sidebar/editor tabs and Task timeline rows. Local v2 transfer and cloud-agent CLI completion do not prove unified cloud history/import. | +| Editor/terminal context: `package.json` command contributions | Add selection or terminal content to context; explain/fix/improve code; fix/explain/generate terminal command; generated commit message. | Code actions/enhance/commit-generation row and VS Code chat. Command names are not tested workflows; editor integration is not a mandatory CLI copy. | +| Autocomplete: `src/services/autocomplete/` and command contributions | Classic FIM, chat input completion, next-edit accept/jump/dismiss and associated settings/model choice. | Existing Inline autocomplete/FIM row remains unverified; these are distinct editor behaviors, not ordinary chat inference. | +| Agent Manager: `package.json` contributions and settings `AgentManagerTab` | Worktree/project selection, sessions/tabs, terminals, scripts, diff panel, base-branch update and PR actions. | Existing Agent Manager row, distinct from the completed Swarm board. Never run scripts, change branches or create worktrees merely to inventory controls. | +| Marketplace: `webview-ui/src/components/marketplace/MarketplaceView.tsx`, `src/services/marketplace/types.ts` | MCP, agent and skill catalog/install/remove surfaces, project/global scope and provenance/trust. | VS Code settings/sidebar and Kilo configuration UX; native plugin/skill loading is not marketplace-install parity. Installation remains untested and requires its own safe fixture. | +| Web tools: `webview-ui/src/components/settings/BrowserTab.tsx` | Web-search setting versus browser automation and system-Chrome preference; trace their separate runtime consumers. | Settings and client-specific chat tools. Native `webfetch`/`websearch` does not establish browser-automation equivalence. | +| Notifications/display/language: corresponding settings tabs | Attention sounds/events, reasoning collapse, token-throughput/approval-reason display, tool rendering, shortcuts and locale coverage. | Reuse v2 notification/theme/input primitives; settings and VS Code renderer own remaining policy. Do not replay notification sounds during observation. | +| Experimental controls: `webview-ui/src/components/settings/ExperimentalTab.tsx` | Remote startup, sharing policy, formatter/LSP, batch tools, image generation/model, shared board, notebook tools, continue-on-deny, multi-project, browser automation, task-model choice and MCP timeout. Record each gate and backend separately. | Settings-key mapping and relevant runtime/client rows. An experimental toggle is not evidence of supported v2 execution; native engine matches require exact behavior checks. | +| KiloClaw: `package.json` command contributions | Registered entry point is source-present. Resolve its product boundary and whether it is an external handoff or a port requirement. | **Unassigned scope candidate**, not silently included in cloud CLI or marked obsolete. No completion credit; scope decision can remain deferred. | + +The existing model/agent/sidebar follow-up in the plan remains authoritative +for Gateway-first presentation, Kilo auto metadata, balance/Pass privacy and +refresh, native agent policy, custom Plan transitions and sidebar sections. +This ledger adds details and cross-client distinctions rather than replacing it. + +## How to keep the inventory honest + +For each implementation slice, record: source SHA/version, client surface, +underlying provider/API/schema field, native v2 equivalent, remaining Kilo +delta, owning inventory row, and the exact positive/negative test evidence. +Use a missing-data state when the backing field cannot be verified. Do not +infer that an extension screenshot is the required CLI layout. + +The historical `25/43 (58.1%)` figure is a **coarse capability-row checkpoint**, not +a measured percentage of all Kilo user workflows. New detail under unfinished +rows does not change its arithmetic; a gap within an actually completed row +must reopen that row. Truly unassigned capabilities stay visible as scope +candidates, and any future denominator change needs an explicit versioned +inventory revision rather than silently preserving a favorable percentage. + +The subsequent [memory correction](memory-v2-parity.md) reopened one completed +row; current coarse coverage is **24/43 (55.8%)**. + +## Completed self-test observations (older runtime) + +Framework: `/Users/johnnyamancio/.config/kilo/scripts/vscode-self-test/cli.mjs`; +delegate `thr_7bdux6wma9`. VS Code `1.135.0` arm64. Existing extension artifact +SHA-256 `713bac9cf684a46e9e0f143e9597dd3cf9f782245ce72df6e33506160faab7d2`; +bundled CLI SHA-256 `5e8a5ac27339df6df19e156451e808baade23aebe2c9a2187a8619ab5ebab1fb`. +These hashes identify the observed binaries, not their source commit. + +| UI-observed surface | Concrete observations | Acceptance / boundary correction | +|---|---|---| +| First-run choice | Review First versus High Autonomy, with permission/detail descriptions | Add permission-policy and disclosure presets to the existing onboarding/settings checklist. Visible descriptions do not prove enforcement. | +| Models | Auto Frontier/Balanced/Efficient/Free; Recommended; FREE badges; provider groups. Default, Small, Subagent, Autocomplete and Speech-to-Text fields; prompt-training-model filter and per-mode overrides | Confirms grouping/settings surfaces in the older runtime. Does not prove current eligible catalogs or model execution. | +| Agents | Code/Ask/Debug/Orchestrator/Plan primary picker; Explore/General labeled subagents; Orchestrator deprecated in settings | Record deprecated visibility discrepancy: new v2 policy intentionally omits Orchestrator rather than claiming exact picker parity. | +| Plan | Visible statement restricting edits to plan files | Supports the Plan boundary acceptance item; no edit/exit/implementation transition was exercised. | +| Permissions | Session Cost Alert and per-tool matrix; External Directory/Bash/Doom Loop Ask, most others Allow in this fixture | Do not infer user defaults or runtime enforcement from settings labels. | +| Memory / context | Disabled project memory, separate auto-save, Inspect/storage, compaction threshold and pruning | Supports separate consent and settings requirements; none was enabled or modified. | +| Indexing / sandbox | Project/global indexing controls and disabled status; embedding/vector settings. Sandbox network restriction, destinations, writable paths | Existing shell-only v2 confinement is not full displayed sandbox-policy parity. No index or sandbox action was run. | +| Web tools | Web Search, Playwright MCP browser automation, System Chrome, Headless | Keep web search separate from browser automation; neither was enabled. | +| Agent Manager | Worktrees/sessions, tabs, Run, search/advanced options and composer in a separate editor | Opening it wrote only disposable fixture metadata. No worktree/session/script action was exercised. | +| Other settings | Snapshots; inline/chat completion controls; notification sound/test; Local Config/Global Config/Reload; provider controls | Editor presentation remains Phase 5. No provider connection, sound test, completion, snapshot or config action was executed. | + +The inert `GITHUB_TOKEN=invalid` isolation defense caused a GitHub Copilot +ENVIRONMENT-connected row; it is a harness artifact, not a real connection. +All HOME/XDG/profile/workspace state was isolated. The delegate verified no +`auth.json`, no prompt/model selection or inference, telemetry off, and no +Account Pooler access. Owned processes and disposable profile/workspace/socket +artifacts were cleaned up; the existing user window/stores were untouched. + +Sanitized evidence: [model groups](/Users/johnnyamancio/.bb/thread-storage/thr_7bdux6wma9/v1-selftest-model-picker.png), +[agent configuration](/Users/johnnyamancio/.bb/thread-storage/thr_7bdux6wma9/v1-selftest-agent-behaviour.png), +[runtime agent picker](/Users/johnnyamancio/.bb/thread-storage/thr_7bdux6wma9/v1-selftest-agent-picker.png), +[first-run sidebar](/Users/johnnyamancio/.bb/thread-storage/thr_7bdux6wma9/v1-selftest-sidebar-inner.png), +[Agent Manager](/Users/johnnyamancio/.bb/thread-storage/thr_7bdux6wma9/v1-selftest-agent-manager.png). + +This was framework-driven navigation and observation, not model/tool workflow +execution or live-contract verification. No completion credit is added. diff --git a/migration-tracking/technical-notes/baseline/external-resume-parity.md b/migration-tracking/technical-notes/baseline/external-resume-parity.md new file mode 100644 index 000000000000..68d46101fbac --- /dev/null +++ b/migration-tracking/technical-notes/baseline/external-resume-parity.md @@ -0,0 +1,71 @@ +# External resume parity (v2) + +Validated against the pinned v2 baseline `76dbaf20adbd43fd208a00ef3cda4a51e125a234` and the local Kilo `origin/main` snapshot `f7115470740c51fd4200fedd9ad3edfad60589ce` on 2026-09-05. The source implementation audited here is Kilo main commit `e87dc77b73` (`feat(cli): resume Claude and Codex sessions`) plus the import endpoint added by `b59ebd7bbd` (`feat(cli): import Claude Code and Codex transcripts via a session-resume endpoint`). + +## Scope + +The v2 capability is an explicit local-file adapter in `packages/kilo-cli/src/resume-external.ts`: + +- `import-external --model provider/model --agent name` reads one caller-supplied Claude or Codex JSONL file. +- It resolves the target location through the public client, creates a fresh v2 session ID, and calls the public session-import endpoint. +- It does not discover or read ambient `~/.claude` or `~/.codex` history, write to the source transcript, start execution during import, or depend on private Core/Server APIs. +- Import is format-specific and bounded to Claude major 2 and Codex major 0. + +The v1 implementation has additional user-visible workflows that are intentionally not claimed here: + +| v1 behavior | v2 status | Reason | +|---|---|---| +| `/resume-claude` and `/resume-codex` ambient discovery by current project | gap | v2 does not read provider home directories or infer a source root | +| Interactive picker and source migration markers | gap | v2 has an explicit list/ID backend but no chooser or source deduplication marker | +| Import into an existing empty session through the v1 session service | gap | v2 uses public `session.import` and always mints a fresh session | +| Unsupported blocks represented as visible dropped markers/notices | intentionally stricter | v2 rejects opaque or lossy structures before import | +| Incomplete tool calls | supported with explicit error state | the tool remains in history with `status: incomplete`; no call is silently dropped | +| Explicit-directory list and ID selection | supported subset | `external-sessions.ts` requires an absolute caller-supplied directory and applies v1 filename rules without provider defaults | + +## Proven common records + +The Kilo fixtures under `packages/kilo-cli/test/fixtures/external/` are synthetic, non-user data tied to the audited v1 envelope shapes. They cover the common records that v1's source fixtures exercise without copying real provider history. + +| Format | Accepted records | Preserved fields | +|---|---|---| +| Claude v2 | `user`, `assistant`, `thinking`, `text`, `tool_use`, `tool_result`; `ai-title`, `last-prompt`, `snapshot`, `mode`, `permission-mode`, and `attachment` metadata; sidechain records | transcript text, readable reasoning, tool IDs/names/JSON inputs, tool output text, error flags, source model, reasoning signatures, format/version | +| Codex 0 | `session_meta`, `response_item` message/function/custom-tool/reasoning records; `turn_context`, `event_msg`, and `world_state` metadata | input/output text, readable reasoning summaries, tool IDs/names/JSON inputs, output text, source status, source session ID, provider/model, format/version | + +Provider reasoning signatures are retained under assistant message metadata as provenance only. They are not sent back as replayable provider state. Source tool status is retained under tool-state metadata; failed and incomplete results become explicit v2 error states. + +The adapter rejects unsupported content, encrypted-only reasoning, malformed records, unknown tool outputs, duplicate tool outputs, mixed formats, unsupported major versions, and inconsistent source session/model identity. This keeps a source field from being silently discarded. The target continuation model is used for assistant history only when the source transcript contains no model; no `unknown/unknown` provenance is fabricated. + +## Explicit-directory selection + +`listExternalSessions({ directory, source, limit })` and `selectExternalSession({ directory, source, id })` are the non-ambient discovery boundary. They require an absolute directory supplied by the caller and never consult `HOME`, `~/.claude`, `~/.codex`, or a current-working-directory default. Claude scans only direct regular files named `.jsonl`; Codex scans regular files recursively whose names match `rollout-…-.jsonl`, matching the v1 picker’s accepted source names. The default list is ten entries, Claude is ordered by file modification time, and Codex by the rollout timestamp in the filename. Selection rescans the complete explicit directory, so an older ID outside the display window remains selectable. + +The audited v1 implementation scopes Claude to `/` and filters Codex’s ambient root scan by the first `session_meta.payload.cwd`; its picker then offers at most ten UUID labels. The explicit v2 API makes the source directory itself the caller-visible scope, because inferring a provider root or silently applying the target project’s `cwd` would violate the no-ambient-read boundary. + +Each candidate is parsed through the same bounded converter used by import. Malformed, unsupported, wrong-format, or oversized candidates remain visible as `valid: false` entries with the concrete error; selection refuses them rather than silently dropping them. Listing and selection only read source bytes. Unlike v1’s ambient Codex discovery, the explicit API does not infer or apply a project `cwd` filter—the caller chooses the source directory, making that scope visible and avoiding an implicit history read. + +## Audited upstream fixtures + +Origin/main's `packages/opencode/test/kilocode/fixture/session-resume/claude.jsonl` and `codex.jsonl` deliberately include unsupported blocks and interrupted tool rounds. The v1 parser preserves those as unsupported/error markers and appends an import notice. The v2 adapter now preserves interrupted calls as explicit incomplete tools, but still rejects the opaque `server_tool_result`, `redacted_thinking`, empty encrypted reasoning, and unknown content records. Those are documented compatibility gaps rather than silently dropped history. + +## Proof and done criteria + +Done for this slice means: + +- bounded local reads and strict Claude 2/Codex 0 parsing pass; +- source text, reasoning, tool IDs/inputs/outputs, statuses, and available source provenance survive `buildExternalTransfer`; +- source session/model changes fail closed instead of mislabeling messages; +- absent source model metadata falls back to the requested continuation model without recording fabricated source provenance; +- explicit-directory listing and selection apply audited Claude/Codex filename rules, expose invalid candidates, and do not read provider defaults; +- import uses only public `location.get` and `session.import`, with a fresh `ses_` ID; +- the bundled Bun 1.4 host test imports both fixtures, prompts each through a local fake OpenAI-compatible model, exports both sessions, and byte-compares the source copies before and after; +- no real provider history, paid model, shared upstream hook, commit, or push is required. + +Validation on this checkout: + +- system Bun 1.3.14: `bun test test/resume-external.test.ts` — 9 pass, 1 runtime-gated host test skipped, 40 assertions; +- bundled Bun 1.4.0: `./dist/interactive/bun --no-env-file test test/resume-external.test.ts` — 10 pass, 0 fail, 56 assertions; +- system Bun 1.3.14: `bun test test/external-sessions.test.ts` — 3 pass, 17 assertions; +- bundled Bun 1.4.0: `./dist/interactive/bun --no-env-file test test/external-sessions.test.ts` — 3 pass, 17 assertions; +- package typecheck and the repository's broader checks remain to be run by the parent integration owner. + +This is format/import parity for the supported subset, not full v1 Resume Claude/Codex cutover. Ambient discovery, picker/migration UX, unsupported-block fidelity, and other provider-specific records remain open by design. diff --git a/migration-tracking/technical-notes/baseline/goal79-scope-boundary.md b/migration-tracking/technical-notes/baseline/goal79-scope-boundary.md new file mode 100644 index 000000000000..6670dd15f6ef --- /dev/null +++ b/migration-tracking/technical-notes/baseline/goal79-scope-boundary.md @@ -0,0 +1,56 @@ +# Goal 79 scope boundary — 2026-09-07 + +The canonical inventory has 44 rows: one obsolete, 43 active, and 31 accepted +(28 done plus three upstream-equivalent). The target requires 34 accepted rows. +No partial implementation or skipped external check receives credit. + +## Remaining routes + +| Rows | Current boundary | +|---|---| +| Sharing | Explicit target deployment/end-to-end gates remain; the attempted local service bundle also lacks cached pg 8.20.0. Viewer compatibility and deletion/revert contracts remain incomplete. | +| Remote | Local translations and suggestions are verified; the documented compatibility and deployment boundaries remain open. | +| Updater/packaging | Actual macOS portable apply/rollback is verified; other-platform and distribution/release requirements remain open. | +| Six IDE rows | VS Code/JetBrains implementation is explicitly deferred by the current execution scope. | +| Three previously unknown cross-client rows | Their defining consumers are editor features, as classified below. They are not CLI substitutes or completed rows. | + +Even accepting remote and packaging in full would produce only 33/43. Closing +the 79% goal therefore also requires sharing acceptance or reopening deferred +client work. Neither changing the denominator nor counting host-only halves +satisfies the goal. + +## Cross-client classification + +The audit used local v1 commit `ecccd1f` and current v2 source. Paths below are +paths in that pinned v1 tree, not claims that those files exist in this worktree. + +- **Inline autocomplete/FIM:** + `packages/kilo-vscode/src/services/autocomplete/classic-auto-complete/AutocompleteInlineCompletionProvider.ts` + implements editor completion using VS Code context and FIM construction. + Ghost-text presentation, acceptance, and re-trigger behavior require the + deferred editor consumer. A host proxy alone is partial. +- **Code actions, enhance prompt, commit generation:** + `packages/kilo-vscode/src/services/code-actions/code-action-provider.ts` + implements `vscode.CodeActionProvider`; the remaining actions use editor/SCM + consumers and host generation endpoints. Porting just those endpoints cannot + close this composite row. +- **Voice/speech-to-text:** + `packages/kilo-vscode/src/speech-to-text/transcribe.ts` sends captured audio to + `/kilo/audio/transcriptions`; the feature also needs microphone capture and + transcription-provider behavior. The existing editor capture surface is + deferred, and external provider acceptance is unavailable under local-only + execution. + +Detailed delegate evidence is in parent-task thread storage: +`thr_qc5c8kziy3/goal79-cross-client-remaining-audit.md`. The parent directly +checked the completion provider, code-action provider, and transcription +request source; the completion-provider filename above corrects the audit's +`AutocompleteInlineCompletionItemProvider.ts` path typo. + +## Subsequent continuation + +After the scope question, the user instructed "Continue the goal". Work resumed +with local IDE implementation as the next route, preserving the prohibition on +external hosts. The deferrals above describe the audited earlier boundary; +they no longer prevent starting VS Code integration. Acceptance requirements +and the 43-row denominator remain unchanged. Coverage remains 31/43. diff --git a/migration-tracking/technical-notes/baseline/ide-preview-integration.md b/migration-tracking/technical-notes/baseline/ide-preview-integration.md new file mode 100644 index 000000000000..91b262c99b8d --- /dev/null +++ b/migration-tracking/technical-notes/baseline/ide-preview-integration.md @@ -0,0 +1,49 @@ +# IDE preview integration — 2026-09-08 + +**Scope corrected on 2026-09-08:** this document records the upstream-app wrapper +prototype, which Johnny rejected as a substitute for porting the existing Kilo +extension. Its infrastructure checks remain valid, but do not close the existing +sidebar, editor, or settings port rows. Current coverage is 31/43; the real port +must preserve the existing extension UI and workflows. + +## Implemented + +`packages/kilo-vscode` is a separately named extension composing the existing public v2 app in sidebar/editor webviews. It verifies an explicit loopback v2 endpoint and stores its credential in VS Code SecretStorage. The webview uses a separate ephemeral proxy credential. Health verification refuses v1/malformed/unauthorized responses and redirects, with a deadline covering the body. + +Commands cover Connect, Open Chat, Open Chat in Editor, New Chat, Open Conversation, Open Settings, Reconnect, and Disconnect. New Chat uses the workspace or a selected folder. Open Conversation lists the 100 latest top-level sessions. Each editor retains its route; the sidebar saves its route in workspace state. Reconnect and window restoration rebind the encoded server key to the new proxy. Settings routes do not replace the last conversation route. Disconnect closes the proxy and deletes the selected credential. + +The Kilo-owned embedding supplies the public app's flex-column/full-viewport root layout, preventing the narrow project selector from overlapping the session list. Settings reuse the existing browser-safe Kilo settings RPC and native profile/project revision checks, without a new public Server/Protocol route. + +The proxy contains static assets and forwards authenticated HTTP/SSE only to the verified loopback server. Terminal upgrades require the preview origin, native connect route, and a ticket. Both credentials are removed from upgrade headers; upstream validates the one-use ticket. Absolute request targets are refused before upstream work. No authorization fallback exists. + +## Acceptance + +- Built extension and app: pass. Package host/browser typechecks: pass. +- Connection, HTTP and real terminal suites: **20 pass, 78 assertions**. +- Actual Node proxy runtime: **6 checks pass**, including SSE completion/disconnect behavior. +- Packaging: **3 pass, 33 assertions**; actual `vsce` from local cache, real LICENSE, no workspace runtime dependencies in the VSIX. +- Browser real-host acceptance: pass for prompt streaming, shell permission/continuation, stale session-route rebinding, profile Save/reopen/Reset, project isolation, stale-edit refusal, explicit boolean drafts, and narrow viewport. +- **Installed VSIX native acceptance: pass, 6.82 seconds, 10 wrapper assertions plus driver/host assertions.** The actual workbench connects through the normal command and password inputs, streams a sidebar conversation, continues in an editor after keyboard approval, opens a separate conversation, reconnects, reloads the entire window and restores conversations, saves a setting verified on disk, creates exactly one new session, and disconnects. +- Independent command/proxy review: no blocking findings. Its minor credential and empty-workspace observations were addressed. + +Artifact: `packages/kilo-vscode/dist/kilo-code-v2-preview-0.0.1.vsix`, **9,819,328 bytes / 912 entries** at acceptance. The same artifact was installed into a disposable profile; no development-extension loading was used for that run. Source and commands are documented in `packages/kilo-vscode/README.md`. + +## Isolation and native-runner resolution + +The macOS browser/native wrappers deny external outbound access for the entire host/editor process tree. Johnny approved the test-only Chromium sandbox opt-out while retaining that policy. Native tests now add `--use-mock-keychain`: this controlled change resolved the earlier CDP/webview observation stall and avoids the user's real keychain. It does not establish which internal native keychain operation caused the old stall. + +VS Code's sticky extension-disabled notification overlapped the editor permission button in the development-host run. The acceptance exercises its normal keyboard activation; browser acceptance independently covers mouse approval. Test-owned editor processes are explicitly cleaned up, and the final process inspection found no remaining fixture profiles. Tests do not change live Kilo stores or VS Code profiles. + +Historical correction retained: an earlier browser fixture omitted the session agent and placed only Chromium under the OS policy, allowing host fallback-model requests. Those earlier requests cannot be claimed local-only. The corrected shared fixture selects the agent/model and the current wrappers enclose the entire process tree. No external deployment acceptance is credited. + +## Reproduction and limits + +Use the package README's build/package and `test:native-app` commands. `KILO_TEST_VSIX` selects the real local artifact for isolated installation; `KILO_TEST_VSCODE_EXECUTABLE` selects the installed editor. No browser, editor, or tooling is downloaded. Logs and screenshots are under root thread storage (`vscode-installed-app.log`, `vscode-installed-app/native-app.png`, `vscode-focused.log`, `vscode-node.log`, `vscode-browser.log`, `vscode-build.log`). + +The current verified editor platform is macOS. Marketplace publication, signing/distribution policy, Linux/Windows native acceptance, Agent Manager, and other v1 IDE capability rows remain outside these accepted conversation/settings rows. The extension requires an existing explicitly selected local server and does not elect or replace daemons. + +### Workspace lockfile follow-up + +The installed VSIX is self-contained and verified. The initial network-denied lockfile generation attempt failed and left the lock unchanged. A later read-only audit recovered the exact `@types/vscode@1.125.0` tuple and SHA-512 integrity from the local sibling `kilocode/.kilo/worktrees/extreme-brick/bun.lock`; the extracted package's version also matches. Root added the `packages/kilo-vscode` workspace descriptor, workspace package link, and that existing tuple to this lockfile without changing other records. These are sourced entries, not invented integrity values. + +**Fresh source installation remains unverified.** The delegate's prediction that these additions would make frozen installation succeed offline was tested and did not hold. `bun install --frozen-lockfile --dry-run --ignore-scripts`, enclosed by a policy denying all network, still requested other unavailable registry metadata; both available Bun runtimes failed. No actual install or unrestricted retry was run. Logs: `vscode-frozen-lockfile.log` and `vscode-frozen-system-bun.log` in root thread storage. The tested VSIX is unchanged and does not need workspace dependencies installed. diff --git a/migration-tracking/technical-notes/baseline/indexing-v2-parity.md b/migration-tracking/technical-notes/baseline/indexing-v2-parity.md new file mode 100644 index 000000000000..b91aff92b9c6 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/indexing-v2-parity.md @@ -0,0 +1,235 @@ +# Codebase indexing v2 parity boundary + +This record covers the port of Kilo's codebase-indexing engine to the v2 line. +Source of truth is Kilo `origin/main` pinned at +`ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`; the v2 base is `HEAD` +`59b29de40966`. No refs were fetched or moved for this work. + +## Shipped v1 implementation + +Codebase indexing in v1 is vector search over the workspace, in four layers: + +- **Engine package** `origin/main:packages/kilo-indexing` (49 source modules): + `CodeIndexManager`, orchestrator, config manager, state manager, cache + manager, search service, service factory; ten embedder providers (`kilo`, + `openai`, `ollama`, `openai-compatible`, `gemini`, `mistral`, + `vercel-ai-gateway`, `bedrock`, `openrouter`, `voyage`); two vector stores + (LanceDB default, Qdrant); tree-sitter chunking with 30 language query files + and wasm assets; directory scanner, file watcher, ignore rules, worktree + overlay. +- **Host runtime** `origin/main:packages/opencode/src/kilocode/indexing.ts` + (582 lines): per-directory lifecycle cache, VSCode consent gate, + plugin-enabled detection, Kilo auth plus embedding-model catalog resolution, + primary-worktree baseline overlay, LanceDB runtime externals, telemetry + forwarding, warning parsing, `Bus` status events. +- **Worker isolation** `indexing-worker.ts`, `indexing-worker-protocol.ts`, + `indexing-worker-client.ts`: a Bun `Worker` bundled into the v1 binary, with + `init` / `search` / `dispose` requests and `status` / `telemetry` / `warning` / + `log` events. +- **Surfaces**: the `semantic_search` tool + (`origin/main:packages/opencode/src/kilocode/tool/semantic-search.ts`), + registered only when `KiloIndexing.ready()` is true + (`kilocode/tool/registry.ts`, gated on the manager's `initialized` flag); the + HttpApi `indexing` group (`status`, `warnings`, `models`, `consent` — no + search endpoint); TUI dialog and sidebar; VSCode `IndexingTab`; console + `IndexingRoute`. `experimental.codebase_search` is retired config that v1 + deletes with a warning. + +## Why the engine was copied rather than reused in place + +Verified before any code was written: + +1. `@kilocode/kilo-indexing` is unpublished — `npm view` returns 404, as it does + for `@kilocode/kilo-memory`. It could not be added as an external dependency. +2. v1 exposes no out-of-process search. The HttpApi indexing group is + status/warnings/models/consent only, there is no indexing CLI command (no + matches for `indexing` under `origin/main:packages/opencode/src/cli`), and + search exists only as in-process `KiloIndexing.search`. +3. The v1 engine worker is not attachable: `indexing-worker.ts` is compiled into + the v1 binary as a Bun `Worker` (resolved through + `KILO_INDEXING_WORKER_PATH` inside bunfs) and speaks `postMessage`. +4. v2 has no substitute. `packages/core/src/ripgrep.ts` is lexical search, and + no package in the tree provides embeddings or vector storage. + +## What was ported + +### `packages/kilo-indexing` (new Kilo-owned package, `@kilocode/indexing`) + +118 files copied verbatim from the pinned SHA: the whole engine plus its 30 test +files. Copies were produced with `git show :`, so content matches the +source exactly. + +Not copied, with reasons: + +- `src/plugin.ts` — v1 no-op plugin entry (`async () => ({})`), the only + `@kilocode/plugin` consumer. +- `src/server/routes.ts` — v1 hono router; v2's server is Effect `HttpApi`. Only + consumer of `hono` and `hono-openapi`. +- `src/detect.ts` — v1 plugin-name detection for `hasIndexingPlugin(cfg.plugin)`; + v2 registers the plugin explicitly from the host. +- `test/kilocode/indexing/detect.test.ts` — covered only the unported module. + +Dependencies dropped versus v1's manifest: `@kilocode/plugin`, +`@kilocode/kilo-gateway`, `hono`, `hono-openapi`. All other v1 runtime pins are +kept identical (`@lancedb/lancedb` 0.26.2, `@parcel/watcher` 2.5.1, +`@qdrant/js-client-rest` 1.17.0, `web-tree-sitter` 0.25.10, `tree-sitter-wasms` +0.1.13, `openai` 6.27.0, both AWS SDK packages, `async-mutex`, `glob`, `ignore`, +`minimatch`, `p-limit`, `uuid`); `effect` and `zod` use the tree catalog, whose +`zod` 4.1.8 matches v1's pin exactly. No root catalog or lockfile edits. + +### Gateway divergence (`packages/kilo-indexing/src/gateway.ts`) + +v1 imported three things from `@kilocode/kilo-gateway`: `getDefaultHeaders` +(used only by the openrouter embedder), and `resolveKiloGatewayBaseUrl` plus +`HEADER_FEATURE` / `HEADER_ORGANIZATIONID` (used only by the kilo embedder). The +v2 `@kilocode/gateway` has no equivalent — its single export map entry exposes +`deviceAuth`, `fetchProfile`, `defaultOrganizationID`, `serverUrl`, +`createGatewayPlugin`, `registerGateway`, and `registerSessions` only: + +- No request-header surface exists at all. +- `serverUrl` is not a substitute for `resolveKiloGatewayBaseUrl`. It validates + and trims a base URL but does not read `KILO_API_URL`, does not honor a + token-encoded URL prefix (v1 `getKiloUrlFromToken`), and does not append the + `/api/gateway/` route. It is also stricter: it throws on non-loopback HTTP and + on any query or fragment, both of which v1 accepts and strips. + +Those helpers were therefore ported verbatim into an owned module rather than +re-expressed against a different contract. **Retirement condition:** when +`@kilocode/gateway` publishes a gateway-URL and request-header surface, delete +`src/gateway.ts` and import from it. + +### `packages/kilo-cli/src/indexing.ts` (v2 host) + +Drives the real `CodeIndexManager`: constructs it per Location, subscribes to +`onProgressUpdate`, calls `initialize(toIndexingConfigInput(settings))`, projects +status through the engine's own `normalizeIndexingStatus`, searches through +`manager.searchIndex`, and disposes the manager on scope close. The +`semantic_search` tool is registered through the public `ctx.tool.transform` +seam with v1's workspace-relative path scoping, escape rejection, result +projection, and output format, gated on an `available` flag that mirrors v1's +`initialized` (`ready()` = initialized and not `Disabled`). + +Execution approval is host-owned rather than inferred from the plugin's +`options.permission` visibility filter. The host authorizer runs before the +engine search with v1's `semantic_search` action, the query as its resource, +and `save: ["*"]`; direct `IndexingHost.search` remains a local engine API. +Without a host authorizer, the transformed tool fails closed before querying. + +Two host-level safety rules that v1 did not have: + +- **The configured store directory cannot escape the isolated index root.** + `indexing.lancedb.directory` is otherwise an arbitrary path the engine would + open and write, including an existing store belonging to something else. A + value outside the root is refused with an `Error` status and no engine start — + never silently rewritten — and the offending path is not echoed. +- **Failure diagnostics carry no engine text.** v1 put the raw failure message + into its status. An embedder failure can carry a request URL, a provider + response body, or a base URL with an embedded token, so initialization + failures report only provably safe values: the error class (a source-level + identifier) and a numeric HTTP status when present. Engine-emitted `Error` + statuses are collapsed to a stage taken from the engine's own fixed prefixes + (initial scan, file watch, embedder setup, vector store setup). Full detail + stays in the engine's own log, which is off unless `KILO_INDEXING_LOG` is set + and writes to stderr. + +### `packages/kilo-cli/src/indexing-input.ts` (explicit opt-in file) + +`readIndexingConfig(file)` backs the public `--indexing-config ` flag. +Enabling indexing sends source code to the configured embedding provider, so the +input is a file the user names explicitly and never something discovered from the +project. The read is bounded and fails closed: no symlink, regular file only, +64 KiB limit, JSONC parsed with `jsonc-parser`, object documents only, then +decoded against the engine's own `IndexingSchema` with `onExcessProperty: "error"` +so a typo cannot silently disable a setting. Because such a file holds provider +API keys and endpoints, no failure message repeats a key path, a value, the +document, or the file path. + +## Divergences from v1, deliberate + +- **In-process, not worker-isolated.** v1 runs the engine in a bundled Bun + `Worker`; v2 runs it in the server process. Bundling a worker entry is a + build-manifest decision outside this slice. +- **No Kilo embedding-model catalog or auth enrichment.** v1's host resolves + `fetchKiloEmbeddingModelCatalog` and injects API key, base URL, and + organization ID from `@kilocode/kilo-gateway`. v2's `@kilocode/gateway` has no + embedding catalog, so the `kilo` provider is ported but unwired: it requires + explicit `indexing.kilo.*` settings and a model ID. +- **No consent gate, telemetry sink, or Qdrant version warnings.** The engine + still emits telemetry and logs; the v2 host does not subscribe. +- **No HttpApi routes, TUI dialog, or sidebar.** +- **No worktree baseline overlay computation.** `baselineDirectory` is accepted + and forwarded; nothing computes it yet. +- **Settings come from an explicit file, not `kilo.json`.** v2 config has no + `indexing` key and upstream `Config.Info` rejects unknown keys. The shared + config schema was not touched; the host takes settings from + `--indexing-config` through `readIndexingConfig`. +- **The engine's own tests are not typechecked**, matching v1's tsconfig, whose + `include` is `src/**/*` only. Eight of the copied test files rely on loose + mock shapes that fail under `tsgo`; they were left byte-identical rather than + edited, and `include` stays `["src"]`. + +## Verification + +Run from package directories with the bundled runtime +(`packages/kilo-cli/dist/interactive/bun`, Bun 1.4.0): + +- `packages/kilo-indexing`: `bun test` — 501 pass, 9 skip, 0 fail across 31 + files. The nine skips are v1's own `test.skip`/`describe.skip` rate-limit and + base64 cases, unchanged. `bun run typecheck` — clean. +- `packages/kilo-cli`: `bun test test/indexing.test.ts test/indexing-input.test.ts` + — 17 pass, 0 fail. `bun run typecheck` — clean. + +`packages/kilo-cli/test/indexing.test.ts` is a real end-to-end test, not a fake +driver: it writes a fixture checkout, serves embeddings from a loopback Bun HTTP +server implementing the OpenAI `/embeddings` shape (deterministic hashed +bag-of-words vectors, so no paid inference), and lets the engine build an actual +LanceDB table. Coverage: + +- the real indexed file count after the engine's ignore rules exclude `vendor/`, + and that a `vendor/` file never appears in results +- top-ranked semantic matches with scores and line ranges, LanceDB + prefix-filtered scoped search, the empty-result message, workspace escape + rejection, and tool registration plus execution through the plugin transform +- a store directory outside the isolated root refused with no write and no + embedding request; a directory inside the root accepted and populated +- disabled and enabled-but-unconfigured projects: no tool, no embedding request, + no index directory +- an unreachable embedding service reported as an `Error` status +- **host wiring through `launch(input, { indexing })`**: a loopback model that + calls `semantic_search`, with the tool result read back from public history + (`client.message.list`), asserting real matches from `src/auth.ts`, that + ignored `vendor/` files never reach the model, and that the engine indexed the + activated location's own store directory under the isolated state root +- the same host wiring with `enabled: false`: the model still asks for the tool, + but nothing is registered, so the transcript carries no tool output and no + source, no embedding request is made, and no index directory is created +- `packages/kilo-cli/test/indexing.test.ts` also runs the real host with a local + fake model through ask/reject, allow, and resource-specific deny decisions; + rejected and denied queries never reach the embedding/search path. The + transform test proves a missing authorizer fails closed. + +`packages/kilo-indexing/test/log.test.ts` is the only new file in the engine's +test tree (the other 29 are verbatim v1 copies under `test/kilocode/`). It +imports `src/util/log.ts` directly and asserts the opt-in logger contract in +separate processes, since the module reads the environment at import: silent on +both streams by default and for unrecognised values, JSON on stderr only — never +stdout, which would corrupt stdio protocols such as ACP — for `1` and `true`. + +`packages/kilo-cli/test/indexing-input.test.ts` covers `readIndexingConfig`: +JSONC with comments and trailing commas, no inferred opt-in, rejection of +missing, irregular, symlinked, and oversized files, malformed and non-object +documents, and seven schema violations including unknown keys — asserting for +each that the message never contains the API key, the key name, or the file +path. + +## Still parent-owned + +- `packages/kilo-cli/package.json` dependency on `@kilocode/indexing`, the + `launch(input, { indexing })` option with its dynamic + `createIndexingPlugin({ state, settings })` registration, and the + `--indexing-config` flag parsing and help text. All landed. +- CLI build packaging: LanceDB native externals and, if worker isolation is + wanted, an indexing worker entry. v1 solves both in + `packages/opencode/script/build.ts`; the engine resolves LanceDB dynamically + through `src/indexing/vector-store/lancedb-loader.ts`, overridable with + `KILO_LANCEDB_PATH`. diff --git a/migration-tracking/technical-notes/baseline/memory-activity-next-slice.md b/migration-tracking/technical-notes/baseline/memory-activity-next-slice.md new file mode 100644 index 000000000000..50d9db374141 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/memory-activity-next-slice.md @@ -0,0 +1,75 @@ +# Memory sidebar save pulse v2 slice + +Follow-up to [memory-sidebar-v2-parity.md](memory-sidebar-v2-parity.md). Source +reference: pinned v1 `ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`, +specifically the save pulse in +`packages/opencode/src/kilocode/cli/cmd/tui/component/memory-status.tsx`: when a +`memory.status` or `memory.updated` event arrives with a matching `sessionID` +and `detail.type === "saved"`, the Enabled bullet turns success for 5 seconds. + +## Delivered + +Accepted by root after user scope confirmation and independent source review +on 2026-09-07. Root verification: events 5 tests / 9 assertions; CLI memory +and sidebar suites 34 tests / 171 assertions; both packages typecheck clean. +After the format-only cleanup, the pulse-focused subset passed again +(5 tests / 18 assertions), and all eight changed TS files are formatter-clean. +This is bounded save-pulse acceptance, not whole-row completion. + +| Behavior | Implementation | +|---|---| +| Per-session saved evidence exists and is published | The engine already publishes `MemoryEvents` payloads carrying `detail.type: "saved"` and a real `sessionID` from the actual producers: the turn-close typed consolidation (`packages/kilo-memory/src/effect/capture.ts`) and the explicit effect saves (`packages/kilo-memory/src/effect/index.ts`). No engine capture/save semantics changed. | +| Hosts observe events without overwriting each other | New additive `MemoryEvents.subscribe(listener)` returns a disposer (`packages/kilo-memory/src/effect/events.ts`). The legacy single `setSink` is preserved; sink and listener failures are contained independently so a failing observer never breaks a persisted memory op or another observer. | +| Scoped forwarding with attribution checks | The v2 memory plugin subscribes in its own scope (disposed at scope close) and emits the new public `saved` RPC event only when the payload has `detail.type === "saved"`, a present `sessionID`, a `directory` equal to this host's memory root, and the named session's actual Location matching the host Location (`packages/kilo-cli/src/memory-plugin.ts`). Same root across locations or hosts cannot misattribute; unscoped saves pulse nothing. | +| Minimal public payload | `MemoryRpc.Definition.events.saved` carries only `{ sessionID }` (`packages/kilo-cli/src/memory-rpc.ts`): no content, counts, or paths. | +| v1 5s success pulse | The sidebar subscribes via `rpc.events.on("saved")`, pulses the Enabled bullet success for 5s on a session match (latest save resets the window), and returns to muted on expiry (`packages/kilo-cli/src/tui-plugin/sidebar-memory.tsx`). | + +Explicit public RPC `remember`/`correct`/`forget` saves have no session scope +and intentionally pulse nothing; attributing them to whichever session happens +to be focused would be invented attribution. Their state remains visible +through the status RPC and the memory dialog. + +## Honest pending item + +Coverage limits: foreign-root rejection is tested; the same-root, +foreign-Location rejection is source-reviewed, not separately exercised. +Pulse expiry is exercised with the real renderer and real timer; resetting +the window on successive saves is source-reviewed. The frame-driven expiry +wait depends on the current renderer cadence. No live-account or +cross-platform acceptance is claimed. + +The durable-marker half of the v1 active tone remains open: current main also +turns Enabled success when the viewed session has memory markers in its durable +message parts (memory injected into that session's history). V2 injects memory +into ephemeral request system parts, so no durable per-session marker exists to +read. Recording durable per-session injection evidence (and exposing it +per-session) is an engine/host-owner decision, deliberately not approximated by +the latest-project `lastInjectedSessionID` statistic. + +## Verification + +All with the bundled runtime (Bun 1.4.0): + +- `packages/kilo-memory`: `bun test test/events.test.ts` — 5 pass. Delivery + until disposal, additivity with the legacy sink, per-listener failure + containment (sync and async), disposal of one listener preserving others. +- `packages/kilo-cli`: `./dist/interactive/bun run script/test.ts + test/sidebar-memory.test.tsx` — 3 pass, including the pulse tone mapping. +- `packages/kilo-cli`: `./dist/interactive/bun run script/test.ts + test/memory.test.ts -t "publishes the saved RPC event"` — 1 pass against a + real launched host with a loopback model. Covers: unscoped explicit RPC save + emits nothing; producer saves without a session or for a foreign root emit + nothing; a real turn-close typed consolidation emits `saved` for its own + session; a second session's real save emits with its own session ID; a turn + inside the 300s consolidation interval adds nothing; listener disposal stops + delivery. +- `packages/kilo-cli`: `./dist/interactive/bun run script/test.ts + test/sidebar-memory-ui.test.tsx` — 1 pass (stable across two consecutive + runs). Real production host and TUI with the loopback model: compact bullet + row at 160 and 140 columns, detail lines absent, muted bullet vs default + label, the real capture saved event pulses the bullet success and the 5s + expiry restores muted, a real save attributed to another session never + pulses, native auto-sidebar hides the row at 100 columns and restores at 140, + the saved operation persists in the memory index, and the only session work + is the one prompted turn (one user + one assistant message, empty inbox). +- `bun run typecheck` clean in `packages/kilo-cli` and `packages/kilo-memory`. diff --git a/migration-tracking/technical-notes/baseline/memory-sidebar-v2-parity.md b/migration-tracking/technical-notes/baseline/memory-sidebar-v2-parity.md new file mode 100644 index 000000000000..c3233202795f --- /dev/null +++ b/migration-tracking/technical-notes/baseline/memory-sidebar-v2-parity.md @@ -0,0 +1,87 @@ +# Memory right-sidebar v2 parity + +Source reference: pinned v1 +`ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`, specifically the right-sidebar row +component `packages/opencode/src/kilocode/cli/cmd/tui/component/memory-status.tsx` +and its slot registration `packages/opencode/src/kilocode/plugins/memory-status.tsx`. +This is a local source and isolated-host comparison, not a live-account claim. + +## Delivered surface + +The v2 sidebar memory section +(`packages/kilo-cli/src/tui-plugin/sidebar-memory.tsx`, appended to the native +`sidebar.content` slot) renders exactly the compact current-main status row: a +bold **Memory** header and one `• {label}` row, bullet tone-colored, label in +default text color. + +| Source behavior | V2 behavior | Evidence / boundary | +|---|---|---| +| Label set `Loading`, `Unavailable`, `Disabled`, `Enabled` from status fetch state | Same labels via exported `memoryRow` mapping | Unit test `test/sidebar-memory.test.tsx` runs the actual exported mapping | +| `Loading` and `Disabled` bullet muted | Both use `theme.text.subdued` | Fixture span assertions; mapping unit test | +| `Unavailable` bullet error-toned | `theme.text.feedback.error.default`; the earlier v2 adapter used warning | Mapping unit test asserts `tone: "error"` | +| `Enabled` bullet success only with per-session evidence (durable session message markers, or a 5s saved pulse after a `memory.status`/`memory.updated` event with `detail.type: "saved"`); muted otherwise | The 5s save pulse half is delivered via the public `saved` RPC event (see [memory-activity-next-slice.md](memory-activity-next-slice.md)); the durable-marker half has no v2 seam, so without a fresh save the row stays muted. | Real capture-path pulse covered in the renderer fixture; marker half documented below | +| Bullet `•` + label in a row, label in default text | Same row layout and colors | Fixture asserts bullet and label spans exist with different foregrounds (muted bullet vs default label) | + +The earlier v2 adapter also rendered `Auto:`, `Data:` estimated tokens, +`Injected:` tokens, `Last save:`, and `Data truncated` lines taken from real +`status` RPC fields. Johnny's parity request is the compact source row, so these +lines are consolidated out of the sidebar. No data is deleted: the same fields +remain available in the `/memory status` dialog +(`packages/kilo-cli/src/tui-plugin/memory-dialog.tsx`) and the public +`kilocode.memory` RPC; the sidebar still refreshes on enable/disable commands, +`session.execution.succeeded`, and a 5s poll while enabled, so label transitions +stay live. + +Refresh wiring is unchanged: session/location changes re-fetch with the prior +request superseded, `/memory` mutations notify through the in-process memory UI +refresh bus, and a disabled memory is not polled. + +## Activity acceptance — 2026-09-07 + +Both activity sources now reach the sidebar. A real per-session save produces +the existing five-second pulse. Separately, after the context hook appends +nonempty enabled memory, the Kilo plugin records `injected:v1:` in +its persistent storage with the memory root as its value. This stores no +memory content and creates no transcript or inbox entry. It records context +injection, not proof of model consumption or a successful save. + +The optional session ID on the existing status RPC is validated against the +requested Location before reading the marker. The returned session-scoped +boolean lights Enabled after remount/restart; unrelated sessions remain +inactive, and Disabled stays disabled regardless of prior activity. Old +sessions are not backfilled from the project's latest-injection statistic. +This is a v2 storage adaptation of the v1 durable-marker indicator; v2's +request-scoped memory context still is not copied into durable message parts. + +The real renderer fixture preserves the pulse-and-expiry proof, then disables +auto-save and makes a second actual local model request with saved memory. +It proves the injection marker, unrelated-session false, active tone after a +narrow/wide remount, and a second host reading the same fact after shutdown. +A foreign Location status request is refused. Four focused sidebar tests pass; +the fixture checks that only the two requested user/assistant pairs exist and +that no extra inbox items are admitted. All endpoints are loopback fixtures. + +## Original compact-row verification (before the save-pulse follow-up) + +`packages/kilo-cli` ran with the bundled runtime (Bun 1.4.0): + +``` +./dist/interactive/bun run script/test.ts test/sidebar-memory.test.tsx +./dist/interactive/bun run script/test.ts test/sidebar-memory-ui.test.tsx +./dist/interactive/bun run typecheck +``` + +All pass. The real-renderer fixture launches the isolated production host and +TUI with models disabled, drives `/memory enable` and `/memory disable` through +the actual prompt, asserts the compact bullet row at 160 and 140 columns, +asserts the removed detail lines are absent, asserts the bullet span is muted +while the label span is default text, verifies the native auto-sidebar policy +hides the row at 100 columns and restores it at 140 without model work, and +confirms no session message or inbox item is created. + +The save-pulse follow-up intentionally adds one loopback-model session to the +fixture so it can prove an actual saved operation changes the bullet color +and then expires. Its evidence and remaining limits are recorded in +[memory-activity-next-slice.md](memory-activity-next-slice.md); the earlier +no-session-work statement describes the compact-row checks, not that new +capture scenario. diff --git a/migration-tracking/technical-notes/baseline/memory-ui-v2-parity.md b/migration-tracking/technical-notes/baseline/memory-ui-v2-parity.md new file mode 100644 index 000000000000..ff046ad3b5be --- /dev/null +++ b/migration-tracking/technical-notes/baseline/memory-ui-v2-parity.md @@ -0,0 +1,48 @@ +# Memory UI v2 parity slice + +Source reference: local `origin/main` at +`ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`, specifically +`packages/opencode/src/kilocode/cli/cmd/tui/component/dialog-memory.tsx`. +This is a local source and isolated-host comparison, not a live-account claim. + +## Delivered UI surface + +| Source behavior | V2 UI behavior | Evidence / boundary | +|---|---|---| +| Memory help presents an actionable command surface instead of a plain alert | Bare `/memory` opens a native selectable menu for status, show, inspect, enable/disable, auto, rebuild, remember, correct, forget, and purge. Text actions use native prompts and purge requires native confirmation. Invalid input retains a large custom usage-help dialog with the parser reason. | Menu selections re-enter the same parser/RPC dispatcher used by direct slash arguments; cancel and empty prompts make no mutation. The public plugin dialog API has no supported append-to-composer operation, so it does not recreate v1's selectable prompt drafts. | +| Show presents state, root, source counts, and stored items | `/memory show` has a large refreshable, scrollable dialog with the real `show` RPC state, formatted root, source names, parsed stored entries, and page controls | Entries are parsed only from actual `- key :: text` source lines. No source-specific count or item is invented. | +| Status presents state and index details | `/memory status` has the same refreshable, scrollable dialog shell and uses the real `status` RPC fields for state, automatic mode, index bytes/tokens/truncation, local-file presence, and persisted engine activity. | Activity is projected from real engine state: injection time/bytes/estimated tokens, session-digest time, typed-consolidation time, and operation count. It does not present a cost or model-usage value that the public `generate.text` result does not supply. | +| Mutating commands provide lightweight feedback | Enable, disable, auto, rebuild, remember, correct, forget, purge, and inspect now use native toasts rather than blocking alerts | Operations continue through the existing typed local RPC and retain refresh notifications for the sidebar. | +| Palette has one Memory command | `kilo.memory` is not suggested, preventing the native unfiltered command palette from rendering it in both Suggested and Kilo sections | The command remains palette-visible and slash-visible. | +| Slash completion has one executable Memory command | The existing native shadow rule keeps the local `/memory` ahead of the server command; the real-renderer fixture counts actual slash rows rather than just a matching description | The public server command remains available to non-TUI clients; no backend command was removed. | + +## Verification + +`packages/kilo-cli` ran: + +``` +./dist/interactive/bun run script/test.ts test/memory-ui.test.tsx +``` + +It passed with Bun 1.4.0. The isolated production-host/TUI fixture verifies one +actionable `/memory` autocomplete row and one palette row, immediate +enable/auto/remember RPC effects, native menu selections for Status and Show, +a cancelled Remember prompt that preserves the source bytes, the rendered +Memory show dialog, persisted source text, and no created session or +inbox/model work. + +`./dist/interactive/bun run typecheck` also passes. + +## Deliberate gaps + +- The public host generation result supplies text, not provider usage or cost. + The status dialog consequently omits consolidation billing/token claims; the + persisted activity fields above are timestamps, operation count, and actual + injection byte/estimated-token measurements, not a cost estimate. This is + narrower than the v1 capture port, which receives provider usage for its + internal token statistic; v1 itself hard-codes consolidation cost to zero, so + cost is not a missing v1 user-facing capability. +- The public plugin dialog API cannot inject a selected command draft into the + composer, so v1's selectable help-menu drafting is not ported. +- This UI slice does not change the separate engine/capture gaps recorded in + [memory-v2-parity.md](memory-v2-parity.md). diff --git a/migration-tracking/technical-notes/baseline/memory-v2-parity.md b/migration-tracking/technical-notes/baseline/memory-v2-parity.md new file mode 100644 index 000000000000..86f403c2f112 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/memory-v2-parity.md @@ -0,0 +1,187 @@ +# Memory v2 parity boundary + +This record compares the bounded v2 port to `origin/main` at +`ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`. It distinguishes shipped v1 +behavior from the intentionally smaller v2 port; it is not a proposal to +recreate the v1 memory engine. + +## Historical reopening after user smoke test — 2026-09-05 + +The previous completed inventory status was too broad. Remote main was verified +read-only to still be `ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`. Its existing +`dialog-memory.tsx` and `memory-palette.tsx` provide a selectable help menu, +structured status/source/stored-item views, autosave activity, refresh and +scrolling. At reopening, v2 `tui-plugin/memory.tsx` instead used generic alerts. +That was a reduced reimplementation, not an old modal copied verbatim, and did +not establish the requested latest-main parity. The replacement and current +evidence are recorded below and in `memory-ui-v2-parity.md`. + +The duplicate-command regression was insufficient: it counts only occurrences +of the local description. The native palette separately repeats suggested +commands under Suggested and their normal category; Kilo Memory then set +`suggested: true`. Inline autocomplete and the palette required distinct +positive/negative checks, including actual row counts. Both are now covered; +the issue was not dismissed as an old process. + +The engine/capture findings also reopened the coarse row. The subsequent +engine replacement and lifecycle tests below supersede the old compact +adapter; row credit still follows the integrated acceptance review. + +## Reusable engine port — 2026-09-05 + +The source-backed engine now lives in the Kilo-owned +`packages/kilo-memory/src` tree, copied from local `origin/main` at +`ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`. Its storage, markdown, +index/recall, digest, typed-consolidation, session-lock, and port abstractions +remain standalone (Effect and Zod only); the unused v1 HTTP adapter is not +ported. + +Two deliberate v2 safety deltas apply. Fresh state defaults +`autoConsolidate` to false, and malformed state reads fail closed without the +v1 package's backup/delete/rewrite recovery. Existing preview roots retain the +same `kilo-memory` v1 manifest and source markdown names. The old compact +state expands in memory; only an intentional mutation may persist the added +engine statistics. + +The v2 host adapts only public plugin APIs: execution started/succeeded/failed/ +interrupted events, `session.context`, `session.get`, the selected session +model, and `generate.text`. It uses the reusable per-session lock and capture +pipeline. V2 exposes an execution-drain rather than a per-promoted-turn event. +At an observed Started event, only while the root is already enabled with auto +mode on, the adapter snapshots completed assistant IDs. Its terminal capture +then accepts every complete group not in that baseline; the persisted marker +only deduplicates a matching eligible group, never authorizes replay from a +period when auto mode was off. Missing Started evidence (including activation +or enablement mid-execution) skips the terminal capture. Enable/disable/auto/ +purge clear that scope-local baseline. Failed and interrupted drains use the +engine's bounded no-model fallback digest path. + +The host now provides the optional `readSnapshotDiff` reader. Only after +Started-baseline filtering does it use a capture group's first filtered +assistant `snapshot.start` and last filtered assistant `snapshot.end`, in the +session's owning Location scope. Missing bounds or a failed read omit `diffs`, +which the engine represents as unavailable rather than no edits; a successful +empty diff remains an honest empty list. Recall provenance is available from +the completed public `kilo_memory_recall` tool result and its positive +`metadata.count`, so short recall echoes are suppressed without a heuristic. +The public generation result has text but not provider usage, so v2 does not +infer provider cost. This loses no shipped v1 gate: the reusable consolidation +path records cost as zero, and the v1 lifecycle caller never supplies the lower +port's optional `memoryModel` argument. Neither host exposes a dedicated memory +model setting merely because that lower-level argument exists. + +## Shipped v1 behavior + +- Project scope only is real shipped behavior. `origin/main:packages/kilo-memory/src/commands.ts` + rejects `personal`, `use-personal`, `personal-context`, and + `personal-in-project`. +- The explicit command surface is real: enable, disable, status, show, + remember, correct, forget, inspect, rebuild, and confirmed purge are parsed + in `origin/main:packages/kilo-memory/src/commands.ts` and routed by + `origin/main:packages/opencode/src/kilocode/cli/cmd/tui/memory-command.ts`. +- Automatic mode is also real shipped v1 behavior, not a parser placeholder. + The same command parser accepts `auto on|off`; the TUI routes it to + `memory.configure({ autoConsolidate })`; and + `origin/main:packages/kilo-memory/src/memory.ts` persists that setting. +- Automatic mode is an auxiliary model-backed capture engine. The v1 lifecycle + subscription in `origin/main:packages/opencode/src/kilocode/memory/turn.ts` + runs on turn close, and `origin/main:packages/kilo-memory/src/effect/capture.ts` + resolves and calls a model for digest and typed consolidation. The plan in + `origin/main:packages/kilo-memory/src/capture/plan.ts` gates both calls on + `autoConsolidate`. +- V1 startup injection is not a passive read. Its prompt assembly calls + `KiloMemory.context` from + `origin/main:packages/opencode/src/kilocode/session/prompt.ts`, and + `origin/main:packages/kilo-memory/src/memory.ts` records injection metadata. + The real v1 state schema includes `lastInjectedAt`, bytes, tokens, and + session ID in `origin/main:packages/kilo-memory/src/schema.ts`. + +## V2 acceptance boundary + +- Project isolation, default-off state, explicit local operations, typed RPC, + save/recall tools, and the TUI dialog command are implemented in + `packages/kilo-cli/src/memory-plugin.ts`, `memory-rpc.ts`, + `memory-command.ts`, and `tui-plugin/memory.tsx`. +- Enabled v2 memory injects the existing bounded index through the public + Location-scoped `session.context` hook. The hook is registered in + `packages/kilo-cli/src/memory-plugin.ts`; upstream v2's public hook is + applied immediately before request dispatch by + `packages/core/src/session/model-request.ts`. +- Injection never emits a synthetic message or runs a second model call. A + disabled root creates no files or calls; an enabled nonempty reference block + records real injection statistics through the reusable engine. Ordinary + malformed-state reads fail closed without backup/delete/rewrite. It is marked + as reference data rather than instructions. +- V2 exposes the real `auto on|off` control through the same command, RPC, and + TUI paths, persisted as `autoConsolidate` with a default of `false` in + `packages/kilo-cli/src/memory-plugin.ts`. Auxiliary capture is eligible only + when both `enabled` and `autoConsolidate` are true; turning on auto while + memory is disabled performs no model work. +- Model-facing memory tools use the host-provided `ToolAuthorizer` before any + store mutation or recall. Save maps the v1 approval to + `kilo_memory_save`, `resources: [action]`, and `save: []`; the query-only v2 + recall maps its fixed `search` mode to `kilo_memory_recall` with + `save: ["*"]`. Direct RPC and `MemoryStore` calls remain independent, while + a plugin instance without a host authorizer fails closed at tool execution. +- `packages/kilo-cli/src/memory-capture.ts` uses only public v2 plugin APIs: + `ctx.event.subscribe`, `ctx.session.get/context`, and `ctx.generate.text`. + One `MemoryService` lives for the plugin scope, preserving engine session + locks across closes. Started execution cancels a pending idle flush; scope + teardown clears owned timers and signals. Real public child `parentID` is + passed to the engine, which excludes child sessions. +- The v2 completion boundary is an execution drain, not v1's individual + turn-close lifecycle. The adapter groups all settled assistant steps with the + consecutive promoted user messages they answer, rather than dropping every + user but the last in a steering batch. Its Started-time baseline admits only + later groups, including when there is no persisted marker. Before the engine + receives a view, `recent` is rebuilt exclusively from those eligible groups, + so pre-consent/context-compacted text cannot leak through its auxiliary + context. The source engine's interval behavior remains unchanged: a later + eligible close schedules the final eligible bounded `recent` view for idle + flush, instead of forcing extra model calls. Failed/interrupted terminal + events write only the reusable engine's bounded fallback digest and make no + auxiliary model call. +- The optional host snapshot reader resolves only an eligible capture group's + first filtered-assistant start and last filtered-assistant end in that + session's Location scope. Missing bounds or reader failures omit the diff + field, which remains unavailable rather than a fabricated empty diff. A + successful no-change result is the only path that supplies `[]`. Completed + public `kilo_memory_recall` results provide exact positive-count recall + provenance. `ctx.generate.text` is an Effect; the adapter runs it with the + engine abort signal through `Effect.runPromise(..., { signal })`. That + standard interruption reaches the public host generation/HTTP path: the + held-model fixture observes its request abort when memory is disabled. Its + public result carries text only, so no provider usage/cost value is used as a + capture gate or displayed as inferred cost. +- No personal/global scope, cloud sync, or retention contract is inferred. + Personal scope is explicitly unsupported by both implementations. + +## Evidence + +- `packages/kilo-cli/test/memory.test.ts` covers explicit persistence, strict + per-project routing, disabled no-write context preparation, malformed-state + non-repair, context-hook registration, enabled/disabled provider-request + injection (including compaction), restart persistence, non-durable history, + and a real host with a local fake model proving that no auxiliary call occurs + until both memory and auto mode are explicitly enabled, that `auto off` stops + later calls, and that ask/reject/allow/deny decisions happen before memory + mutation. Its held-primary steer-batch fixture proves all three opted-in + promoted inputs reach auxiliary capture while pre-opt-in text does not. It + also proves Started-time multi-group selection excludes prior groups, exact + completed recall provenance, execution-error and + held-execution-interrupted fallback-digest persistence with no auxiliary + call, and held auxiliary-request cancellation. The transform test also + proves a missing authorizer fails closed. +- `packages/kilo-cli/test/memory-ui.test.tsx` covers the production TUI command + through an isolated host and asserts it performs no session or model work. +- `packages/kilo-cli/test/memory-diff.test.ts` creates real Git snapshots in a + session Location scope, proves the normalized structured diff, and proves an + invalid snapshot is unavailable. The normal fake-model host fixture uses a + committed Git project and canonicalizes its Location directory with + `realpath` before activation and session creation. It proves the auxiliary + prompt receives `added memory-diff-proof.txt +1 -0`. The earlier missing + bounds were a macOS fixture-only `/var` versus `/private/var` alias: the + Session Location used the former while the Project/Snapshot worktree used the + latter, so snapshot scope's raw relative-path check reported the Location + outside the project. This is a Location-path canonicalization limitation, + not general normal-host snapshot absence. diff --git a/migration-tracking/technical-notes/baseline/model-info-bench-seam.md b/migration-tracking/technical-notes/baseline/model-info-bench-seam.md new file mode 100644 index 000000000000..b47eee2ed3bc --- /dev/null +++ b/migration-tracking/technical-notes/baseline/model-info-bench-seam.md @@ -0,0 +1,154 @@ +# Model-info bench seam — v1 surface vs v2, relocated capability — 2026-09-07 + +Bounded implementation of the missing v1 model-info display surface. Provenance: +this checkout at the frozen accepted batch; pinned v1 source +`ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`; accepted B1 files untouched. + +## Detail expansion, pricing fixes, and scrolling — 2026-09-07 + +The relocated model-info panel now carries and renders full source-backed display metadata: +- **Description & Reasoning**: Gateway source contracts (`v1 api/models.ts`) publish `description` + and `supported_parameters` (including `"reasoning"`). These are now captured in `packages/kilo-gateway/src/models.ts` + and carried as display-only optional fields on `KiloModels.Entry` (`packages/schema/src/kilocode/models.ts`). + They are never written to request settings and render only when present in the catalog. +- **Family**: captured from `opencode.family` when present on the Gateway record and projected to `Entry`. +- **Cached Free rule**: fixed `fmtCachedPrice` per v1 rule (`model-info-panel-utils.ts:28`): cached price + renders when `cache.read > 0` or when `input === 0` (rendering `Cached: Free`). +- **Tiered context pricing**: displays actual tiered pricing entries using the source threshold + (`context > tier.size`, matching `packages/core/src/session/usage.ts:25`), showing input, output, + and cached rates for each tier (including zero-threshold tiers `tier.size === 0`, with no invented + `> 0` filter). No invented average costs are calculated or displayed. +- **Scrolling**: wrapped panel details in an OpenTUI `` with dynamic terminal bounds + (`useTerminalDimensions()`), while keeping the `Browse only; the session model is unchanged.` notice + anchored at the dialog base. Bounded scrolling verified at short terminal height (20 rows) with footer visible. + +Real-host renderer coverage: +- Valid metadata (description, reasoning, family, bench, and free cached pricing). +- Configured native `Model.Info` context-tier quotes (`ordinary`: base `$3.00/1M`, `$15.00/1M`, `$0.30/1M`; `Context > 0:` `$4.00/1M`, `$20.00/1M`, `$0.40/1M`; `Context > 128K:` `$6.00/1M`, `$30.00/1M`, `$0.60/1M`). +- Bounded scrolling at short terminal height (`setup.resize(120, 20)` with footer `"Browse only; the session model is unchanged."` remaining permanently visible). +- Real loopback 503 metadata RPC failure (`scenario === "unavailable"`), asserting the warning `"Kilo model metadata unavailable for this account scope"`, omission of bench section, and zero public session mutation. +- Absent metadata (clean omission of optional rows without fabrication). +- Signed-out empty catalog feedback ("No models available for this location."). +- Direct unit tests in `packages/kilo-gateway/test/models.test.ts` cover tolerant schema decode and projection (2 tests / 20 assertions). + +## Public path testability note + +- Metadata RPC failure is fully and cleanly driven through real loopback Gateway controls (503 response on `/api/openrouter/models` after initial catalog load). +- Location model sync (`ctx.data.location.model.sync(location)`) communicates with the in-process local OpenCode server and cannot be induced to fail through public loopback endpoints without terminating the local server process itself; its error catch-block toast remains source-reviewed behavior. + +## Finding + +**No faithful existing UI equivalent exists, and no clean Kilo-owned seam can +mount the exact v1 surface.** + +- The exact v1 surface: `ModelInfoPanel` + (`ecccd1f packages/opencode/src/kilocode/components/model-info-panel.tsx`) — + a 30-wide, left-bordered scroll panel (name, provider, BYOK/collects-data + disclosures, Family, Released, Input/Output/Cached/Avg Cost, Context, + **Terminal Bench 2.0**, Reasoning/Caps/Out, description) mounted beside the + model picker list on wide terminals (width ≥ 108 → dialog "xlarge") for the + highlighted item — `packages/tui/src/component/dialog-model.tsx:12` and + `:172-173` at the pin (Kilo changes). +- The v2 native picker (`packages/tui/src/component/dialog-model.tsx`, shared + file, host-presentation markers only) has no preview panel, no wide sizing, + and emits no per-option details. The upstream `DialogSelect` does support + per-option `details` strings (`packages/tui/src/ui/dialog-select.tsx:71-73, +769-783`), but feeding them from the model dialog is still a shared-file + edit. The host presentation boundary + (`TuiModelPicker`/`TuiModelGroup`, `packages/tui/src/context/runtime.tsx:5-26`) + is group metadata only (category/order/footer/hidden/preferred) — no panel + surface. +- Therefore the **exact picker preview stays pending**: restoring it faithfully + requires a shared native patch, which is out of scope without root's explicit + exception. This is a layout difference, not a retired behavior. + +## Implemented: relocated capability (Kilo-owned, public primitives only) + +`packages/kilo-cli/src/tui-plugin/model-info-dialog.tsx` (new) — a standalone, +browse-only **"Kilo model info"** command (command palette + `/model-info` +slash) via the public `ctx.keymap.layer` + `ctx.ui.dialog.select` + +`ctx.ui.dialog.show` primitives, the same public surface the memory dialogs +use (`tui-plugin/memory-dialog.tsx:20-26`): + +- **Browse-only selection**: `ctx.ui.dialog.select` lists the location's + enabled models from the reactive data store + (`ctx.data.location.model.list`, public generated `ModelInfo` records), + pre-highlighting the viewed session's current model (durable + `model-switched` truth, shared with the accepted bench sidebar). Choosing an + entry opens the panel; nothing mutates the session model. The eligibility + identity snapshots `ctx.location` (a reactive getter, + `packages/tui/src/plugin/api.tsx:113`) **before the first await** and + rechecks the live value after the metadata fetch and after the select, and + the open panel's deferred scope guard includes the live location — a scope, + account, or location change during the awaits or while the panel is open + closes it. Fixture note: the browse list is driven with the proven + `pressArrow` navigation (no filter-input race). +- **Source-backed metadata**: the panel renders name, provider, family + (optional), Released (`time.released` — the producer is + `packages/core/src/models-dev.ts:116-119`, epoch milliseconds), Context + (`limit.context`), and the bounded cost rule — exactly one untiered quote is + the labeled base quote (Input/Output, Cached when `cache.read > 0`); + tiered entries are only announced ("Additional context pricing tiers + available", no guessed inequality); an empty cost array or multiple untiered + quotes render "Pricing unavailable" / the tiers line without presenting + first-array-entry prices as universal cost. Formatting units are pinned from + v1 `model-info-panel-utils.ts:10-48`, reimplemented locally (no source-tree + imports). V1 stored a single cost object; the array-selection rule above is + a v2 adaptation, not a v1 behavior. No description/Reasoning rows: the public `ModelInfo` shape has no + such fields and none are fabricated. +- **Terminal Bench 2.0**: from the accepted `KiloModels.Entry.terminalBench` + via a **fresh per-open** `KiloModels.list` fetch (no stale catalog reuse; the + fetch is aborted when the dialog closes), exact units + (`(score*100).toFixed(1)%`, `$usd.toFixed(2)`), rendered only for + `providerID === "kilo"` models, omitted when absent or malformed. +- **Unavailable scope**: when the metadata RPC fails, the panel shows an + explicit "Kilo model metadata unavailable for this account scope" warning and + no bench section. +- **Scope invalidation**: with root's existing account/revision accessors, a + scope or account change while the dialog is open closes it rather than + retaining stale eligibility (no new cache). + +**Installed through root-owned `tui.tsx` wiring:** + +```ts +import { installModelInfoDialog } from "./model-info-dialog" +installModelInfoDialog(ctx, { client, account, revision, signal: controller.signal }) +``` + +placed next to the accepted `installBenchSidebar` call; `client` is the +existing `kiloHttp` client, `account`/`revision` the existing accessors passed +to `installAccountSidebar`, `signal` the existing controller signal. No +`tui.tsx` edit was made by this slice. + +## Remaining boundaries + +The exact picker-attached layout remains deferred to retain the native v2 picker +without shared-file edits; the relocated standalone dialog provides the capability +through public primitives. Zero-cost cache pricing now correctly renders "Free" per v1 +rules. Tiered pricing renders actual thresholds (`context > tier.size`); no average cost +is invented. + +## Wider expected-value update needed outside ownership + +In `packages/kilo-gateway/test/plugin.test.ts:665`: +```ts +expect(yield* host.models().list({}, call())).toEqual([ + { id: "fixture", hasUserByokAvailable: true, mayTrainOnYourPrompts: false }, +]) +``` +Because `family` is now carried on `KiloModels.Entry` and the test mock supplies +`opencode: { family: "fixture" }`, the returned entry includes `"family": "fixture"`. +Since `packages/kilo-gateway/test/plugin.test.ts` is outside this slice's ownership, this +expected-value update is reported to parent for integration rather than edited directly. + +## Verification + +- `packages/kilo-cli/test/model-info-dialog.test.tsx`: **5 pass / 0 fail / 19 assertions** + under bundled Bun 1.4.0 (13.3s). Covers `fmtCachedPrice` (v1 `cache.read > 0 or input === 0` rule), + `fmtPrice`, `fmtContext`, and real renderer tests across valid metadata (with Description, Reasoning, + Family, and Free Cached pricing), absent metadata (verifying clean omission without fabrication), + and signed-out empty catalog. +- `packages/kilo-gateway/test/models.test.ts`: **2 pass / 0 fail / 20 assertions**. Covers + tolerant schema decode and projection of `description`, `reasoning`, `family`, and `terminalBench`. +- Typechecks: `packages/schema`, `packages/kilo-gateway`, and `packages/core` pass clean (`tsgo --noEmit`). + `packages/kilo-cli` typecheck passes clean (`tsgo --noEmit`). diff --git a/migration-tracking/technical-notes/baseline/model-loading-v2-parity.md b/migration-tracking/technical-notes/baseline/model-loading-v2-parity.md new file mode 100644 index 000000000000..6bdbb57decbb --- /dev/null +++ b/migration-tracking/technical-notes/baseline/model-loading-v2-parity.md @@ -0,0 +1,167 @@ +# Model-picker loading parity — 2026-09-05 + +This documents the bounded v2 fix for the Kilo model picker’s first-render reorder. + +## Delivered behavior + +- Opening a picker containing a Kilo catalog begins with a nonactionable `Loading Kilo model metadata…` state. Native model rows, filtering, selection, and actions remain unavailable until the request settles, so a user cannot select a row that later moves underneath the input. +- A successful metadata response groups only native Kilo catalog IDs: Kilo Auto from `kilo-auto/*` or legacy `auto-small`, and Recommended from actual `recommendedIndex` entries. `autoRouting` names targets for an Auto model; it does not make a regular model Auto. +- A metadata failure unlocks the normal native inventory, without fabricating recommendation data, and identifies that fallback in the dialog footer. +- Changing the location/catalog aborts the prior request; an aborted response cannot update the replacement dialog state. + +## Scope and cache decision + +The existing optional host presentation seam also carries a row footer, without +changing model identities or inference settings. Kilo renders source-positive +`BYOK` and `May train` disclosures (the exact v1 CLI labels) alongside native +Free labels. Favorites and Recents retain those disclosures. The real renderer +verifies both labels disappear when the account changes to a team whose catalog +does not report them; missing/false metadata never claims a privacy guarantee. +This uses the native row layout, not a port of the full v1 model-info panel. + +`kilocode.models.list` makes its authenticated selected-account decision in the Gateway but returns only `KiloModels.Entry[]`. The response contains no account, organization, token, catalog revision, or other invalidation identity. A location is not an account identity and must not be used as one. + +Accordingly, this slice deliberately adds **no metadata cache**. Every new picker effect asks the RPC for current metadata and passes the current location. It may be optimized only after the RPC supplies a proven account/catalog invalidation identity. + +## Evidence and remaining gates + +Loopback TUI coverage exercises delayed metadata, grouping, ready no-match rendering, and reopening after a confirmed close with native favorite behavior; the fixture asserts a second fresh metadata RPC on that reopen. A focused picker test confirms abort propagation, failure propagation to the TUI fallback boundary, and that each invocation passes its current location. It does not use an account or inference provider. + +The v1 CLI TUI at the Kilo repo's `origin/main` `ecccd1f` uses Favorites, Recent, and Kilo Recommended (`packages/tui/src/kilocode/model-picker.ts`); it has no Most Used group. VS Code’s separate Most Used section persists a count/timestamp usage map (`packages/kilo-vscode/src/kilo-provider/model-usage.ts`), so it is not a CLI acceptance requirement and native CLI Recents remain the equivalent surface. + +## Routed Auto target + +V1’s `packages/opencode/src/session/llm/ai-sdk.ts` reads the gateway's `finish-step` response model, rather than inferring a target from catalog `autoRouting`. Default/OpenRouter Auto entries use the Kilo-owned `packages/ai/src/kilocode/openrouter-routed.ts` provider. Explicit OpenAI-compatible Auto entries now use the Kilo-owned `packages/ai/src/kilocode/openai-compatible-routed.ts` provider, which reuses the native OpenAI-compatible Chat protocol route/parser seam with the same Kilo-owned routed wrapper: it retains only the actual response model alongside native parser state, adds it to terminal metadata under the same `kilo` provider-metadata namespace, lifts source settings into providerOptions through a supported-option allowlist (the OpenAI-compatible reasoning/generation fields such as `reasoningEffort`), and strips the closed Kilo account key set from the `http.body` overlay exactly like the OpenRouter wrapper. Both wrappers reuse their native request body and OpenAI Chat framing/parser; neither duplicates parsing, tees the response, or invents a target from the request ID. Core already persists the route's metadata namespace as public assistant `providerState`; the sidebar only renders it for the latest settled Kilo Auto assistant. + +The local launched-host fixture uses the normal Kilo host registration (no fixture plugin injection), a fake authenticated Gateway SSE stream with `model: "provider/actual"` on an earlier chunk but not the terminal chunk, and public `message.list`. It proves both provider paths preserve `routedModelID`, requests retain the selected Auto ID rather than the routed ID, and no routed state appears for an ordinary Kilo model. Both native wrappers retain raw usage metadata rather than deleting it to force the retired AISDK extractor's smaller routedModelID-only object shape, so compatible Auto assistants now carry usage fields alongside `routedModelID` in `providerState`. Unit coverage rejects unsafe response/display strings and hides a prior Auto target when a newer ordinary assistant has settled; the retired extractor-composition cases went away with the alias. This is sidebar selector coverage, not a full terminal rendering test. + +The native wrappers use the existing file-URL provider seam, resolved per installation by `import.meta.resolve` from Kilo CLI's explicit workspace AI dependency. Bare dynamic imports otherwise resolve inside `util`, which does not depend on AI. No shared Core provider registry override was added. Source-launched execution is verified; the preview bootstrap build is not proof of a fully distributed interactive package, whose packaging row remains open — the compiled-artifact probe found `import.meta.resolve` file-URL specifiers throw in a compiled headless bundle, so packaging remains a separate tracked gate rather than a current source-mode break. The compatible runtime alias (`@ai-sdk/openai-compatible/kilo`), its SDK-hook metadata extractor, and its provider-option key derivation are removed; compatible Auto is a native route like the OpenRouter Auto wrapper. Neither path has a live deployment canary. + +Because Core's model resolver merges credential metadata into the resolved settings (and, for key credentials, the body overlay) before calling the wrapper's `model()`, the OpenRouter wrapper lifts source settings into OpenRouter `providerOptions` through a supported-option allowlist (`debug`, `models`, `plugins`, `provider`, `reasoning`, `usage`, `web_search_options` — source `user` is deliberately excluded as it collides with profile metadata; explicit `providerOptions` overlays pass through unchanged) and strips the closed Kilo account key set from the `http.body` overlay; the compatible wrapper applies the same isolation with the OpenAI-compatible option allowlist. Unit coverage proves the compiled body and the actual transport wire body both exclude server/organizationID/email/profile/token while preserving routing, reasoning, and BYOK `api_keys` header payloads. + +Both wrappers share one Kilo-owned helper module, `packages/ai/src/kilocode/routed.ts`: the closed account key set and body isolation, the bounded response-model reader, the terminal routedModelID metadata decoration, and the routed chat-protocol wrapper. Only the per-dialect option allowlists, settings shapes, and facades remain per route. Explicit `extraBody` config passes both wrappers through unchanged — provenance reviewed — while only Core's merged body overlay is isolated; because the closed account set includes top-level `name`/`user`, a deliberately configured body `name` or `user` is also stripped, the same documented collision the Gateway hook already records. The launched-host fixture's account sentinels include the live loopback Gateway origin and an exact serialized `server` key, so a server-field regression cannot hide behind an inert placeholder host. + +## Gateway catalog discovery + +The original v2 Gateway transform only rewrote headers and the base URL for models already present in the native catalog; its Kilo-model RPC could label those rows but could not create API-only Auto or catalog models. The Kilo Gateway now fetches the same authenticated personal (`/api/openrouter/models`) or selected-team (`/api/organizations/:id/models`) catalog during its account refresh and adds each supported model through `editor.model.update`. The source record fields follow the Kilo repo's v1 `ecccd1f` `packages/kilo-gateway/src/api/models.ts`: `name` and a positive integer `context_length` are required, malformed optional `autoRouting` is discarded per record, explicit non-tool models are excluded, and context/output limits plus input/output modalities are mapped. Prices must parse as a complete finite numeric string and remain finite after conversion from per-token to dollars per million; overflowing and suffix-bearing values become unknown rather than a branded infinity. Missing architecture gets conservative text-only capabilities rather than the native image-capable default. + +The snapshot is retained only inside one successful refresh and is discarded before every reload; there is no cross-account, cross-server, or cross-organization cache. A failed or empty personal fetch leaves the explicit native baseline available. A failed or empty selected-team fetch disables all native Kilo rows for that scope, instead of leaking personal/static models into the team selection. Existing account selection and credential events already cause the refresh, so the same selected organization header and base URL apply to new entries. + +Gateway remains host-agnostic: the catalog chooses a native-compatible provider package, while the Kilo CLI registers its routed-model plugin after the post-phase Gateway plugin to add Auto response metadata. A direct Gateway test covers API-only Auto creation, field mapping, required-field rejection, malformed-routing tolerance, conservative capabilities, nonfinite-price omission, the exact team request scope, unsupported-tool exclusion, failed-team eviction, and personal fallback. A launched Kilo host test confirms that a configured `providers.kilo.models..disabled: true` remains disabled even when that API-only model is discovered; the model-picker TUI fixture additionally performs a successful personal→team switch, proves team inventory replacement, and checks a fresh cold metadata state before showing team groups. Routed SSE coverage separately proves the enabled dynamic Auto path. + +The Gateway now forwards source `hasUserByokAvailable` and `mayTrainOnYourPrompts` exactly through `KiloModels.Entry`, including explicit `false`; absent source data remains absent, so no privacy or BYOK state is inferred. It also maps v1 `opencode.variants` into the public `Model.Variant` overlays. Source variants are applied first and an existing variant with the same ID wins, matching v1's configured-model precedence and avoiding a dynamic catalog refresh overwriting local configuration. The direct Gateway test covers an API source variant, configuration collision, both disclosure fields, and strict price rejection. The launched Kilo-host fixture selects the API's `high` Auto variant and proves the fake Gateway receives OpenRouter `reasoning.effort: "high"` or compatible `reasoning_effort: "high"`, according to the selected protocol. This is request-path coverage rather than catalog-shape-only coverage. + +API-derived `name`/`limit`/`cost`/`capabilities` enrichment follows the v1 config fold: explicitly configured fields win, and every other model (API-only, native-baseline default, or static) takes the current account's API values. The plugin Context cannot reach the location-scoped Config service, so the embedding host injects a `configEntries` reader (`GatewayOptions.configEntries`, resolved through `LocationServiceMap`) that the Gateway folds — lowest to highest priority — into an explicit-field map at setup and on every `config.updated` (name, whole cost/capabilities, and each `limit` subfield independently, mirroring the host fold's shallow limit merge). The transform runs after ConfigProvider on each rebuild, so preservation needs no stale cross-rebuild memory and account refresh replaces non-explicit values deterministically. Direct coverage: API-only/default enrichment, configured-field precedence, account refresh without stale retention, and config-update re-reads. The hook ordering also keeps security routing (baseURL, organization headers, activation) always applied. + +`opencode.prompt` remains unsupported. The [prompt audit](model-prompt-v2-audit.md) +corrects the earlier blanket claim that v2 has no usable prompt seam: a context +hook exists, but several historical prompt assets assume nonexistent tools or +conflict with v2 delegation rules. Tolerantly dropping the selector preserves +native v2 guidance until those assets and precedence are properly adapted. + +## Catalog-selected protocols + +The source audit found that the cloud repo's dev-line HEAD `c32be17d` and the cloud repo's `origin/main` at `08c4887fa68738f19089284101084f404eb6c9b8` (the Kilo repo's own `origin/main` is the unrelated `ecccd1f`; these are different repositories and the SHAs are not interchangeable) +catch-all route support Chat Completions, Responses and Messages through the +Gateway alias. This is source evidence, **not deployed endpoint verification**. +The catalog's closed `opencode.ai_sdk_provider` enum now selects the matching +native v2 provider, without model-name heuristics or another SDK dependency: + +| Source tag | Native compatibility package | Gateway suffix | +|---|---|---| +| `anthropic` | `aisdk:@ai-sdk/anthropic` | `/messages` | +| `openai` | `aisdk:@ai-sdk/openai` | `/responses` | +| `openai-compatible` | `aisdk:@ai-sdk/openai-compatible` | `/chat/completions` | +| `openrouter`, absent or malformed | `aisdk:@openrouter/ai-sdk-provider` | `/chat/completions` | + +An explicit configured model package still wins over this catalog-derived +choice, matching v1 config precedence; only an otherwise-unpackaged catalog +model receives the source-selected native package. The Kilo Auto wrapper then +recognizes the native OpenRouter package without changing explicit protocol +choices. + +Four source-backed request deltas live in the existing public HTTP request +hook, scoped to Kilo and the loaded Gateway server's exact POST endpoints. +Anthropic key authentication promotes the SDK's existing `x-api-key` to the +Bearer header expected by Kilo, winning over any configured or foreign +`Authorization` header exactly like v1's unconditional Bearer override; an +existing OAuth Bearer without `x-api-key` is retained. Gateway request bodies +(`/messages`, `/chat/completions`, `/responses`) are stripped of the closed +reserved Kilo account/credential key set at the top level only, because Core's +model resolver merges credential metadata into model settings and (for key +credentials) the request-body overlay before any adapter serializes a request. +Nested provider routing (`provider`, `models`), BYOK payloads, reasoning, and +arbitrary user content are preserved; stripping reserved top-level `user`/`name` +is a documented collision — neither is a supported Kilo Gateway dialect field. +Stateless Responses requests additionally drop `item_reference` entries and +provider item IDs, matching v1 `kilo-gateway/src/responses.ts`, while retaining +encrypted reasoning; `store:true` keeps item references but is not a +credential-hygiene exemption. Malformed JSON and foreign endpoints are not +rewritten. No credential is added to a catalog, model settings, or RPC response. + +The fourth delta closes the v1 request-policy gap: when the effective +`hide_prompt_training_models` setting is an explicit `true`, the hook merges +`provider.data_collection: "deny"` into every serialized Gateway request body +on all three endpoints — the same wire field v1's `createKilo` wrapper applied +through `transformRequestBody` (v1 `ecccd1f` `kilo-gateway/src/responses.ts`), +which v1 derived in `patchKiloProviderPrivacy` and the `kilo` custom loader. +The merge keeps an existing record `provider` (routing fields preserved), +replaces a missing or non-record one with exactly the marker, leaves an +already-applied body byte-identical, and never rewrites a body that does not +decode as a JSON record — an unexpressible policy is never silently promised. +The host bridges the effective value through +`GatewayOptions.dataCollectionPolicy` (`kilo-cli/src/request-policy.ts`), +resolved per location by the real settings store fold and the picker's own +project-over-profile resolution, so false, unset, or invalid values request +nothing; a stored `null` is invalid (not absent) and an invalid scope falls +back to a valid value in the other scope without inventing a reset, and a +document the host ignores natively (for example a missing `{file:...}` +reference) still supplies its raw Kilo-only value, exactly as the settings +dialog reports it. The plugin caches the resolved policy per location and re-reads it on +startup, every account refresh (credential events, `organization.set`), and +`config.updated`; a failed read keeps the last known value, and the in-memory +cell means no per-request settings I/O or external metadata fetch. This +freshness contract differs from the picker's on purpose: the picker reads the +same setting live per dialog open, while the request policy follows the host's +loaded-config lifecycle — the preview's config file watcher is off and the +settings dialog already reports `restartRequired`, so an edit reaches the wire +no earlier than the next account/config refresh and no later than restart. +Direct plugin coverage proves all three dialects, BYOK/reasoning preservation, +the allow-override, refresh, and keep-last-known. The launched-host protocol +fixture proves the marker's absence while the setting is unset across all four +source-selected protocols, then opts the profile in and proves the +account-switch refresh carries the marker to the wire; the routed-model +fixture asserts the deny marker on every wire body — title, both native Auto +routes, and the ordinary model — alongside the preserved `reasoning`/ +`reasoning_effort` variant payloads. + +Resolved boundary: an independent source review corrected the earlier claim +that title generation bypasses the hook middleware — `session/title.ts` runs +through `SessionContext.prepare`, which is `SessionModelRequest.prepare`, so +its `StreamOptions.http` middleware applies the hook there too. The only real +bypass was the compatible Auto alias `@ai-sdk/openai-compatible/kilo`, kept on +the AISDK adapter for its routedModelID metadata extractor; AISDK-adapter +transports (`packages/core/src/aisdk.ts` `modelFromLanguage`) never apply +`StreamOptions.http`. That alias is retired: compatible Auto models resolve to +the Kilo-owned native `packages/ai/src/kilocode/openai-compatible-routed.ts` +route, which isolates credential metadata at the provider-package boundary and +transports through the same hook middleware as every other native route. The +launched-host gate `routed-model-title-leak.test.ts` is converted from the +known-failing bypass measurement into a positive all-wire regression: the +fixture requires a title request, both Auto requests, and the ordinary request +to be present, and asserts every wire body excludes the reserved account +fields and the credential itself. Startup and fixture-marker failures fail the +test normally. + +The normal execution-host fixture (`test/gateway-protocol.test.ts`) runs two +requests through each source-selected protocol with deliberately opaque model +IDs, checks settled assistant history and Bearer/team headers, and verifies +encrypted reasoning survives the stateless Responses replay with item IDs +removed. It then switches to personal scope where the same model ID has a +different protocol, proving both its transport and organization header refresh. +All endpoints are loopback fixtures; no account or paid inference is used. +The direct Gateway suite covers this batch with its execution-host protocol +integration coverage kept separate; the launched routed-model host additionally +imports a hostile-metadata key credential and asserts every wire body — +title, both Auto paths, and ordinary — excludes account fields and the +credential itself. diff --git a/migration-tracking/technical-notes/baseline/model-prompt-policy-v2-parity.md b/migration-tracking/technical-notes/baseline/model-prompt-policy-v2-parity.md new file mode 100644 index 000000000000..664aa492fdd4 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/model-prompt-policy-v2-parity.md @@ -0,0 +1,180 @@ +# Gateway model-prompt policy v2 parity + +Prompt-selector policy parity for the catalog `opencode.prompt` closed enum. Read against Kilo +`origin/main` `ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`, the audit in +[model-prompt-v2-audit](model-prompt-v2-audit.md), and the v2 source at `61a8707c03`. + +## What landed + +- `packages/kilo-cli/src/model-prompt-policy.ts` (Kilo-owned). A post-phase + `ctx.session.hook("context")` plugin gated to the `kilo` provider. For all eight catalog closed-enum + tags (`anthropic`, `trinity`, `anthropic_without_todo`, `codex`, `gemini`, `ling`, `gpt55`, `beast`) + it replaces `event.system[0]` with the appropriate maintained, verified, or adapted v2 asset while + preserving native composition (instruction/project/history parts and native OpenAI appends). For + unknown tags and Object prototype names, it makes no override and leaves the native heuristic or + default baseline in place. +- `packages/kilo-cli/test/model-prompt-policy.test.ts` + `model-prompt-policy-fixture.ts` (Kilo-owned). + Direct unit tests for `promptFor` and a real launched interactive-server against a loopback + `Bun.serve` Gateway under bundled Bun 1.4.0, asserting on captured wire request bodies. +- `packages/kilo-gateway/test/prompt-selector.test.ts` (Kilo-owned). Direct Gateway tests driving + `registerGateway` with multiple Locations across all 8 closed-enum selectors. + +## Selector dispositions (all eight catalog enum values) + +| Catalog selector | Disposition | Justification & implementation | +|---|---|---| +| `anthropic` | Maintained v2 asset | Reuses `SystemPromptPlugin.AnthropicPrompt` via public export. | +| `trinity` | Maintained v2 asset | Reuses `SystemPromptPlugin.TrinityPrompt` via public export. | +| `anthropic_without_todo` | Verified v2 equivalent | In v1, this tag mapped to `default.txt` (the baseline prompt) rather than `anthropic.txt`. On v2, it restores the native v2 baseline via `SessionSystemPrompt.make(tools)` with actual tool guidance, overriding conflicting `claude` heuristics without legacy tool names. | +| `codex` | Adapted v2 asset | Source-backed port of v1 `codex.txt` (`origin/main` `ecccd1f`), adapting actual v2 tool names (`read`, `edit`, `write`, `glob`, `grep`, `shell`), removing `apply_patch` (replaced with `edit`/`write`), and removing `Bash`. Composes cleanly with native `OpenAIPlugin` (`gpt-extension.txt`) append at `system[1]`. | +| `gemini` | Adapted v2 asset | Source-backed port of v1 `gemini.txt`, adapting background shell execution to the v2 `background: boolean` parameter on `shell` (replacing `&` in command strings), adapting file-path arguments to the `path` parameter, replacing `Bash` with `shell`, replacing guaranteed confirmation-dialogue claims with respecting actual configured permission rules and user refusals, and preserving all core engineering mandates and workflows. | +| `ling` | Adapted v2 asset | Source-backed port of v1 `ling.txt`, omitting the non-existent shell `description` schema parameter requirement, adapting long-running processes to `background: true` on `shell`, replacing `Bash` with `shell`, and preserving all objectivity, edit stop-signal, and brevity rules. | +| `gpt55` | Adapted v2 asset | Source-backed port of v1 `kilocode-gpt-5.5.txt`, adapting tool names to v2 (`read`, `edit`, `write`, `glob`, `grep`, `shell`), removing `apply_patch`, and reconciling subagent delegation with native v2 delegation rules (replaces proactive Task delegation with adhering to configured delegation rules and avoiding proactive subagent spawning). Composes with native `OpenAIPlugin` (`gpt-extension.txt`) append. | +| `beast` | Adapted v2 asset | Source-backed port of v1 `beast.txt`, preserving autonomous iteration, thorough reflection, and rigorous verification, while replacing legacy Task/Todo assumptions and mandatory recursive web crawling with native v2 tools (`glob`, `grep`, `edit`, `write`, `shell`) and omitting mandatory recursive web crawling and hardcoded `.github/instructions/memory.instruction.md` paths. | + +## Selector provenance — typed reader, not family + +The catalog tag is not read from `Model.Info.family` or any catalog field. `family` is not +`opencode.prompt` provenance, and no implicit family→selector mapping or Gateway family +overwrite is used anywhere. The plugin takes a typed reader the host injects: + +```ts +export type ModelPromptSelector = (location: Location.Ref, model: Model.Ref) => string | undefined + +createModelPromptPolicy({ selector }) +``` + +`selector` is called per context event with the plugin's own activation `Location` (mapped the +same way as the Gateway's `configuredLocation`: directory plus optional workspace identity) and +the event's model reference, and returns the current `opencode.prompt` tag for that Location, or +`undefined` when the Location's Gateway cache carries none. The plugin does no fetch, no cache, +and no `model.settings` writes; the reader is the only source of the tag. Only verified or adapted +selectors map to an asset (`promptFor`); unknown values resolve to no override. + +## Location-scoped metadata handoff (implemented) + +`createGatewayPlugin` activates per Location and each activation owns its own account-scoped +catalog cache (`loaded.current.models`), so a single host variable overwritten by the last setup +would be wrong. The handoff is a host-owned, Location-keyed reader registry: + +- `packages/kilo-gateway/src/models.ts` — `CatalogModel.prompt?: GatewayPrompt`, decoded + tolerantly from the already-parsed `opencode.prompt` with a closed v1-enum schema + (`Schema.Literals` of the pinned eight values, `decodeUnknownOption` per record). A malformed + or out-of-enum value decodes to no tag and the model is kept. The tag is never written into + model family, settings, or credentials. +- `packages/kilo-gateway/src/gateway.ts` — `GatewayOptions.promptSelector?: { register(location, + read): Effect }`. +- `packages/kilo-gateway/src/plugin.ts` — `registerGateway` calls `register` once per activation + with `configuredLocation(ctx.location)` and a reader that reads `loaded.current?.models` + directly (no duplicated selector map or second cache). The reader reports no selector whenever + the cache is absent (failed or revoked scope), matching the catalog transform. Registration is + yielded inside the plugin scope so the host's cleanup runs on Location teardown. +- `packages/kilo-cli/src/interactive-server.ts` (root-owned) — a per-launch `Map` keyed by + `JSON.stringify([location.directory, location.workspaceID ?? null])`; the `register` callback + sets the reader and adds a scope finalizer that removes it only when it is still the registered + reader (an obsolete scope cannot remove a successor). The policy is registered post-phase after + the Gateway with `selector: (location, model) => readers.get(key(location))?.(model.id)`. + +## Shared-file exception (root-approved Option A) + +The maintained Anthropic/Trinity v2 assets were not publicly resolvable from `kilo-cli` (the +core package map is `"./*" -> "./src/*.ts"` and the `.txt` files are only imported relatively +inside core). Root approved a narrow marked exception: two added public exports of the existing +assets in the shared file `packages/core/src/plugin/system-prompt.ts`: + +- `packages/core/src/plugin/system-prompt.ts:38-42` — a `kilocode_change` comment plus + `export const AnthropicPrompt = PROMPT_ANTHROPIC` (line 41) and + `export const TrinityPrompt = PROMPT_TRINITY` (line 42). + +No text was copied, no package `exports` map was edited, and the native +`SystemPromptPlugin.Plugins` behavior is unchanged. The policy imports them via +`SystemPromptPlugin.AnthropicPrompt` / `SystemPromptPlugin.TrinityPrompt`, so a future upstream +edit to the assets propagates without a kilo-cli change. + +## Composition guarantees (tested) + +The native v2 composition is preserved, not cleared: + +- Custom `agent.system` always wins: a truthy custom agent system skips the override entirely, + mirroring the native replace plugins (`ctx.agent.get(...).data.system` guard). +- Only the base part `event.system[0]` is replaced; instruction, project, history, and native + supplementary parts (e.g. the environment block, baseline tool-guidance, and native OpenAI appends) + survive into the request. +- Native heuristic plugins still run first (they are internal `pre` builtins; this plugin is a + post-phase SDK registration), so an untagged model keeps the native heuristic or + native-default result, and an explicit tag wins over a conflicting model-name heuristic. +- Models with `codex` tag and a name matching the `gpt` heuristic receive both the adapted Codex + base prompt at `system[0]` and the native `OpenAIPlugin` append (`gpt-extension.txt`) at `system[1]`. +- Models with `anthropic_without_todo` tag and a name matching the `claude` heuristic override + the native Anthropic heuristic back to the native baseline with tool-dependent guidance. + +## Test coverage + +**Launch acceptance** — `packages/kilo-cli/test/model-prompt-policy.test.ts` + +`model-prompt-policy-fixture.ts` run under the bundled `dist/interactive/bun` 1.4.0. The fixture +launches the real interactive-server with the production `promptSelector` wiring (no test-only +registry) against a loopback `Bun.serve` Gateway that serves `opencode.prompt` tags in its +catalog records, and asserts on captured wire request bodies: + +| Case | Model id | Catalog tag | Expected base / composition | +|---|---|---|---| +| Tag beats no heuristic | `tagged-anthropic` | `anthropic` | Anthropic asset | +| Trinity tag | `tagged-trinity` | `trinity` | Trinity asset | +| Tag beats conflicting name heuristic | `conflict-claude-trinity` | `trinity` | Trinity asset, not claude/Anthropic | +| anthropic_without_todo beats claude heuristic | `tagged-without-todo-claude` | `anthropic_without_todo` | Native baseline prompt with tool guidance, not Anthropic asset | +| Codex adapted prompt | `tagged-codex` | `codex` | Adapted Codex asset (v2 tool names, no apply_patch, no Bash) | +| Codex + GPT composition | `tagged-codex-gpt` | `codex` | Adapted Codex base + native `OpenAIPlugin` gpt-extension append | +| Gemini adapted prompt | `tagged-gemini` | `gemini` | Adapted Gemini asset (v2 tool schemas, background: true, path parameter) | +| Ling adapted prompt | `tagged-ling` | `ling` | Adapted Ling asset (omits shell description requirement, background: true) | +| Gpt55 adapted prompt | `tagged-gpt55` | `gpt55` | Adapted GPT-5.5 asset + native `OpenAIPlugin` gpt-extension append | +| Beast adapted prompt | `tagged-beast` | `beast` | Adapted Beast asset (v2 tools, deep autonomy, no mandatory web crawling) | +| Unknown/out-of-enum tag | `tagged-unknown` | `unknown-selector` | Native default baseline | +| Untagged keeps native heuristic | `heuristic-claude-opus` | none | Native Anthropic (claude) heuristic | +| Custom agent wins | `tagged-anthropic` + `ask` agent | `anthropic` | Custom `ask` system | +| Preservation | `tagged-anthropic` | `anthropic` | Environment block + tool-guidance retained | +| Account/location refresh | `tagged-anthropic`, team→personal | `anthropic`→`trinity` | Override follows personal tag; team header dropped | +| Account/location refresh (without_todo) | `tagged-without-todo-claude`, team→personal | `without_todo`→`anthropic` | Override follows personal tag to Anthropic asset | +| Account/location refresh (codex) | `tagged-codex`, team→personal | `codex`→`without_todo` | Override follows personal tag to native baseline | +| Account/location refresh (gemini) | `tagged-gemini`, team→personal | `gemini`→`ling` | Override follows personal tag to Ling asset | +| Account/location refresh (ling) | `tagged-ling`, team→personal | `ling`→`gemini` | Override follows personal tag to Gemini asset | +| Account/location refresh (gpt55) | `tagged-gpt55`, team→personal | `gpt55`→`beast` | Override follows personal tag to Beast asset | +| Account/location refresh (beast) | `tagged-beast`, team→personal | `beast`→`gpt55` | Override follows personal tag to GPT-5.5 asset | + +**Direct Gateway tests** — `packages/kilo-gateway/test/prompt-selector.test.ts` drives the real +`registerGateway` with two simultaneous Locations and a host registry mirroring the production +wiring (JSON-tuple key, exact-reader scoped cleanup). It covers: independent per-Location readers +with no last-setup overwrite; live-cache reads across all 8 closed-enum tags (`anthropic`, `trinity`, +`anthropic_without_todo`, `codex`, `gpt55`, `gemini`, `ling`, `beast`); out-of-enum and non-string +tags decoding to no selector without dropping the model; the tag never reaching the materialized +record's family/settings/headers; no per-request fetch (models endpoint hit only on refresh); and +scoped disposal (closing one Location's scope removes only its reader and cannot clobber a sibling or +successor). A second test covers a failed/revoked account cache clearing the selector while the +Location reader stays registered. + +## Validation + +- Bundled Bun 1.4.0 launch acceptance (`packages/kilo-cli/test/model-prompt-policy.test.ts`): 2 passed (61 assertions). +- Direct Gateway tests (`packages/kilo-gateway/test/prompt-selector.test.ts`): 2 passed (38 assertions). +- Full Gateway test suite (`packages/kilo-gateway/test/`): 65 passed across 6 files (697 assertions). +- Typecheck: clean for `packages/kilo-gateway` (`tsgo --noEmit`) and `packages/core` (`tsgo -b tsconfig.json tsconfig.tests.json`). +- Kilo-cli prompt policy files (`src/model-prompt-policy.ts`, `test/model-prompt-policy.test.ts`, `test/model-prompt-policy-fixture.ts`) have zero type errors. + +## Whole-row remaining gates + +The Gateway prompt-policy capability acceptance is complete across all eight closed-enum selectors: +- 2 maintained v2 assets (`anthropic`, `trinity`) +- 1 verified v2 equivalent (`anthropic_without_todo` → native baseline with tool guidance) +- 5 source-backed adapted assets (`codex`, `gemini`, `ling`, `gpt55`, `beast` → adapted to actual v2 tool schemas) +- 0 unported selectors remaining. + +Remaining whole-row gates for Row 5 (Gateway catalog, BYOK, org routing): +- The Auto acceptance item from the canonical plan ("verify eligible catalogs and unavailable selections across real scopes, and show a routed model only when response metadata actually supplies it") has local loopback fixture verification, but product-level deployed-account verification remains an open question/interpretation. +- Whole-row closure remains subject to root acceptance; no fabricated completion count or premature whole-row closure is claimed. + +### Constraint preservation — final parent review + +The Beast adaptation explicitly respects user constraints (including local-only +work), cancellation, denied tools and unavailable capabilities. Persistence is +conditional on those boundaries; blocked work is disclosed rather than falsely +claimed complete. URL retrieval requires permitted network access. Direct and +real loopback wire assertions pass: 2 tests / 62 assertions. diff --git a/migration-tracking/technical-notes/baseline/model-prompt-v2-audit.md b/migration-tracking/technical-notes/baseline/model-prompt-v2-audit.md new file mode 100644 index 000000000000..20936a0115ec --- /dev/null +++ b/migration-tracking/technical-notes/baseline/model-prompt-v2-audit.md @@ -0,0 +1,56 @@ +# Gateway model-prompt audit — 2026-09-05 + +Read-only comparison of Kilo `origin/main` at `ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7` +and the v2 source inherited from `59b29de4` (port checkpoint `82040801cd`). +No live catalog or model call was used. No historical prompt assets were installed. + +## Existing extension and precedence + +V1 `packages/opencode/src/session/system.ts:49-75` selects a prompt from the +catalog's closed `opencode.prompt` enum before model-name heuristics. Its LLM +request uses a custom agent prompt instead when one is present. + +V2 already has a real mutable `ctx.session.hook("context")` system-prompt seam. +`packages/core/src/session/model-request.ts` constructs the agent or native base +prompt before this hook. Native `packages/core/src/plugin/system-prompt.ts` +replacements skip truthy custom agent systems. A Kilo post hook could likewise +replace the base part while preserving instruction/history parts and custom +agents; a new shared Core hook is not required. The catalog's policy tag would +need an in-process, location/account-scoped handoff, not model settings or a +second authenticated fetch on every prompt. + +That seam does **not** make historical prompt contents compatible. Native GPT +adds a separate system part; replacing only the base leaves its delegation +instructions intact. Any port must test this composition, not clear the entire +system array and accidentally discard project instructions. + +## Asset compatibility + +| Catalog selector | Source finding | Safe next step | +|---|---|---| +| `anthropic` | Historical prompt names TodoWrite, Task, Bash and WebFetch. | Reuse the maintained v2 Anthropic asset, which already uses v2 tools. | +| `trinity` | Historical prompt names Bash, Task and an `ls` tool. | Reuse the maintained v2 Trinity asset. | +| `anthropic_without_todo` | Historical `default.txt` still names nonexistent Bash/Task/ls tools. | Use the native v2 baseline, including its tool-dependent guidance. | +| `codex` | Mostly reusable guidance, but references `apply_patch` and title-case tool names. | Port only with explicit tool-name corrections and real request tests. | +| `gpt55` | Same tool vocabulary problems, plus proactive Task delegation conflicts with native GPT's delegation restriction. | Resolve against v2 delegation semantics; do not silently combine conflicting prompts. | +| `gemini` | Incorrect tool/argument names, multiple-path assumptions, and legacy background execution guidance. | Requires a separately reviewed v2 adaptation. | +| `ling` | Requires a nonexistent shell `description` argument and legacy tool/lifecycle behavior. | Requires a separately reviewed v2 adaptation. | +| `beast` | Assumes unavailable durable Todo/memory/sequential-thinking tools and mandatory recursive web crawling. | Do not install unchanged; requires an explicit v2 design. | + +Actual v2 tool schemas, not substitutions inferred from display labels, are the +authority: for example `shell` accepts `command`, `workdir`, `timeout` and +`background`; native delegation is `subagent`. Historical `gpt.txt` is not one +of the catalog enum selections and must not be invented as another selector. + +## Resolution and v2 adaptations (2026-09-07) + +All eight catalog `opencode.prompt` closed-enum selectors now have resolved, source-backed v2 implementations: +- `anthropic` and `trinity`: reuse maintained v2 assets via public core exports (`SystemPromptPlugin.AnthropicPrompt`, `SystemPromptPlugin.TrinityPrompt`). +- `anthropic_without_todo`: verified v2 equivalent using `SessionSystemPrompt.make(tools)` with actual tool guidance, overriding conflicting `claude` heuristics without legacy tool names. +- `codex`: source-backed adaptation of v1 `codex.txt`, adapting tool names to lowercase v2 tools (`read`, `edit`, `write`, `glob`, `grep`, `shell`), replacing `apply_patch` with `edit`/`write`, and preserving scannable CLI formatting. +- `gemini`: source-backed adaptation of v1 `gemini.txt`, adapting background shell execution to `background: true` on `shell`, adapting file paths to the `path` parameter, replacing `Bash` with `shell`, and replacing guaranteed confirmation-dialog claims with respecting configured permission rules and user refusals. +- `ling`: source-backed adaptation of v1 `ling.txt`, omitting the non-existent shell `description` schema parameter requirement, adapting long-running processes to `background: true`, and preserving stop signals and objectivity. +- `gpt55`: source-backed adaptation of v1 `kilocode-gpt-5.5.txt`, adapting tool names to v2 (`read`, `edit`, `write`, `glob`, `grep`, `shell`), removing `apply_patch`, and reconciling subagent delegation with native v2 delegation rules (no proactive subagent spawning). +- `beast`: source-backed adaptation of v1 `beast.txt`, preserving autonomous iteration and rigorous verification while replacing unavailable web/Task/Todo tool assumptions with native v2 tools. + +Provider transport support is tracked separately: fixing `ai_sdk_provider` does not earn prompt-policy parity. diff --git a/migration-tracking/technical-notes/baseline/model-sidebar-v2-parity.md b/migration-tracking/technical-notes/baseline/model-sidebar-v2-parity.md new file mode 100644 index 000000000000..2d1b7e601000 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/model-sidebar-v2-parity.md @@ -0,0 +1,73 @@ +# Model, agent and sidebar batch — 2026-09-05 + +**Superseded memory completion claim:** the subsequent user smoke test still +found duplicate memory entries and latest-main UI/feature gaps. See the +[memory correction](memory-v2-parity.md#reopened-after-user-smoke-test--2026-09-05). +The prior duplicate test below was too narrow; current inventory is 24/43, +not this batch's historical 25/43 checkpoint. + +Source: local v1 `origin/main` at `ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`; +current v2 `59b29de40966803e2c7cd734d439843fb773f6a6` plus local Kilo changes. +This is source-backed and local-workflow-tested, not live-contract-verified. +Tests use isolated profiles, loopback services and no paid inference. + +| Surface | Implementation / evidence | Still open | +|---|---|---| +| Gateway priority | Optional `TuiApp.modelPicker` presentation hook; Kilo supplies `preferredProviderID: kilo`. Native picker/actions/storage stay upstream-owned. Native default remains OpenCode without the option. | No automatic model/default change is intended. | +| Recommended | `kilocode.models.list` resolves the authenticated selected account; personal `/api/openrouter/models`, team `/api/organizations/:id/models`; actual `preferredIndex` becomes recommendation order. Only IDs already in native inventory are grouped. | Most Used is not Recents; no usage-history clone or live endpoint verification. | +| Kilo Auto | Available `kilo-auto/*` IDs and actual `autoRouting` metadata produce the Auto group. The current native snapshot already has Auto IDs. Missing metadata falls back without fabricated recommendations. | This is not a dynamic catalog replacement, proof of every eligible model, or routed-model transcript metadata parity. | +| Favorites | Real TUI test toggles a native favorite and reopens the picker to prove retention. No favorites are seeded or v1 store imported. | Cross-version preference migration is not part of this batch. | +| Built-in agents | Production post-plugin names native `build` Code and supplies absent Ask/Debug policies; custom IDs and native hidden/delegated agents survive. Real native permission checks and loopback denied-tool execution. | Native Plan is retained; Kilo custom saved-plan/exit/implementation lifecycle remains open. See [agent policy](agent-policy-v2-parity.md). | +| Duplicate `/memory` | Native autocomplete now matches dispatch precedence: local command names/aliases shadow same-named server commands and skills. Real TUI asserts one row and immediate local handling; public command inventory still includes memory. | No server API removal or separate interactive-only host flag. | +| Memory sidebar | Native `sidebar.content` slot, active session location, loading/unavailable/disabled/enabled, automatic-consolidation status and estimated index tokens. Local command mutations and native execution completion refresh status. No implicit enable or source-text rendering. | No invented busy/activity indicator; arbitrary external store writes have no dedicated event contract. | +| Credits / Pass | Native `sidebar.footer`; validated selected-account balance, personal Pass only, real zero distinct from unavailable. Privacy masks credit amounts and suppresses the entire Pass section. Account/session changes discard stale responses. | Source response schema is fixture-tested, not deployed verification. No purchase/account changes. | +| Native sidebar | Existing context/spend, MCP and location/branch views remain untouched. Existing auto-hide threshold is preserved (>120 available columns). | Indexing, jobs, PR and detailed usage sections remain separate inventory work. | + +## Boundaries and refresh + +Model metadata uses the existing authenticated Gateway transport and account +selection lock. No account bearer enters presentation metadata. Picker effects +abort old requests when catalog/location changes; failed metadata leaves the +native inventory usable. Sorting never changes provider/model identities. + +The bounded shared changes are in native runtime/model-picker presentation, +autocomplete de-duplication, and agent-name rendering in the picker/composer. +Each is marked `kilocode_change`; Kilo policy, transport, metadata and panels +remain in Kilo-owned modules. No Core execution engine or sidebar layout is +forked. Agent selection still uses IDs (including `build`), not display names. + +Balance uses `/api/profile/balance`, with the selected organization header only +for a team. Personal Pass uses the source-backed batched +`/api/trpc/kiloPass.getState` envelope. Independent failures return absent data, +not a zero balance. The account view clears stale data on refresh and account +changes, refreshes after the selected session's successful execution, and polls +teams every 60 seconds. Disposing a view cancels its requests/subscriptions. + +## Verification targets + +Final bundled Bun 1.4.0 checkpoint: `kilo-cli` full `script/test.ts` **373 pass, +0 fail, 2,413 assertions, 64 files** (324.52s); `kilo-gateway` full suite +**38 pass, 0 fail, 284 assertions**. Focused native TUI model ordering, +preferences and autocomplete suites: **23 pass, 0 fail**. Typechecks pass in +Kilo CLI, Gateway, Client, Schema and TUI. Targeted formatting and +`git diff --check` pass. Initial broad-run renderer races were fixed with +test-only frame/input synchronization before the final green rerun. + +- `kilo-cli`: `model-picker.test.ts`, `model-picker-ui.test.tsx`, + `agent-policy.test.ts`, `sidebar-memory-ui.test.tsx`, `memory-ui.test.tsx`, + `sidebar-account.test.tsx`, `sidebar-account-ui.test.tsx`, and existing + `gateway-integration.test.ts`. +- `kilo-gateway`: account and plugin tests cover independent failures, zero, + team scoping and authenticated model metadata. +- `tui`: `test/kilocode/model-presentation.test.tsx` preserves upstream ordering + without the optional host policy. + +Real TUI fixtures prove both sidebar panels at 160 columns and privacy changes +through public RPC. Account visual coverage is specifically personal zero and +privacy suppression; team scoping and independent failures are transport/RPC +tests, not a completed team-switch sidebar visual test. A bounded attempt at +that extra dialog fixture did not produce a team refresh and was not retained; +no production correctness claim is drawn from that inconclusive attempt. +Narrow terminal/scrolling and all remaining source-specific sections are not +claimed complete. This batch's historical coarse checkpoint was **25/43 (58.1%)**; +the subsequent memory correction above supersedes it. diff --git a/migration-tracking/technical-notes/baseline/non-ide-closure-checkpoint.md b/migration-tracking/technical-notes/baseline/non-ide-closure-checkpoint.md new file mode 100644 index 000000000000..efc16a970ed5 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/non-ide-closure-checkpoint.md @@ -0,0 +1,387 @@ +# Non-IDE closure checkpoint — 2026-09-06 + +## Goal 70% accepted checkpoint — 2026-09-07 + +The canonical plan accepts **31/43 (72.1%)** after completing safe config import, +Gateway prompt/catalog/routing, sandbox spawn policy and CLI/TUI acceptance. +Source-backed refusals do not count as implemented legacy features. Gateway +acceptance is local (72 tests / 764 assertions); terminal creation is refused +through both public paths (5 native PTY tests plus 20 shell/MCP regressions). +CLI/TUI uses the documented automated equivalents and v2 layout decisions; +manual scenario statuses remain NOT RUN. See +[CLI/TUI evidence](cli-tui-closure-verification.md) and the canonical plan for +final build `build-AbricZ`, loopback smoke and exact remaining boundaries. +The older checkpoints below remain historical; their stale open-slice counts +must not override this current acceptance. External-gated sharing, remote and +production distribution remain open. + +## Goal 62% superseding checkpoint — 2026-09-07 + +The canonical plan now accepts **27/43 (62.8%)**, closing the Settings scopes +row after its consumer/control comparison, real enabled-project TUI +edit/reset/restart acceptance, focused contracts and independent review. See +[settings parity](settings-v2-parity.md). The config-import row remains open; +refusals and non-identical presentation mappings receive no import credit. +The earlier “no whole row meets acceptance” conclusion below is historical. +The MCP connection-scoped hook is also implemented and accepted as a slice, +with its narrow shared exception approved; the aggregate sandbox row remains +started because PTY/git coverage is not complete. + +## Superseding implementation checkpoint — 2026-09-07 + +This audit's per-row descriptions below are historical, not a fresh list of +missing code. Since it was written, R3 status/authoritative queue translation +and root-ID cancellation have been accepted (root: 23 tests / 317 assertions, +clean typecheck/formatting). Both portable PTY paths and real launcher startup +are accepted on macOS arm64, and the Gateway local real-scopes criterion is +accepted. None alone closes a whole capability row. The canonical plan owns +the current acceptance record and pending deployment/product requirements. +CLI/TUI closure verification is the next bounded task; existing manual +scenarios remain NOT RUN unless separately exercised and recorded as such. + +Read-only checkpoint audit. Provenance: this checkout at `61a8707c03` plus the +frozen accepted batch (portable launcher, persistent-PTY daemon, remote R1/R2, +R4, request policy, prompt selectors, Explore policy, PR/process sidebars, +authored policy import); Kilo v1 source `origin/main` +`ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`; canonical plan +`plans/kilo-opencode-v2-issue-13750.md` (local working copy). Evidence classes +are separated: accepted focused slices, the pre-final-R4 full-suite snapshot +(**501 tests / 2 skipped / 0 failures / 3,130 assertions / 86 files**), and +NOT-RUN manual scenarios. Accepted coverage stays **26/43**; nothing here +changes the count, marks a refusal as capability, or proposes a new upstream +exception. The approved shared-patch count is 7 (root-owned record); the +narrow Core MCP hook exception is **requested, not approved**. + +## Does any whole row already meet canonical acceptance? + +**No.** All eight rows below were re-checked against their canonical +acceptance text and current ledgers; each retains at least one still-required +capability or gate. The two closest rows are `kilo.jsonc` key mapping (every +Kilo-only key now has a source-backed disposition plus tests — closure is +root's acceptance of that disposition set) and Settings scopes (scope model +and field surface done; remaining work is the recorded field comparison plus +any still-required implementation). No row is claimed closed. + +## Row 3 — Sandbox PTY / MCP / git spawn policy (`not-started`) + +Accepted evidence: shell-hook OS confinement (macOS + Linux Bubblewrap, +[kilocode/baseline/sandbox-linux-validation.md](sandbox-linux-validation.md)); +the MCP argv launcher and rewrite exist in Kilo-owned +`packages/kilo-cli/src/sandbox-mcp.ts` with 15 passing tests and two +deliberately skipped gated scenarios +([sandbox-spawn-next-slice](sandbox-spawn-next-slice.md) §5); production +registration is **withdrawn**. + +Smallest still-required: (a) a fail-closed MCP activation gate, then +re-registration and the two gated host scenarios re-enabled and passing; +(b) an explicit PTY disposition — v1 refused to enable the sandbox while +interactive terminals were live (`ecccd1f` +`packages/opencode/src/kilocode/sandbox/activation.ts:66-118`); v2 has no PTY +seam at all (`packages/core/src/pty.ts:164-182` spawns through `#pty` +directly); (c) root acceptance of the git disposition (v1's per-command git +classification is superseded by v2's whole-shell confinement for +model-initiated git — ledger §6). + +Proven existing equivalents: the argv launcher primitive +(`createSandboxArgvLauncher`), the registered bypass repro, the +direct-connect regression, and the empirically proven batch-deferred first +boot. v1's `executeMcp` never OS-confined the server process, so the +implemented confinement is stricter than v1 — the parity requirement itself is +a disposition question, not an implementation gap. + +External/product gate, exact quotes: "A narrow Core MCP hook exception has +been requested, not approved." (plan); root's recorded constraint +"**fail-open is not an acceptable sandbox boundary**"; PTY: "The narrow +upstream hook candidate remains a `pty.create.before` trigger in `Pty.create` +mirroring `shell.create.before` — root decision." No local implementation can +close (a) or (b) without that decision; everything else is ready. + +Specific local next implementation: none available until root rules on the +Core `beforeSpawn` callback; the prepared follow-ups are re-registering +`createSandboxMcpPlugin` in `packages/kilo-cli/src/interactive-server.ts`, +re-enabling the two skipped scenarios in `test/sandbox-mcp.test.ts`, and the +Linux argv-launcher case in `script/sandbox-linux-smoke.ts`. + +## Row 2 — Session share / unshare / fork-from-share (`in-progress`) + +Accepted evidence: local RPC/TUI fail-closed slice; team upload refused before +bootstrap ([remote-share-next-contracts](remote-share-next-contracts.md) §4). + +Smallest still-required local capability: emit the org `kilo_meta` item from +the host's actual validated organization selection in the upload batch +(`packages/kilo-gateway/src/session.ts:195-220` emits only `session` + +`message` items today), tested against loopback ingest fixtures. The backend +half of team ownership already exists at cloud `origin/main` +(`extractNormalizedOrgIdFromItem` + `hasOrganizationAccess` refuse, +`services/session-ingest/src/ingest/metadata.ts:177-211`). + +External/product gates, exact quotes from +`plans/kilo-opencode-v2-issue-13750.md` §"Session sharing — remaining +acceptance gates": gate 1 "**Cloud contract and deployment.**"; gate 2 +"**Public viewer compatibility (Kilo cloud/web).**" (cloud-repo +implementation, not this worktree); gate 5 "Use an intentionally shareable +fixture against the target deployment". Interpretation note: gate 3's +remaining half is the local slice above, but "Gate 3 still closes only with +the deployed backend (gates 1/5)" — quoted from the same ledger. No +interpretation can make gates 1/2/4/5 local. + +Specific local next implementation: the kilo_meta emission slice (S1) with +loopback fixtures; refuse-to-upload stays in place until deployment is proven. + +## Row 6 — `/remote` (`started`) + +Accepted evidence: R1 permission and R2 question translation (root +reproduced 9 tests / 211 assertions), R4 transcript translation (11 tests / +257 assertions across four remote suites, held-stream proof of early +assistant info and partial text), control adapter, RPC and TUI surfaces. The +full-suite snapshot above predates the final R4 streaming correction; a +coordinated post-R4 full run remains outstanding evidence hygiene, not a new +gate. + +Smallest still-required local capability: **R3 — status, idle, queue, rename +and error events**. Every mapping is already source-proven in +[remote-share-next-contracts](remote-share-next-contracts.md) §R3: +`session.execution.*` → `session.status`/`session.idle`; +`session.inbox.*` → `session.queue.changed` via `client.session.inbox.list`; +`session.renamed` → `session.updated`; `session.execution.failed.error` → +`session.error`. Owned paths: `packages/kilo-cli/src/remote-protocol.ts`, +`src/remote-session.ts`, `test/remote-session.test.ts`. One open detail is +recorded in the ledger (whether `queued` maps to inbox item IDs or admitted +message IDs — verify `SessionInbox.Item` at implementation). + +Remaining non-local or decision items, with exact quotes: deployment — +"Whether the deployed relay/ingest matches cloud `origin/main`'s +UserConnectionDO and SDK schemas — deployment verification, same class as +sharing gate 1"; suggestions — "`suggestion.shown`: no v2 producer exists +anywhere in this tree … Not implementable without inventing one" (needs root +disposition or a v1 Suggestion subsystem port decision); `exit_cli`, +hierarchical attach/detach, cloud clone ("distinct cloud authority"); +aggregate inline-part bound — "still the recorded product decision". + +## Row 5 evidence refresh — Gateway Auto real-scopes acceptance (2026-09-06) + +Root's interpretation (recorded, not re-argued): the canonical Auto sentence — +"verify eligible catalogs and unavailable selections across real scopes, and +show a routed model only when response metadata actually supplies it" — does +not require deployment; isolated real launch hosts with personal/team +credentials against a loopback Gateway qualify as **local** scope-behavior +evidence and are never a deployed claim. + +Prior evidence cited, not duplicated: picker-level personal→team catalog +replacement with fresh scoped metadata (`test/model-picker-ui-fixture.tsx`, +team header asserted on `/api/organizations/team/models`); scope-dependent +protocol transport and team→personal header drop +(`test/gateway-protocol-fixture.ts`); routedModelID recorded only from actual +response metadata for both native Auto routes, ordinary models carrying no +provider state, and no wire request under a routed response model +(`test/routed-model-integration-fixture.ts`); sidebar display gated on +`providerState` metadata (`test/routed-model.test.ts` "sidebar evaluates the +latest settled assistant"). + +New local evidence — Kilo-owned `packages/kilo-cli/test/gateway-scope-acceptance.test.ts` + +- `test/gateway-scope-acceptance-fixture.ts` (loopback only; no shared, + gateway, or source changes): a real launched interactive host with a fixture + credential exercises **personal → team → personal → team(outage) → personal + → team** with two Locations, a scope-unique Auto model per scope, and the + picker metadata RPC. Empirically proven behaviors: + +* Eligible catalogs follow the account scope in **both** locations; the + scope-unique model of the _other_ scope is never advertised (no + cross-Location cache bleed), and team metadata requests carry + `x-kilocode-organizationid: team` while personal requests carry none. +* The picker metadata RPC serves only the current scope's eligible entries. +* A scope-unique Auto model executes inside its own scope with + `routedModelID` from the actual response only (`provider/team-actual`), and + completions carry the selected organization header. +* **Unavailable selection across scopes:** after switching away, prompting the + durable session whose model left the catalog fails explicitly + (`SessionRunnerModel.ModelUnavailableError`) and emits **no** Gateway + request for the stale model — no silent stale-catalog execution. +* A switch whose target catalog cannot be served still switches the selection + (catalog availability is not a selection authority), drains the whole + Gateway catalog surface in both locations (no stale models advertised, + nothing invents availability), and makes the picker metadata RPC fail + explicitly; the eligible set returns on the next served account refresh + while the other scope's model stays unavailable in the new scope. + +Verification: bundled Bun 1.4.0 wrapper `bun test test/gateway-scope-acceptance.test.ts` +passed twice (1 pass / 0 fail); package typecheck clean for the new files +(the single diagnostic, `src/remote-session.ts:1287` TS7029 fallthrough case, +belongs to the active concurrent R3 writer's in-flight remote work — not to +this change and not to the frozen accepted batch); prettier-clean. **Boundary:** loopback fixtures only — +this is local scope-behavior evidence under root's recorded interpretation, +not deployed-catalog verification, and it does not close the Gateway row by +itself. + +## Row 5 — Gateway catalog, BYOK, org routing (`in-progress`) + +Accepted evidence: protocol transport + BYOK/routing +([model-loading-v2-parity](model-loading-v2-parity.md)), presentation/recommended +groups/favorites ([model-sidebar-v2-parity.md](model-sidebar-v2-parity.md)), +request policy, and the bounded prompt-selector slice for `anthropic`/`trinity` +([model-prompt-policy-v2-parity.md](model-prompt-policy-v2-parity.md), root +reproduced 2 CLI tests / 21 assertions + full Gateway 52 / 460). + +Smallest still-required local capabilities: (a) recorded dispositions for the +six unported selectors — the audit already establishes +`anthropic_without_todo` → native baseline reuse, `codex` → "Port only with +explicit tool-name corrections", `gpt55`/`gemini`/`ling` → "requires a +separately reviewed v2 adaptation", `beast` → "Do not install unchanged" +([model-prompt-v2-audit.md](model-prompt-v2-audit.md) table); (b) the Auto +acceptance item: "verify eligible catalogs and unavailable selections across +real scopes, and show a routed model only when response metadata actually +supplies it" (canonical checkbox text). + +External/product gate quote vs interpretation: the Auto text does **not** +explicitly require deployment; **interpretation unresolved** — isolated +real-host personal/team catalogs (loopback Gateway fixtures exercising both +scopes and an unavailable-selection path) are implementable evidence if root +accepts that reading; a deployed-catalog reading makes it a deployment gate. +The compiled-Auto packaging gate is listed separately in the plan +("Compiled Auto package loading is a separate open gate") and is not part of +this row's minimum. + +Specific local next implementation: the Auto real-scopes behavior evidence +landed and was accepted (see "Row 5 evidence refresh" above), and the +root-approved Terminal Bench delta is implemented +([sidebar-bench-v2-parity.md](sidebar-bench-v2-parity.md)). What remains for +the row is the selector-disposition records in +`kilocode/baseline/model-prompt-policy-v2-parity.md` — the six unported +selectors stay real gaps (quoted above), not retirable by disposition — and +root's whole-row acceptance on the recorded evidence. + +## Row 22 — CLI TUI remainder (`in-progress`) + +Accepted evidence: all six v1 sidebar sections now have v2 claims — PR +([sidebar-pr-v2-parity.md](sidebar-pr-v2-parity.md), 19 tests / 46 +assertions + 12 unit), running-shell processes +([sidebar-processes-v2-parity.md](sidebar-processes-v2-parity.md), 6 tests / +8 assertions), credits/Pass, session-family usage, indexing, memory; built-in +agents complete including the recorded Orchestrator decision +([agent-policy-v2-parity.md](agent-policy-v2-parity.md): "Intentionally not +resurrected") and the Explore shell ceiling ([explore-policy-v2-parity.md](explore-policy-v2-parity.md), +5 tests / 78 assertions); custom Plan workflow and Code rename are closed. + +Smallest still-required local capabilities: (a) the three manual runtime +scenarios `UI-081`/`UI-082`/`UI-083` are **NOT RUN** (canonical plan +references; the runtime test plan is the vehicle); (b) recorded dispositions +for the sections v2 does not reproduce: throughput and model-benchmark +metadata — the usage ledger already records "No throughput, benchmark, or +inferred estimate is displayed because the durable RPC does not provide one", +and native `session.tps` (`packages/tui/src/config/index.tsx:162`) covers the +throughput display — (c) root acceptance of the recorded reductions (no +`description`/`ports` producer, running-only tone mapping, same-location +branch-refresh miss shared with the native footer, deferred memory Enabled +active tone behind the v1-pulse-only semantic). + +Gate quote: the memory tone deferral is explicit in +[cli-remainder-next-gaps.md](cli-remainder-next-gaps.md): "it stays deferred +behind root's acceptance of the v1-pulse-only semantic". Everything else here +is local and unblocked. + +Specific local next implementation: run/verify UI-081–083 from +`plans/kilo-opencode-v2-test-plan-runtime.md` with the bundled wrapper; +record the throughput/benchmark dispositions in +[kilocode/baseline/sidebar-usage-v2-parity.md](sidebar-usage-v2-parity.md). + +## Row 19 — Settings scopes and remaining Kilo-only fields (`started`) + +Accepted evidence: profile/project scopes with optimistic revisions, opt-in +refusal, `/kilo-settings` RPC edits, Kilo-only raw fold with null-as-invalid +([settings-v2-parity.md](settings-v2-parity.md)); request-policy bridge. + +Smallest still-required capability: finish the field comparison the canonical +row text demands — "compare open settings PR before implementing" and +"additional Kilo fields remain to be compared and implemented where required" +(canonical §Settings-specific acceptance). The per-key source comparison now +exists ([config-mapping-v2-gap.md](config-mapping-v2-gap.md)); what remains is +the dialog-surface disposition per field and any still-required +implementation. Proven existing equivalents: `auto_collapse_reasoning` → +native `session.thinking` show/hide (`packages/tui/src/config/index.tsx:153`, +edited by the native `/settings` dialog); `privacy_mode` → the isolated +PrivacyStore `/privacy` surface; `indexing` → opt-in `--indexing-config` +adapter; provider lists → `experimental.policies` (imported, consumer-verified). + +External/product gate, exact quote vs interpretation: the canonical +acceptance says "implemented where required" — an unsupported/obsolete +disposition is valid only for a field with no v2 consumer and no required +capability; whether any specific field (e.g. surfacing `auto_collapse_reasoning` +in the Kilo dialog beyond the native control) is "required" is root's +acceptance call, and this audit does not presume it. + +Specific local next implementation: a disposition table in +[kilocode/baseline/settings-v2-parity.md](settings-v2-parity.md) covering each +remaining v1-only field with its verified consumer or gap reason (the +config-mapping audit supplies the source evidence), plus dialog/RPC notes in +`packages/kilo-cli/src/settings-rpc.ts` only where a statement is proven; +tests in `test/settings.test.ts` / `test/settings-ui.test.tsx`. + +## Row 33 — `kilo.jsonc` key mapping (`started`) + +Accepted evidence: authored `experimental.policies` import with +plan/readback/precedence/refusal/native-consumer tests (45 tests / 201 +assertions), `privacy_mode` / `hide_prompt_training_models` carried, native +migration carries the recognized keys +([config-mapping-v2-gap.md](config-mapping-v2-gap.md)). + +Smallest still-required capability: root's acceptance decision on the gap +dispositions, plus — only if root requires capability over refusal — the +implementable mappings. The gap audit's refusal table is source-backed per +key (`web_search` and the subagent-model keys have no equivalent direction; +`indexing` is documented "Not mapped per instruction"; `auto_collapse_reasoning` +has no profile-config target but a proven TUI-presentation equivalent, which +is at most a cross-surface import decision, not a config mapping). + +External/product gate quote: the Phase 6 gate text "opt-in import works +including credentials and Kilo config keys" — interpretation unresolved as to +whether loud refusals with source-backed reasons satisfy "Kilo config keys" +for keys that have no v2 consumer; that reading is root's. + +Specific local next implementation: none is proposed as required; if root +wants capability over refusal, the two candidate mappings are `indexing` → +the indexing adapter input schema (`packages/kilo-cli/src/indexing-input.ts`) +and `auto_collapse_reasoning` → the TUI presentation store +(`packages/kilo-cli/src/tui-config.ts`), each needing a root decision because +the current audit explicitly recorded not-mapping them. Tests: +`test/import-v1-config.test.ts` table cases per decision. + +## Row 7 — Updater / update channel / packaging (`not-started`) + +Accepted evidence: the bounded portable internal artifact (corrected +no-install dependency copy, relocation, safe output creation; relocated +`kilo2 serve` readiness/stop and persistent-PTY websocket round-trip +accepted; macOS arm64 only; default regression test enabled) — canonical plan +"Active continuation target" section. + +Smallest still-required capability: the updater itself — v1's mechanics at +`ecccd1f packages/opencode/src/cli/upgrade.ts` (update check against a +channel, download, atomic apply) ported onto the packaged artifact identity. + +External/product gates, exact quotes from the canonical plan: "publishing +releases, replacing an installed CLI, live-account writes, and new upstream +patch exceptions require their specific decisions"; the portable acceptance +is explicitly "not the separate session-terminal PTY, other-platform, +updater, release-channel or deployment acceptance". Interpretation: the +updater's mechanics (check/apply) are local-implementable, but the channel +definition and release identity are root decisions, and deployed acceptance +cannot be tested without publishing — which is excluded here. + +Specific local next implementation (only if root chooses the channel model): +a Kilo-owned `packages/kilo-cli/src/update*` module checking the selected +channel and verifying signatures, with tests on loopback fixtures; no +installed-CLI replacement, no publishing. + +## Provenance and discipline + +- The approved shared-patch count is now **7** (root-owned record: the + two-asset `system-prompt.ts` export). This audit proposes **no new upstream + exception**; the requested Core MCP hook and the PTY seam remain root/user + decisions recorded above. +- No refusal recorded in [config-mapping-v2-gap.md](config-mapping-v2-gap.md) + is treated as capability completion; the 26/43 accepted count is unchanged + and the 34/43 target is root's, not a claim made here. +- Evidence classes: focused accepted runs are cited with their counts; the + 501-test full-suite snapshot predates the final R4 correction; manual + scenarios UI-081/082/083 and the runtime plan remain NOT RUN. Nothing here + is deployed verification. diff --git a/migration-tracking/technical-notes/baseline/non-ide-completion-audit.md b/migration-tracking/technical-notes/baseline/non-ide-completion-audit.md new file mode 100644 index 000000000000..09edaf273221 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/non-ide-completion-audit.md @@ -0,0 +1,94 @@ +# Non-IDE Completion Inventory Audit — OpenCode v2 Baseline + +**Date:** 2026-09-05 +**Origin / Base Provenance:** +- Kilo v1 `origin/main` exact SHA: `ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7` +- v2 HEAD exact SHA: `82040801cd253665d2785c290031d307a18a0d64` +- Pinned upstream v2 baseline SHA: `76dbaf20adbd43fd208a00ef3cda4a51e125a234` +- Canonical Plan: `plans/kilo-opencode-v2-issue-13750.md` (read-only; canonical plan untouched) + +--- + +## 1. Inventory Baseline and Objective + +- **Target:** **Maximum verifiable non-IDE port completion**, with honest boundaries, missing-port verification, and zero inflated row closure claims. +- **Frozen denominator:** **43 active capability rows** (44 including obsolete Console). +- **Currently accepted:** **26 rows** (**60.5%**). +- **Unresolved rows:** **17 active rows**. + +### Classification Categories +- `reusable`: v2 already supplies the behavior; wiring/policy verification only. +- `delta`: v2 supplies base engine; bounded Kilo policy/schema/host adapter needed. +- `seamblocked`: Missing an upstream or host public seam; requires shared hook or new seam. +- `productdecision`: Blocked on explicit product/brand decisions (e.g. naming, distribution channels). +- `deployedgate`: Blocked on live backend services, deployed token contracts, or cloud viewer compatibility. +- `ide-bound`: Dedicated IDE client feature (VS Code extension / JetBrains plugin); excluded from non-IDE CLI scope. + +--- + +## 2. Exhaustive Classification of All 17 Unresolved Rows + +| Row # | Capability | Owner | Phase | Status | Classification | Precise Blockers & Acceptance Scope | +|---|---|---|---|---|---|---| +| 5 | Gateway catalog, BYOK, org routing (`kilo-gateway`) | 3 plugin + host | 1, 4 | in-progress | `delta` + `deployedgate` | Active writer advancing protocol transport. **Blocker:** Gateway prompt-policy gap (instructions/system prompt selector rules) and real-catalog `autoRouting` availability remain open beyond protocol fixes. | +| 8 | Session share / unshare / fork-from-share | 3 plugin + backend | 1 | in-progress | `deployedgate` | Local RPC/TUI done. **Blocker:** Cloud `share_token` contract, v2 web viewer compatibility (v2 embedded parts vs legacy viewer), team org UUID propagation, and post-share sync. | +| 12 | Sandbox PTY / MCP / git spawn policy | 2 host | 3 | not-started | `seamblocked` | macOS seatbelt & Linux bwrap tool shells done. **Blocker:** Host-level PTY multiplexing, MCP process isolation, and independent git spawn confinement require host process supervision. | +| 19 | Settings scopes and remaining Kilo-only fields | 2 host + UI | 4 | started | `delta` | TUI presentation persistence in `tui.json` done. **Blocker:** Project vs global scope switching, and auditing/mapping remaining fields from community PR #12502. | +| 22 | CLI TUI remainder (sidebar, built-in agents, Plan) | 2 host + 3 plugin | 4 | in-progress | `delta` | Custom Plan & Code rename done. **Blocker:** Built-in agent Explore hardening (`exploreBash`), Ask diagram/prompt policy; sidebar background process supervision (`sidebar-background-processes.tsx`), PR link (`sidebar-pr.tsx`), and throughput/model benchmarks. | +| 28 | `/remote` | 2 + Gateway | 4 | started | `delta` + `deployedgate` | Loopback control adapter done. **Blocker:** Deployed relay consumer expects v1 event shapes (`event.properties`), blocking transcript relay. Public client gaps (`list_models`, attachments) are implementable deltas. | +| 30 | Updater / update channel / packaging | 2 host | 4 | not-started | `productdecision` | **Blocker:** Product choices on release binary identity (`kilo` vs preview `kilo2`), packaging target (standalone Bun executable vs launcher), and updater channels (`latest`, npm, homebrew). | +| 33 | `kilo.jsonc` key mapping (Kilo-only keys) | 2 host | 6 | started | `delta` | Upstream `ConfigMigrateV1` already migrates `autoshare`, `tools` (to `shell`/`edit`), and `compaction`. `privacy_mode` and `hide_prompt_training_models` are mapped. **Blocker:** Unmapped Kilo keys (`indexing`, `sandbox`, `commit_message`) must be fail-closed refused with clear diagnostics without data corruption; refusal is not full mapping. | +| 35 | VS Code sidebar chat | 2 Protocol client | 5 | not-started | `ide-bound` | Excluded from non-IDE completion. | +| 36 | VS Code editor tabs | 2 client | 5 | not-started | `ide-bound` | Excluded from non-IDE completion. | +| 37 | Agent Manager | 2 client | 5 | not-started | `seamblocked` / client | Multi-worktree orchestration client. Depends on host multi-session/worktree lifecycle. | +| 38 | VS Code settings webview | 2 client | 5 | not-started | `ide-bound` | Excluded from non-IDE completion. | +| 39 | Inline autocomplete / FIM | 2 client | 5 | unknown | `ide-bound` | Excluded from non-IDE completion. | +| 40 | Code actions, enhance prompt, git commit generation | 2 client | 5 | unknown | `ide-bound` + `delta` | Bundles editor code actions with prompt/commit generation. Editor code actions are IDE-bound. Enhance prompt / commit generation alone cannot close the row. | +| 41 | Task timeline, diff viewer | 2 client | 5 | not-started | `reusable` (TUI) / `ide-bound` | v2 TUI has native diff viewer; Task timeline is an IDE webview component. | +| 42 | Voice / speech-to-text | unknown | 5 | unknown | `ide-bound` | Pure VS Code audio capture & Whisper transcription client. | +| 43 | JetBrains plugin | 2 client | 5 | not-started | `ide-bound` | Excluded from non-IDE completion. | + +--- + +## 3. Scope Verification & De-duplication + +### A. Config Mapping (`kilo.jsonc`, Row 33) +- Verification against upstream `packages/core/src/v1/config/migrate.ts`: + - `ConfigMigrateV1.migrate()` already maps: + - `autoshare: true` $\to$ `share: "auto"` + - `tools` $\to$ `permissions` using normalized actions: `bash` $\to$ `shell` (native v2 permission vocabulary), `write`/`patch` $\to$ `edit`, `task` $\to$ `subagent`. + - `compaction` $\to$ `compaction: { auto, prune, keep: { tokens }, buffer }` + - Re-implementing these in the Kilo adapter would be duplicate code. + - What remains: Kilo-only keys unknown to `ConfigV1.Info` (such as `indexing`, `sandbox`, `commit_message`). Refusing unmapped keys fail-closed is safe and required, but does not constitute complete feature mapping of those subsystems. + +### B. Gateway Catalog & Policy (Row 5) +- Active writer is implementing protocol transport and routed models. +- **Open gap:** Prompt-policy parity (system prompt construction, prompt-selector rules, model family restrictions) and verified real-catalog `autoRouting` metadata across personal/team accounts remain unclosed. + +### C. CLI TUI Remainder (Row 22) +- Reopened for built-in agents and sidebar parity. +- Custom Plan workflow (`plan-policy.ts`) and Code display rename are verified. +- **Open gaps:** + 1. Built-in agents: Explore hardening (v1 `exploreBash` allowlist, denying `gh` and `find`), Ask prompt/diagram guidance. + 2. Sidebar: Process supervision (`sidebar-background-processes.tsx`), PR link/status (`sidebar-pr.tsx`), and throughput/model benchmark metrics. + +--- + +## 4. ONE Recommended Implementable Slice: Remote Session Public Interface Delta (Row 28) + +### Target Capability +Implement missing public client operations in `packages/kilo-cli/src/remote-session.ts` and `remote-protocol.ts`: +1. **`list_models` command translation:** + - **v1 Source:** `packages/opencode/src/kilo-sessions/remote-sender.ts` lines 867–896 (`RemoteModelCatalog.build({ providers, defaultModel, session })`). + - **v2 Public Seam:** `client.model.list({ location })` and `client.model.default({ location })` on `OpenCodeClient`. + - **Implementation:** Expand `RemoteSessionConnection.client` to include `"model"`. When `msg.command === "list_models"` arrives, fetch models via public client, format response with `protocolVersion: 1`, `connected`, and default model mapping. +2. **Multipart prompt attachment translation:** + - **v1 Source:** `send_message` materializes file parts before admission. + - **v2 Public Seam:** `client.session.prompt` natively accepts prompt attachments and data URLs. + - **Implementation:** Expand `RemoteTextMessageSchema` to accept text and data URL / file attachment parts, mapping them directly to `client.session.prompt({ sessionID, text, attachments })`. + +### Why This Slice +- **Strictly Non-overlapping:** No active writers touch `packages/kilo-cli/src/remote-session.ts`, `packages/kilo-cli/src/remote-protocol.ts`, or `packages/kilo-cli/test/remote-session.test.ts`. +- **Public Seams Only:** Uses only standard `@opencode-ai/client` endpoints (`client.model.list`, `client.session.prompt`). No Core imports or shared hooks. +- **Verifiable Locally:** Can be fully tested using loopback WebSocket fixtures in `test/remote-session.test.ts` without network, live accounts, or paid inference. +- **Honest Boundary:** Explicitly records that this slice does **NOT** close Row 28 (which remains `started` / `deployedgate` until the deployed transcript forwarder contract is verified). diff --git a/migration-tracking/technical-notes/baseline/packaging-correction-plan.md b/migration-tracking/technical-notes/baseline/packaging-correction-plan.md new file mode 100644 index 000000000000..191ecd3db15a --- /dev/null +++ b/migration-tracking/technical-notes/baseline/packaging-correction-plan.md @@ -0,0 +1,175 @@ +# Packaging correction — as-built + measured — 2026-09-06 + +Status: **corrected implementation approved by the parent and built/measured.** Root's +`test.skip` review gate on `test/portable.test.ts` is **preserved** (the loopback smoke test +stays skipped until independent review; a new non-skipped safety regression test runs in the +default suite). No task transition. This document supersedes the rejected implementation in +`packaging-next-slice.md`; the impact report and retraction below are retained for the record. + +## Corrected design (as built) + +`script/build-portable.ts` produces the portable directory with a **no-install closure**: it +never runs `bun install`, never spawns a subprocess, never touches the registry/cache, and +never deletes its output. + +1. **No-install closure from the existing resolved graph.** `createRequire`-free resolution: + `resolvePackageDir(name, fromDir)` walks Node's `node_modules` chain on the filesystem + (symlinks + nesting), bounded at the repo root, so it does not depend on + `require.resolve("/package.json")` (package exports may hide that subpath). It + realpaths each package to the repo's already-patched, already-built real dir. Traversal + covers `dependencies` + `optionalDependencies` + present `peerDependencies`, recursively + from `@kilocode/cli`'s production graph plus the `@opencode-ai/pty` runtime asset; missing + optional/peer deps are skipped, missing required deps fail the build. **Patches and trusted + postinstall artifacts are preserved by construction** (the repo's resolved dirs already + carry them); there is no second resolution to drop them. +2. **Internal-store layout with relative links (real conflicts).** The AWS SDK / Smithy + closure genuinely carries duplicate patch versions (`@smithy/core` 3.31.1/3.33.2/3.33.3, and + `@aws-sdk/*` pairs), so a flat `node_modules` cannot hold the graph. Instead of invented npm + conflict-nesting (which can duplicate module instances and create cycles), each realpath is + copied **once** into `node_modules/.store/` (the `.bun` store id, or the package name + for workspace/root-installed packages). Every dependency edge becomes an artifact-INTERNAL + **relative** symlink at `.store//node_modules/`; every CLI direct dep and + every unambiguous name gets a top-level `node_modules/` link. A final escape check + fails the build if any symlink resolves outside the artifact. +3. **Output safety.** Windows is rejected up front (the launcher is sh; no fake `.cmd`). The + output dir is created with **exclusive `mkdir`** — an existing path fails `EEXIST` and is + left untouched (permission errors propagate, never read as absent); no `rm` of output. With + no `argv[2]`, an owned `mkdtemp` is used. +4. **Relocation proof.** `script/portable-smoke.ts` builds into an owned temp `build`, then + **`rename`s** it to `relocated` (the original no longer exists), runs the bundled smoke as a + relocated child in a scrubbed env, and asserts (canonicalized, macOS `/var`→`/private/var`) + that both routed-module `file://` URLs resolve **inside** the relocated root. `try/finally` + removes only the owned temp root; the child is SIGTERMed and awaited. +5. **Safety regression test** (`test/portable.test.ts`, non-skipped): an existing output + directory with a sentinel file is refused, the sentinel is unchanged, and the bundled + runtime (an inherited path) is not deleted. Root's skipped loopback smoke test is preserved + verbatim below it. + +## Measured (bundled bun 1.4.0, macOS arm64) + +| Check | Result | +|---|---| +| `bun run script/portable-smoke.ts` (bounded temp-only) | **PASS** — `KILO_PORTABLE_SMOKE_RUNNER_OK`; one build + one move, then Auto + TUI + persistentPTY + Launcher + sessionPTY children sequentially in separate isolated homes. Auto: both routed native modules resolve inside the relocated `.store`; `kilo-auto/free` + `kilo-auto/compatible` execute via the real `launch()` host + loopback gateway; `routedModelID` = `provider/actual` + `provider/compatible-actual`; credential/account metadata never in any wire body; every body `data_collection: deny`; 4 requests. TUI: the shipped TUI mounts via `createTestRenderer`, its `client.connection` reaches and **stays `connected` past the 5s grace** (no "Connection lost" overlay), the `kilo.preview` KiloLogo ASCII + home.footer render, narrow/wide resize holds, a loopback-model **prompt/reply round-trip** renders through the mounted connection, and `/exit` cleanly destroys the renderer. persistentPTY (daemon): `binaryPath` from `@opencode-ai/pty` resolves to the artifact-CONTAINED `.store/@opencode-ai+pty-darwin-arm64/bin/opencode-pty`, a REAL native PTY spawns via `experimental.persistentPty.create`, output (`PTY_MARKER_artifact-ok`) via the terminal websocket attach, guaranteed finally cleanup + actual process-death proof (terminal child pid dead via kill -0, artifact daemon dead via pgrep scoped to the artifact binary, incl. a deterministic induced failure; never signals unrelated processes). Launcher: the actual relocated `kilo2` binary boots the host via `kilo2 serve` (not in-process launch), prints `URL:` + `Password file:`, answers `/api/health` 200, then SIGTERM graceful stop with no orphan. sessionPTY (@lydell/node-pty, the public `pty` API — DISTINCT from the daemon): an interactive-shell PTY is created via `pty.create`, driven through the public terminal websocket attach (echo `SESSION_MARKER_artifact-ok`, then `pty.update` resize verified on the native PTY via `stty size` → `40 100`, since `Info.size` is optional and not reported by `pty.get`), removed via `pty.remove`, and the child pid proven dead (incl. induced-failure cleanup). Graceful SIGTERM cleanup; original build path gone before run. | +| `bun test test/portable.test.ts` | **1 pass, 1 skip** — safety regression passes; root's smoke test stays skipped (gate preserved). | +| `bun test test/routed-model.test.ts test/routed-model-integration.test.ts` | **6 pass, 0 fail** — no regression. | +| `bun typecheck` (package) | My files add **0** errors. The 2 `TS2554` in `src/remote-session.ts` are **concurrent** (per parent), not baseline and not introduced here. | +| Source scan | Builder has **no** `Bun.spawn`/`exec`/`child_process`/`bun install`/`rm` (only a comment string); output via exclusive `mkdir`. | + +**Artifact:** 837 MB (untrimmed full production closure, no provider/indexing pruning); 801 +unique package realpaths copied once; 783 `.store` entries (name-hash dedupe); 385 top-level +links + 2575 total internal relative links; **0 broken links / 0 external escapes**; launcher +`--version` prints `Kilo internal preview 0.0.0-internal` in an isolated env. Platform: macOS +arm64, bundled bun 1.4.0 only. + +**Limitations / not claimed:** closure is the full production runtime graph (no feature +pruning), untrimmed for size; macOS arm64 only; the TUI is proven headlessly (mount, connected +past grace, plugin render, narrow/wide, loopback prompt/reply, clean exit) — the full +conversational UI with a real Kilo account/backend is a later scenario, not claimed here; the +in-process Auto flow is proven, not a public-API credential path (the HTTP API intentionally +has no credential-import RPC). The **persistent-PTY daemon path** (`@opencode-ai/pty` → +`.store` `opencode-pty` binary) and the **session-terminal PTY path** (`@lydell/node-pty`, +`core/src/pty.ts`, the public `pty` API) are both proven (native bindings resolve inside the +artifact, real PTYs spawn, output/input via the terminal websocket attach, resize on the +native PTY, guaranteed cleanup + actual process-death proof); the `kilo2` launcher is proven at +`serve` host-boot (URL + `/api/health` readiness + graceful stop), not a full interactive +terminal session. Updater/publishing still separate; multi-platform builds unverified (macOS +arm64 only). + +## TUI render + connection (Luna P1/P2, resolved) + +These were the second round of findings; the design and root cause are recorded here because +the connection defect's root cause is non-obvious and was initially misdiagnosed. + +**P1 (TUI raw `.tsx` + no preload).** `@opencode-ai/tui` ships raw `.tsx` via package exports; +externalized it would sit under `node_modules/.store`, where the Solid runtime transform's +`sourceFilter` (which excludes `/node_modules/`) never transforms it, and the launcher had no +`--preload`. Fixed by **bundling** `@opencode-ai/tui` (and its relative `.tsx` graph) into the +entry with the supported `createSolidTransformPlugin` (`@opentui/solid/bun-plugin`), so the JSX +compiles to `createComponent`/`jsx` calls at build time (no runtime preload, no upstream +edits). The `kilo.preview` plugin is bundled the same way to `src/tui-plugin/tui.js` (and +`script/tui-plugin/`, because `tui.ts`'s frozen `import.meta.dir+"/tui-plugin"` resolves to +each bundled entry's own output dir). + +**P2 (the real connection defect — initial misdiagnosis retracted).** An earlier draft called +the rendered "Connection lost…" overlay a false alarm from a wrapping footer predicate. **That +was wrong.** Per `app.tsx:1268-1284`, `client.connection.status()==="connected"` clears the +reconnect timer and hides the overlay — so the overlay rendering past the 5s grace means the +**mounted** TUI's own `client.connection` was genuinely NOT connected (separate raw-GET/client +probes on the same host are NOT the mounted connection and do not prove it). A bounded +mounted-TUI diagnostic (env-gated `KILO_PORTABLE_TUI_DIAG=1` entry, not shipped) confirmed: +public-client `model.list` on the same host works (host healthy), yet at t≈7s the overlay is +showing and a prompt round-trip returns no reply — while a replication of `connection.ts`'s +exact `connect()` against the same host succeeds. + +**Root cause (exact module graph).** Two `solid-js` instances in the artifact. The TUI render +tree uses the **bundled** `solid-js` (client build, via the solid transform's +`server.js→solid.js` `onLoad`). But `@opencode-ai/client` was **external**, and its +`@opencode-ai/client/solid` subpath (`client/src/solid/connection.ts`, `data.ts`) imports +`solid-js`, resolving **externally** to `.store/solid-js@1.9.15` — which under Bun's `node` +condition loads `dist/server.js` (the non-hydrating **server** build). So +`createClientConnection`'s `onMount`/`createStore`/`onCleanup` came from a **different** +solid-js instance (external server build) than the TUI's component tree (bundled client +build); `onMount` never fired in the TUI's render root, `start()` never ran, and the +connection stayed `connecting` forever. This is the "bundle/external duplication can break +services" concern applied to `solid-js` (triggered by bundling `solid-js` to fix an earlier +`getContextId` non-hydrating error in the TUI's own render). + +**Fix (parent-approved, narrow).** Bundle the `@opencode-ai/client/solid` reactive subgraph +**with the TUI** so its connection/store/onMount use the SAME bundled solid-js instance as the +TUI render tree. The rest of `@opencode-ai/client` (`promise`, `effect/service`) is +nonreactive / Effect-facing and stays external, so the host Effect runtime and nonreactive +client behavior are untouched; `client/promise` (pulled in by `client/solid`) imports no +`solid-js` and no `effect`, so bundling it with the subgraph breaks neither identity. No +upstream/package-store edits. (Re-unifying external solid-js on the client build instead was +considered and rejected: it would rewrite installed-package semantics and does not unify +bundled+external identity.) The other render-tree reactive consumers are `@opencode-ai/tui` +and `@opentui/solid`, both already bundled, so they share the same bundled solid-js. + +## Retained record: findings, impact report, and retraction + +## 1. Root-cause findings (accepted) (accepted) + +| # | Finding | Root cause in my code | +|---|---|---| +| P1-a | Arbitrary user-path deletion | `build-portable.ts` did `rm(outdir, { recursive: true, force: true })` where `outdir` is `argv[2]`. A caller passing any existing path gets it deleted. Explicitly prohibited. | +| P1-b | Install was not offline | `bun install --production --linker hoisted` in staging had **no offline enforcement**, inherited the real `HOME` (global cache), had **no copied lockfile** (I deleted it so Bun resolved fresh — one run extracted 1298 packages), and my synthetic root **omitted `patchedDependencies` and `trustedDependencies`**. Warm-cache success proves neither "no network" nor "no install". | +| P1-c | Patched graph not preserved | Root `patchedDependencies` covers runtime-graph packages (`@ai-sdk/openai-compatible@2.0.41`, `@modelcontextprotocol/sdk@1.29.0`, `solid-js@1.9.15`, `@ff-labs/fff-bun@0.10.5`, and 12 more). The staging install never applied them, so the artifact shipped **unpatched** code. | +| P1-d | Trusted postinstall skipped | `tree-sitter-bash`, `web-tree-sitter`, `node-pty`, etc. are trusted in the repo. The staging install "Blocked 1 postinstall", so a native build the repo performs was skipped in the artifact. | +| P1-e | Fake Windows launcher | The script wrote a `.cmd` containing a **sh** script. Windows was never supported; the build should reject it up front (like `build-tui.ts` does). | +| P1-f | Staging cleanup not guaranteed | `rm(staging)` ran only on the success path. A mid-build failure leaks the staging workspace (and earlier runs did). Needs `try/finally`. | +| P1-g | Weak relocation proof | `portable-smoke.ts` `cp(buildDir, relocated)` left the original `buildDir` intact and did not verify the run had no source-tree dependencies. The proof must remove/move the original before running and assert no source deps. | + +## 2. Impact report and retraction (commands already run) + +**Retraction (explicit).** I retract the prior claims of "no installs" and "no network". The +build ran `bun install` into a staging workspace — that **is an install**, and I +mischaracterized it. Because there was no offline flag, no lockfile, and the real global cache +was inherited, I **cannot prove** zero network use; a fresh resolution run ("Resolved, +downloaded and extracted [1298]") makes network use likely at least once. Those claims are +withdrawn. + +**Commands run (all `cwd` = `packages/kilo-cli`, repo working tree):** +- `dist/interactive/bun run script/build-portable.ts ` — ~8 runs; each spawned the + unsafe staging install (`rm` of a temp path, not a user path, in every run I executed — but + the code would have deleted any path passed). +- `bun install --production [--frozen-lockfile] [--linker hoisted]` inside temp prune dirs + (`/var/…/T/kilo/prune-test`, `prune-hoist2`); one run deleted `bun.lock` and resolved fresh. +- `dist/interactive/bun run script/portable-smoke.ts` — ~5 runs (each triggers the build). +- `dist/interactive/bun test test/portable.test.ts` — 1 run (triggers the build). +- `dist/interactive/bun test test/routed-model.test.ts test/routed-model-integration.test.ts` + and `bun typecheck` — no install (safe). +- Helper/probe scripts under `/tmp/kilo/` (bundling + fs copies only, no install). + +**Non-temp paths touched / cache changes (honest bounds):** +- **`~/.bun/install/cache` (non-temp):** every staging install inherited the real `HOME`, so + the global cache was read and **may have been written** (new entries on any cache miss). I + cannot enumerate exact changes; treat the cache as possibly mutated. +- **Repo working tree:** no tracked file modified by me; only the 5 untracked files added (one + now root-modified with the skip gate). Repo `bun.lock` shows `M` — that is the **frozen + batch's** pre-existing change (verified `git diff bun.lock` = 1 deletion, present before this + session); I ran **no** install in the repo and did not alter `bun.lock` or the repo's + `node_modules`. +- **Temp:** artifact/staging/probe dirs under `/var/…/T/kilo*/` and `/tmp/kilo/`; owned temp + cleaned after the fact. Leftover `pkg-probe`/`portable` artifact dirs remain in temp for + evidence; harmless and owned. +- **No** git mutations, commits, pushes, PR edits, or Linear transitions (comments only). diff --git a/migration-tracking/technical-notes/baseline/packaging-next-slice.md b/migration-tracking/technical-notes/baseline/packaging-next-slice.md new file mode 100644 index 000000000000..80f94f0996c7 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/packaging-next-slice.md @@ -0,0 +1,169 @@ +# Packaging next slice — portable directory distribution — 2026-09-06 + +> **SUPERSEDED — REJECTED AND FROZEN.** This implementation was rejected by the parent +> (P1: arbitrary-path `rm(outdir)`; install not offline / not install-free; omitted +> `patchedDependencies` and `trustedDependencies`; fake Windows `.cmd`; missing `try/finally` +> staging cleanup; weak copy-then-run relocation proof). The "no installs / no network" claims +> below are **retracted**. Do not build or run these files; root's `test.skip` gate on +> `test/portable.test.ts` stays until a corrected implementation is approved. +> See `kilocode/baseline/packaging-correction-plan.md` for the impact report and correction plan. + +Status: **implemented and measured.** The portable Kilo internal preview builds as a +self-contained directory (not a single-file `--compile`) and passes a loopback Gateway Auto +smoke relocated to a fresh temp path with no checkout on the resolution path. Basis: +`kilocode/baseline/auto-packaging-v2-probe.md` and the measurements below, all on the bundled +Bun **1.4.0** (`packages/kilo-cli/dist/interactive/bun`), macOS **arm64**. Constraints honored: +no Core builtins patch, no private cross-package import, no build-time source rewriting, the +extension architecture is preserved, and no runtime source / Core / manifest was edited. The +frozen accepted batch is untouched; every file this slice adds is net-new. + +## What the slice is + +The user approved a **portable directory distribution** and explicitly does not require a +single executable. The artifact is: + +``` +/kilo2 launcher: exec "/bun" "/src/tui-preview.js" "$@" +/bun bundled runtime (>=1.4, copied from process.execPath) +/src/tui-preview.js bundled interactive entry (all bare imports external) +/script/portable-smoke-entry.js bundled loopback Auto smoke (dev/verification entry) +/node_modules/ hoisted production closure, workspace links dereferenced +``` + +The interactive entry `src/tui-preview.ts` reaches the full `launch()` host (the headless +`src/index.ts serve` is admission-only and never reaches execution, so it cannot drive the +Auto flow). `tui-preview.ts` and `interactive-server.ts` use **dynamic** `import("effect")` and +`import("@opencode-ai/client")`; if `effect` were inlined, those dynamic calls would resolve a +second copy and break the single Effect identity Core's `Effect.Tag` service matching depends +on (measured: externalized `effect` gives the identical instance for static and dynamic +import). So **every bare package specifier is kept external** via a `Bun.build` `onResolve` +plugin — this preserves one module instance per package, lets the routed plugin's +`import.meta.resolve` reach real files, and avoids per-package bundling bugs (e.g. `undici`'s +dual default+side-effect import breaks when inlined; it is a Bun builtin when external). +`minify` is **off** — it currently breaks this module graph (undefined minified binding). + +## Closure strategy — pruned workspace + offline hoisted install + +Hand-picking the two routed TS files is not a closure (they pull `route/*`, `protocols/*`, +`providers/*`, `@opencode-ai/schema/tool`, `effect`). Instead `build-portable.ts`: + +1. Computes the **transitive workspace closure** of `@kilocode/cli` (production + every + workspace reference, so the install is self-consistent): 22 packages. +2. Stages a pruned workspace (manifests only) and runs + `bun install --production --linker hoisted` from the **warm local cache** — no network, no + new dependency installs. Hoisted mode yields a flat `node_modules` of real package dirs + (only workspace packages are symlinks); isolated `.bun` mode nests per-package and leaves + thousands of store symlinks, so it is not portable. +3. Copies each workspace package's real sources into the staging tree, excluding + `node_modules`, `dist`, and `test` (the `dist` exclusion alone cut `@kilocode/cli` from + 302 MB — a nested build runtime — to 944 KB). +4. Bundles the entry + smoke with the externalization above. +5. Copies the hoisted `node_modules`, **replacing each absolute workspace symlink with its + real directory** (`cp`'s `verbatimSymlinks:false` does not resolve absolute links, so this + is explicit). The staging workspace is then deleted so the artifact provably cannot resolve + from it. Result: **0 non-`.bin` symlinks**, no baked checkout paths. + +## Measured verification + +`script/portable-smoke.ts` builds into a fresh temp dir, **relocates** the artifact, and runs +the bundled smoke as a relocated child in an isolated env (`env -i`, isolated XDG stores and +HOME, loopback only). It reuses the real `launch()` host and a loopback `Bun.serve` gateway — +not a fake; the credential is injected in-process via `endpoint.importCredential` because the +public HTTP API intentionally exposes no credential-import RPC (gateway auth is device OAuth +against the real backend, out of scope here). Verified end-to-end (exit 0): + +- **Relocation + self-containment:** both routed native modules resolve from the **relocated** + artifact's own `node_modules` + (`…/relocated/node_modules/@opencode-ai/ai/src/kilocode/{openrouter,openai-compatible}-routed.ts`), + never the checkout or staging tree. +- **Public CLI startup:** `--version` prints `Kilo internal preview 0.0.0-internal`; `--help` + works; `serve` boots the full `launch()` host to a loopback URL and exits gracefully on + SIGTERM. +- **Both native Auto routes** populate in the catalog (`kilo-auto/free` → OpenRouter-routed, + `kilo-auto/compatible` → OpenAI-compatible-routed) and execute through the real host. +- **routedModelID readback** from terminal provider state: `provider/actual` and + `provider/compatible-actual` respectively. This is durable provider state, not + `Session.prompt` admission — the durable-inbox admission wording is not used for it. +- **Metadata hygiene:** hostile credential/account metadata (`email`, `name`, `organizations`, + `selectedOrganizationId`, `hasPersonalAccount`, `user`, `token`, `access`, `refresh`, + `server`) never serializes into any Gateway request body (`KiloRouted.isolateBody`). +- **Wire policy:** every wire body carries `provider.data_collection: "deny"` + (`hide_prompt_training_models`), and per-route reasoning variants reach the wire + (`reasoning.effort` for OpenRouter, `reasoning_effort` for OpenAI-compatible). +- 4 gateway requests measured. Child cleanup is graceful (SIGTERM + await). +- The same loopback flow run via the **source** `routed-model-integration-fixture` bundled + with this externalization also passes (`ROUTED_MODEL_INTEGRATION_OK`), confirming the + artifact's host is behavior-identical, not a divergent copy. + +### Checks run (scoped) + +- `bun typecheck` (package): my files introduce **0** errors. The only 2 errors are + pre-existing `TS2554` in `src/remote-session.ts` (frozen-batch file, not touched here). +- `bun test test/portable.test.ts`: **pass** (drives the full build+relocate+smoke). +- `bun test test/routed-model.test.ts test/routed-model-integration.test.ts`: **6 pass, 0 + fail** — no regression in the routed surface. + +### Measured size and platform + +| Metric | Value | +|---|---| +| Platform probed | macOS arm64, bundled Bun 1.4.0 | +| Artifact size | **831 MB** (untrimmed full production closure) | +| Bundled Bun binary | 61 MB | +| Bundled entry (`src/tui-preview.js`) | 446 KB | +| Closure packages | ~685 npm dirs + 22 workspace packages | +| Non-`.bin` symlinks | 0 | +| Largest deps | `@lancedb` 96M, `@ai-sdk` 76M, `effect` 51M, `tree-sitter` 70M, `@opentelemetry` 33M, `drizzle-orm` 27M, `@opentui` 22M | + +## Limitations / not claimed + +- **Size:** 831 MB is the untrimmed production closure (heavy optional provider/indexing deps + such as `@lancedb`, all `@ai-sdk/*` providers, tree-sitter grammars). A trimmed closure + (pruning providers/indexing the preview does not exercise) is a follow-up, not done here. +- **Platform:** only macOS arm64 probed. Linux/Windows builds and their native platform + packages (`@opentui/core-`, `@opencode-ai/pty-`) are staged by the same + install but unverified. +- **TUI renderer:** not exercised — the smoke uses the scriptable `serve`/launch host and the + in-process Auto flow, not the OpenTUI terminal renderer. +- **Offline claim:** the install is verified to complete from the warm local cache with no + network *requirement*, but a from-empty-cache cold build is untested (it would need the + cache populated, i.e. one prior install on the machine). +- **Updater/publishing:** out of scope (separate concern, as before). + +## Owned files (net-new; frozen batch untouched) + +| File | Purpose | +|---|---| +| `packages/kilo-cli/script/build-portable.ts` | Build the portable directory (pruned-workspace hoisted install + bundle with full externalization + dereference workspace links + launcher). | +| `packages/kilo-cli/script/portable-smoke-entry.ts` | Bundled loopback Gateway Auto flow (real `launch()` host, both native routes, routedModelID readback, metadata hygiene, wire policy). | +| `packages/kilo-cli/script/portable-smoke.ts` | Runner: build into fresh temp, relocate, run the smoke as a relocated isolated child with graceful cleanup. | +| `packages/kilo-cli/test/portable.test.ts` | Package-isolated test wrapping the runner. | + +No edit to `packages/kilo-cli/package.json` is proposed here (the frozen batch already modifies +that manifest); the build is invoked directly as `bun run script/build-portable.ts` and the +smoke as `bun run script/portable-smoke.ts` until the parent clears the manifest. No runtime +source, Core, or wiring is touched by this slice. + +## Deferred (needs parent ownership) + +- Whether the portable directory **replaces or augments** the existing source launcher + `dist/interactive/kilo2` (identity/channel surface — parent owns it). The artifact keeps the + `kilo2` name and `Kilo internal preview` banner. +- **Closure trimming** (drop unused providers/indexing) and the final external set. +- **`package.json` script registration** (`build:portable`, `smoke:portable`) once the manifest + is unfrozen. +- **Multi-arch / Windows** builds and verification. +- **Updater/publishing** — unchanged, still a separate concern. + +## Exact root wiring sent to parent (decisions pending) + +1. Entrypoint: bundle `src/tui-preview.ts` (reaches `launch()`/execution), not headless + `src/index.ts serve` (admission-only). Scriptable surface for the smoke = the `serve`/ + launch host path, not the TUI renderer. +2. External set: **all bare package imports** (workspace + npm), via `onResolve`, to preserve + single Effect identity and `import.meta.resolve`. +3. Staged assets: hoisted production `node_modules` (real dirs, workspace links dereferenced), + `@opencode-ai/pty` + `@opencode-ai/pty-`, `@opentui/core` + + `@opentui/core-`, `@opentui/solid` preload, bundled Bun runtime. +4. Smoke: real `launch()` host + loopback gateway + in-process `importCredential` (public HTTP + has no credential-import RPC; OAuth needs the real backend). diff --git a/migration-tracking/technical-notes/baseline/pinned-v2-baseline.md b/migration-tracking/technical-notes/baseline/pinned-v2-baseline.md new file mode 100644 index 000000000000..b005831e67ae --- /dev/null +++ b/migration-tracking/technical-notes/baseline/pinned-v2-baseline.md @@ -0,0 +1,250 @@ +# Pinned upstream v2 baseline + +Phase 0 baseline for `plans/kilo-opencode-v2-issue-13750.md` (originally +captured as slice 1 of the now-superseded `plans/kilo-opencode-v2-port.md`, bead +`kilocode-cmy`). + +This records the exact refs the Kilo OpenCode v2 line is based on, the +authoritative validation commands **as defined by this v2 tree**, and the +results of running them on pristine upstream v2. Everything below was produced +on 2026-08-13 on branch `johnnyeric/v2-baseline` with no Kilo product code in +the tree. + +Reproduce with `bun kilocode/script/v2-baseline.ts --checks`. + +## Refs + +Fetched with `git fetch origin main` and `git fetch upstream dev v2` immediately +before recording. `origin` is `https://github.com/Kilo-Org/kilocode.git`; +`upstream` is `https://github.com/anomalyco/opencode.git`. + +| Ref | SHA | Commit date | Subject | +|---|---|---|---| +| `upstream/v2` (pinned) | `76dbaf20adbd43fd208a00ef3cda4a51e125a234` | 2026-08-12T18:18:24-04:00 | `fix(catalog): serve app shell at lab route (#42159)` | +| `upstream/dev` | `14b37df39168eaf6a6faf862ec4a7bbe9c825bbd` | 2026-08-12T22:36:40Z | `chore: update nix node_modules hashes` | +| `origin/main` | `f7115470740c51fd4200fedd9ad3edfad60589ce` | 2026-08-12T10:38:03-06:00 | `Merge pull request #13077 from Kilo-Org/fix/ci-test-stability-in-process` | + +`upstream/dev` moved after the pin was chosen. The pin is deliberately the +`upstream/v2` tip at pin time, not the newest `upstream/dev` commit. + +## Merge bases and ancestry + +``` +$ git merge-base upstream/dev upstream/v2 +0e2dd4ad150d0182fc9e43d81424d8db11465977 +$ git merge-base origin/main upstream/v2 +0e2dd4ad150d0182fc9e43d81424d8db11465977 +$ git merge-base origin/main upstream/dev +dc67b7a3bed6334a9639fe1362bb90c56288bc7c +$ git merge-base --octopus origin/main upstream/dev upstream/v2 +0e2dd4ad150d0182fc9e43d81424d8db11465977 +``` + +`0e2dd4ad150d0182fc9e43d81424d8db11465977` (`chore: generate`, 2026-06-26) is +the dev/v2 divergence point and also the base shared with Kilo `origin/main`. + +Commits ahead of that base, verified 2026-08-13: + +| Ref | Commits ahead of `0e2dd4ad` | +|---|---| +| `upstream/dev` | 899 | +| `upstream/v2` | 1470 | +| `origin/main` | 3391 | + +Ancestry checks at the time of writing: + +``` +$ git rev-parse HEAD +76dbaf20adbd43fd208a00ef3cda4a51e125a234 +$ git merge-base --is-ancestor 76dbaf20adbd43fd208a00ef3cda4a51e125a234 HEAD; echo $? +0 +$ git rev-list --count upstream/v2..HEAD +0 +$ git rev-list --count HEAD..upstream/v2 +0 +``` + +At baseline capture, `HEAD` was exactly the pinned SHA: the tree carried no Kilo +commits, so every result below is pristine upstream v2. The slice's own commit +sits on top of that; `git rev-list --count upstream/v2..HEAD` is expected to be +non-zero afterwards, while the first two checks must keep holding. + +`johnnyeric/kilo-opencode-v2` (the integration branch) also pointed at the +pinned SHA and is checked out in a different worktree; this slice did not touch +it. + +## Toolchain + +| Tool | This host | Upstream CI at the pin | +|---|---|---| +| bun | 1.3.14 | 1.3.14 (`packageManager` in root `package.json`, via `.github/actions/setup-bun`) | +| node | v22.23.2 | 24 for install/unit; 26.4.0 for the Node build step; 24.15 for e2e | +| git | 2.50.1 (Apple Git-155) | 2.52.0 | +| OS | macOS 26.5.1, Darwin 25.5.0, arm64 | `blacksmith-4vcpu-ubuntu-2404` and `blacksmith-4vcpu-windows-2025` | +| ffmpeg | not installed | installed on Linux by the unit job | + +`bun install` reported `5088 packages installed` and left `bun.lock` unchanged +(`git status --porcelain` empty afterwards). + +## Authoritative v2 commands + +Derived from this tree only — root `package.json`, `turbo.json`, +`.github/workflows/typecheck.yml`, `.github/workflows/test.yml`, and per-package +`package.json` scripts. Kilo main's `packages/opencode` commands do not apply: +that package does not exist on v2. + +| Purpose | Command | Working dir | +|---|---|---| +| Install | `bun install` | repo root | +| Typecheck (all packages) | `bun typecheck` (= `bun turbo typecheck --concurrency=3`) | repo root | +| Unit tests (all packages) | `GITHUB_ACTIONS=false bun turbo test` | repo root | +| Generated client contract | `bun run check:generated` | `packages/client` | +| Generated docs/OpenAPI | `bun run check:generated` | `packages/www` | +| CLI binary build | `bun run script/build.ts --single --skip-install` | `packages/cli` | +| Service lifecycle smoke | `bun run script/service-smoke.ts` | `packages/cli` | +| Node CLI build + smoke | `bun run script/build-node.ts --single --skip-install --outdir=dist/node` then `bun run script/service-smoke.ts --node` | `packages/cli` | +| Client regeneration | `bun run generate` | `packages/client` | + +Constraints this tree imposes: + +- Tests cannot run from the repo root. `bunfig.toml` sets + `[test] root = "./do-not-run-tests-from-root"` and the root `test` script + exits 1. Use `bun turbo test` or run from a package directory. +- Typecheck must go through `bun typecheck` / `bun turbo typecheck`, never + `tsc` directly (root `AGENTS.md`). Packages use `tsgo`. +- `.husky/pre-push` enforces the bun version and runs `bun typecheck`. +- The `e2e` job in `.github/workflows/test.yml` is skipped on the v2 branch by + its own `if: github.ref_name != 'v2' && github.head_ref != 'v2'`. It is not + part of the v2 baseline gate. +- `bun run lint`, `bun run lint:effect-patterns`, and `bun run test:lint-rules` + exist but are not run by any workflow in this tree. + +## Results on pristine upstream v2 + +`GITHUB_ACTIONS=false bun turbo test` was run twice: once as CI runs it, then +once with `--continue` so a single failure would not mask the rest. + +| Check | Result | Detail | +|---|---|---| +| `bun install` | pass | 5088 packages, 13.2s, lockfile unchanged | +| `bun typecheck` | pass | 34 of 34 turbo tasks successful, 17.3s | +| `GITHUB_ACTIONS=false bun turbo test` | **fail** | 17/23 tasks successful, stopped at `@opencode-ai/app#test` | +| `GITHUB_ACTIONS=false bun turbo test --continue` | **fail** | 20/23 successful; failures: `@opencode-ai/app#test`, `@opencode-ai/tui#test`, `opencode-drive#test` | +| `packages/client` `bun run check:generated` | pass | no generated drift | +| `packages/www` `bun run check:generated` | pass | no generated drift | +| `packages/cli` build + `service-smoke.ts` | pass | `cli-darwin-arm64` built, smoke exits 0 | +| `bun run lint` | **fail** | 4232 warnings, 1 error (not CI-enforced) | +| `bun run lint:effect-patterns` | **fail** | 33 errors (not CI-enforced) | +| `packages/cli` Node build + `service-smoke --node` | not run | CI pins Node 26.4.0 for this step; this host has Node 22.23.2 | + +## Pre-existing failures + +None of these were introduced by this slice. Every one reproduced with `HEAD` +exactly at the pinned SHA and no Kilo files in the tree. + +### Also red on upstream CI at the pinned SHA + +Upstream's own `unit (linux)` and `unit (windows)` checks fail at +`76dbaf20`. Read-only confirmation: + +``` +$ gh api repos/anomalyco/opencode/commits/76dbaf20adbd43fd208a00ef3cda4a51e125a234/check-runs +unit (linux) completed failure +unit (windows) completed failure +typecheck completed success +build-cli completed success +generate completed success +e2e completed skipped +``` + +1. **`opencode-drive`**, in `test/instance/config.test.ts`: "instance + configuration > initializes the simulation provider with the current OpenCode + provider shape" fails with `SyntaxError: Failed to parse JSON`. Reproduced + locally and present verbatim in the upstream `unit (linux)` log. This is the + failure that turns upstream's job red. +2. **`@opencode-ai/tui`**, in `test/mini/footer.view.test.tsx`: "run entry + content preserves monochrome markdown grammar" expects the ASCII fallback + `Caf? -> ...` but receives `Café → …`. Reproduced locally and present in the + upstream `unit (linux)` log at the same SHA. + +### Host-environment failures, green on upstream CI + +3. **`@opencode-ai/app`**, in `src/i18n/desktop-native.test.ts`: "desktop native + locale detection > uses Unicode likely subtags for script-sensitive bundles" + — `detectDesktopNativeLocale(["pa-PK"])` returns `"en"`, expected `"pa"`. + Root cause is ICU data, not code. `packages/app/src/i18n/desktop-native.ts:158` + maps `pa` to `pa-Arab-PK` and compares maximized scripts, but this host + resolves the newer Nastaliq subtag: + + ``` + $ bun -e 'console.log(new Intl.Locale("pa-PK").maximize().toString())' + pa-Aran-PK + $ node -e 'console.log(new Intl.Locale("pa-PK").maximize().toString())' + pa-Arab-PK + ``` + + Upstream `unit (linux)` at the same SHA reports `676 pass / 0 fail` for this + package; this host reports `675 pass / 1 fail`. Local only. + +4. **`opencode-drive`** — three `test/driver/index.test.ts` cases fail with + `NotFound: ChildProcess.spawn (ffmpeg ...)`. The upstream unit job installs + ffmpeg on Linux; this host has none. Local only; install ffmpeg to clear + them, which still leaves failure 1. + +### Not gated by CI + +5. `bun run lint` (4232 warnings, 1 error) and `bun run lint:effect-patterns` + (33 errors) are red on pristine v2. No workflow in this tree runs them, so + they are not a regression signal on their own. Use them only as a delta + against this baseline. + +**Consequence for later slices:** a red `bun turbo test` does not by itself mean +a Kilo change broke something. Compare against this table, and re-run +`bun kilocode/script/v2-baseline.ts --checks` from a clean checkout of the +pinned SHA when a failure is ambiguous. + +## Why no fork CI workflow yet + +`.github/workflows/` is upstream-owned and this slice adds no workflow file, +deliberately: + +- This effort is local-only by explicit instruction. Nothing is pushed, so no + workflow added here could actually run; committing one would claim CI + coverage that does not exist. +- The baseline is already red upstream (failures 1 and 2). A fork workflow + running the same commands would be red on its first run, which is a worse + signal than this report plus the reproducer script. +- The integration branch `johnnyeric/kilo-opencode-v2` has no remote yet, so a + workflow has no meaningful trigger to target. `test.yml` and `typecheck.yml` + trigger on `push` to `dev`/`v2` and on `pull_request`; adapting them needs the + fork's branch and remote model to exist first. +- Editing shared upstream workflow files conflicts on every upstream merge and + is exactly what the fork conventions try to avoid. + +Revisit when the integration branch gets a remote. At that point the smallest +useful step is a Kilo-owned workflow that runs +`bun kilocode/script/v2-baseline.ts --checks` and compares against this report, +not a copy of upstream's `test.yml`. + +## Reproducer + +`kilocode/script/v2-baseline.ts` is the automation for this report. + +``` +bun kilocode/script/v2-baseline.ts # offline ref and ancestry checks only +bun kilocode/script/v2-baseline.ts --checks # also replay the commands above +``` + +It asserts the pinned SHA is still an ancestor of `HEAD` and that local +`upstream/v2` still resolves to it, then runs the install, typecheck, test, +generated-artifact, and CLI build/smoke commands, writing per-check output to +`kilocode/baseline/logs/` (gitignored by the root `logs/` rule). It exits +non-zero if any check fails; it never suppresses a known-red check, because +suppression would hide a real regression behind a pre-existing one. + +`kilocode/script/` is intentionally not a workspace package, so the script is +not covered by `bun typecheck`: every typecheck project in this repo lives +inside a workspace, and `@types/bun` is only installed in packages that declare +it. Making `kilocode/` a workspace would mean editing upstream's root +`package.json` for one script. It is validated by running it in both modes +instead. Revisit if `kilocode/script/` grows beyond a couple of files. diff --git a/migration-tracking/technical-notes/baseline/plan-v2-parity.md b/migration-tracking/technical-notes/baseline/plan-v2-parity.md new file mode 100644 index 000000000000..447de7277831 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/plan-v2-parity.md @@ -0,0 +1,127 @@ +# V1 Plan workflow parity boundary + +At Kilo `origin/main` commit `ecccd1f`, Plan is a workflow rather than only a +read-only agent: the native Plan prompt prohibits implementation, `plan_exit` +requires a saved plan, and `PlanFollowup` presents Start new session, Continue +here, and Keep refining. Dismissal or rejection performs no implementation. + +V2's built-in `opencode.plan` is narrower. It gives agent `plan` write access +only to `$HOME/.opencode/plan`, injects that directory in its hard-coded +reminders, and has no plan-file completion or follow-up transition. + +The existing SDK post-registration seam can make a host-enforced definition +with ID `opencode.plan` win the built-in duplicate ID. A separate post policy +cannot remove the builtin reminder or its `$HOME/.opencode/plan` allowance, so +replacement is required to establish `.kilo/plans` as the sole Plan boundary. + +## Implemented public seam + +`packages/kilo-cli/src/plan-policy.ts` is a Kilo-owned post replacement. The +supervisor deliberately retains the last SDK post plugin for an enforced +duplicate ID, so the builtin is suppressed rather than composed. No shared Core +patch or private Core import is involved. + +`ToolEditor.get("question")` exposes the native bound executor, input schema, +output schema, and `ToolContext`. The policy validates through those schemas, +calls that executor with the original `plan_exit` context, and thus reuses +native Form display, question permission evaluation, and its deliberate +cancellation defect. It only calls public `SessionDomain.create`, +`switchAgent`, or `prompt` after an exact listed answer. Rejection, dismissal, +and an unlisted free-form answer do not transition to implementation. + +`plan_exit` is additionally passed through the interactive host's +`ToolAuthorizer` before path validation or a completion Form. The plugin fails +closed if that host callback is absent. Its visibility carries the same +`plan_exit` action, while explicit configured global denials are copied from +`build` when the native Plan agent is constructed after config discovery. A +reload resets Kilo ownership before checking for a configured `plan` agent, so +a newly configured Plan agent remains wholly user-owned. New-session handoff +preserves the active session's selected model, matching V1's fallback when no +separate Code model is configured. + +The replacement keeps V1's planning behavior and safe-tool posture while making +the V2 boundary explicit: only existing, non-empty regular Markdown files whose +realpath is beneath `/.kilo/plans` can complete Plan mode. V2 has no +equivalent to V1's read-only shell allowlist, so `shell` is denied rather than +presenting a non-enforced read-only claim. Config discovery runs first. A +configured `plan` agent is left wholly untouched, including a `question` deny; +that custom agent cannot use Kilo's `plan_exit`. The public editor has no +provenance for individual built-in versus configured permission rules, so the +owned native Plan policy does not try to retain arbitrary preexisting denies. + +## Save permission regression fix (2026-09-05) + +The Plan agent could never actually save a plan file. Core tool authorization +evaluates in-Location mutations against a Location-relative resource +(`LocationMutation.Target.resource`, e.g. `.kilo/plans/approved.md`), while the +Plan policy's `edit` allowance used an absolute `/.kilo/plans/*.md` +glob. `Wildcard.match` never matched the two forms, so every real `write`/`edit` +call fell through to the trailing `*`/`*` deny. Earlier tests precreated the +plan file, called `plan_exit` directly, and evaluated `Permission.evaluate` +with absolute paths, masking the mismatch. The allowance now uses the +Location-relative `.kilo/plans/*.md` form. A dead `external_directory` +allowance for the location's own plans directory was removed: `LocationMutation` +marks those paths internal lexically, so an external request for them is +unreachable, and no blanket external allow exists. Inherited configured denies +stay appended last, so global user denies (e.g. `edit *.md`) still win over the +Plan allowance, verified through a real denied `write` and unchanged file +contents. Saved-permission grants still cannot bypass configured denies. + +`plan_exit` diagnostics for missing targets now distinguish ENOENT ("save the +plan ... before calling plan_exit") from other filesystem errors, which surface +with their real message instead of being reported as nonexistent. + +## Prompt save-consent workflow (2026-09-05) + +The owned native plan prompt now instructs the model to use the native question +tool to ask the user to choose between "Finalize and save the plan" and +"Continue refining" before creating or updating a plan file, matching V1's Plan +File reminder behavior at `ecccd1f`. This save consent is prompt-directed only: +there is no host-enforced deterministic write-consent gate, and the host cannot +force a modal before `write`. Host enforcement remains at `plan_exit`, which +requires the saved non-empty real Markdown file; a cancelled save form leaves +no file, no completion form, and no implementation (covered by a test), while a +user-declined save ("Continue refining" answer) is prompt-directed behavior not +host-tested. The prompt also resolves the former contradiction where the final +"Do not implement source or documentation changes" line read as prohibiting the +plan file itself: plan files under `.kilo/plans` are now stated as the one +permitted exception, and the old "tell the user to switch agents" line is +scoped so normal completion always flows through `plan_exit` rather than a +manual switch suggestion. + +Test evidence exercises the real flow against a loopback fixture model: with no +`.kilo/plans` directory, the model's native question opens the save form, a +real `write` tool call creates the plan file (persisted contents asserted), +`plan_exit` opens the completion form, and "Continue here" implements in +session. The captured model request is asserted to advertise the `write` and +`edit` tools and to carry the updated system prompt including the +save-consent instruction and the plan-file exception. Writes outside the plan +directory are denied through real tool authorization and leave the tree +untouched. + +## Timestamped filenames and TUI handoff (2026-09-10) + +The built-in Plan prompt now supplies a concrete Unix-millisecond filename prefix +and instructs new plans to use `-.md`. +Refinements retain the same filename. This is a model instruction, not a rename +of existing plans or a filesystem-level naming restriction. + +The server already created and prompted a fresh implementation session after +“Start new session”, but the TUI had no handoff consumer. Successful `plan_exit` +now carries the destination session ID in tool-result metadata. The owned TUI +listener opens it only when the originating Plan session is currently displayed, +with duplicate handling and activation cleanup. The source remains in Plan mode; +the destination uses Code and retains the selected model. Tabs-disabled clients +navigate to the new session instead. + +The native plugin `tabs.open` implementation previously only selected a route, +which could replace a preview tab. It now promotes the target before selection. +The handoff promotes the source first, preserving the original Plan tab. + +Validation: 15 Plan policy and real-renderer handoff tests pass. The renderer test +chooses “Start new session” through the actual question UI, observes the new +implementation prompt and Code agent, and verifies the original tab remains. +CLI and TUI typechecks pass. The broader native tab suites recorded 64 passes +and two shared-storage notification timeouts; those suites exercise native tab +contexts directly rather than the modified plugin API. No blanket green-suite +claim is made. All checks were local; no running user session was restarted. diff --git a/migration-tracking/technical-notes/baseline/reliability-checkpoint-2026-09-07.md b/migration-tracking/technical-notes/baseline/reliability-checkpoint-2026-09-07.md new file mode 100644 index 000000000000..6ca5c1076829 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/reliability-checkpoint-2026-09-07.md @@ -0,0 +1,46 @@ +# Port reliability checkpoint — 2026-09-07 + +Scope: current working tree, including existing uncommitted ports. No live +accounts or user stores were used. This is a regression checkpoint, not whole +capability acceptance or deployed-service verification. + +## Fixed regressions + +- **Signed-out Gateway availability.** A configured endpoint could leave the + provider enabled when its public catalog was empty or failed. The Gateway + transform now disables the provider when there are no anonymous free records, + in addition to withdrawing models. A later successful refresh restores the + provider and free model. The existing real-host login test reproduced the + failure before the fix and passes afterward. +- **Sidebar registration lifetime.** Repeated narrow/wide resizing duplicated + credits/version rows and clipped benchmark content. Sidebar installers were + called inside the app-slot render callback, although their registrations live + for the plugin activation. Register them once in plugin setup instead. The + existing benchmark resize test failed independently before the change and + passes afterward; it now also checks repeated resizes and exactly one credits + section and version row. UI hooks remain in the app contribution. + +## Validation + +- Fresh-artifact CLI regression run: **528 passed, 9 skipped, 2 failed** across + 92 files (3,154 assertions). The two failures are the regressions above. The + run started before the fixes; do not describe this as a clean final-tree run. +- Gateway package: **65 passed**, 687 assertions. After adding the recovery + assertion, the plugin suite passed **34 tests**, 528 assertions. +- Real-host Gateway login and anonymous catalog scenarios: **5 passed**, + 88 assertions after the fix. +- Benchmark sidebar, full TUI and privacy retests: **7 passed**, 31 assertions. +- Gateway and CLI package typechecks passed; whitespace checks passed. +- Portable launcher rebuilt with both fixes and passed real TUI rendering, + daemon start/reuse and real-terminal attachment with independent client exit. +- Independent GLM review confirmed the sidebar registration lifetime correction; + the upstream remount trigger itself remains unconfirmed. + +The nine skips comprise seven host-dependent sandbox cases and two explicitly +gated MCP host scenarios. They are not passing coverage. Existing deployment, +IDE and sandbox activation-seam gates remain open. + +Detailed logs are in BB thread storage for `thr_p5ywnfq8qz`, named +`reliability-suite.log`, `reliability-gateway.log`, +`reliability-gateway-recovery.log`, `reliability-sidebar-final.log` and +`reliability-bundle-smoke.log`. diff --git a/migration-tracking/technical-notes/baseline/remote-share-next-contracts.md b/migration-tracking/technical-notes/baseline/remote-share-next-contracts.md new file mode 100644 index 000000000000..78352c2e97c4 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/remote-share-next-contracts.md @@ -0,0 +1,464 @@ +# Remote and sharing — next implementation contracts + +Consultation audit, 2026-09-06. Read-only except this file. Scope: the frozen +accepted batch in this worktree is untouched; no task transitions, commits, +installs, fetches, credentials, or live relay/share traffic. Sources, all read +locally: this checkout (`kilo-v2` at `61a8707c03` plus the frozen dirty batch), +Kilo v1 pinned `ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7` (`git show`), and the +sibling cloud checkout `/Users/johnnyamancio/Workspace/kilo_workspace/cloud` +(clean worktree at `c32be17d6`; its `origin/main` +`08c4887fa68738f19089284101084f404eb6c9b8` read through local git objects only). +Sharing and remote are kept distinct throughout. The accepted remote +alias/admission and inline-attachment slice is not re-audited. + +## 1. New consumer-contract evidence (remote line) + +`kilocode/baseline/remote-v2-parity.md` records transcript forwarding and +permission/question translation as blocked because "no relay consumer schema is +present in this source tree to validate a translation." That boundary is now +sharpened: the consumer contract exists in the local cloud checkout and is +readable without network. + +- Relay frame and forwarding: cloud `origin/main` + `services/session-ingest/src/types/user-connection-protocol.ts` — + `CLIOutboundMessageSchema` accepts `{ type: "event", sessionId, +parentSessionId?, event: string, data: unknown }`; the CLI's own + `remote-protocol.ts` shapes are already a strict subset. + `UserConnectionDO.handleCliEvent` forwards these frames verbatim to + subscribed web sockets (`WebInboundMessageSchema` re-exposes the same loose + envelope). +- Deployed consumer validation: cloud `origin/main` + `packages/cloud-agent-sdk/src/normalizer.ts` — `normalizeCliEvent(eventType, +data)` is documented as the UserConnectionDO consumer entry point and + validates per-event data schemas in + `packages/cloud-agent-sdk/src/schemas.ts`: + `permissionAskedDataSchema` = `{ id: string.min(1), permission: string, +tool?: { callID }, patterns: string[] (catch []), metadata: record (catch +{}), always: string[] (catch []) }`; `questionAskedDataSchema` = `{ id, +tool?: { callID }, questions?: array (catch undefined) }`; + `messageUpdatedDataSchema` = `{ info: { id, sessionID } }` passthrough; + `messagePartUpdatedDataSchema` = `{ part: { id, sessionID, messageID } }` + passthrough; `messagePartDeltaDataSchema` = `{ sessionID, messageID, partID, +field, delta }`; `messagePartRemovedDataSchema` = `{ sessionID, messageID, +partID }`; `sessionStatusDataSchema` = `{ sessionID, status: busy | idle | +retry }`; `sessionIdleDataSchema` = `{ sessionID }`; + `sessionQueueChangedDataSchema` = `{ sessionID, queued: string[] }`; + `questionReplied/RejectedDataSchema` and `permissionRepliedDataSchema` = `{ +requestID }`. +- Transcript rendering consumes v1 `Message`/`Part` shapes from + `@kilocode/app-shared/opencode` (`packages/app-shared/src/opencode.gen.ts`, + the v1 opencode algebra): the SDK chat processor upserts `message.updated` + info and `message.part.updated` parts into storage keyed by v1 message/part + identities; the mobile agent-chat screen imports + `@kilocode/cloud-agent-sdk` (cloud `origin/main` `apps/mobile/src/app/(app)/ +agent-chat/[session-id].tsx`). + +This is the same evidence class the sharing audit used for the ingest service +(local cloud refs, no deployment proof). It converts the previously open +"consumer unknown" concern into a testable translation target and leaves one +honest residual: whether the deployed relay matches `origin/main` — a +deployment-verification gate of the same class as sharing gate 1, not a local +gap. + +## 2. Legacy wire shapes retained in the v2 tree + +No cross-repo fixtures are needed for the legacy encoder side; the v2 tree +retains the exact v1 contracts: + +- `packages/schema/src/v1/permission.ts` — `PermissionV1.Request` `{ id, +sessionID, permission, patterns, metadata, always, tool?: { messageID, +callID } }`, `Reply` `once|always|reject` (lines 27-44). +- `packages/schema/src/v1/question.ts` — `QuestionV1.Request` `{ id, sessionID, +questions: Info[], tool? }`, `QuestionV1.Info` `{ question, header, options: +[{label, description}], multiple?, custom? }`, `QuestionV1.Answer` = + `string[]`, `QuestionV1.Reply` `{ answers: string[][] }` plus + `Replied`/`Rejected` event shapes (lines 15-56). +- `packages/schema/src/v1/session.ts` — `SessionV1` message/part algebra: + `partBase { id: PartID("prt_…"), sessionID, messageID }` (81-85), + `TextPart`, `ReasoningPart`, `ToolPart { callID, tool, state: +pending|running|completed|error, metadata? }` (259-315, 315-324), `User` / + `Assistant` messages (332-490), `Part` union (486-500). +- `packages/kilo-cli/src/remote-protocol.ts:30-44` — `RemoteEventSchema` (`{ +type: "event", sessionId, parentSessionId?, event, data }`) is already in + `RemoteOutboundSchema`; the adapter simply never emits `event` frames today + (`packages/kilo-cli/src/remote-session.ts:92-105` subscribes to the v2 event + stream but only uses it to trigger heartbeats). + +## 3. Remote slices, strongest first + +### R1 — permission translation (out, in, replay) + +Fully mechanical; every field is source-proven on both ends. + +Outbound: v2 ephemeral `permission.asked` (data = `Permission.Request.fields`: +`packages/schema/src/permission.ts:25-53`) → legacy event frame +`permission.asked` with `PermissionV1.Request` data: + +| PermissionV1 field | v2 source | Ref | +| ------------------ | -------------------------------------------------------------------------------------------- | ----------------------- | +| `id` | `request.id` (`per_…`) | permission.ts:10-14, 38 | +| `sessionID` | `request.sessionID` | permission.ts:26 | +| `permission` | `request.action` | permission.ts:27 | +| `patterns` | `request.resources` | permission.ts:28 | +| `metadata` | `request.metadata ?? {}` | permission.ts:30 | +| `always` | `request.save ?? []` | permission.ts:29 | +| `tool` | `request.source` when `{type:"tool"}` → `{ messageID: source.messageID, callID: source.id }` | permission.ts:16-23 | + +The consumer validates this exactly (`permissionAskedDataSchema`), including +the `per_`-prefixed id (min 1, no prefix check). `permission.replied` → legacy +`permission.replied` `{ sessionID, requestID, reply }` (consumer needs +`requestID`). + +Inbound: v1 command `permission_respond` with data `{ requestID, reply: +"once"|"always"|"reject", message?, interactive? }` (v1 sender +`remote-sender.ts:44-50,1296-1312`) → public +`client.session.permission.reply({ sessionID, requestID, reply, message? })` +(`packages/protocol/src/groups/permission.ts:117-135`; client input +`packages/client/src/promise/generated/types.ts:5638-5643`). The `interactive` +bit: v1's only consumer is the sensitive-approval gate at +`ecccd1f packages/opencode/src/permission/index.ts:290-307` (refuses +non-interactive approvals of `metadata.skillShell`/`sandboxEscalation` +requests). V2's `Permission.reply` (`packages/core/src/permission.ts:254-310`) +has no interactive field and no sensitive marker gate, and no v2 producer +marks `skillShell` metadata today (`packages/kilo-cli/src/skill-policy.ts` sets +none). The translation must not invent a wire field; the honest options are (a) +parse and ignore the bit as v1 wire compatibility — the relayed reply is +human-originated by transport construction (JWT-authenticated web client), the +same trust position v1 encoded by sending `interactive: true` — or (b) record a +follow-up to give v2's reply API an interactive gate. Option (a) is the local +slice; it is a documented wire-compat drop, not a claim the v1 gate exists. + +Replay on subscribe: v1 replayed pending permissions +(`remote-sender.ts:511-553`); v2 source is +`client.permission.request.list` (location-scoped, +`packages/protocol/src/groups/permission.ts:23-36`) or +`client.session.permission.list` (session-scoped, :89-100). + +### R2 — question translation (out, in, replay) + +The v2 Core already defines the question↔form bridge the adapter needs — +this is what resolves the parity doc's "mapping either would silently discard +semantics" note. The producer convention is `packages/core/src/tool/ +plugin/question.ts`: forms carry `metadata: { kind: "question", tool: { +messageID, id } }` (lines 78-81) and one field per question `toField(question, +index)` = `{ key: "q${index}", title: header, description: question, type: +multiple ? "multiselect" : "string", options: [{ value: label, label, +description }], custom: true }` (115-128); answers are read back positionally +from `state.answer["q${index}"]` and normalized to `string[][]` (94-100). + +Outbound: v2 ephemeral `form.created` (`{ form: Info }`, +`packages/schema/src/form.ts:160`) with `metadata.kind === "question"` → legacy +`question.asked` with `QuestionV1.Request` data: `id` = `form.id`, `sessionID` += `form.sessionID`, `tool` = `{ messageID, callID: id }` from +`form.metadata.tool`, `questions[i]` = `{ question: field.description, header: +field.title ?? "", options: field.options.map(o => ({ label: o.value, +description: o.description ?? "" })), multiple: field.type === "multiselect", +custom: field.custom }`. Core's producer never emits the v1 Kilo hint +extensions (`labelKey`, `mode`, `default`), so their omission is faithful, not +lossy. `Form.Info`/`Field` shapes: `packages/schema/src/form.ts:39-133`. + +Inbound: `question_reply` `{ requestID, answers: string[][] }` (v1 sender +`remote-sender.ts:31-35,1233-1247`) → `client.session.form.reply({ +sessionID, formID: requestID, answer })` where `answer` is built by mapping +each positional answer through the form's actual `fields` order (fetch via +`client.session.form.get`, client paths +`packages/client/src/promise/generated/client.ts:1397-1479`): index i → +`fields[i].key`; multiselect → `string[]` (labels are values by producer +construction); string-with-options → first label; empty → omit (Core reads +missing keys as `[]`). Keys are derived from the form, never assumed `q${i}` — +that is what avoids the silent-semantics discard. `question_reject` `{ +requestID }` → `client.session.form.cancel`. Non-question forms (MCP +elicitation and any other `metadata.kind`) stay refused, as today. + +Outbound lifecycle: `form.replied` → legacy `question.replied` `{ sessionID, +requestID, answers }` (positional reconstruction from the same field order); +`form.cancelled` → `question.rejected` `{ sessionID, requestID }`. Replay on +subscribe: `client.form.request.list` (location-scoped, +`packages/protocol/src/groups/form.ts:39-53`) filtered to +`metadata.kind === "question"`, matching v1's `replay()` +(`remote-sender.ts:511-530`). + +### R3 — status, idle, and queue events + +- `session.execution.started` → `session.status` `{ sessionID, status: { type: +"busy" } }`; `session.execution.succeeded|failed|interrupted` → + `session.idle` `{ sessionID }` plus a `session.status` idle frame; + `session.retry.scheduled` (`packages/schema/src/session-event.ts:547-558`) → + `session.status` `{ type: "retry", attempt, message, next }` (attempt/at + from the event; consumer `sessionStatusDataSchema`). The adapter already + reconciles busy/idle from `client.session.active()` for heartbeats + (`remote-session.ts:92-105`); this slice adds the legacy event emission on + the same subscription. +- `session.inbox.enqueued|delivered|cancelled` (event.ts:188-217) → + `session.queue.changed` `{ sessionID, queued: string[] }` from the public + `client.session.inbox.list` (`packages/protocol/src/groups/session.ts:527`), + emitted on each event and replayed on every (re)subscribe exactly like v1's + always-sent queue snapshot (`remote-sender.ts:552-562`). Open detail to + settle while implementing: whether `queued` maps to inbox item IDs or the + admitted user-message IDs the adapter already records in `admitted` + (`remote-session.ts:75`) — v1's payload is queued message IDs + (`sessionQueueChangedDataSchema` says "user-message IDs"), and v2 inbox IDs + are `SessionMessage.ID`s, so the two are the same identifier family; verify + against `SessionInbox.Item` at implementation time. +- `session.renamed` (`session-event.ts:102-111`) → `session.updated` `{ info: { +id, title } }` (consumer `sessionUpdatedDataSchema` needs `info.id` only). +- `session.error`-shaped failures: `session.execution.failed` carries `error` + (event.ts:226-231) → legacy `session.error` `{ sessionID, error }` + (consumer `sessionErrorDataSchema` tolerant). + +### R4 — transcript part streaming (implemented in remote-transcript.ts) + +R1/R2 accepted; the slice is implemented in the new Kilo-owned +`packages/kilo-cli/src/remote-transcript.ts` (+ `test/remote-transcript.test.ts`, +minimal `remote-session.ts` dispatch). Every SessionV1 required field maps to a +proven event/projection source; frames with unmapped required fields are never +emitted. + +Module shape: new Kilo-owned `packages/kilo-cli/src/remote-transcript.ts` + +isolated `remote-transcript.test.ts`; `remote-session.ts` gains only dispatch +through the existing ownership gates and `send`. Encoder validation once at +emission against the retained `SessionV1` schemas +(`packages/schema/src/v1/session.ts`) via `Schema.decodeUnknownOption`. + +Part identity: deterministic ids `prt_${assistantMessageID}_t${ordinal}` / +`_r${ordinal}` / `_c${toolCallID}` — stable across started/delta/ended and +across adapter restarts. Emission is full-value `message.part.updated` upserts +only: `session.part.delta`-style append frames are dropped because the +consumer's `applyPartDelta` appends (cloud `origin/main` chat-processor +`message.part.delta` → `sessionStorage.applyPartDelta`), so redelivered deltas +would duplicate text, while full-value upserts are idempotent +(`upsertPart` replaces by id). Live streaming is preserved by adapter-side +accumulation: each delta updates the running text and re-emits the full +accumulated value; `ended` carries the authoritative text and reconciles any +drift from missed deltas. + +Exact field/source matrix: + +- Assistant `message.updated` — NOT emittable from `session.step.started` + alone. `SessionV1.Assistant` requires `time.created`, `parentID` (v1 + MessageID), `modelID`, `providerID`, `mode`, `agent`, `path {cwd, root}`, + `cost` (finite), `tokens {input, output, reasoning, cache.read, cache.write}` + (v1/session.ts:453-485). Proven sources: + - `modelID`/`providerID`/`variant`/`agent` ← `session.step.started` + `{assistantMessageID, agent, model: Model.Ref}` (session-event.ts:310-321). + - `cost`/`tokens` ← `session.step.ended` `{cost: Money.USD, tokens: +TokenUsage.Info}` (session-event.ts:334-351; `TokenUsage.Info` matches the + v1 token shape field-for-field, `total` omitted — optional in v1). So the + assistant info frame is emittable only at `step.ended` (or `step.failed` + when its optional cost/tokens are present); parts stream before the info + frame, which the consumer stores via `upsertPart` keyed by messageID — + rendering-before-info is a consumer behavior to verify in the cloud repo + during implementation. + - `time.created` ← the projected message's `time.created` + (`SessionMessageAssistant.time.created`) or the event envelope `created`. + - `parentID` ← the tracked last delivered user-message id (from the fetched + `session.messages` user message after `session.inbox.delivered`) — a real + id, not a zero; a step with no tracked predecessor delays the info frame + until one exists. + - `path {cwd, root}` ← `cwd` from the adapter's `ctx.location.directory`; + `root` from the session's project root on `Session.Info.location` — verify + the exact projection field at implementation. + - `mode` ← verified producer precedent: the pinned v1 creator sets + `mode: agent.name` with the same real agent it sets on `agent` + (kilocode `ecccd1f` `packages/opencode/src/session/prompt.ts` ~1690 and + ~2352), so v2's `step.started.agent` is the source for both fields; the + remaining unproven fields are the real `path.root`, message `time`, the + `parentID` predecessor, and cost/tokens semantics (the v2 projection's + cost/tokens are optional — the v1 producer initialized them to zeros at + creation and finalized later; the adapter must not invent that). Prefer + the current public projection's full-value snapshots over duplicated + event-side caches where feasible. +- User `message.updated` — `session.inbox.delivered` carries `inboxID` only; + the adapter fetches via public `client.session.messages` + (`packages/protocol/src/groups/message.ts:26`); `SessionMessageUser` + `{id, time.created, text, files?, agents?, skills?}` (generated types + 1692-1701) maps to `SessionV1.User` `{id, sessionID, role: "user", +time.created, agent, model}` — `agent`/`model` on the v1 User message are + REQUIRED (v1/session.ts:332-354) and have no verified v2 user-message source + (the session's selected model/agent are adapter-known facts, recorded as the + candidate mapping to verify). Parts: TextPart from `text`; FileParts from + `files` (below). +- File parts — in scope, not deferred: remote-admitted v2 user messages + already carry inline attachments. `PromptFileAttachment` + `{data: PromptBase64, mime, source, name?, description?}` (generated types + 502-509) → `SessionV1.FilePart` `{mime, filename: name, url: +data:${mime};base64,${data}, source?}` (v1/session.ts:171-176) — the same + data-URL form the accepted send_message slice admits. `PromptFileSource` → + `SessionV1.FilePartSource` (`file` path / `symbol` / `resource` variants, + v1/session.ts:144-169) mapping verified at implementation. +- Tools — `message.part.updated` `SessionV1.ToolPart {callID: id, tool: name, +state}` (v1/session.ts:313-321). `name`: primary source + `session.tool.input.started` `{id, name}`; recovery after a missed + input.started from the PUBLIC message projection — `SessionMessageAssistant +.content[]` carries `name` and the full state union + (`SessionMessageAssistantTool`, generated types 2060-2073), so the adapter + fetches the projection for an unknown call instead of trusting an + unpopulated cache. `called` → the v1 running state + `{status: "running", input: event.input, time: {start: event.created}}` — + every field source-proven; the event envelope carries a real `created` + timestamp (schema/event.ts:61). `failed` → the v1 error state with `input` + from the projection's error state and `error` from the structured error's + real `message`. BLOCKED, recorded: the v1 completed state requires `title: +string` and no v2 event or projection field supplies it — terminal success + never upgrades the running frame (the test asserts zero completed upserts). +- Dropped/omitted, recorded: `message.part.delta` frames (redundant under + full-value upserts; not idempotent on redelivery), `message.part.removed` + (no v2 producer), Shell/Synthetic/System/Skill messages (no v1 Message + counterpart), `session.renamed` → `session.updated` (NOT implemented — R3 + was not part of the accepted slice; it belongs to an explicit later slice, + not "already covered"). +- History authority boundary: a relay-only v2 session has no ingest snapshot — + ingest uploads are the sharing line, and the deployed viewer is v1-only + today (sharing gate 2). No-backfill therefore means remote-relayed v2 + sessions have NO viewer-side history until the sharing line's viewer work + lands; live relay frames are the only transcript surface. This dependency + is recorded rather than assumed covered. +- Tests: a real loopback host drives a text run and a tool run through the + relay; user, assistant, text, file, and running-tool frames are decoded + against the retained `SessionV1` schemas (assistant parentID = the tracked + user message, `path.root` = the public project canonical, real cost/tokens + from `step.ended`), and zero completed tool upserts prove the recorded + title gap. + +### Still gated for remote (not closable locally) + +- Whether the deployed relay/ingest matches cloud `origin/main`'s + UserConnectionDO and SDK schemas — deployment verification, same class as + sharing gate 1. Local tests prove frame + consumer-schema compatibility + only. +- `suggestion.shown`: no v2 producer exists anywhere in this tree (v1's + Suggestion subsystem has no v2 port; the v2 tool set has no `suggest` tool — + tool set verified in `kilocode/baseline/cli-remainder-next-gaps.md`). Not + implementable without inventing one; the consumer would render it, but there + is no v2 fact to forward. +- `exit_cli`, hierarchical attach/detach, cloud clone: unchanged boundary + (`kilocode/baseline/remote-v2-parity.md`). +- Aggregate inline-part bound on `send_message`: still the recorded product + decision; do not close it here. + +## 4. Sharing slices (kept distinct from remote) + +The five canonical gates stand as quoted in +`plans/kilo-opencode-v2-issue-13750.md:1346-1379`; the local side is already +fail-closed. What source inspection adds: + +- Gate 3 (team ownership) has its backend half implemented at cloud + `origin/main`: `kilo_meta` items are extracted by + `services/session-ingest/src/dos/session-ingest-extractors.ts` + (`extractNormalizedOrgIdFromItem`, platform, gitUrl, gitBranch) and the + `organization_id` write is refused without membership proof — + `services/session-ingest/src/ingest/metadata.ts:177-211` calls + `hasOrganizationAccess` and deletes the claim otherwise. The missing half is + plugin-side: the v2 upload batch built in + `packages/kilo-gateway/src/session.ts:195-220` emits only `session` + + `message` items. Emitting `{ type: "kilo_meta", data: { platform, +orgId? } }` from the host's actual validated organization selection (the + account/selection state the gateway already resolves — never session + metadata or a guessed default) is a Kilo-owned local slice, testable against + loopback ingest fixtures. It interacts with the existing fail-closed + `authorizeShare` team refusal: the org claim is what enables validated team + ownership once the deployed backend verifies membership; until deployment is + proven, the refusal stays. Gate 3 still closes only with the deployed + backend (gates 1/5). +- Gate 2 (public viewer) is cloud-repo work, not this worktree: the viewer at + `apps/web/src/app/s/[sessionId]/shared-transcript.ts` (audited at cloud + `origin/main`) requires v1 `message.info.role` and separate parts; v2 + self-contained messages are accepted by ingest storage but dropped by the + viewer. Implementation belongs to the cloud checkout with its own tests; + deployment to the target closes the gate. +- Gates 1/4/5 remain non-local: deployment verification, post-share + synchronization contract, and end-to-end acceptance against the target. + +### 4.1 S1 — organization kilo_meta emission (implemented in session.ts) + +S1 is implemented locally in `packages/kilo-gateway/src/session.ts` and +`packages/kilo-gateway/test/session.test.ts`: + +- Contract alignment: `buildIngestBatch` produces `{ type: "kilo_meta", + data: { platform, orgId? } }` as the leading item of the ingest batch, + matching cloud repo `origin/main` `services/session-ingest/src/types/session-sync.ts` + (`SessionItemSchema`). +- Pure producer and consumed path: `buildIngestBatch` is exposed as the pure + batch builder and is the exact path invoked by `upload` during snapshot + ingestion, eliminating dead code. Speculative unconsumed fields (gitUrl/gitBranch) + are omitted. +- Host-owned validation reuse: organization membership validation is owned by + the existing host `authorizeShare` callback in `plugin.ts`; `session.ts` + validates UUID syntax on host selection without duplicating authority or + introducing unused helpers. +- Fail-closed team share refusal: existing team-share refusal + (`"Team session sharing is not supported in this preview yet"`) is preserved + both on `authorizeShare` and in `handlers.share`, preventing any real team + session upload before deployed backend validation. Stale and non-UUID account + selections fail closed with `"The selected Kilo account is not available"` + with zero cloud calls. +- Personal session uploads: personal accounts emit `{ type: "kilo_meta", + data: { platform: "cli" } }` without `orgId`. +- Local tests (14 passing in `test/session.test.ts`): + - Ingest batch fixture matches cloud ingester schema under real JSON + serialization (`JSON.stringify` / `JSON.parse` round-trip). + - Pure producer helper verified for valid org UUID emission, non-UUID + rejection, and custom platform overrides. + - Stale/invalid organization selection refuses share before network requests. + - Team sharing attempt refuses share with zero backend calls. + +### 4.2 Exact remaining sharing gates + +1. **Gate 1 (Deployment verification):** Verify target deployment runs the + newer purpose-bound share token contract and public read route with + `kilo_meta` extractor support. +2. **Gate 2 (Public viewer v2 support):** Update cloud repo + `apps/web/src/app/s/[sessionId]/shared-transcript.ts` to render + self-contained v2 messages instead of dropping them. +3. **Gate 3 (Team ownership backend acceptance):** Client-side S1 emission is + complete; backend authorization and ownership transition remain gated on + target deployment acceptance. +4. **Gate 4 (Continuous sync & tombstone contract):** Implement post-share + incremental synchronization and removal/revert handling once backend + contract settles. +5. **Gate 5 (Deployed end-to-end acceptance):** Validate deployed behavior + with an intentionally shareable fixture. + +## 5. Minimal ownership proposal — next slice + +Implement R1 + R2 together as one slice (permission and question translation); +they share the same seams and tests, and R2 without R1 would leave the relay +answering questions but not permissions. + +- Owned paths only: + - `packages/kilo-cli/src/remote-protocol.ts` — local zod schemas for the + legacy permission/question event data (mirroring `PermissionV1.Request` / + `QuestionV1.Request` fields) and the three inbound commands + `permission_respond`, `question_reply`, `question_reject`; reuse the + in-tree `PermissionV1`/`QuestionV1` values from + `@opencode-ai/schema/v1/*` where the export path allows, otherwise mirror + the shapes locally with provenance comments (no protocol-package changes). + - `packages/kilo-cli/src/remote-session.ts` — translate the v2 event + subscription (`permission.asked/replied`, `form.created/replied/cancelled` + filtered to `metadata.kind === "question"`) into `RemoteEventSchema` + frames; add the three inbound commands mapping to + `session.permission.reply` / `session.form.reply|cancel`; replay pending + permissions and question-forms on `subscribe`; scope everything to the + adapter Location exactly as existing handlers do + (`remote-session.ts:238-260`). + - `packages/kilo-cli/test/remote-session.test.ts` (+ fixture) — extend the + existing loopback-relay host tests: assert outgoing `permission.asked` / + `question.asked` frames decode as `PermissionV1.Request` / + `QuestionV1.Request`; drive a real host form through the question tool + path and round-trip `question_reply` positional answers into the form's + keyed reply; assert `question_reject` cancels durably; assert replay + contents; assert unknown/foreign sessions answer as today. Bundled + `packages/kilo-cli/dist/interactive/bun`, package-local runs only. +- Explicitly out of the slice: transcript part streaming (R4 — own slice, + design decisions above must be settled first), suggestions, exit_cli / + attach/detach / clone, aggregate bound, and any deployment claim. No + Protocol/Server `HttpApi` change is required, so no `bun run generate` + surface is touched. +- Do not count this slice as closing the `/remote` row: the ledger's + transcript-forwarding and deployed-verification boundary remains the row's + own acceptance, and R1/R2 do not claim it. + +Sequencing: R3 is an independent small slice on the same files; R4 is the +substantial one and should land after R1-R3 prove the event-translation +pattern. Sharing slice S1 (kilo_meta emission) is independent of all remote +work and touches `packages/kilo-gateway/src/session.ts` only. diff --git a/migration-tracking/technical-notes/baseline/remote-v2-parity.md b/migration-tracking/technical-notes/baseline/remote-v2-parity.md new file mode 100644 index 000000000000..627f20294d34 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/remote-v2-parity.md @@ -0,0 +1,484 @@ +# Remote-session v2 parity boundary + +This is a source-backed boundary for the Kilo Sessions relay. It does not +authorize a production relay connection or reuse the unrelated Cloud Agent +transport. + +## V1 contract evidence + +- `/remote` controls the Kilo Sessions WebSocket relay, not Cloud Agent: + `origin/main:packages/opencode/src/cli/cmd/remote.ts`, + `origin/main:packages/opencode/src/kilocode/kilo-commands.tsx`, and + `origin/main:packages/opencode/src/kilo-sessions/kilo-sessions.ts` + (`enableRemote`, `disableRemote`, `remoteStatus`). +- V1 dials + `KILO_SESSION_INGEST_URL ?? "https://ingest.kilosessions.ai"` as + `/api/user/cli?token=&connectionId=`; the envelope definitions + are in `origin/main:packages/opencode/src/kilo-sessions/remote-protocol.ts`. + Relay enablement is token-based. `orgId` is optional only on v1 + `create_session`, so a personal account is valid. +- `origin/main:packages/opencode/src/kilo-sessions/remote-sender.ts` accepts + `create_session`, `list_models`, `list_commands`, `list_directories`, + `send_command`, `send_message`, `interrupt`, queue drop, questions, + suggestions, permissions, and `exit_cli`. It forwards arbitrary legacy Bus + events as `{ type: "event", event, data: event.properties }` after + session-id extraction. + +## Implemented v2 adapter + +`packages/kilo-cli/src/remote-session.ts` is Kilo-owned and accepts only an +explicit relay URL, bearer token, and host-injected authenticated +`OpenCodeClient`. It neither reads credentials/environment/account state nor +calls the Cloud Agent or shared-session gateway. + +- Each open, lifecycle transition, subscribe, unsubscribe, and ten-second + tick sends a heartbeat from public `client.session.list(...)`, not just the + subscribe set. It filters to the plugin Location. Busy/idle is reconciled + from public `client.session.active()`; the real v2 + `session.execution.started|succeeded|failed|interrupted` events make that + reconciliation immediate. There is no assumed `session.status` publisher. +- The connection retains its v1 UUID across transient reconnects, buffers at + most 200 outbound frames, and retries once per close with one-second + exponential backoff capped at 60 seconds. Auth/conflict closes + (`4401`, `4403`, `4409`) clear buffered frames and stop retries. Direct + adapter installation returns a read-only live-socket `connected` handle; + the ordinary plugin wrapper deliberately does not expose lifecycle state. +- `create_session` supports v1 protocol version 1, optional agent/model/orgId, + and only the adapter's exact Location. A contained child directory is + deliberately refused: heartbeat and inbound controls are exact-Location + scoped, so accepting it would create an invisible remote session. It uses + public `session.create`, preserves optional `orgId` as metadata, heartbeats + the created session, and returns v1 `{ protocolVersion: 1, sessionID }`. + Cloud cloning is explicitly rejected because its deployed import contract is + a distinct cloud authority. Multi-location remote ownership remains open. +- Text-only and inline-multipart `send_message` call public `session.prompt`. + Parts must be exactly one text part plus zero or more file parts whose URL + is a `data:` URL; http(s) URLs (which v1 materialized by fetching), `file:` + URLs (which would read caller-named local paths), and agent/subtask parts + are explicitly rejected before any prompt is attempted. Inline parts map to + public prompt `files` (`uri`, optional `name` from the v1 `filename`); the + server-side prompt admission owns byte decoding, canonical-base64 and MIME + validation, and the 20 MB limit, and materializes attachments before durable + admission, so a rejected attachment produces an error response and admits + nothing. Its relay success response is emitted only after that public call + resolves, which is the v2 durable inbox-admission boundary. A rejected + admission returns an error; no background prompt is detached or swallowed. + `drop_queued_message` resolves the target from public state only: the command + session must belong to this adapter's Location, and the ID must be pending in + that session's real inbox projection or in one of its same-location + descendants' (the `parentID` walk, matching the root-ID contract for advertised + child queues). It then calls public `session.inbox.cancel`. Because nothing + adapter-local gates it, every advertised ID stays cancellable across a restart, + a resubscribe, or an admission made by another client; a foreign Location, an + unknown session, and an ID that is no longer pending are all refused with + `message not queued`. The descendant walk's only bound is the same 8 parent + edges `subscribedRoot` accepts on the way up, and it follows the listing cursor + to exhaustion, so the discoverable set equals the set whose queues are + advertised — a sibling past any single page is still cancellable by root ID. + The cost is one pending read per descendant, paid only on an explicit + cancellation or an explicit subscribe/reconnect replay, never per forwarded + event. +- `list_models` accepts the sessionless instance-picker path and a + Location-verified session path. It reads only the public + `model.list`/`model.default`/`provider.list` APIs scoped to the adapter's + Location and emits the v1 `RemoteModelCatalog.build` wire shape + (ecccd1f `remote-model-catalog.ts`) with that source's sanitizer bounds: + names sliced to 256, finite non-negative limits, 32 variants of at most 64 + characters with empty values, 2,048-model truncation flag, zeroed cost, + empty options/headers/env, `source: "custom"`, and v1 `Provider.sort` + ordering for the per-provider default. v2 provider/model `settings`, + `headers`, and `body` are never copied onto the wire. Capability facts are + limited to what the public API proves: `toolcall` from v2 `tools` and the + text/audio/image/video/pdf modality booleans from the v2 input/output + arrays. `currentModel` comes from the session's selected model (variant + omitted when default); `defaultModel` from `model.default`, both validated + present in the emitted catalog. A host client without a model catalog gets + an explicit `model catalog is unavailable` error rather than an + approximation. +- `send_command` preflights the location-scoped public command catalog and + rejects an unavailable name before attempting `session.command`. It maps + only protocol-v1 command/arguments; model, variant, and messageID extensions + are rejected rather than discarded. `list_commands` maps public command + names and descriptions to the v1 response with required empty hints and + advertises `canExitSession: true`, the v1 producer contract every CLI emits + (ecccd1f remote-command.ts `build()`), on which the deployed consumer gates + its exit flow. `list_directories` lists one contained canonical directory + level, caps its directory results at 256, and excludes symlink/junction + escapes. `interrupt` and `session.renamed` use public session APIs after + Location verification. +- Every heartbeat advertises `capabilities: { attachments: true }`, the v1 + producer contract the ws layer stamps on every send (ecccd1f remote-ws.ts + `remote-protocol.ts` Capabilities). The deployed relay spreads it onto each + `sessions.heartbeat`/`sessions.list` row (cloud origin/main + UserConnectionDO `handleHeartbeat`/`aggregateSessions`), and the consumer + enables its remote attachment path only from a row advertising it + (cloud-agent-sdk `activeSessionSchema` → `publishCapabilities` → the + mobile fail-closed gate), so the adapter's existing inline-attachment + `send_message` path becomes consumer-reachable. `protocolVersion` is not + advertised: no consumer reads the heartbeat field (protocol detection uses + the `list_models` probe), so no host fact is sourced for it. `sessionClone` + stays absent while cloud clone is refused — v1's contract is that the flag + is present only when the CLI accepts the clone, so its absence keeps the + consumer's clone gate fail-closed. +- Every inbound handler maps public-client rejections to typed errors, so a + failing call always answers the relay (an error response or silence for + fire-and-forget system events) instead of dying on an unhandled defect; + unknown or foreign session IDs answer `session unavailable`. +- `packages/kilo-cli/test/remote-session.test.ts` uses a real loopback Bun + WebSocket plus isolated authenticated v2 hosts. It verifies the exact relay + query form, pre-subscription discovery, create response, durable message + admission before ACK, inline attachment bytes/MIME/name in public history, + rejection of http(s), `file:`, and oversized inline attachments with no + admission, the exact v1 catalog wire shape for a config-seeded provider with + per-provider and catalog defaults, `currentModel`, secret sentinels absent + from all outgoing frames, durable cancellation of an admitted message while + its session is busy, foreign-location denial, contained directory + translation and symlink exclusion, command preflight, rename, interrupt, + transient reconnect, permanent auth-close suppression, and the explicit + no-catalog error. No deployment, account, or upload is used. + +## Explicit remaining compatibility boundary + +The public v2 client does expose session create, permission, and form APIs; +they are not being described as unavailable. The remaining v1 wire shapes do +not map one-for-one yet: + +- Transcript forwarding is IMPLEMENTED (accepted R4, `remote-transcript.ts`): + user/assistant info frames, text/reasoning parts, running/error tool parts, + and file parts stream as full-value upserts with stable producer-ordinal + identities. Known recorded gaps: the v1 tool completed state's `title` has + no v2 source (terminal success never upgrades the running frame), Shell/ + Synthetic/System/Skill messages have no v1 counterpart, and live updates are + full-value upserts (no delta append frames — idempotency over raw + streaming). +- Permission asks/replies and question asks/replies/rejections are translated + (accepted R1/R2; see the next section), as are session status, rename, and + queue events (R3 lane, `remote-status.ts`). Status, retry, error and rename + frames are a pure mapping over the durable payloads. The queue lane is not: + `session.queue.changed` is an authoritative FIFO the consumer reconciles + wholesale, so every frame — live invalidation and subscribe/reconnect replay + alike — is a fresh read of the public pending inbox projection + (`session.inbox.list`, enqueue-ordered, pending rows only). Inbox events only + invalidate; no local list is ever extended, truncated, or replayed as the full + queue, so a fresh adapter, a restarted host, an evicted session, and an item + admitted by another client all advertise the same IDs, and the FIFO is never + bounded. Queue IDs are the pending user inbox item IDs, which are the IDs + `session.inbox.cancel` accepts. Reads are ordered by a per-session ticket: a + read overtaken by a newer one is dropped rather than applied, and the caller's + epoch/transport guard cancels a read whose subscription ended, so no stale + snapshot resurrects a delivered or cancelled item. Unchanged snapshots are + suppressed on the live lane and always re-sent on replay. Both lanes carry the + same liveness guard (captured epoch, subscription membership, transport + generation), so a read that outlives its subscription is discarded instead of + published to a superseded or reconnected consumer. Two consequences worth + stating: an item that is admitted and delivered before the read completes is + never advertised as queued, because it genuinely is not pending; and a + transient pending item can therefore be invisible to the consumer even though + a v1 host would have shown it briefly — the queue is authoritative, not + optimistic. Non-user + inbox work (synthetic, compaction, move) is never advertised. A recovered + execution re-emits busy (idle→busy across shutdown is explicit). Rename + forwards `session.updated` with the real title on the wire, but the pinned + consumer reads only `info.id` and discards the title — no delivered-rename + claim. Subscribe/reconnect replay covers the subscribed session and its + same-location descendants over the same 8-hop, cursor-exhaustive `parentID` + walk the cancellation path uses, so the reconciled set is the set whose live + queues are advertised; permission/question replay continues to use the + validated ancestry walk. Recorded limitations of the queue lane: a descendant + more than 8 parent edges below a subscribed root is neither forwarded nor + reconciled (the forwarding side has the same bound); a session that moves + Location mid-walk is excluded per node rather than transactionally; and the + epoch/transport half of the liveness guard is defence in depth — read ordering + already prevents stale content, so its only observable effect is suppressing a + publish whose subscription was superseded, which + `packages/kilo-cli/test/remote-status.test.ts` covers by frame count. +- V1 send_message `model`/`agent`/`variant` fields and attachments uploaded as + remote URLs require materialization authorities this adapter deliberately + lacks; they are rejected rather than approximated. +- Open concern from review, unresolved pending product review: the relay + `send_message` frame is accepted without an adapter-side bound on the number + of parts or the aggregate inline data: bytes before the server's per- + attachment 20 MB decode check. Core still bounds each attachment and + materializes before durable admission, but no aggregate or part-count cap + and no canonical data-URL syntax restriction exists at the adapter + boundary. Adding one is a product-semantics decision, not an approximation + gap, so it is recorded here instead of implemented. +- The v1 catalog wire model carries capability booleans + (`temperature`, `reasoning`, `attachment`, `interleaved`) and kilo metadata + flags (`recommendedIndex`, `isFree`, `mayTrainOnYourPrompts`, + `hasUserByokAvailable`) with no public v2 source; the adapter omits them + instead of asserting values. v1 `source` provenance has no v2 public + equivalent and uses v1's own `"custom"` fallback. `currentModel` has no + message-history fallback because v2 user messages do not carry a model + field; the session's selected model is the authoritative v2 source. + Suggestions are now ported through existing Kilo seams (the slice below); + cloud clone still requires a distinct cloud authority and is not + approximated. The v1 attach/detach + machinery is in-process with no wire surface beyond heartbeat presence and + `exit_cli`; both sides of that deployed wire contract are translated (the + heartbeat presence model above and the session-detach slice below). + +## exit_cli session detach (implemented) + +`exit_cli` is v1's compatibility wire name for session-detach (ecccd1f +remote-sender.ts, K1 W1): verify ownership, cancel the session's active +prompt, stop advertising the session. The deployed consumer gates its exit +flow on the `canExitSession` flag of the parsed `list_commands` catalog +(cloud origin/main cloud-agent-sdk remote-command-catalog.ts and +cli-live-transport.ts `exitSession`, read from the local cloud checkout) and +sends data exactly `{ protocolVersion: 1 }`; the relay (cloud origin/main +UserConnectionDO) forwards the command only with that data shape and a +`sessionId`, routed to the CLI connection whose heartbeat owns the session, +and resolves a pending exit on the CLI's ACK, on its heartbeat dropping the +session, or on its socket closing. + +- The adapter's `list_commands` response carries `canExitSession: true` — + without it the consumer fails closed before the command can ever arrive. +- Ownership is v1's `hasSession` rule: the exact session must resolve in the + adapter's Location and not already be detached; anything else answers + `session not owned by this CLI` and leaves presence untouched. +- The active prompt is cancelled by awaiting public `session.interrupt` + before the detach (a v2 interruption of an idle session is a no-op); a + failed cancel refuses the ACK (`failed to exit session`) and leaves the + session advertised, like v1's roll-back path. +- Detach adds the session to a process-local set excluded from every + subsequent heartbeat, and the fence heartbeat is sent before the ACK, so + the relay sees the ownership release even if the ACK were lost. Detach + does not delete or unsubscribe: the session stays resolvable through + public state and subscriptions remain consumer-driven (v1 detach touches + presence and pending attaches only). +- No process-exit claim: v2 has no RemoteExit seam, so the adapter follows + v1's headless path — ACK and keep the host alive; the host keeps + advertising and can create a new session from zero. A host restart + re-advertises detached sessions from public state, the same restart + semantics as the adapter's other process-local state. +- Tests: real loopback relay and gateway fixtures drive exit on a busy + session (prompt cancelled, `session.active()` drops), invalid-data and + foreign-session refusals that leave presence untouched, the detached + heartbeat fence (a heartbeat omitting the session after the last one + advertising it), public resolvability after detach, and a second exit + refused by the ownership rule. `list_commands` assertions cover the + `canExitSession` advertisement. Validation is local-only: loopback + fixtures plus local reads of the pinned v1 sender and the local cloud + checkout's relay/SDK sources; the deployed relay and consumer are not + exercised, so deployed-behavior parity remains the same + deployment-verification gate recorded for the other lanes. +- Heartbeat capability tests: the fixture asserts the attachment capability + on the first heartbeat and on the exit fence heartbeat, and that no + heartbeat frame ever carries `sessionClone` while clone is refused. + +## Suggestions port (implemented through existing seams) + +v1's `suggest` tool + remote accept/dismiss port through Kilo-owned seams with +zero shared Core edits, using the parent-provided optional gateway tool +capability (kilo-gateway/src/plugin.ts `GatewayContext.tool?: Context["tool"]`; +createGatewayPlugin passes the full plugin Context through at runtime, and +optional keeps minimal catalog hosts valid): + +- Producer (`packages/kilo-cli/src/remote-suggestions.ts`): the `suggest` tool + registers via `ctx.tool.transform` — directly on the plugin context in + `createRemoteSessionPlugin`, and inside the enabled child Scope in + `registerRemote` only when the extension context carries the tool capability + (a disabled or capability-less host exposes no tool). It is a direct + model-facing tool (`options.codemode: false`, like v1 — never CodeMode-wrapped) + with v1's `{suggest, actions 1..2}` parameters. `execute` pends on the + per-enabled-remote holder (`createRemoteSuggestions()` — no global + singleton), `Effect.onInterrupt` dismisses the pending card when the turn is + interrupted (v1 abort-listener parity), and the accepted action's prompt is + admitted exactly once through the public client session API: + `session.command` dispatch for slash actions (v2's real command path resolves + the template server-side, replacing v1's resolvePrompt fetch), raw-prompt + admission for plain actions, and v1's unknown-command fallback admits the raw + text; dispatch failures after a resolved command fail the tool explicitly. +- Subscriber boundary: remote enabled at the Location does not mean a viewer + subscribed to every local session. The adapter installs a viewer-eligibility + accessor on the holder (`setEligibility` — true only while the session is in + the adapter's validated subscription scope), and the tool runs the awaited + check inside its own interruptible execution BEFORE the synchronous + `holder.show` insertion: an ineligible session settles the tool with an + explicit "not delivered" result, and cancellation during the lookup prevents + any later insertion (no orphaned visible card can outlive an interrupted + turn; a holder without an accessor reports ineligible). Reconnect retains + and replays still-pending cards only for sessions inside that same scope; + disable and interruption clear pendings unconditionally. +- Request identity: ids are `sug_` + fresh `crypto.randomUUID()` per holder — + never a process-lifetime counter, so a daemon restart cannot mint the same id + a resumed session already saw (a stale remote accept then resolves nothing). +- Settle-before-publish: accept resolves the awaiting tool before the accepted + update is published, and listener callbacks are isolated (one throwing + listener neither strands the already-removed entry's settle nor skips its + peers), so a listener failure can never leave a settled suggestion hanging. +- Wire frames: the holder's updates flow through the adapter's sequential event + stream into consumer-shaped frames — `suggestion.shown` + `{id (sug_ prefix), sessionID, text, actions[1..2], tool?{messageID, callID}}` + (cloud origin/main cloud-agent-sdk schemas.ts:722-746 `suggestionShownDataSchema`), + `suggestion.accepted` `{requestID, index, action}`, `suggestion.dismissed` + `{requestID}` — each ownership-gated via the adapter's subscribed-root walk + with descendant frames carrying the subscribed root as `parentSessionId`. +- Commands: `suggestion_accept {requestID, index}` / `suggestion_dismiss + {requestID}` (v1 ecccd1f remote-sender.ts:50-53, :1263-1299) resolve the + holder entry after ownership validation: the pending must live in the + adapter's validated subscription scope and the relay-routed session must be + the pending's session or its subscribed ancestor. Unknown requestID or an + invalid index is v1's exact `"suggestion not found or invalid action index"` + error; dismiss of an unknown id is an idempotent no-op success; a concurrent + settle between lookup and resolution refuses instead of double-resolving. +- Lifecycle: a newly queued prompt on the session auto-dismisses its pendings + (v1 SessionPrompt parity, driven by the public `session.inbox.enqueued` + event); reconnect replays still-pending shown frames for the subscribed + subtree (v1 replay parity) with terminal invalidation so a settled card is + never resurrected, and accept idempotence guarantees no duplicate follow-up + prompt; scope close (disable) rejects every pending. Known difference: the + session reports busy while a card is pending (v1 faked an idle status via the + v1-internal SessionStatus service; v2 has no public status override). +- Tests: `test/remote-suggestions.test.ts` drives real loopback host tool + execution (the scripted model calls `suggest`), the full accept flow with the + accepted frame and a single admitted follow-up that the model answers, + duplicate-accept refusal, dismiss + idempotent second dismiss, invalid data, + invalid index, foreign routed session, queued-prompt auto-dismiss, the + reconnect replay, the holder-level boundaries (fresh random identity, + settle-before-publish under a throwing listener, no-eligibility refusal), and + the unsubscribed-session boundary (a suggest call in an unsubscribed session + settles promptly with no delivered card while the subscribed flow is + unaffected). `remote-session.test.ts` regression suites stay green. + +`cloneFromKiloSessionId` (retired on the current consumer): + +- v1 wire contract (pinned ecccd1f remote-sender.ts `:72-75, :937-1050`): + `create_session` accepts `cloneFromKiloSessionId`, imports the cloud + session in-process via `importFromCloud` → `CloudSessionImportInProcess` + (v1 kilo-sessions.ts `:914-933`), and refuses to fall back to a fresh + create. +- Current consumer: cloud origin/main `cli-live-transport.ts` + `buildCreateSessionWireData` (`:44-56`) builds only `{ protocolVersion: 1, + agent?, model?, orgId? }` — `cloneFromKiloSessionId` appears NOWHERE in the + current SDK, mobile app, or DO (only unrelated local storage-clone + helpers). The deployed consumer no longer sends the field, so no + consumer-supported local gap exists; the adapter's refusal also matches + v1's "missing importFromCloud seam is a wiring bug, never a fallback" + posture. If a future consumer reintroduces it, the import authority is the + deployed cloud backend (`importSessionWithoutRestore` reads the cloud + session store), which is not locally available. + +## Permission and question translation (implemented) + +`remote-protocol.ts` mirrors the v1 reply-command data shapes +(`permission_respond`, `question_reply`, `question_reject` — non-strict, from +`ecccd1f remote-sender.ts`), and `remote-session.ts` translates both domains: + +- Permission asks: the v2 `permission.asked` event payload is renamed onto the + retained `PermissionV1.Request` wire shape (`packages/schema/src/v1/ +permission.ts`) and validated with that exact schema before sending: + `action→permission`, `resources→patterns`, `save→always`, tool source + `id→callID`. The v2 `message` field has no v1 request counterpart and is not + forwarded. Replied events become `permission.replied` `{sessionID, +requestID, reply}`. +- Questions: only forms the question tool creates translate (`metadata.kind === +"question"`), and only with v1-renderable fields: `string`-with-options or + `multiselect`, without `when` visibility conditions. Each question is + validated against the retained `QuestionV1.Info` schema. The frame id is the + v2 `frm_` form id — the retained `QuestionV1.ID` brand (`que_`) cannot + accept it, while the deployed consumer (`cloud-agent-sdk` + `questionAskedDataSchema`) validates a plain string; the id is therefore + validated as a non-empty string, not invented. Replies map positional v1 + `answers: string[][]` onto the form's actual field order (`fields[i].key`) + — no `q${i}` assumption — so field identity is preserved, resolving the + earlier "would silently discard semantics" concern. Lossy cardinality is + refused explicitly (multiple labels for a single-select field; answer count + ≠ field count) and never truncated. Rejections cancel the durable form. +- The `interactive` bit is parsed as explicit v1 wire data and never inferred + from transport authentication. Approvals (`reply !== "reject"`) of requests + carrying `metadata.skillShell === true` or `metadata.sandboxEscalation === +true` are refused without `interactive: true` and the request stays pending, + mirroring the v1 sensitive gate (`ecccd1f permission/index.ts`). No current + v2 producer marks that metadata, so the gate is wire-compat defense only — + documented here rather than claimed as an active protection. +- Subscription ownership: only sessions in the adapter's Location can be + subscribed; events are forwarded only for subscribed sessions — or sessions + whose subscribed ancestor is within eight hops, validated by fetching and + location-checking every ancestor including the subscribed root, so a moved + intermediate or root invalidates stale membership. Replay derives ancestry + per pending item through the same validated walk (no whole-listing + traversal, so the location listing's default page size cannot hide older + descendants): a pending request or form replays only under the subscription + owning its nearest subscribed ancestor — the subscribed session itself + (frames without `parentSessionId`) or that root (frames with it) — and the + subscribed session must still resolve in the adapter's location, so a moved + root drops its subtree on replay. A per-session epoch guards + subscribe/unsubscribe races and duplicates: an immediate unsubscribe + supersedes the in-flight subscribe and its replay before any frame is sent; + a duplicate active subscribe (the relay resends on reconnect) is deduped; + the reconnect replay captures the epoch so a resubscribe cancels it. + Replies resolve requests through location-scoped listing or direct lookup + and refuse foreign session/request mismatches with + `permission request not found`. Each Location runs its own adapter, so + another location's requests are unreachable from it by construction. +- Replay snapshots are invalidated by terminals per item: + `permission.replied`, `form.replied`, and `form.cancelled` bump an epoch + keyed by the resolved request's or form's identity, and a replayed ask for + that item is dropped — a late `permission.asked` re-adds a stale + interaction on the deployed consumer (cloud `origin/main` + `packages/cloud-agent-sdk/src/service-state.ts` `processQuestionAsked` / + `processPermissionAsked`, dispatch at the event switch), while sibling + pendings on the same session stay replayable and answerable. The epoch map + is bounded like the outbound frame buffer. +- Terminal question events gate on the supported question discriminator and + field shapes (`metadata.kind === "question"` plus v1-renderable fields, no + `when` conditions): nonquestion control forms (websearch, skill-shell) are + never emitted as `question.asked`, their replies and rejections are refused + with `unsupported question form` (they are never cancelled or answered + through the question path), and `question.replied` is sent only when the + positional answers are reconstructable — omitted entirely otherwise, since + v1's replied event requires answers. Question-kind forms with unsupported + fields are refused on ask, reply, and reject alike. +- Tests: real loopback model sessions drive a shell permission ask (resolved + by a relay reply), a sensitive refusal (no interactive bit) and its + interactive approval, replay after unsubscribe/re-subscribe and after + reconnect (retained connectionId), a duplicate-subscribe dedupe with frame + counts, same-location descendant replay with the root as `parentSessionId` + raised while unsubscribed so no live frame masks the replay, a real + `session.move` of the subscribed root checked against live events and a + reconnect replay, a moved intermediate with an ambient leaf, a + terminal-during-replay reject isolated on its own session (a reject + declines all same-session siblings, so the cascade cannot mask later + assertions), a same-session two-pending test resolving one noncascade item + during the replay window (resolved item not replayed, sibling replayed and + answered through the relay), an immediate subscribe/unsubscribe race, a mismatched + session/request refusal, question round-trips through the real question + tool form (single-select + multiselect), cardinality refusal with + pending-state verification, question rejection cancelling the durable + form, and nonquestion control forms (created through the public form API + with a `websearch.provider` discriminator) plus question-kind forms with + `when` conditions refused on ask, reply, and reject with pending-state + verification. Emitted permission frames are asserted against + `PermissionV1.Request` and question frames against `QuestionV1.Info`. + +## Opt-in host and TUI integration + +`remote-plugin.ts` composes directly with the existing in-process +`GatewayExtension`: the adapter needs only its public location/event domains, +not access to a private Core service or a new Gateway token RPC. The ordinary +interactive host registers `kilocode.remote` status/enable/disable methods. +Registration and status are inert. Enable resolves the active validated account +inside the host, supplies an authenticated local client, and starts a child +scope. Disable, credential changes, and host/plugin shutdown close that scope. +Enablement is not persisted. The response distinguishes enabled intent from +the transport's actual `connected` state and never exposes a bearer token. + +The relay origin remains separate from `account.server`. Its default is the +source-proven `https://ingest.kilosessions.ai`; explicit loopback launch options +exist for tests. `/remote` displays the current status and the compatibility +limits above; enabling requires confirmation explaining session advertisement +and remote command authority. No test enables a live relay. + +`test/remote-rpc.test.ts` exercises the normal host over authenticated HTTP: +no-credential refusal without dialing, real loopback device sign-in without +automatic enablement, one connection after repeated enables, heartbeat and +session-create translation, disable/re-enable, logout-driven closure, and +token-free status/frames/diagnostics. `test/remote-ui.test.tsx` uses the real +renderer and host to verify truthful limitations and that cancelling the enable +dialog leaves the relay untouched and admits no session/model work. + +The row remains partial under the compatibility boundaries recorded above. +Transcript, Form/permission, queue, and suggestion translations have local +coverage; deployed relay-version verification remains outside these tests. diff --git a/migration-tracking/technical-notes/baseline/sandbox-linux-validation.md b/migration-tracking/technical-notes/baseline/sandbox-linux-validation.md new file mode 100644 index 000000000000..c5c6fa408a7c --- /dev/null +++ b/migration-tracking/technical-notes/baseline/sandbox-linux-validation.md @@ -0,0 +1,67 @@ +# Linux shell sandbox validation — 2026-09-05 + +The actual `packages/kilo-cli/src/sandbox.ts` Bubblewrap adapter passed a Linux +probe from `script/sandbox-linux-smoke.ts`. This replaces the earlier +source-only Linux evidence, not the explicit shell-only scope limitation. + +## Environment and result + +- Host checkout: `59b29de40966803e2c7cd734d439843fb773f6a6` plus local Kilo changes. +- Linux: aarch64 Docker Desktop, Debian 13, Node 20.20.2, Bubblewrap + `0.12.0-1~deb13u1`. The probe bundles the production adapter for Node; + it does not substitute a fake sandbox backend. +- Base image: `nikolaik/python-nodejs:python3.11-nodejs20`, resolved digest + `sha256:8f958bdc1b4a422bfafd97cab4f69836401f616ae985d4b57a53d254f5bcb038`. +- Passed: workspace write; outside-root write denied with source unchanged; + explicit protected-path write denied; local HTTP allowed as a control and + denied under the network policy; unsupported nonempty Linux `denyNames` + refused. Repeated after removing unreachable name-scanning code. +- The container had no host repository, home, credentials, or sockets mounted. + Its external network was disabled during the probe. Only the built probe + was copied into the image; fixtures were created inside the container. + +## Reproduction + +From `packages/kilo-cli`, build the probe into an explicit temporary context: + +```sh +./dist/interactive/bun build script/sandbox-linux-smoke.ts --target=node --outfile=/sandbox-linux-smoke.mjs +``` + +The context's Dockerfile used: + +```dockerfile +FROM nikolaik/python-nodejs@sha256:8f958bdc1b4a422bfafd97cab4f69836401f616ae985d4b57a53d254f5bcb038 +RUN apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq bubblewrap +COPY sandbox-linux-smoke.mjs /sandbox-linux-smoke.mjs +CMD ["node", "/sandbox-linux-smoke.mjs"] +``` + +```sh +docker build -t kilo2-sandbox-validation:local +docker run --rm --name kilo-linux-sandbox-validation --network none \ + --cap-add SYS_ADMIN --security-opt seccomp=unconfined \ + --security-opt systempaths=unconfined kilo2-sandbox-validation:local +``` + +Output: `KILO_LINUX_SANDBOX_OK: workspace write, outside/protected denial, +network allow/deny, unsupported names refusal`. + +Docker's default masked `/proc` prevented nested Bubblewrap from mounting proc. +SYS_ADMIN plus unconfined seccomp alone was insufficient; the explicit +systempaths option permitted the nested sandbox. These are disposable test +container privileges, not recommended production daemon configuration. + +## Scope + +The macOS test suite separately passed 7 tests / 26 assertions, including the +actual SDK post-registration hook and model shell invocation. Linux proves the +real OS adapter, not a complete Linux Bun/TUI distribution. Neither proves PTY, +MCP, independent git spawns, read-access confinement, proxy networking, or +future basename-based denials on Linux. Those remain separate plan gaps. + +The v1 standalone sandbox package cannot be reused unchanged: it returns +arbitrary executable/argv commands and decorates filesystem/HTTP/spawner +services. The v2 public shell hook only changes the selected shell invocation; +the Kilo-owned launcher is required by that narrower contract. No upstream +spawn override was added. diff --git a/migration-tracking/technical-notes/baseline/sandbox-spawn-next-slice.md b/migration-tracking/technical-notes/baseline/sandbox-spawn-next-slice.md new file mode 100644 index 000000000000..c00659b60ad4 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/sandbox-spawn-next-slice.md @@ -0,0 +1,410 @@ +# Sandbox spawn coverage — next-slice audit and MCP implementation + +## Current continuation — 2026-09-07 + +Following the prepared patch and Johnny's continuation, the host now installs +`Mcp.SpawnHooks.beforeSpawn` through its Location node override when sandboxing +is enabled. The narrow shared edit is `packages/core/src/mcp/index.ts`; policy +stays in `packages/kilo-cli/src/sandbox-mcp.ts`. The hook executes inside the +existing MCP connection scope and failure pipeline. Each local connection +acquires an argv launcher and disposes it when the connection closes; raw +catalog configs remain unchanged. There is no activation-time MCP plugin or +transform to register, dispose or wait for. + +Core typecheck and two native MCP lifecycle regressions pass (15 assertions). +Independent implementation review found no blockers. The test delegate verified +13 MCP tests and 7 shell sandbox tests on the native host, including cold-add +confinement, disabled direct-connect, reload and connection cleanup. The parent +sandboxed run reports 7 pass / 13 skip; those skips are not additional native +acceptance. Portable build `build-udBYOF` passes isolated loopback TUI, daemon +reuse and default PTY attach smoke checks; exiting the client preserves the +daemon. CLI typecheck passes after explicit local-config narrowing in the +tests; no casts are used to hide the hook's local/remote return type. +PTY, remote MCP and separate git spawns remain outside this change. The sections below retain the earlier audit and +rejected transform/latch designs as history; their registration and approval +status is superseded by this continuation. + +Date: 2026-09-06. Checkout: `kilo-v2` at `61a8707c03` plus the frozen accepted batch. +Scope: sandbox PTY/MCP/git audit; the MCP slice is implemented in Kilo-owned +files only, on root's approval. The accepted Explore shell policy +(`packages/kilo-cli/src/agent-policy.ts`) is untouched and stays intact. + +## 1. Two different ceilings — keep the vocabulary exact + +- **Shell permission ceiling** (model policy): `createExplorePolicy` and + `createAgentPolicy` (packages/kilo-cli/src/agent-policy.ts) gate *which + commands the model may request* at the Permission layer. They evaluate parsed + command resources through `permission.assert` (packages/core/src/tool/plugin/shell.ts:144-152) + and `permission.evaluate` (packages/core/src/permission.ts:174). They never + change process capability. +- **OS sandbox** (enforcement): `createSandboxPlugin` (packages/kilo-cli/src/sandbox.ts:161) + replaces the selected shell with a short-lived launcher + (sandbox.ts:208-221) so the spawned process tree runs under + `sandbox-exec`/`bwrap` — confining writes outside `sandbox.root`, `.git` + (denyNames), and network regardless of any permission grant. + +They compose at different points and neither implies the other: permission +answers "may this run"; the OS sandbox bounds "what a permitted command can +touch once running". A command the policy denies never spawns; an allowed +command still runs confined. The MCP slice below adds OS confinement only; the +permission ceiling is a separate, already-accepted surface. + +## 2. Spawn producer inventory — exact hook ordering and call paths + +| Surface | Producer chain (exact references) | Public seam today | Covered by `--sandbox`? | +|---|---|---|---| +| Shell tool (model) | `ShellTool` execute → `shell.create(input, prepare)` (core/src/tool/plugin/shell.ts:210) → invocation built (core/src/shell.ts:257-267) → `hooks.trigger("shell","create.before")` (shell.ts:268) → tool `before` = permission prepare (shell.ts:269, tool/plugin/shell.ts:114-162) → `environment.spawner.spawn(ChildProcess.make(invocation.shell, args))` (shell.ts:293-302) | `shell.hook("create.before")` (packages/plugin/src/effect/shell.ts:11-17) | Yes — hook rewrites shell→launcher before spawn (kilo-cli/src/sandbox.ts:208-221); launcher set before permissions run (command string unchanged) | +| MCP stdio servers (user-configured, model-invoked tools) | `ConfigMcpPlugin` sets configs via `mcp.transform` (core/src/config/plugin/mcp.ts:39-56, internal `pre` phase, core/src/plugin/internal.ts:190-191) → `Mcp.Service.startServer` (core/src/mcp/index.ts:512-537) → `McpClient.connect` destructures `config.command` (core/src/mcp/client.ts:218-229) → `McpStdio.make` → `environment.spawner.spawn` (core/src/mcp/stdio.ts:82-92), Environment provided at index.ts:534 | `ctx.mcp.transform` editor (packages/plugin/src/effect/mcp.ts:6-12) | No — rewrite implemented but unregistered pending the activation gate (§4) | +| PTY (user-initiated TUI terminal) | `Pty.Service.create` (core/src/pty.ts:164-182) → direct `spawn(command, args)` from `#pty` — Bun.spawn (core/src/pty/pty.bun.ts:7) or node-pty (core/src/pty/pty.node.ts:13). Bypasses every spawner service and hook; command is the user's login shell (pty.ts:166) | None — plugin context exposes only `experimental.terminal.read` (packages/plugin/src/effect/plugin.ts:33-35) | No — open, see §6 | +| Git / VCS machinery (host-internal) | Global `AppProcess.Service` (core/src/git.ts:167; spawns at git.ts:315, 444, 622, 718), VCS git plugin (core/src/plugin/vcs/git.ts:27,170-177), worktree.ts:154, project.ts:99, integration.ts:265/611, formatter.ts:31 | `AppProcess.node = makeGlobalNode({ deps: [CrossSpawnSpawner.node] })` (packages/util/src/process.ts:268); replaceable only at the host composition root via `profile().overrides` (kilo-cli/src/host.ts:20-28, server/src/routes.ts:140-157) — process-global | No — provisional, see §6 | +| Files exec defaults (stat/read/write/list/remove/move/mkdir via `sh -c`) | `execDefaults(driver.spawner)` (core/src/environment/exec-defaults.ts:73-148) wired by `makeFiles` (core/src/environment/index.ts:24-27) | None (inside Environment layer) | No — host-trusted | +| Ripgrep, workspace connect, integration, persistent-pty daemon | core/src/ripgrep.ts:120, ripgrep/binary.ts:41, workspace.ts:286, integration.ts:611, persistent-pty/daemon.ts:230 | None relevant | No — host-trusted | + +Shared plumbing: `Environment.node` deps `[CrossSpawnSpawner.node, Location.node, Workspace.node]` +(core/src/environment/environment.ts:37-41). Shell and MCP stdio share the same +per-Location spawner; git uses the global `AppProcess` node on the same +underlying `CrossSpawnSpawner` implementation but through a different, +process-global service. + +## 3. Refutation: "MCP confinement needs an upstream seam" + +The next-batch row (plans/kilo-opencode-v2-next-batch.md:49) claims Core owning +the MCP spawn (`core/src/mcp/index.ts:533` execution plane) means MCP +confinement needs an upstream seam or a shared patch. The spawn *call* is +upstream, but the spawned *command* is mutable config state behind a public +plugin seam: + +- `MCPDomain.transform` (packages/plugin/src/effect/mcp.ts:6-12) exposes + list/get/set/update/remove over in-memory `Mcp.ServerConfig`s + (core/src/mcp/index.ts:681-707). Nothing persists to user config; `reconcile` + (index.ts:619-657) diffs state vs applied entries and restarts replaced + servers, and `startServer` spawns from the rewritten `entry.config`. +- Ordering is safe: core's `ConfigMcpPlugin` registers in the internal `pre` + list (core/src/plugin/internal.ts:190-191); a Kilo plugin registered with + `phase: "post"` folds after it on every rebuild — `State.get()` replays + transforms in registration order over a fresh editor (core/src/state.ts:119-128), + and `config.updated` → `mcp.reload()` re-runs the same order + (core/src/config/plugin/mcp.ts:24-35). `MCP.add` overrides survive reloads + through `initial()` (index.ts:683-690) and are rewritten by the same replay. +- Precedent: `McpCodeModeExclusionPlugin` already rewrites MCP configs from a + plugin (core/src/plugin/mcp-codemode-exclusion.ts:12). + +The launch mechanism is the already-validated persistent launcher +(macosPersistentLauncher/linuxPersistentLauncher, kilo-cli/src/sandbox.ts:391-425), +applied at the argv level instead of the shell-hook level. No upstream edit, no +monkeypatching, no process-global interception. + +Residual honesty, now empirical: config-defined servers spawn only after the +whole activation batch completes, because every plugin load folds inside one +`State.batch` (core/src/plugin.ts:102-144) whose notifications run after the +batch (core/src/state.ts:137-152) — so the first reconcile already sees the +sandbox rewrite. Proven empirically: the enabled-from-initial-config host test +uses an exclusive first-boot sentinel that records that boot's own +outside-root write attempt, and it reads denied. Hosts that register plugins +late would see a brief unconfined first connect — same class as the shell hook +(post-registration spawns only). + +## 4. MCP slice — implemented in Kilo-owned files, NOT accepted + +Root approved the MCP implementation within bounded Kilo-owned scope. The +independent reviewer (Luna) rejected it with two P1 findings; root withdrew the +production registration. The code and tests stay; the feature is unregistered +and must not be re-registered until the activation gate below ships. No row +credit is claimed. + +- **P1 — activation race (Luna, reproduced).** `client.mcp.add` reaching a + cold location spawns the server while plugin activation is still in flight: + the server handlers resolve `Mcp.Service` directly and `service.add` + reloads/reconciles immediately (packages/server/src/handlers/mcp.ts:22-35), + while the supervisor activates plugins asynchronously. A first boot before + the sandbox transform registers runs unconfined (sentinel `wrote`); the + post-activation control is denied. The earlier enabled-from-initial sentinel + test passed only because it awaited activation first. +- **P1 — Linux separator (Luna).** `linuxArguments` already terminates with + `--` (sandbox.ts:534), so the argv launcher appended a second separator and + produced `bwrap ... -- -- `, which execs the separator. Fixed in + this round; regression covers both backends. + +- **New `packages/kilo-cli/src/sandbox-mcp.ts`:** + - `sandboxServerCommand(server: Mcp.ServerConfig, launcherPath: string): + SandboxServerCommand | undefined` — pure decision boundary; returns + undefined for remote servers, disabled servers, and empty argv. + - `registerSandboxMcpTransform({ transform, directory }, config)` — creates + one launcher, registers an `Effect.addFinalizer` cleanup in the owning + scope, and registers the `mcp.transform` rewrite. + - `createSandboxMcpPlugin(config): Plugin` — id `kilo2.sandbox-mcp`, phase + `post`; inert unless enabled, fail-fast when the backend is unavailable. +- **One exported helper in `packages/kilo-cli/src/sandbox.ts`, with no change + to any existing line:** `createSandboxArgvLauncher(config, root)` returning + `{ path, dispose }`. It reuses the existing OS policy builders + (`macosProfile`, `linuxArguments`) through two new private argv script + builders. The confined executable arrives as the launcher's first argument, + so argv is fully preserved and zero-argument MCP commands work; the existing + shell launchers and their two-argument guard are untouched. +- **Rewrite semantics:** `command` becomes `[launcherPath, ...originalCommand]` + via `editor.update`. When the original `command[0] === "opencode"`, + `BUN_BE_BUN: "1"` is baked into the server environment (user values keep + precedence). Verified against `core/src/mcp/client.ts:226`: after wrapping, + the destructured `command` is the launcher path, so the native special case + no longer fires and the baked marker is the only source. +- **No path-based skip.** A user-controlled command path that references a + `kilo2-sandbox-` directory is still wrapped; a path is not provenance. +- **Idempotence through real state rebuild:** production folds transforms in + registration order over fresh state (core/src/state.ts:119-128), so the + rewrite is unconditional and always applies the same launcher path. The test + replays the config-set and sandbox transforms against core's real + `State.create` across a full `reload()` and asserts one stable launcher with + no nesting. +- **Scope lifecycle (empirically verified parts only):** plugin effects run in + a per-plugin child scope (core/src/plugin.ts:44); the transform disposal + (core/src/state.ts:171) and the launcher finalizer bind to that same scope, + so launcher files exist exactly while the transform is registered. + Proven in the real host: a config reload (`client.mcp.add`) restarts the + server via reconcile and the restart reuses the same launcher and stays + confined; host shutdown with the server child still live stops the child and + removes every launcher directory the host created (temp baseline captured + before the host launch); and an enabled-from-initial-config scenario shows + the first boot — whenever it ran relative to activation — was already + confined, via the exclusive first-boot sentinel. What is NOT asserted: the behavior of a + plugin-only transform removal while a Location stays alive (not reachable + from any public host API in this host) — the earlier claim that reconcile + would then safely restart servers unconfined is retracted until proven. +- **Registration withdrawn:** root removed the `createSandboxMcpPlugin` + registration from `interactive-server.ts` after the P1 rejection; the + `--sandbox` help text stays unchanged. The plugin must not be re-registered + until the activation gate below is in place and the gated host scenarios + pass. +- **Honest limits:** env is still host-extended (`extendEnv`, stdio.ts:86); cwd + outside `sandbox.root` stays readable (read confinement is out of scope, as + already documented); the server command changes to the launcher path in the + plugin editor view (public `servers()` exposes name/status/integrationID, + index.ts:719-723). + +MCP OS confinement is stricter than v1: v1's `executeMcp` (origin/main +packages/opencode/src/kilocode/sandbox/policy.ts:638-639) asserted delegated +network authority around MCP tool calls (origin/main .../sandbox/network.ts:95-99) +and never OS-confined the server process. + +### Activation blocker — analysis frozen; gate design on hold for review + +Independent proposal review — 2026-09-07: the consultation's monotonic-latch +patch is rejected as written. `Latch.open` is an Effect action rather than a +boolean state read; uncontained hook failures would leave `pending` status; +and a latch left open across transform disposal permits an unwrapped restart. +The activation-await deadlock is confirmed. A revised spawn-time verification +of the current folded configuration needs contained failure status and an +activation/disposal regression. PTY refusal also needs a typed error decision; +the proposed hook alone does not yield a clean public refusal. No MCP/PTY Core +edits or production registration were made. The shared-hook decision remains +pending; the original proposal must not be implemented verbatim. + +Gate implementation is ON HOLD. Two prior proposals are rejected and recorded +only as constraints: the cyclic `Plugin.node` dependency, and the +`Effect.serviceOption` absent→proceed seam (root: **fail-open is not an +acceptable sandbox boundary**; watchers-off is not proof that +`config.updated` cannot fire mid-activation). + +**Reviewer direction (Luna), recorded not endorsed:** a narrow Core +`beforeSpawn` callback at the single `startServer` producer plus refreshed +config after the gate. `startServer` is the single producer for every spawn — +automatic reconcile (core/src/mcp/index.ts:512-537), `add`/`reload` +reconcile, and the public `connect` (index.ts, `connect` → `startServer`) — +so one callback covers all spawn paths including plugin-fiber reloads. Root +requested USER approval for the shared-file (Core) exception under the fork +conventions; **no Core edits until the user replies**, and this document does +not assert the proposal deadlock-free — the deadlock analysis (initial +batch-deferred reconcile vs. gate ordering) is the reviewer's to prove. + +**Additional confirmed bypass, fixed in this round:** a disabled local server +is startable via public `mcp.connect` — `connect` calls `startServer` +unconditionally, while the `disabled` flag only gates the automatic reconcile +spawn (core/src/mcp/index.ts:595, 634, 670). The rewrite now covers all local +configs including disabled ones (`sandboxServerCommand` no longer skips +`disabled`): the rewrite keeps the disabled flag intact so nothing spawns +automatically, but a direct connect lands on the launcher. Regression: with +the policy registered through the fixture-owned option, a direct +`client.mcp.connect` after activation starts the server confined (startup +probe denied). Production registration stays off. + +The first proposal (a Kilo-owned node depending on `Plugin.node`) was +correctly rejected by root as cyclic, and its "remove cannot spawn" claim was +unsupported. Verified against source: + +- **Cycle confirmed.** `Plugin.node` is a location node whose only dep is + `PluginHost.requirements` (core/src/plugin.ts:188-192), and that group + includes `Mcp.node` (core/src/plugin/host.ts:496-503). A gated Mcp node + depending on `Plugin.node` therefore requires itself. Dead. +- **`remove()` reloads state.** `MCP.remove` runs `overrides.set(name, false)` + then `state.reload()` (core/src/mcp/index.ts:744-750) — the same + reload/reconcile machinery as `add`, so the gate scope is add, connect, + remove, and reload. + +Still-valid facts for the reviewer (source-backed, design-neutral): + +- **Cycle constraint.** `Plugin.node` is a location node whose only dep is + `PluginHost.requirements` (core/src/plugin.ts:188-192), and that group + includes `Mcp.node` (core/src/plugin/host.ts:496-503). Any gated Mcp + composition that requires `Plugin.Service` as a layer dependency requires + itself; the dependency must not be a composition edge. +- **Gate scope.** `MCP.remove` also runs `overrides.set(name, false)` then + `state.reload()` (core/src/mcp/index.ts:744-750) — the same + reload/reconcile machinery as `add`. The gate must cover add, connect, + remove, and reload; the handlers resolve `Mcp.Service` directly with no + activation wait (packages/server/src/handlers/mcp.ts:22-59). A disabled + server is additionally reachable through `connect` (startServer + unconditional), so config rewriting must include disabled servers — it does + (sandbox-mcp.ts `sandboxServerCommand`). +- **Non-cyclic decoration exists if needed.** `ProviderNode.mapLayer` + preserves a node's dependency wiring and service contract + (packages/util/src/effect/layer-node.ts:94-103), so a decorator can wrap the + service without new composition edges. Whether decoration can be made + fail-closed without the rejected absent→proceed fallback is exactly what the + review is deciding; any variant that resolves the activation service + optionally and proceeds when absent is rejected and must not return. +- **Batch-deferred reconcile stays safe.** The initial config-server set + mutates state through `transform`; its reconcile is deferred past the + activation batch (core/src/plugin.ts:102-144, core/src/state.ts:137-152), + which the enabled-from-initial sentinel scenario proved confines the first + boot. The bypass is specifically the out-of-batch public paths + (add/connect/remove/reload) racing activation, reproduced by the registered + bypass repro below. + +## 5. Test evidence (package-local; bundled `packages/kilo-cli/dist/interactive/bun` 1.4.0) + +`bun run typecheck` passes; `dist/interactive/bun test test/sandbox-mcp.test.ts +test/sandbox.test.ts` passes 15 tests / 0 fail with 2 intentionally skipped +gated scenarios. (3 of the passing tests are the separator regression and the +registered bypass repro; the bypass repro is stable across repeated runs — the +supervisor's 100 ms activation debounce makes the cold-add window +deterministic, core/src/plugin/supervisor.ts:229). The earlier duplicate `State.create` + replica-editor test was +removed once the real host superseded it; the useful primitive tests stay: + +1. `sandboxServerCommand` rewrites local servers, preserves argv, and leaves + remote/disabled/empty servers unchanged. +2. Pure rewrite invariants without state machinery: the `opencode` marker is + baked (`BUN_BE_BUN: "1"`; user values keep precedence through the transform + spread), a command path that already references a `kilo2-sandbox-` + directory is still wrapped — a path is not provenance — and disabled local + servers are rewritten because a direct `mcp.connect` starts them + unconditionally. +3. `createSandboxMcpPlugin({ enabled: false })` is inert; transform + registration with a disabled config fails closed. +4. Primitive live test (skipIf backend unavailable): a real newline-delimited + JSON-RPC MCP server under the launcher — `initialize` handshake, + `tools/list` with a real input schema, `tools/call` inside-root write + succeeds, outside-root write denied, loopback fetch denied with zero + listener hits, startup outside-write already denied; child shutdown then + launcher cleanup, idempotent second `dispose`. + 5. **Registered bypass repro (runs now; the P1 failure, not acceptance):** + through the fixture-owned launch option the host registers + `createSandboxMcpPlugin`; an immediate `client.mcp.add` on the cold + location — before `awaitActivation` — connects the server, the exclusive + first-boot sentinel reads `wrote` (the registered rewrite was bypassed), + and the reviewer's control holds: the post-activation restart of the same + path is denied (fresh pid, startup probe denied). When the activation gate + ships, the first-boot assertion on this exact path must flip to `denied`. + 6. **Direct-connect regression (runs now):** with the policy registered + through the fixture-owned option and the server configured `disabled`, + a public `client.mcp.connect` after activation starts the server and the + startup probe reads denied — the disabled rewrite holds on the + connect-overrides-disabled path. + 7. **Skipped — blocked on the activation gate (re-enable after the gate ships + and production registers).** Real-host scenarios through + `launch({ sandbox })` and the public client with the production MCP client: + - **Enabled from initial config, sentinel proof:** the fixture host config + enables the local MCP server from the start; after activation the + exclusive first-boot sentinel (written once by whichever boot ran first, + recording that boot's own outside-root write attempt) reads denied, and + the model-driven probe writes inside root — no unconfined process ran + before or during plugin activation. Registration then went through + `launch` and these scenarios passed; they are re-run as the gate's + acceptance criteria. + - **Disabled → enabled → reload:** the host configures the server + `disabled`; `client.mcp.add` runs the production reload/reconcile + pipeline and the server connects through the native MCP client; the + server's startup probe proves the FIRST spawn was already confined + (outside-root write denied before any model call); the fake model calls + `probe_probe` (registered from the real tool registry, permission allowed + via config) — inside-root write succeeds, outside-root write denied, + loopback fetch denied with zero listener hits; a second `client.mcp.add` + restarts the server (new pid, new marker) and the fresh startup probe is + denied again. The launcher temp baseline is captured before the host + child launches, so every launcher the host created is owned by the test: + host shutdown with the child live stops the child (pid poll) and leaves + zero new `kilo2-sandbox-*` directories. + +Linux follow-up: extend `script/sandbox-linux-smoke.ts` with one argv-launcher +case; the Linux adapter evidence +(kilocode/baseline/sandbox-linux-validation.md) already proves the OS primitive. + +## 6. Acceptance status, PTY and git — still open + +- **MCP slice — rejected by review, unregistered.** Three P1-class findings + are recorded in §4: the activation race (reproduced with the registered + policy; fix pending the fail-closed gate), the Linux separator (fixed with a + regression), and the disabled-server connect bypass (fixed; rewrite covers + disabled configs with a direct-connect regression). No acceptance, no row + credit; production registration stays off until the user-approved gate + lands. + +- **PTY — implemented, fail-closed refusal.** The seam is public, no Core or + Protocol edit: `ProviderNode.mapLayer` (packages/util/src/effect/layer-node.ts:94-103) + decorates `Pty.node`'s implementation while preserving its dependency wiring + and service contract (replace precedent: workerd.ts:84; graph entry: + core/src/instance.ts:78). `sandboxPtyRefusalReplacement(config)` + (packages/kilo-cli/src/sandbox-pty.ts) wraps `Pty.Service.create` to fail + before any process spawns when the sandbox is enabled, installed through + `profile().overrides` only for `sandbox?.enabled` — a non-sandbox host keeps + the stock service. The refusal is a defect, matching the accepted public + contract: `Pty.Interface.create` has an empty error channel (core/src/pty.ts) + and the `pty.create` endpoint declares no error + (packages/protocol/src/groups/pty.ts), so the refusal surfaces as an untyped + HTTP failure; `--sandbox` help (packages/kilo-cli/src/commands.ts) now states + session terminals are refused while the sandbox is active. Existing sessions + keep working (`attach`/`write`/`remove` pass through). The TUI's + new-terminal path is user-facing and runs through the experimental persistent + surface (packages/tui/src/context/session-terminals.tsx calls + `experimental.persistentPty.create`), so the same seam also decorates + `PersistentPty.node` (`sandboxPersistentPtyRefusalReplacement`, + packages/kilo-cli/src/sandbox-pty.ts): only user-facing `create` is denied, + through the service's declared `UnavailableError` channel, which the HTTP + handler maps to the already-declared ServiceUnavailableError — no Protocol + edit. Actual daemon infrastructure (bootstrap, reads, attach, input, remove, + shutdown) stays with the inner service and is not refused; the daemon itself + remains host-trusted, and only its user-facing terminal-create path is + gated. v1 parity: v1's sandbox + refused to enable while interactive terminals were live (origin/main + packages/opencode/src/kilocode/sandbox/activation.ts:66-118); v2 decides the + sandbox at launch before terminals exist, so the parity point is creation + time. Evidence: `test/sandbox-pty.test.ts` (5 pass, bundled bun 1.4.0) — + refusal guards, both decorators' create refusal (typed defect for Pty / + declared `UnavailableError` for PersistentPty) with passthrough against mock + services, and real isolated hosts: a stock host spawns through BOTH surfaces + (location PTY and the persistent daemon terminal) with sentinel plus remove + teardown, and the sandbox host refuses the location surface (untyped failure) + and the persistent surface (declared 503) with the sentinel file absent. + +- **Git — disposition recorded with source evidence.** v1's classification + engine re-verified on `origin/main` + (`packages/opencode/src/kilocode/sandbox/git.ts`): READONLY and MUTATING + command sets classified **model-requested** git commands inside v1's bash + tool; host-internal git machinery was never OS-confined in v1. v2 mapping: + model-initiated git reaches execution only through the shell tool, which the + accepted `shell.create.before` confinement already confines whole-command — + stricter than v1's per-command classification and covering the same surface. + Host-internal git spawns are `AppProcess.Service` operations (core/src/git.ts + node :703, callers plugin/vcs/git.ts, worktree, project, formatter, + integration) performing snapshot and worktree writes into `.git`, which the + sandbox profile denies by design — confining them would break host + integrity, and there is no v1 coverage to match. Disposition: out of sandbox + scope by design, not a parity gap; provisional until root accepts it, no + further work proposed. + +## 7. Constraints honored + +No Core or upstream edits, no monkeypatching or build rewrites, no +process-global interception (the Environment/`AppProcess` node-replacement +route was considered and rejected for blast radius — it would wrap daemon, +formatter, and provider spawns). The Explore shell policy and the frozen +accepted batch are untouched. Tests ran focused, package-local, on the bundled +bun; no task transitions and no commits. diff --git a/migration-tracking/technical-notes/baseline/settings-v2-parity.md b/migration-tracking/technical-notes/baseline/settings-v2-parity.md new file mode 100644 index 000000000000..d0c75579355b --- /dev/null +++ b/migration-tracking/technical-notes/baseline/settings-v2-parity.md @@ -0,0 +1,384 @@ +# Kilo settings management on v2: parity audit + +Evidence for the Kilo settings adapter in `packages/kilo-cli/src/settings.ts`, +its public contract in `src/settings-rpc.ts`, and its TUI surface in +`src/tui-plugin/settings.tsx`. Compares Kilo `origin/main` (v1) against this v2 +tree. Source comparison: Kilo `origin/main` at +`ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`, current v2 HEAD +`59b29de40966803e2c7cd734d439843fb773f6a6` (not the historical pin alone). +Nothing is inferred from v1 documentation alone. + +## v1 has no settings command + +| Surface | What it is | Where | +|---|---|---| +| CLI | `config check` only — no read, write, or scope commands | `packages/opencode/src/cli/cmd/config.ts` | +| Docs | CLI settings are hand-edited JSONC | `packages/kilo-docs/pages/getting-started/settings/index.md`, CLI tab | +| HTTP | `GET /config/overlay|sources|effective|rules|model-state`, `PATCH /config/overlay` with `{scope: global\|project, set, unset, expected:{path,revision}}` | `src/kilocode/server/httpapi/groups/config-console.ts` | +| Scope model | scopes `global\|project`; targets `kilo.jsonc\|kilo.json\|opencode.jsonc\|opencode.json` plus `.kilocode\|.kilo`; revision hash; writability; fixed `fieldPaths` | `src/kilocode/config/overlay.ts` | +| Writer | jsonc-parser `modify`/`applyEdits`, whole-document validation, `target-changed`/`revision-conflict`/`target-not-writable`, mode 0600 for new global files | `src/kilocode/config/writer.ts` | +| Clients | VS Code (`client.config.overlayUpdate`) and the v1 TUI indexing dialog (Global/Project picker, indexing fields only) | `packages/kilo-vscode/src/KiloProvider.ts`, `src/kilocode/components/dialog-indexing.tsx` | + +The v1 TUI indexing dialog is the only precedent for a scoped settings dialog, +and it manages Kilo indexing keys that do not exist on v2. + +## v2 configuration is read-only end to end + +| Surface | State at this baseline | Where | +|---|---|---| +| Protocol | `GET /api/config` returning `Config.Entry[]`; no write, no scope, no revision | `packages/protocol/src/groups/config.ts` | +| Filesystem API | read, list, find only | `packages/protocol/src/groups/fs.ts` | +| Core service | `Config.Interface` is `entries()` + `changes()`; no update | `packages/core/src/config.ts:35` | +| Precedence | `Config.latest` takes the last document defining a top-level key | `packages/core/src/config.ts:30` | +| Accumulating keys | `permissions` concatenate, `agents` merge per key across documents | `packages/core/src/config/plugin/agent.ts:87` | +| Native `/settings` | `opencode.settings` opens `DialogConfig`, presentation only (theme, animations, sidebar, scroll, attention, mouse) | `packages/tui/src/app.tsx:929`, `packages/tui/src/component/dialog-config.tsx` | +| v1 field mapping | owned upstream by `ConfigMigrateV1.migrate`, already consumed by the Kilo v1 import | `packages/core/src/v1/config/migrate.ts`, `packages/kilo-cli/src/import-v1-config.ts` | + +Consequences: a Kilo settings surface cannot reuse a native write path because +none exists, and it does not shadow native `/settings`, which touches no +`Config.Info` field. + +## Scope model deltas + +| v1 | v2 in this host | Adapter behavior | +|---|---|---| +| `scope: "global"` | the global config directory is disabled (`Config.configured({ global: false })`) and the profile file is passed as the explicit config source | scope is named `profile`, targeting `layout.config` supplied by the host | +| project scope always active | project documents are opt-in (`ProjectConfig.configured({ enabled })`, `--project-config`) | the project scope reports `writable: false` with the opt-in reason and refuses writes | +| project target = first existing candidate | the host loads every eligible project document, and `.kilocode/kilo.jsonc` outranks `.kilo/kilo.jsonc`, which outranks a root `kilo.jsonc`, which outranks `kilo.json` | project values fold every document `ProjectConfig.readProjectEntries` returns; the write target is the highest-priority loaded document, or `.kilo/kilo.jsonc` when none exists | +| revision-based conflict detection | Kilo-owned optimistic edit token | TUI passes the snapshot's path and SHA-256 revision; a changed target/content is refused before applying the edit. | + +Sources: `packages/kilo-cli/src/host.ts:22`, `src/paths.ts`, +`src/project-config.ts`, `packages/core/src/config.ts:186`, +`packages/kilo-cli/test/project-config.test.ts` ("legacy directory wins"). + +## Managed fields + +Native `Config.Info` fields with a verified consumer at this baseline are +offered, plus one Kilo-only key whose value the host decode drops. Each row +names that consumer. + +| Field | Type written | Consumer | +|---|---|---| +| `model` | `"provider/model"`, `"provider/model#variant"`, or the explicit object | `packages/core/src/config/plugin/provider.ts:42` | +| `default_agent` | non-empty string | `packages/core/src/config/plugin/agent.ts:91` | +| `shell` | non-empty string | `packages/core/src/config/plugin/shell.ts:16` | +| `snapshots` | boolean | `packages/core/src/config/plugin/snapshot.ts:16` | +| `websearch` | `false` or `{ provider }` | `packages/core/src/config/plugin/websearch.ts:14` | +| `warming` | boolean (a stored object form is preserved, not editable here) | `packages/core/src/plugin/warming.ts:19` | +| `compaction.auto` | boolean | `packages/core/src/config/plugin/compaction.ts` configures native automatic compaction | +| `compaction.buffer` | non-negative integer, tokens (not a percentage) | same plugin; `session/compaction.ts` subtracts the buffer from model input/context limits | +| `compaction.keep.tokens` | non-negative integer, including zero | same plugin; `session/compaction.ts` selects the retained recent-history tail | +| `tool_output.max_lines` | positive integer | `packages/core/src/config/plugin/tool-output.ts:16`, `packages/core/src/shell.ts:233` | +| `tool_output.max_bytes` | positive integer | same | +| `media.image.auto_resize` | boolean | `packages/core/src/config/plugin/image.ts:18` feeds the `Image` service (`packages/core/src/image.ts:70`); an oversized image is resized when on and refused when off (`packages/core/src/image/photon.ts:48`) | +| `media.image.max_width` | positive integer | same consumer; bounds the resize and pass-through check | +| `media.image.max_height` | positive integer | same | +| `media.image.max_base64_bytes` | positive integer | same | +| `experimental.subagent_depth` | non-negative integer | `packages/core/src/tool/plugin/subagent.ts:129` (last-wins leaf; defaults to 1) | +| `experimental.portable_shell_scanner` | boolean | `packages/core/src/tool/plugin/shell.ts:123` (last-wins leaf; defaults to false) | +| `hide_prompt_training_models` (Kilo-only) | boolean | the Kilo model picker presentation in `packages/kilo-cli/src/model-picker.ts` reads it through this store; mirrors v1 `filterPromptTrainingModels` (`packages/opencode/src/kilocode/provider/model-filter.ts`) | + +### Deliberately not managed + +Reconciled against the current source on 2026-09-07: `media` and `experimental` were +previously listed here wholesale, but their scalar leaves have verified last-wins +consumers (`media.image.*` via the `Image` service, `experimental.subagent_depth` and +`experimental.portable_shell_scanner` via `Config.latest`), so they are now managed +leaf-wise like compaction. The remaining unmanaged groups: + +| Group | Fields | Why | +|---|---|---| +| Accumulating or secret-bearing collections | `agents`, `permissions`, `mcp`, `providers`, `commands`, `formatter`, `lsp`, `references`, `instructions`, `skills`, `plugins`, `watcher` | they merge, concatenate, or accumulate across documents rather than resolving last-wins (`watcher.ignore` contributes per document at `packages/core/src/config/plugin/location-watcher.ts:17`), and provider or MCP entries can carry credentials; `experimental.policies` stays accumulating for the same reason | +| Parsed but unconsumed | `update`, `share`, `username`, `enterprise` | re-verified 2026-09-07: no consumer exists outside `packages/core/src/v1/config/migrate.ts` and `packages/core/test/config/config.test.ts`, so presenting them as working settings would be false | +| Metadata | `$schema` | not a user setting | + +### Settings surfaces and v1 key migration are separate + +The settings comparison follows current consumers and controls, including renamed +fields. It does not infer a missing capability from an absent v1 key name. The +separate `kilo.jsonc` import row remains open; a refusal is not an implemented +feature or a successful migration. + +| V1 field or surface | Current control / consumer | Disposition | +|---|---|---| +| `auto_collapse_reasoning` | Native `/settings` Thinking and `/thinking` update `session.thinking` in the isolated `tui.json`; the session view consumes it | Reuse reasoning visibility. Hiding by default differs from collapsing after completion; do not claim literal semantics or cross-surface import parity. | +| `terminal_command_display` | Native `mini.shell_output` controls CLI shell output (`packages/tui/src/mini/footer.ts` and `footer.command.tsx`) | Related CLI presentation control, not an exact import mapping for the v1 VS Code expanded/collapsed field. VS Code remains phase 5. | +| `privacy_mode` | Kilo `/privacy` and the isolated privacy store | Existing Kilo control, carried by the importer; not a missing settings implementation. | +| `indexing.*` | Explicit `--indexing-config` input and `/indexing` status/control surface | Retain the existing opt-in adapter and its credential boundary. No silent migration or new general configuration field. | +| `enabled_providers` / `disabled_providers` | Native migration produces `experimental.policies`, consumed by `config/plugin/policy.ts` | Existing configuration behavior. The scalar settings dialog does not edit accumulating policies. | +| `hide_prompt_training_models` | `/kilo-settings`, picker filtering and Gateway request policy | Managed Kilo-only boolean; live filtering and provider data-collection request covered separately below. | +| `web_search` | Native `websearch` provider field | Managed v2 field; the v1 force-enable boolean is not silently reinterpreted as provider selection. | +| `remote_control` | Kilo `/remote` | Remote capability is tracked in its own open row; no v1 auto-start-key consumer is claimed. | +| `commit_message.prompt`, `code_edit_display`, `mcp_tool_display`, Console fields | No corresponding shipped v2 setting consumer | Leave their IDE/commit-generation/Console capability dispositions in the separate plan rows. Unsupported import remains explicit. | + +`ConfigMigrateV1` carries recognized native keys under their native names (for +example `snapshot` → `snapshots`, `small_model` → `agents.title.model`). The +settings adapter does not invent a second migration engine. + +### Community settings proposal comparison + +The canonical plan records #12502 as an open proposal, with the decision to +reuse native controls and implement the scoped Kilo delta rather than transplant +its unified v1 dialog. This is a local source comparison, not a fresh remote PR +status check. + +| Proposed workflow | Current behavior and boundary | +|---|---| +| Presentation controls | Native `/settings` and persistent isolated `tui.json`; no second presentation store | +| Global/project edits | Kilo Profile/Project picker, loaded-document precedence, optimistic revisions, atomic saves, reset fallback and opt-in refusal | +| Immediate save | RPC set/reset persists immediately; UI explicitly requires restart for host configuration to take effect | +| Provider connection management | Native integration dialog adds credentials and removes a selected credential; this is not a generic persistent provider enable/disable editor | +| Model choice | Native picker/favorites plus Kilo metadata/filtering; scoped default `model` is managed by `/kilo-settings` | +| Agent choice | Native `/agents` selects an agent; scoped `default_agent` is managed; it does not edit agent definitions | +| Plugin controls | Native dialog can toggle TUI plugin activation and inspect/update server packages. Host-owned plugin composition is fixed; `createTuiConfig` excludes plugin persistence. Do not claim arbitrary host plugin disable/re-enable persistence. | +| Permission/agent/provider/MCP collection editing | Outside the existing scalar adapter scope. Existing engines/configuration remain; the proposed unified collection editor is not implemented or claimed equivalent. | +| Kilo-specific fields | Managed training-model preference plus existing privacy, indexing, and native display controls described above | + +### Kilo-only keys read raw + +The native decode drops keys that `Config.Info` does not declare, so +`hide_prompt_training_models` is read from separately parsed raw documents +instead of the re-encoded ones. The same fold rules apply: every loaded project +document path contributes, lowest to highest priority, so an ancestor document +that is not the write target can still supply the key; a reset on the target +folds that ancestor contribution back in. A stored value that does not decode is +reported through a fixed `invalid` explanation on the field state — never as the +raw value, which could echo arbitrary file content. Within a scope's fold the +raw read distinguishes absent from `null`: only `undefined` is absent, while a +stored `null` is a value that fails the boolean decode and is reported invalid +like any other wrong type. An invalid scope then contributes nothing, so a +valid value in the other scope still wins — the invalid scope neither shadows +it nor invents a reset. (Native fields keep the store's existing nullish fold, +unchanged.) + +The raw fold is also independent of native document acceptance. Native +config failures are almost entirely field-level — a wrong-typed field drops, +a missing `{env:...}` becomes empty — so a document survives them. The genuine +whole-document failures (a missing `{file:...}` reference fails substitution; +an unparseable or non-object file) skip the document for native fields, yet a +raw Kilo-only value in the same document still applies: the dialog shows it, +the picker honors it, and the request policy denies. This is deliberate, +checkpoint-accepted behavior, now measured by a regression test using a missing +`{file:...}` reference; the broken scope's reason says precisely that the host +ignores the document's native configuration while separately parsed Kilo-only +values may still apply. The host never honors the document's native +configuration in this state — no claim to the contrary is made anywhere. + +The v1 provider-level `dataCollection: "deny"` that v1 also derives from this +key (`packages/opencode/src/kilocode/provider/provider.ts`, +`patchKiloProviderPrivacy` and the `kilo` custom loader) now has a v2 +equivalent. The host bridges the same raw fold above to the Gateway plugin as +`GatewayOptions.dataCollectionPolicy` (`src/request-policy.ts`, wired in +`src/interactive-server.ts`), reusing this store and the picker's own effective +resolution (`hidePromptTrainingModels` in `src/settings-rpc.ts`): project wins +over profile, and only an explicit stored `true` resolves `deny`. False, unset, +or invalid values resolve nothing — an invalid scope does not shadow a valid +one, exactly as the snapshot's `values` report — so the wire never invents a +restriction. The plugin re-reads the policy on startup and on every account +refresh (credential events, `organization.set`, startup) plus `config.updated`, +keeps the last known value on a failed read, and applies it in the existing +`http.request` hook: `provider.data_collection: "deny"` is merged into every +serialized Gateway request body (`/messages`, `/chat/completions`, `/responses`) +with v1 `transformRequestBody` semantics — an existing record `provider` keeps +its routing fields, a missing or non-record one becomes exactly the marker. +Wire proof: the launched-host fixtures assert the marker on title, both native +Auto routes, and ordinary protocol bodies, and its absence while the setting is +unset. This is a deny *request* to the Gateway, not a guarantee that providers +honor it; the picker's hide behavior remains presentation policy only. + +## Write and read semantics + +- Values reported per scope are the canonical values the host resolves: the same + substitute, jsonc parse, normalize, decode pipeline the host applies + (`packages/core/src/config.ts:96`), re-encoded to JSON. A literal + `"anthropic/claude"` therefore reads back as `{ providerID, model }`. +- Kilo-only keys are the documented exception: they are read from raw parsed + documents (no substitution, no Info decode) and are reported as stored. A + stored value that does not decode yields a fixed `invalid` explanation instead + of the arbitrary value; a wrong-typed write is refused with a content-free + message. +- An invalid field is dropped by `ConfigNormalize` exactly as the host drops it, + so a bad value is reported as unset rather than as configuration. +- A malformed document is reported with a reason and is never rewritten from a + partial parse. +- Writes use jsonc `modify`/`applyEdits`, so comments and unrelated keys survive. + A value must decode with its native field schema, and a document that decoded + before the edit must still decode after it. Schema failures never surface, + because they can echo decoded input. +- Writes are atomic (`wx` temp file, `chmod`, `rename`), preserve an existing + file mode, and create new documents `0600` rather than depending on the umask. + Profile writes re-run `preflight` before the rename. +- A failed profile preflight stops every read of that path instead of reporting + the contents of a protected or symlinked file. +- Edits share the isolated profile's in-process queue with other settings + Locations and `/privacy`. Concurrent RPCs cannot overwrite each other's keys, + and a rejected edit cannot poison later edits. A real two-store plus privacy + concurrent-write regression test verifies this; external editor conflicts + still have the separate limitation below. + +## Limitations + +- The dialog now carries `expected: {path, revision}` (null for a missing + target). Content changes, first-write collisions, and a changed project target + are refused with a reopen message. This is an optimistic stale-dialog check, + not a cross-process filesystem transaction; an external write after the + check can still race the rename. Direct callers that omit the optional token + retain the previous immediate-edit behavior. +- Resolution covers the two managed scopes only. Values from sources outside them + (well-known or managed manifests, `OPENCODE_CONFIG_CONTENT`) are not represented. +- Reset edits only the write target. A lower-priority project document may still + contribute the key afterwards, which the snapshot then reports honestly. +- Compaction is a verified leaf-wise exception to the collection limitation: + the native plugin applies each defined `auto`, `buffer`, and `keep.tokens` + leaf in document order. The dialog uses these existing v2 meanings, without + converting v1 percentage thresholds or claiming v1 prune-policy equivalence. + The `media.image` leaves and the `experimental.subagent_depth` / + `experimental.portable_shell_scanner` leaves share that fold shape: the host + applies defined leaves across documents in order (last definition wins), so + the dialog manages them leaf-wise too. +- Loaded configuration changes take effect on host restart: the preview disables + config file watching (`packages/kilo-cli/src/host.ts:18`), and the only other + reload triggers are credential switches, well-known updates, and a ten-minute + refresh (`packages/core/src/config.ts:236`). `restartRequired` carries this to + the UI. +- Collection editing (agents, permissions, MCP, providers) is out of scope, so + the v1 VS Code settings panels have no equivalent here. + +## Wiring + +The host owns registration. `createSettingsPlugin({ layout, project })` registers +the RPC in `interactive-server.ts`; `installSettingsUi(ctx, { client, signal })` +adds `/kilo-settings` in the TUI plugin. The UI imports only `settings-rpc.ts`, +never the filesystem implementation. + +## Verification + +Run from `packages/kilo-cli` with the packaged Bun 1.4 runtime +(`dist/interactive/bun`): + +| Command | Covers | +|---|---| +| `bun typecheck` | the settings modules, the picker, and their test files | +| `bun test test/settings.test.ts` | scope reporting, opt-in refusal, comment and mode preservation, project folding across ancestor documents (including the Kilo-only key), reset fallback, schema refusals, queue recovery, preflight and boundary refusals, no secret echo, Kilo-only null-as-invalid fold (sole/profile/ancestor, content-free notice), the missing-`{file:...}` measurement that a natively ignored document still supplies raw Kilo-only values, and the media/experimental leaf write-refuse-reset roundtrip (2026-09-07: 21 pass / 0 fail with the packaged Bun 1.4) | +| `bun test test/model-picker.test.ts` | group/category derivation, `mayTrainOnYourPrompts === true` hiding only for real metadata (unknown and other providers stay visible), effective project-over-profile preference read live per open, abort and error propagation | +| `bun test test/import-v1-config.test.ts` | v1 config mapping reports, and the Kilo-only boolean import: carried for true/false, read back raw by the settings store, wrong type refused content-free | +| `bun test test/request-policy.test.ts` | the Gateway policy bridge: explicit true/false, project-over-profile precedence, reset fall-through, invalid values resolving unset without shadowing a valid scope, stored null as invalid (sole, non-shadowing, no invention), the missing-`{file:...}` raw-independence pin, disabled project scope | +| `bun test --preload @opentui/solid/preload test/settings-ui.test.tsx` | real host plus real TUI: the host loads values written through the adapter, `/kilo-settings` drives prompt, choice, and reset edits over the public RPC (including the Kilo-only toggle), an unwritable scope explains itself, and no session is admitted | +| `bun test --preload @opentui/solid/preload test/model-picker-ui.test.tsx` | real host, gateway, and TUI: Auto/Recommended grouping, favorite persistence, and the hide preference read live — enabling it removes the flagged model from every dialog section while the favorite persists, and resetting shows it again | + +The model picker's hide behavior is presentation policy only: it filters +listings served by the picker, and never guarantees which models the Gateway +serves or what the provider does with prompts. + +### Settings UI fixture readiness follow-up (2026-09-05) + +The earlier intermittent UI failure selected the first unfiltered option: +merely finding the target's text in a frame did not prove focus, processed +filter input, or selected-row state. The fixture now waits for the native +editor type, its input text and the selected option's rendered bold span +before sending Enter. Toast assertions also allow line wrapping. No production +dialog behavior was changed. + +The delegate recorded three consecutive isolated settings UI passes. Root +independently reran settings UI plus both memory-sidebar targets with bundled +Bun 1.4: **5 pass / 0 fail**. This closes the focused fixture investigation, +not the pending coordinated full-suite validation. + +### Request policy follow-up (2026-09-05) + +The remaining v1 `dataCollection: "deny"` provider-policy gap is closed as +described under "Kilo-only keys read raw": the host bridges the raw settings +fold to the Gateway plugin, which merges `provider.data_collection: "deny"` +into Gateway request bodies when — and only when — the effective stored value +is an explicit `true`. The settings field description now says so, still +without promising that providers honor the request. Direct plugin coverage +proves all three dialects, provider-routing preservation, the v1 non-record +replacement, byte-identical already-applied bodies, malformed-body passthrough, +and refresh keep-last-known; the launched-host fixtures prove the marker on +title, both native Auto routes, and ordinary bodies, and its absence while the +setting is unset. + +Freshness differs deliberately between the two consumers. The model picker +reads the setting live per dialog open through the settings RPC, so a picker +opened after an edit reflects it immediately. The request policy is a cached +host value: the Gateway plugin re-reads it only on startup, on account refresh +(credential events, `organization.set`), and on `config.updated`, matching how +every other loaded config value behaves here — the preview's config file +watcher is off (`packages/kilo-cli/src/host.ts:18`), the snapshot's +`restartRequired` already tells the dialog user that loaded changes apply on +restart, and settings edits emit no event. An edit can therefore reach the wire +earlier than restart (the next account or config refresh re-reads the raw +documents), but never later than the documented restart boundary, and it is +never re-read per request. + +Two inherited fold semantics were reviewed and closed explicitly. A stored +`null` for the Kilo-only key is invalid (reported with the fixed content-free +notice), not absent; an invalid scope contributes nothing and a valid value in +the other scope still wins — no reset is invented. And the raw fold's +independence from native document acceptance applies to the wire too: a profile +document the host ignores natively (measured with a missing `{file:...}` +reference) still supplies an explicit Kilo-only `true`, so the deny marker +applies while the dialog reports the broken scope in the same snapshot. Both +behaviors are pinned in `test/settings.test.ts` and `test/request-policy.test.ts`. + +### Managed-field reconciliation — 2026-09-07 + +The managed-field list was re-derived from the current source instead of the +2026-09-05 audit. Every `Config.Info` leaf with a verified runtime consumer is +now offered: the new rows are the `media.image` leaves (consumer +`config/plugin/image.ts`, leaf-wise like compaction) and the last-wins +`experimental.subagent_depth` / `experimental.portable_shell_scanner` leaves +(`tool/plugin/subagent.ts:129`, `tool/plugin/shell.ts:123`). The previous +classification of `media` and `experimental` as wholesale accumulating groups +was wrong for their scalar leaves and is corrected above; `experimental.policies` +and `watcher.ignore` remain accumulating and unmanaged. `update`, `share`, +`username`, and `enterprise` were re-verified as unconsumed at this source +(share is declared in `packages/schema/src/config.ts:40` but no runtime reader +exists; the share feature row is separately in progress). + +The v1-only fields with no v2 consumer were re-checked key by key and keep +their refusals: `sandbox`-family keys configure the host only through the +`--sandbox` launch flag with host-typed options (`packages/kilo-cli/src/commands.ts:164`, +`interactive-server.ts:153`), telemetry consent is deliberately a separate +owner-only opt-in file (`packages/kilo-cli/src/telemetry-settings.ts`) that +shared upstream config must not express, and no consumer exists for +`remote_control`, `commit_message`, the console and +display keys, the subagent-model keys, or `web_search`'s force-enable boolean. +The importer already reports every such key through the generic drop probe. + +Correction: `auto_collapse_reasoning` does have a native presentation equivalent: +TUI `session.thinking` is consumed by the session view and exposed through +`/settings` and `/thinking`. It is not a missing control. Import remains refused +because profile-to-`tui.json` migration is a separate configuration surface and +v2 default hiding is not identical to v1 collapsing after completion. + +### Goal-62 validation — 2026-09-07 + +The real settings UI fixture now runs with project configuration both disabled +and enabled. The profile scenario retains the opt-in refusal. The project +scenario selects Project in the real dialog, edits the new image-width field, +resets it to expose the untouched profile fallback, then writes a project +override again. After the TUI and host scopes close, a second isolated host on +the same layout loads profile `1024` followed by project `768`; the public RPC +reports the project source. Both boots have zero admitted sessions. Fixture +entry validates containment before any settings writes. + +Parent validation with packaged Bun 1.4: settings UI **2 pass / 0 fail**; +settings, request-policy, model-picker and TUI-config contracts **38 pass / 0 +fail, 208 assertions**; package `bun typecheck` clean. No production code was +changed for this acceptance check, so the existing portable build is unchanged. +This evidence does not itself close the separate import, IDE or remote rows. + +### Whole-row acceptance — 2026-09-07 + +The parent accepts the Settings scopes row after the scoped comparison above, +the two real-TUI scenarios, the focused contracts and independent review. +This raises the canonical inventory from 26/43 to **27/43 (62.8%)**. + +The review's disposition conditions are resolved within the existing plan: +use native presentation controls without claiming exact v1 semantics; leave +cross-surface migration in the separate open import row; retain unsupported +IDE-only settings with their phase-5 capabilities; keep collection editing +outside this scalar adapter, as already specified. This does not declare the +open community proposal fully implemented, add migration credit for refusals, +or close another capability row. diff --git a/migration-tracking/technical-notes/baseline/sidebar-bench-v2-parity.md b/migration-tracking/technical-notes/baseline/sidebar-bench-v2-parity.md new file mode 100644 index 000000000000..d446bab90590 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/sidebar-bench-v2-parity.md @@ -0,0 +1,100 @@ +# Gateway Terminal Bench sidebar v2 parity — 2026-09-07 + +Root-approved exact delta: tolerantly decode the catalog `terminalBench` +metadata, surface it on the existing Kilo-owned Entry schema, and render the +v1 "Terminal Bench 2.0" sidebar section tied to the viewed session's current +model. No Core/upstream/generated-protocol change, no hardcoded bench data, +absence/invalid means the section is omitted and the model is kept. + +## Contract (resolved against the pinned v1 source) + +- Producer wire shape: the Gateway models response carries a top-level + per-model `terminalBench: { overallScore: number, avgAttemptCostUsd: number }`, + optional and tolerantly decoded (`.optional().catch(undefined)`) at + `ecccd1f packages/kilo-gateway/src/api/models.ts:48` and passed through at + `:307`; v1's own tests pin the record shape + (`packages/kilo-gateway/test/api/models.test.ts:37,76,219`). +- Consumer path: v2 `fetchModelMetadata` → KiloModels `Entry` → the sidebar's + `KiloModels.list` RPC → the viewed session's current model resolved from + durable transcript truth (the latest `model-switched` message, falling back + to the session info's model — `session.model.selected` is not in the + client-facing live event manifest, so the durable message is the honest + seam). +- Display units and tones pinned from v1: rows render in the muted text tone + (v1 `sidebar-usage.tsx:77-79` passes `color={theme().textMuted}` into + `UsageRow`; the v2 semantic token is `theme.text.subdued` on both the label + and the value), with Completion = `(overallScore * 100).toFixed(1)%` and + Cost / attempt = `$avgAttemptCostUsd.toFixed(2)` from + `packages/opencode/src/kilocode/components/model-info-panel-utils.ts:17-25`; + section title "Terminal Bench 2.0" (v1 `sidebar-usage.tsx:256-272`), + collapsible, default open, title in the default text tone. + +## What landed + +- `packages/kilo-gateway/src/models.ts` — tolerant per-record decode + (`overallScore`/`avgAttemptCostUsd` must be finite); a malformed or missing + value omits the metadata and keeps the model (same tolerance as + `autoRouting`); `CatalogModel` and `fetchModelMetadata` carry it through. +- `packages/schema/src/kilocode/models.ts` — optional `terminalBench` field on + the existing Kilo-owned `KiloModels.Entry` (no generated-protocol change; the + RPC is consumed via `client.rpc(KiloModels.Definition)`). Scope note: the + prompt-selector closed-enum changes in `packages/kilo-gateway/src/models.ts` + belong to the previously accepted prompt-selector slice (another owner) and + are not part of this delta; the B1 diff is the `terminalBench` decode, + `CatalogModel` field, and metadata passthrough only. +- `packages/kilo-cli/src/tui-plugin/sidebar-bench.tsx` (new, Kilo-owned) — the + section renders only when the viewed session's current model is a Kilo + catalog model carrying valid metadata; identity + (`revision`/`organization`/`session`/`location`) drives an abortable + refetch; `server.connected` refreshes; semantic theme tokens + (`theme.text.default`/`subdued`); one install line in the production + `tui.tsx` next to the routed-model sidebar. +- Absence/invalid never drops the model: the decode mirrors the AutoRouting + tolerance and the inventory keeps the record (tested). + +## Regression coverage + +- Raw decode through the real RPC (`packages/kilo-gateway/test/plugin.test.ts`): + valid metadata surfaces on the Entry; absent stays absent; malformed + (string, non-finite, wrong shape) is dropped per model without dropping the + model. Tolerance matches the pinned v1 decode exactly: v1's + `z.object({ overallScore: z.number(), avgAttemptCostUsd: z.number() })` with + `.optional().catch(undefined)` rejects non-finite numbers (z.number() + rejects NaN/Infinity) and any wrong shape while keeping the model; the v2 + `Schema.Finite` fields plus per-record decode reproduce that. 24 tests / + 0 failures for the file. +- Real-renderer isolated-host fixtures + (`test/sidebar-bench-ui-fixture.tsx` + `test/sidebar-bench-ui.test.tsx`, + bundled Bun 1.4.0, loopback-only Gateway): 4 pass / 0 failures — + `valid` (exact pinned formatting `42.5%` / `$1.23`, keeping-width resize + does not disturb the section, narrow/wide auto-sidebar hide/restore), + `absent` (no section), `invalid` (no section, no invented value, model kept), + `switch` (model switch hides/returns the section with no stale value; the + account switch to a scope whose catalog carries no bench data hides the + section — the personal value never leaks — and switching back restores it, + driven through the production /profile account refresh that bumps the TUI + revision). +- Consolidated review fixes (same fixtures): the identity-change handler clears + the held entries before the refetch — a **source-reviewed defensive + invalidation**: the public account-switch triggers (the /teams and /profile + commands) wait for the server-side catalog to warm before the revision bump, + so no stale window is empirically demonstrable at those triggers and the test + makes no no-stale-intermediate guarantee; `server.connected` keeps the held + entries until the refresh lands (reconnect without flicker). An actual color + assertion reads the render buffer's fg values and asserts the bench rows + render in the theme's subdued tone (equal to a known subdued element, + distinct from the section title's default tone), per the pinned v1 muted + tone; a reviewer mutation of the tone fails the test. The decode tolerance + test additionally covers a non-finite (NaN) record: dropped per model, + model kept. + +## Honest gaps + +- The section is per-session-view (the current model), not a live metrics + feed: the catalog metadata is static per account scope and refetches only on + the identity changes above. +- v1 also showed bench data in the model-info panel + (`model-info-panel.tsx:131`); the v2 model-info equivalent is not part of + this delta and is not claimed. +- Multi-platform rendering was not probed; the fixture is macOS arm64 with the + bundled Bun 1.4.0, matching the accepted sidebar fixtures. diff --git a/migration-tracking/technical-notes/baseline/sidebar-indexing-v2-parity.md b/migration-tracking/technical-notes/baseline/sidebar-indexing-v2-parity.md new file mode 100644 index 000000000000..e4f0dd320a83 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/sidebar-indexing-v2-parity.md @@ -0,0 +1,32 @@ +# Indexing sidebar — local v2 adapter + +The Kilo sidebar now reads the real location-scoped engine status through +`kilocode.indexing`. It uses the existing `sidebar.content` slot; no upstream +sidebar implementation is copied or changed. + +| Surface | Source / behavior | Validation boundary | +|---|---|---| +| State | Canonical `@kilocode/indexing/status` schema, returned by the active `IndexingHost` | Disabled, loading and unavailable remain distinct. | +| Progress | Engine processed/total file counts and percentage | Shown only during indexing with a nonzero total. No inferred completion. | +| Refresh | Location change plus bounded polling of an enabled engine | Cancels on unmount/location change; disabled status does not poll. | +| Consent off | Lightweight status-only plugin when no indexing configuration is supplied | No engine initialization, index directory, or embedding request. | +| Scope | Current session Location, not process cwd | Existing real-host semantic-search test also checks the same status RPC. | + +`test/sidebar-indexing-ui.test.tsx` boots the real isolated host and TUI, reads +disabled status, checks the visible section across a resize, and verifies that +reading it creates no index, message or inbox work. The engine integration test +checks enabled status through the public RPC alongside real loopback embeddings +and LanceDB search. Tests use no live account or paid inference. + +This is one completed sidebar slice, not completion of the coarse CLI/TUI row. +Background processes, PR details, additional usage presentation and Plan remain +separate acceptance items. The UI exposes only state and numeric progress, not +provider configuration, API keys or arbitrary engine error text. + +## Goal 70 renderer acceptance — 2026-09-07 + +The real indexing engine now has an automated renderer scenario covering In +Progress with actual per-file counts, then Complete after releasing a held +loopback embedding batch. The disabled/no-engine scenario and resize assertion +remain. Parent run: 2 scenarios pass; combined with usage: 3 pass / 6 wrapper +assertions. No component copy or invented producer state is used. diff --git a/migration-tracking/technical-notes/baseline/sidebar-pr-v2-parity.md b/migration-tracking/technical-notes/baseline/sidebar-pr-v2-parity.md new file mode 100644 index 000000000000..bb05c25ca191 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/sidebar-pr-v2-parity.md @@ -0,0 +1,124 @@ +# PR sidebar v2 parity + +Bounded slice: the v1 `sidebar-pr.tsx` line (`order: 50`, +`packages/opencode/src/kilocode/plugins/sidebar-pr.tsx` at +`ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`) ported to the v2 TUI as a +Kilo-owned sidebar claim. Source comparison is source-backed and +isolated-host-tested; no live account, network, or real `gh` call is used. + +## Surface + +`packages/kilo-cli/src/tui-plugin/sidebar-pr.tsx` appends to the native +`sidebar.content` slot (`packages/plugin/src/tui/context.ts:184`) and renders +one muted line `PR #N - title` for the viewed session's repository branch, +hidden when nothing matches. Producers are all public v2 seams: + +| V1 behavior | V2 result | Evidence / boundary | +|---|---|---| +| `api.state.vcs?.branch` + `api.state.path.directory` | Session `location` from `ctx.data.session.get(sessionID)?.location ?? ctx.location`; branch read reactively from `ctx.data.location.vcs.info(ref)?.branch.current` (`packages/client/src/solid/data.ts:1238`, `VcsInfo` at `packages/client/src/promise/generated/types.ts:1636,427`) | The component syncs vcs for its location; the native footer reads the same store (`packages/tui/src/feature-plugins/sidebar/footer.tsx:19`). | +| `gh pr view` (tracking ref, then branch) first | Same two calls, decoded through an Effect Schema for gh's canonical `{ number, title }` record | Malformed view payloads — including `null` — fail closed to "no PR" instead of throwing. | +| `git rev-parse HEAD` then `gh pr list --search` / fork-parent chain | Same chain verbatim: `pr list --search " is:pr"`, `gh repo view --json nameWithOwner,parent`, `pr list -R parent --head branch`, final `pr list -R parent --search` | `selectPr` validates each list entry once against the canonical `{ number, title, headRefOid }` record and skips malformed or non-matching entries — v1's skip-and-keep-scanning semantics, so a bad entry cannot shadow a later valid candidate; only an exact `headRefOid === HEAD` match renders. | +| Per-lookup 20s abort budget; the `timeout: 1000` spawn option is the kill grace after abort, not a per-command deadline; 5-minute `Bun.which("gh")` probe TTL | Same semantics: the component owns one 20s deadline controller for the whole lookup; stopping the child is one consolidated path — abort and read failure both SIGTERM it and one retained timer escalates to SIGKILL after the 1s kill grace, guarded against repeats and cleared when the call settles; the child's exit is awaited on every path; the resolved executable is what every gh spawn uses, never re-resolved mid-lookup | Source: v1 `packages/opencode/src/util/process.ts:86-92` (abort → SIGTERM → `timeout` ms → SIGKILL) and `:118-127` (exit always awaited). `Bun.which("gh", { PATH: process.env.PATH })` and `env: process.env` on spawns because Bun caches the boot-time PATH for bare-name lookups; git still spawns by name with the ambient environment, as in v1. | +| Branch-identity keyed resource, stale responses dropped | `on()` key of directory + workspaceID + current branch; each run owns an `AbortController` (20s deadline) aborted on supersede/unmount, a late result is dropped via the aborted-signal check, and the promise carries a rejection handler so a lookup failure can never surface as an unhandled rejection | Supersede kills in-flight children; no retry of a superseded lookup. | +| Stable unconditional root box; conditional inner text | Same shape (`` always mounted, `` for the line) | The OpenTUI slot registry requires a stable root. | +| Line color `textMuted` | `theme.text.subdued` | The v2 muted role token, per the theme-token rule. | + +The lookup is a client-side spawn of local `gh`/`git`, exactly like v1; no +server RPC, account, or network surface is involved beyond what `gh` itself +does on the user's machine. + +## Production wiring + +Root owns the install line in `packages/kilo-cli/src/tui-plugin/tui.tsx` +(not present in this slice's tree; the fixture registers the same install +through a runtime-written test plugin instead): + +``` +import { installPrSidebar } from "./sidebar-pr" +installPrSidebar(ctx, { signal: controller.signal }) +``` + +placed beside the other sidebar installs inside the `append: "app"` render. + +## Honest boundaries + +- No `description` or `ports` fields exist on v2 (`ShellInfo` producer, + `packages/core/src/shell.ts:262-282`) — the PR line renders only + number/title from `gh`, like v1. +- Live branch changes do not reach the TUI on this macOS host: the host's + location watcher resolves the repository's git directory and watches + `/.git/HEAD` as a file event — no polling — + (`packages/core/src/filesystem/watcher.ts:208-219`, + `packages/core/src/filesystem/location-watcher.ts:36-38`), and a measured + observation on this host is that a same-commit `git checkout` HEAD rewrite + produced no `fs.watch` event for `HEAD` across three runs (only + `index.lock`/`AUTO_MERGE.lock` surfaced). This is a host-specific + measurement — this machine's git/FSEvents combination — not a claim about + macOS generally or about Linux, where the same watcher may well deliver. + Because the location service layer for an existing directory is immortal + (`packages/core/src/location-services.ts` `idleTimeToLive`), the server's + cached branch goes stale until the layer rebuilds. The component therefore + re-looks-up on every store update the host does deliver — location identity + changes (real tab/session switches re-sync vcs, + `packages/tui/src/context/session-tabs.tsx:295`) and `vcs.branch.updated` + store updates wherever the watcher delivers. The native footer shares this + exact producer chain. + cached branch goes stale until the layer rebuilds. The component therefore + re-looks-up on every store update the host does deliver — location identity + changes (real tab/session switches re-sync vcs, + `packages/tui/src/context/session-tabs.tsx:295`) and `vcs.branch.updated` + store updates wherever the watcher delivers. The native footer shares this + exact producer chain. +- The wrapped title uses the text element's default wrapping (v1 had no + `wrapMode` either); narrow widths hide the whole sidebar through the native + auto-sidebar policy before wrapping is a concern. + +## Verification + +Bundled Bun 1.4.0 (`packages/kilo-cli/dist/interactive/bun`), +`packages/kilo-cli` typecheck (tsgo; owned files clean) passes. Owned TS files +are prettier-formatted: + +- `test/sidebar-pr.test.tsx` — 12 pass: `parsePr` accepts the canonical view + record and fails closed on `null`, malformed JSON, arrays, string numbers, + and missing fields; `selectPr` validates each entry once, skips malformed + and `null` entries (bad-before-valid stays safe, matching v1's + skip-and-keep-scanning), rejects PRs merely referencing the SHA, and + returns null for nothing-matching, invalid JSON, empty lists, and non-array + payloads without throwing; `parseRepo` resolves the fork parent name over + the owner/login composite and fails closed on `null`, malformed JSON, + missing self, self-parents, and empty names; `runText` proves cancellation + terminates the owned child within the kill grace (exit awaited, mapped to + no exit code), that a real child ignoring SIGTERM (bash `trap "" TERM`) is + escalated to SIGKILL by the retained one-second grace timer — elapsed + crosses the grace instead of dying with the SIGTERM — and that normal + completion resolves with the real exit code inside the grace. +- `test/sidebar-pr-ui.test.tsx` — 8 real renderer scenarios + (`test/sidebar-pr-ui-fixture.tsx` boots the real isolated host, real + OpenCode client, real `run()` TUI with the production component installed + through a runtime-written test plugin under an isolated `pluginDirectories` + root; a fixture `gh` executable on a restricted PATH replaces the real + binary — no real `gh`, account, or network call): + - found: PR #2 renders, the long title wraps inside the sidebar column, the + hide/show resize cycle performs exactly one fresh lookup, a keeping-width + resize performs none, and `/exit` while a delayed lookup is in flight + aborts it and exits cleanly. + - stale: switching tabs to the sibling repository location supersedes the + in-flight `main` lookup; the delayed stale response never renders, the + new location's PR renders, and the superseded lookup never retries. + - nopr: `gh` reporting nothing renders no line and a stable root. + - nogh: with `gh` absent from the PATH the probe misses, nothing renders, + and the shim is never invoked. + - sha: with `pr view` failing, the real fallback reaches + `pr list --search " is:pr"` and renders the PR whose `headRefOid` + equals the local HEAD exactly. + - sha-wrong: the search returns a PR with a different `headRefOid`; nothing + renders and the wrong-head payload never leaks into the frame. + - parent: with the view and local search failing, `gh repo view` resolves a + fork parent and `pr list -R parent --head branch` renders the matching PR. + - parent-sha: with the parent `--head` lookup failing, the final + `pr list -R parent --search` renders the matching PR (both `pr list` + fallback calls observed in the shim log). + All eight fixtures also assert no session messages or inbox items are + created; the fixture's child TUI exits 0 and prints + `TUI_PR__OK`. diff --git a/migration-tracking/technical-notes/baseline/sidebar-processes-v2-parity.md b/migration-tracking/technical-notes/baseline/sidebar-processes-v2-parity.md new file mode 100644 index 000000000000..18f98d2149dd --- /dev/null +++ b/migration-tracking/technical-notes/baseline/sidebar-processes-v2-parity.md @@ -0,0 +1,174 @@ +# Running-process sidebar v2 parity + +Bounded slice: the v1 `sidebar-background-processes.tsx` line (order 250, +`packages/opencode/src/kilocode/plugins/sidebar-background-processes.tsx` at +`ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`) ported to the v2 TUI as a +Kilo-owned sidebar claim scoped to the viewed session's running background +shells. Source comparison is source-backed and isolated-host-tested; no +model, network, or account surface is used. + +## Surface + +Local restart acceptance — 2026-09-07: the real-host fixture now covers both +client-only reopen (the same live PID remains visible) and server restart on +the same isolated store (stale durable `running` history renders no PID before +or after explicit resync). The server scenario cleans up its owned orphan +process before relaunch. The delegate reported four UI scenarios and four unit +tests passing with bundled Bun 1.4, plus package typecheck. This is automated +coverage for UI-083's restart step; no manual scenario is marked RUN. + +`packages/kilo-cli/src/tui-plugin/sidebar-processes.tsx` appends to the native +`sidebar.content` slot (`packages/plugin/src/tui/context.ts:184`) and renders +one row per **running session shell** of the viewed session: the command plus +a muted `PID ` line when the producer reports a real PID. The section is +hidden when nothing is running and the slot root stays mounted. The section +renders every shell this session owns whose live registry status is running — +the durable `metadata.background` flag is deliberately not a filter, because +background tool shells do not set it +(`packages/core/src/tool/plugin/shell.ts:210-214`) while session-shell mode +sets it even when the submitting client waits (`session/shell.ts:19`); the +wording matches that filter exactly. + +## Producer facts (current sources, not audit assumptions) + +- **Session-scoped membership is durable**: `session.shell.started/ended` + events project into durable session message parts — `SessionMessageShell` + (`type: "shell"`, `shellID`, `command`, `status`, `exit?`, + `metadata.background`) via `packages/core/src/session/message-updater.ts:167-190`. + The plugin reads them through the public typed union — + `ctx.data.session.message.list(sessionID)` returns `SessionMessageInfo[]` + (`packages/plugin/src/tui/context.ts:84-86`, type at + `packages/client/src/promise/generated/types.ts:2137`), fed by the public + `message.list` history API (`packages/client/src/solid/data.ts:1521-1541`), + so a resumed session re-derives membership from durable truth rather than + from live events. The helper narrows the union with `type === "shell"` + directly — no unknown casts and no speculative malformed internal values. +- **Live status**: the plugin store exposes `ctx.data.shell.get(shellID)` + (`context.ts:115`; the client store keeps only in-flight shells, + `packages/client/src/solid/data.ts:92-93`). Membership is the intersection: + a durable part renders only while its live `ShellInfo.status` is + `"running"`. A shell that finished while the session was away, or a server + that restarted, reports no live shell and never renders. +- **Session-scoped by durable membership (data scoping, not a security + guarantee)**: the section renders only shells the viewed session's own + durable history recorded; other sessions' or locations' shells are not in + that history, so they are not members. This is a property of the durable + producer's scoping, not a permission boundary. +- **Real fields only**: command (durable part) and `ShellInfo.pid` — a real + spawner handle PID (`packages/core/src/shell.ts:307-309`, + `packages/util/src/cross-spawn-spawner.ts:462`). v1's `description`, + `PORTS`, and readiness vocabulary have no v2 producer + (`packages/core/src/shell.ts:262-282` builds `metadata` as a free-form + record) and are not fabricated. No full v1 supervision claim. + +## Producer facts learned while building (audit corrections) + +- The `shell.created` event's record predates the spawned process and carries + **no PID** (`packages/core/src/shell.ts:406` publishes the base `info`; the + PID lives on the registry's `Active` copy, `:307-309`). The section + therefore invalidates and re-syncs the shell store on created events. +- `sync.run` without a prior `invalidate` is a no-op for an already-completed + key (`packages/client/src/solid/data.ts:155-163`), so every reconciliation + refresh pairs `invalidate` with `sync` — including the session/location + re-key, which invalidates both the shell store and the session's message + cache so a revisited session reconciles stale rows instead of replaying + them. +- Shell entries publish under the server's canonical response location; the + `exited`/`deleted` events can leave an entry under the canonical key that + the component's raw-location read misses, so the same invalidate+sync + refresh runs on those events too. +- `shell.timeout(id, 0)` is the backgrounding idiom — it clears the deadline + and never kills (`packages/core/src/shell.ts:380-382`); terminating a shell + through the public API requires a nonzero deadline (the tests use 1ms, + which finishes the shell as `timeout` and kills the process). + +## Subscription ownership + +The three `data.on` refresh subscriptions are held for the section's lifetime +and released twice over: `onCleanup` disposes them on every section remount +(tab switches remount the section input), and the install-level abort signal +(disposed through `installProcessSidebar(ctx, { signal })`) disposes them +when the whole plugin tears down. No handler accumulation across remounts. + +## Honest boundaries + +- No `description`, `PORTS`, or readiness fields exist on v2 and none are + rendered. +- v2 does not durably distinguish background tool shells: the bash tool's + background path carries only `metadata.sessionID` + (`packages/core/src/tool/plugin/shell.ts:210-214`), while the session-shell + path sets `metadata.background: true` (`packages/core/src/session/shell.ts:19`). + The section therefore renders **all running session shells** — which are + exactly the long-lived/background ones while they run — and makes no + supervision claim beyond live running state. The component's wording states + this filter, not a background-only one. +- The resume fixture simulates revisiting through session tabs in the same + client (the ended event is observed live, and the re-key reconciliation + clears the cache); a true cross-process resume with a restarted server is + covered by the join semantics — a durable running part without a live + registry entry never renders — which is unit-tested. +- Rows render in durable history order; no v1 collapse-above-two behavior is + reproduced. +- A session-scoped `listBySession` exists on the client store + (`packages/client/src/solid/data.ts:1722-1725`) but is not part of the + plugin `Data` contract, so the section joins the plugin-typed `get` with + the session's durable parts instead of widening the plugin API. + +## Production wiring + +Root owns the install line in `packages/kilo-cli/src/tui-plugin/tui.tsx` +(the fixture registers the same install through a runtime-written test +plugin under an isolated `pluginDirectories` root): + +``` +import { installProcessSidebar } from "./sidebar-processes" +installProcessSidebar(ctx, { signal: controller.signal }) +``` + +placed beside the other sidebar installs inside the `append: "app"` render. + +## Verification + +Bundled Bun 1.4.0 (`packages/kilo-cli/dist/interactive/bun`); whole-package +`tsgo` typecheck passes; owned TS files are prettier-formatted: + +- `test/sidebar-processes.test.tsx` — 4 pass over the typed public producer: + `runningShells` narrows the real `SessionMessageInfo` union (`type === + "shell"`, no unknown casts), joins the durable messages with the live + `ShellInfo` registry, hides messages whose live entry is not running or is + gone, and renders the command without a PID line when the producer reports + none. +- `test/sidebar-processes-ui.test.tsx` — 2 real renderer scenarios + (`test/sidebar-processes-ui-fixture.tsx` boots the real isolated host, real + OpenCode client, real `run()` TUI with the production component installed + through a runtime-written test plugin; the shell is a real `sleep` process + started through the public `session.shell` API — no model, network, or + account): + - lifecycle: the running row renders with the real command and a real + `PID `; re-fetching durable history keeps the row (membership is + history-derived, not event-seeded); switching tabs to a second session + hides the row (no cross-session leak); switching back restores it; the + public `shell.timeout` API terminates the shell and the row and PID row + disappear; narrow/wide resizes and `/exit` cleanup behave. + - resume: the same durable-truth resync keeps the running row; the shell + then completes naturally while a second session is focused, and + revisiting the session reconciles the stale location cache — the ended + shell stays hidden through both the live store and a fresh durable + history fetch. + Both fixtures assert the TUI exits 0 and print + `TUI_PROCESSES__OK`. + +## Manual scenario recommendations for root + +1. Open a session, press the shell-mode binding, run a long command (for + example `sleep 60`), and check the sidebar row appears with the command + and PID, then disappears when the command ends. +2. Start a long shell, switch to another session tab in a different + repository, and confirm the row does not leak into that session's sidebar; + switch back and confirm it returns while still running. +3. Leave a session with a running shell, quit the TUI, reopen the session: + the row returns only while the shell is still live on the server. +4. Run a tool-spawned background command (`background: true` bash) in a + session: the row stays visible while the command runs. +5. Narrow the terminal to the auto-hide width and back: the section hides and + restores without extra shell API traffic. diff --git a/migration-tracking/technical-notes/baseline/sidebar-usage-v2-parity.md b/migration-tracking/technical-notes/baseline/sidebar-usage-v2-parity.md new file mode 100644 index 000000000000..b2e3c851fb34 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/sidebar-usage-v2-parity.md @@ -0,0 +1,13 @@ +# Session family usage v2 parity + +The Kilo CLI sidebar now reads only the local `kilocode.session-usage` RPC. It labels the view **Session family usage** so its durable root-and-descendant aggregate is not confused with the native direct-session context spend shown elsewhere in the TUI. + +It renders reported input, output, reasoning, cache-read, and cache-write tokens, the accumulated step count and USD cost, and per-model step/cost rows. Per-model rows expand to their token breakdown. Cache rate keeps the v1 denominator: `cache.read / (input + cache.read + cache.write)`. No throughput, benchmark, or inferred estimate is displayed because the durable RPC does not provide one. + +The sidebar masks model identifiers and costs while the isolated Kilo privacy accessor is enabled. Mounting, expanding, resizing, and reading the aggregate never invokes a model: they make only the local authenticated usage RPC request. A changing session or location aborts the prior request and returns to loading before fetching the new identity; terminal execution, create/delete/revert/move, and server reconnect events request a fresh aggregate. + +Family membership follows the source semantics: durable `parentID` descendants in the same project. A `session.fork` is transcript provenance (`fork_session_id`), not a family child, and is intentionally excluded. The renderer fixture creates a genuine parent child by exporting a completed session from a second local host and importing it with `parentID` into the root's host; it never uses a paid account or live inference service. + +The fixture proves the public usage RPC includes the imported child, its aggregate has both model rows, the real TUI renders the masked counts, and sidebar-only resize leaves fake-model request count unchanged. A post-mount root completion updates the public aggregate and the visible 160-column sidebar. At 100 columns the native auto-sidebar policy hides the entire sidebar; resizing back to 140 columns restores the refreshed totals without another model request. + +The real renderer fixture now mouse-clicks a distinguishable per-model row, verifies its actual token breakdown, checks that the other row stays collapsed and privacy remains masked, then collapses it again. Both interactions preserve the model-request count. After sidebar clicks the fixture explicitly refocuses the composer before `/exit`; omission of that step caused a test cleanup hang, not a usage-production defect. Parent final usage plus indexing run: 3 pass / 6 wrapper assertions (2026-09-07). diff --git a/migration-tracking/technical-notes/baseline/skill-shell-parity.md b/migration-tracking/technical-notes/baseline/skill-shell-parity.md new file mode 100644 index 000000000000..ccf1d2c1b034 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/skill-shell-parity.md @@ -0,0 +1,61 @@ +# Trusted skill shell parity + +This slice ports the bounded model-initiated backtick-wrapped `!` shell +placeholders from `origin/main@ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`'s +`packages/opencode/src/kilocode/skills/inject.ts` into the Kilo v2 host. It is +deliberately not a general shell expansion mechanism. + +| v1 rule | Kilo v2 behavior | +| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Expand only model-loaded skills | The Kilo post-policy wraps the native `skill` tool; `/skill` remains the literal `Session.skill` path. | +| Ignore fenced and long inline-code examples | `skill-shell-render.ts` preserves those ranges and rewrites only live placeholders. | +| Trust global/builtin source provenance | Trust is derived from the exact isolated profile document/content source or the fixed Kilo builtin identity. `Skill.Info.location` alone never grants trust. | +| Keep project skills untrusted | Project documents, `.kilo/.kilocode` roots, relative paths, URL sources, and ambiguous duplicate source declarations remain untrusted. | +| Prevent symlink trust escape | Candidate and source roots are realpathed; project descendants stay untrusted, and a lexical project skill whose final path resolves outside the project is removed from inventory. Trust resolution failure cannot enable execution. | +| Ask once for the batch | Portable parsing and raw/parsed native shell permission resources are collected before one Core Form containing the verbatim command list. | +| Preserve host execution policy | Approved commands execute through the native `shell` tool via `Tool.Service.snapshot()`, retaining jobs, interruption, sandbox/cwd checks, and native output handling. | +| Bound execution | At most 32 unique commands, 120 seconds per native command, a 300-second post-approval budget checked before each command (matching v1), and 32 KiB per inlined result. | +| Surface unsupported/unsafe input | Portable scanner opaque regions reject before execution; native permission failures and interrupts are not converted into success output. | + +The host callback API is: + +```ts +createSkillShell({ + layout, + sessions, + instances, + global, + fs, + profileContent?, + disabled?, +}) +``` + +It returns `SkillShell(skillID, toolContext)`, an Effect returning a replacement +prepared-output string or `undefined` when the native result must remain +unchanged. `global` and `fs` are supplied by the host because +they are global services and are not provided by `Instance.provide`. + +The host constructs this callback with the isolated profile services and passes +it to `createSkillPolicy({ skillShell })` as a post plugin. No ambient OS +provider history or global `.claude`/`.agents` directory is read by this +implementation. + +Validation: + +- `bun typecheck` in `packages/kilo-cli` passes at this checkout. +- `bun test --preload @opentui/solid/preload --timeout 30000 +test/skill-shell.test.ts` passes 8 tests / 30 assertions covering renderer, + trust, symlink, ambiguity, content-source, cap, and UTF-8 bounds. +- `dist/interactive/bun test --preload @opentui/solid/preload --timeout 30000 + test/skill-shell-live.test.ts` passes 7 tests / 34 assertions covering the + isolated fake-model approval, rejection, native denial, headless + cancellation, external-directory preflight, escaped-project-symlink + inventory refusal, tool-result output, and literal `session.skill` path. + The bundled runtime is required because the host rejects Bun < 1.4. + +This is a bounded compatibility slice, not a claim that every historical v1 +skill workflow is identical. In particular, only model calls through the +native v2 `skill` tool expand trusted placeholders; explicit user skill +activation remains literal, and untrusted/unsupported sources are preserved as +content without execution. diff --git a/migration-tracking/technical-notes/baseline/skills-v2-parity.md b/migration-tracking/technical-notes/baseline/skills-v2-parity.md new file mode 100644 index 000000000000..8d1267aabb28 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/skills-v2-parity.md @@ -0,0 +1,117 @@ +# Skills v2 parity + +Audited on 2026-09-05 at current v2 HEAD +`59b29de40966803e2c7cd734d439843fb773f6a6` and the local Kilo +`origin/main` ref `ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`. +The earlier pin `76dbaf20adbd43fd208a00ef3cda4a51e125a234` is baseline +provenance, not a claim that these host additions exist at that pin. + +This note records the supported Kilo v2 slice and the host-owned shell +compatibility extension. Upstream v2 itself has no v1 skill trust field. + +## Supported v2 slice + +The Kilo interactive host now has an opt-in project configuration adapter in +`packages/kilo-cli/src/project-config.ts`: + +- native project documents are read only when `projectConfig` is enabled; +- `.kilo/` and `.kilocode/` are walked inside the canonical project boundary; +- native and supported v1 markdown agent frontmatter is converted through the + exported v2 `ConfigMarkdown`, `ConfigAgent`, `ConfigAgentV1`, and + `ConfigMigrateV1` schemas; +- `skills/` emits the native `AgentsDirectory` entry, while generic project + directory entries (which can discover plugins) are never emitted; +- symlinked files and directories are ignored, and project plugin declarations + are reported but not loaded; +- `packages/kilo-cli/src/skill-policy.ts` contributes the Kilo configuration + builtin from the real `skill-policy-kilo-config.md` asset; a project skill + with the reserved `kilo-config` ID cannot replace it and produces a fixed + diagnostic. + +The public v2 skill transform is sufficient for that bounded policy. The +transform receives `list`, `get`, `add`, `update`, and `remove` only; it does +not receive source provenance. The host therefore registers this policy in +the post phase, after config skill discovery, and reserves the builtin ID +explicitly. + +## v1 behavior audited + +The relevant v1 sources are under `origin/main`: + +- `packages/opencode/src/skill/index.ts:34-40` defines `Skill.Info.trusted`; + discovery carries a `trusted` bit on each match (`:85-102`), derives it + from the source roots and project realpaths (`:185-195`, `:245-280`), and + seeds builtins before discovered skills (`:296-316`); +- `packages/opencode/src/kilocode/skills/inject.ts:41-105` parses live + `!\`cmd\`` placeholders, leaves fenced/documentation examples inert, blocks + disabled or untrusted skills, asks for permission for the complete command + batch, and only then runs bounded commands; +- `packages/opencode/src/tool/skill.ts:45-56` is the only call site of + `SkillInject.render`. Thus `SkillInject` executes for a model-initiated + `skill` tool load. The user-initiated `/skill` path does not call + `SkillInject` and never gets that injector behavior; +- the separate v1 command/template path in + `packages/opencode/src/session/prompt.ts:2406-2425` has its own shell + substitution branch. That path is not evidence that the v1 `SkillInject` + contract can be reused by a v2 public plugin. + +The v1 builtin inventory is in +`packages/opencode/src/kilocode/skills/builtin.ts`; it embeds +`kilo-config.md` and identifies the builtin with the special `builtin` +location. User-skill replacement by name was a v1 behavior, but v2's Kilo +policy intentionally reserves its own builtin ID because the public transform +cannot distinguish a trusted builtin from a project overlay. + +## Current v2 seams and host-owned approval + +Current v2 `packages/schema/src/skill.ts:19-27` has `id`, `name`, optional +display flags, `location`, and `content`; it has no `trusted`, source-root, or +provenance field. Content is passed directly by +`packages/core/src/skill.ts:37-68`. The model skill tool calls +`Permission.Service` and `Skill.prepare` at +`packages/core/src/tool/plugin/skill.ts:48-60`, and prompt skill attachments +call `Skill.prepare` at `packages/core/src/session/prompt.ts:56-72`; neither +path parses or executes `!` placeholders. + +The public plugin API does not provide the missing approval boundary: + +- `packages/plugin/src/effect/permission.ts:22-24` exposes only + `list`, `get`, `reply`, and an `evaluate` hook; +- `packages/plugin/src/effect/shell.ts:15-17` exposes only a + `create.before` shell hook; +- `packages/plugin/src/effect/plugin.ts:47-64` exposes those domains through + plugin context, but has no first-class forced-interactive approval request. + +The initial plugin-only gap is now closed by Kilo-owned host composition, +not a new shared Core/schema patch. The host supplies source provenance, +native permissions, Core Form approval and the native shell tool to a narrow +callback. The post plugin wraps only the model-initiated `skill` tool. It does +not spawn Bun itself and does not modify user `session.skill` activation. +See [trusted shell evidence](skill-shell-parity.md) for the exact API, bounds, +forced-human confirmation, headless cancellation and native policy tests. + +## Acceptance coverage and open items + +Covered by the Kilo tests: + +- project native/v1 markdown agents, precedence, boundary checks, symlink + rejection, and plugin-directory non-discovery; +- native project skills under `.kilo/` and `.kilocode/` only when opt-in is + enabled; +- the real Kilo builtin asset, reserved-ID collision behavior, and literal + user-activated `!` placeholder content through the interactive host; +- trusted model skill output is expanded only after native preflight and a + mandatory Form; native deny, explicit rejection and headless auto execute + no command; +- project-config filesystem skill roots are canonically confined before + becoming native config entries. Absolute and relative out-of-project + sources are omitted; remote URLs retain native cache confinement and remain + untrusted. Missing or symlinked roots require correction/restart; +- inner project skill-file symlinks resolving outside the project are removed + from inventory, so neither model nor user skill loads expose that content. + +Remaining items are intentionally unclaimed: + +- exporting a new shared v2 skill provenance field (host-owned checks suffice); +- automatic scanning of existing v1 home-directory skill stores; +- broad project plugin trust or generic executable directory discovery. diff --git a/migration-tracking/technical-notes/baseline/startup-loading-v2.md b/migration-tracking/technical-notes/baseline/startup-loading-v2.md new file mode 100644 index 000000000000..53218e15f7d5 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/startup-loading-v2.md @@ -0,0 +1,87 @@ +# Kilo v2 startup optimization + +Continued the startup investigation from BB thread `thr_qr9z7p3cqc` on +2026-09-07. The earlier signed-out catalog and fixture-storage fixes were +already validated and confirmed working by Johnny. The temporary +`Starting Kilo…` text has been removed at his request. + +## Result + +- The native TUI renders without waiting for Gateway network activation. + The existing server `Plugin.awaitActivation` gate still protects prompts, + model execution, and restart recovery. Cold sessions wait for their catalog; + they cannot race model resolution against its arrival. +- Interactive Gateway activation restores a schema-validated snapshot only + when server, credential identity and value, and requested organization match. + Default organization selection and explicit personal selection are distinct. + The single stored snapshot expires after 60 seconds; it contains a hash of + its identity, profile, and raw catalog, never the credential itself. Each + Location still applies its own configuration and policy. +- A matching warm snapshot permits execution while a scoped background refresh + replaces it. Failed refreshes withdraw the snapshot; a response for a changed + credential or organization is discarded. Account and metadata RPCs reuse the + matching loaded data instead of repeating the startup network requests. + One-shot commands retain their synchronous catalog behavior. +- Argument parsing no longer imports the TUI. Execution-host imports are lazy, + including on the daemon client path. Ordinary launches start or reuse a healthy + Kilo daemon, allowing multiple terminal windows to share the store. Explicit + sandbox, swarm, project configuration, indexing, custom server/cloud settings, + and host-owning commands retain their dedicated host path. When a daemon is + running, those paths fail immediately with instructions to stop it first. + Database initialization takes place after acquiring the store lock, so + simultaneous first launches cannot race database creation. +- `build:tui` now builds and selects the portable TUI bundle. Code splitting + preserves dynamic import boundaries, and shared chunks stay under `src/` + so plugin and daemon paths resolve correctly. The existing ACP bridge path + and runtime PATH remain available through the launcher. + +The rebuilt entry is `packages/kilo-cli/dist/interactive/kilo2`. Source edits +now require `bun run build:tui` with Bun 1.4+ to update that compiled launcher; +`bun run tui` remains the direct source-development command. Daemon-backed +clients detach on exit; `kilo2 service stop` stops the daemon explicitly. +After a rebuild, restart an existing daemon to load the updated server code. +Close any older in-process clients once before starting the new default launcher. +Clients with different package versions may replace the shared daemon; concurrent +windows should use the same build. + +## Measurements + +These are local, isolated diagnostic samples, not an apples-to-apples upstream +v2 benchmark or a promise about a particular remote Gateway response time. + +| Measurement | Source / synchronous | Optimized | +|---|---|---| +| Headless real TUI home ready, first sample | 2678 ms from source | 1910 ms bundled | +| Headless real TUI home ready, second sample | 2319 ms from source | 1064 ms bundled | +| Warm catalog activation, controlled 800 ms response delay | 1003 ms | 182 ms with models already available | +| Existing bundled daemon reuse | — | 233 ms, same PID | +| Real terminal default attachment | — | 1493 ms to Kilo footer; `/exit` retained the same daemon | + +The headless measurements include process startup, module loading, host startup, +and the rendered Kilo footer. Both versions run the same loopback prompt/reply +smoke afterward. The controlled catalog probe verifies a persisted warm cache +across separate host processes. Cold model execution still waits for network +readiness, but the TUI no longer shares that wait. + +## Validation + +- Gateway and CLI package `bun typecheck`: pass. +- Gateway plugin, startup-cache, and prompt-selector tests: 37 passed. +- TUI and signed-out Gateway tests: 6 passed, including a catalog response held + until the full home screen renders, plus a prompt started against an explicit + Kilo model before catalog readiness. No prompt message is admitted before + activation, and the prompt completes after the response is released. +- Daemon lifecycle and multi-Location account transitions: passed. +- Portable artifact tests: 2 passed, including relocation, native routed model + requests, TUI prompt/reply, launcher boot, and native PTY cleanup. +- Built bundle TUI smoke and real terminal daemon attachment: passed. The + attachment check confirms `/exit` leaves the same daemon running. +- Cache coverage includes expiry, future timestamps, malformed data, different + servers/credentials/organizations, and a late response after credential change. +- All runtime validation used temporary fixture storage and loopback servers; + no live user credentials were read or changed by the test harnesses. + +The independent GLM 5.3 Flash review found the initial cold-session resolution +race; it was fixed through the existing server activation gate and verified by +the early-prompt test. No shared Core hooks or generated protocol edits were +needed. The launcher review used Gemini 3.8 Flash and returned no actionable findings. diff --git a/migration-tracking/technical-notes/baseline/swarm-v2-parity.md b/migration-tracking/technical-notes/baseline/swarm-v2-parity.md new file mode 100644 index 000000000000..d482b6b770b2 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/swarm-v2-parity.md @@ -0,0 +1,71 @@ +# Kilo Swarm v2 parity boundary + +Kilo Swarm is an explicit, default-off Kilo delta. It ports the v1 shared board +for one root session and its task descendants, not a new agent runtime or an +unrelated-session broadcast channel. + +## Implemented seam + +- `packages/kilo-cli/src/swarm.ts` registers `board_read` and `board_post` only + when the host supplies `enabled: true`, an authenticated public session client, + and an in-process permission authorizer. +- The board uses plugin storage, which is global by plugin ID, so every record + is keyed by its resolved root session ID. `swarm-store.ts` serializes + read-modify-write in one process, schema-validates a present record before + reading or writing it, bounds messages, aggregate storage, and pages, and + returns an existing post only when a repeated source tool-call identity has + identical arguments (v1 rejects changed-argument retries). +- Session ancestry is resolved through the public plugin session API. Explicit + recipients are checked through the supplied public client and must share both + location and root; `main` and `ALL` are the only symbolic recipients. +- Context guidance labels peer text untrusted and states that posts, HOLD, and + VETO do not authorize, wake, assign, cancel, or resume work. The v1 guidance + is adapted to v2's native `subagent` session vocabulary. +- `ctx.tool.hook("execute.after")` adds fixed activity text/metadata to real + completed tool results, never a peer message body. A host-only guard uses + native configured permissions, saved grants and plugin evaluation without + asking for permission. A successful complete read advances only to the + returned read cursor; notifications are deduplicated per session. Interrupts + propagate. No notice or stored post claims delivery, reading, or liveness. + +## Proven v1 source + +`ecccd1f:packages/opencode/src/kilocode/board/store.ts`, `tool/board.ts`, and +`board/context.ts` define the persistent root-descendant board, explicit reads, +idempotent posts, paging, and untrusted-peer boundary. The v1 configuration +flag is `experimental.shared_agent_board` in +`packages/core/src/v1/config/config.ts`. + +## Verification + +Bundled Bun 1.4: `test/swarm-host.test.ts` and `test/swarm-store.test.ts` +plus `test/board-notice.test.ts` pass **7 tests / 47 assertions**. The host test creates actual descendants by +executing the native `subagent` tool against a loopback model, rather than +misusing `session.fork`: v2 conversation forks have no `parentID` and remain +separate board roots. It checks default-off inventory, rejected writes, +feedback, saved approval, completed tool content, admission retry without a +duplicate post, participant identities, deduplicated notices, cross-root and +moved-location refusal, explicit denial, and persisted history after a real +host process restart. Store tests exercise paging, idempotency conflicts, +foreign cursors/replies, bounds and corruption refusal. Expected validation +failures use typed errors; a real invalid-cursor tool call settles as an error +instead of escaping the tool contract as a defect. +The notice-guard host tests verify allow, non-interactive ask, saved approval, +configured deny taking precedence over saved approval, and evaluator hooks. + +The empty-board test found a real adapter bug: an undefined cursor in tool +metadata failed durable JSON projection and left running tool records. Empty +cursors are now omitted; assertions inspect completed output, not tool inputs. +The earlier negative-only fork fixture was replaced by this host coverage. + +## Host boundary and deliberate deltas + +The host wires explicit `--swarm` opt-in for interactive/run/serve/ACP and composes the real Core +permission service into the in-process authorizer. Its exclusive interactive +profile lease is the single-writer boundary for global plugin storage; the +store's semaphore only serializes read-modify-write operations within that +host process. No board state is exposed as RPC and no provider or model request +is made by the board. Daemon/attach flag combinations that cannot reconfigure +their existing host are refused. V1 SQLite rows are not migrated: v2 uses its +existing plugin KV store and opaque cursors. Native v2 session placement, not +fork provenance or guessed participant ownership, defines the board boundary. diff --git a/migration-tracking/technical-notes/baseline/telemetry-v2-overlap.md b/migration-tracking/technical-notes/baseline/telemetry-v2-overlap.md new file mode 100644 index 000000000000..e50c8cb740c6 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/telemetry-v2-overlap.md @@ -0,0 +1,172 @@ +# Telemetry and Observability: v1 Audit and v2 Overlap Analysis + +## 1. Executive Summary + +This document audits the telemetry and OpenTelemetry implementations across Kilo v1 (`origin/main`) and the v2 fork (`HEAD 59b29de409`). + +In v1, Kilo implemented a proprietary PostHog-based telemetry pipeline (`@kilocode/kilo-telemetry`) that was **enabled by default**, sent events to `us.i.posthog.com` with a hardcoded API key, and exposed `/telemetry/capture` and `/telemetry/setEnabled` HTTP endpoints on the server. + +In v2, upstream OpenCode discarded PostHog entirely, removed `experimental.openTelemetry` from user configuration (marking it `unsupportedExperimental`), and moved to standard process-level OpenTelemetry (OTLP HTTP) traces and logs via `@opencode-ai/util/observability` and `@effect/opentelemetry`. + +A privacy review found that attaching the process-level `Observability.layer` would expand export to ambient logs and span attributes, without establishing a transcript/secret-free contract for those producers. The Kilo preview instead uses a bounded activity exporter in `packages/kilo-cli/src/telemetry.ts`. It consumes the public client `event.subscribe` stream, filters an allowlist of safe event names (four registry-update names plus name-only host/session/execution lifecycle names), ignores raw `event.data`, and emits standard OTLP JSON logs to a user-specified collector. Resource fields use fixed defaults, explicitly configurable client/version/channel labels, and a random per-run identifier; they are not derived from sessions. It is **OFF by default** and requires explicit opt-in, now backed by a persistent isolated consent file adapter (`packages/kilo-cli/src/telemetry-settings.ts`). This audit does not claim every upstream log or trace contains sensitive data. + +**Parity statement:** the v2 exporter is not v1 parity. v1 emitted 27 named event types with rich properties; v2 exports a small allowlist of event *names only*, with no payloads, no identity, and no usage metrics. Section 6 enumerates every omission with evidence. + +## 2. Audit of v1 Source (`origin/main`) + +### 2.1 Architecture and Transport +In `origin/main`, telemetry was implemented in `packages/kilo-telemetry/`: +- **Client**: `packages/kilo-telemetry/src/client.ts` used `posthog-node` (v4.4.1), sending data directly to `https://us.i.posthog.com` using a hardcoded project token. +- **Identity**: `packages/kilo-telemetry/src/identity.ts` generated and persisted an anonymous machine ID to `/telemetry-id`, and cached the authenticated email in `telemetry-profile.json` (keyed by token hash, 7-day TTL). When authenticated, it identified the user by email and aliased the machine ID to it, attaching the organization ID. +- **Events**: `packages/kilo-telemetry/src/events.ts` defined a `TelemetryEvent` enum covering CLI lifecycle, sessions, LLM completions, feature usage (commands, tools, agents), indexing metrics, share actions, MCP connections, and error events. +- **Lifecycle**: `packages/opencode/src/kilocode/cli/setup.ts` initialized telemetry during `KiloCli.bootstrap()`, fired `CLI_START`, scheduled background flushes, and called `Telemetry.shutdown(2000)` on process exit. + +### 2.2 Configuration and Defaults +- In v1, telemetry was **enabled by default**. +- `packages/core/src/v1/config/config.ts` defined `experimental.openTelemetry` defaulting to `true` (`Schema.withDecodingDefault(Effect.succeed(true))`). +- `packages/opencode/src/kilocode/cli/setup.ts` resolved consent as `cfg.experimental?.openTelemetry !== false` — an opt-OUT switch. +- `packages/kilo-telemetry/src/telemetry.ts` then applied `KILO_TELEMETRY_LEVEL`: when set, `level === "all"` forced telemetry **on regardless of the persisted opt-out**; any other value disabled it. This is a consent weakness of v1 worth recording. +- Consent could also be flipped at runtime through the `/telemetry/setEnabled` server route. + +### 2.3 Server API Endpoints +- `packages/opencode/src/kilocode/server/httpapi/groups/telemetry.ts` registered `/telemetry/capture` and `/telemetry/setEnabled` routes under `InstanceHttpApi`. +- `packages/opencode/src/kilocode/server/httpapi/handlers/telemetry.ts` bridged these routes to `Telemetry.track()` and `Telemetry.setEnabled()`. + +### 2.4 v1 Event Enum Inventory + +The full `TelemetryEvent` enum (27 values, human-readable names) with data carried per event: + +| Group | Events | Payload data | +|---|---|---| +| CLI lifecycle | `CLI Start`, `CLI Exit` | duration, exit code | +| Session | `Session Start`, `Session End`, `Session Message` | sessionId, model, provider, message counts, token counts, duration, user/assistant source | +| Model usage | `LLM Completion` | apiProvider, modelId, input/output/cache tokens, cost, duration | +| Feature usage | `Command Used`, `Tool Used`, `Agent Used`, `Plan Followup`, `Suggestion Shown`, `Suggestion Accepted` | command/tool/agent names, sessionIds, request ids, choice enums | +| Code indexing | `Indexing Started`, `Indexing Completed`, `Indexing File Count`, `Indexing Batch Retry`, `Indexing Error` | provider, vector store, modelId, file counts, batch errors, error strings | +| Share | `Share Created`, `Share Deleted` | sessionId | +| MCP | `MCP Server Connected`, `MCP Server Error` | server name, error strings | +| Remote | `Remote Connection Opened` | none | +| Auth | `Auth Success`, `Auth Logout` | provider | +| Config | `Telemetry Disabled` | none | +| Feedback | `Feedback Submitted` | provider/model, rating, message ids | +| Errors | `Error` | error string, context | + +Session IDs, provider/model identifiers, error strings, and (via identity) the user email were all transmitted. This is data-bearing telemetry; the v2 safe event contract deliberately does not reproduce it. + +## 3. Audit of v2 Upstream Observability (`HEAD 59b29de`) + +### 3.1 Migration to OTLP HTTP +Upstream v2 replaced custom SaaS telemetry with standard OpenTelemetry (OTLP HTTP): +- `packages/util/src/observability.ts`: Exports `Observability.layer` and `Observability.node`. +- `packages/util/src/observability/otlp.ts`: Configures `OtlpLogger` (from `effect/unstable/observability` targeting `${endpoint}/v1/logs`) and `tracingLayer` (using `@effect/opentelemetry/NodeSdk`, `@opentelemetry/exporter-trace-otlp-http` targeting `${endpoint}/v1/traces`, and `BatchSpanProcessor`). +- Resource metadata is built by `resource(app)`, generating attributes for `opencode.client`, `opencode.run`, `service.instance.id`, and `deployment.environment.name`. + +### 3.2 Removal of `openTelemetry` from Configuration +Upstream explicitly eliminated `experimental.openTelemetry` from the v2 configuration schema: +- `specs/v2/config.md`: line 380 records: + `| experimental.openTelemetry | Enable AI SDK telemetry spans | remove | Do not port; observability is process-level and should use standard OpenTelemetry environment or declarative configuration. |` +- `packages/core/src/config/normalize.ts`: line 50 places `"openTelemetry"` into `unsupportedExperimental`. Supplying `experimental: { openTelemetry: ... }` produces an `unsupported` diagnostic. + +### 3.3 Privacy Analysis of Process-Level Layer +- Attaching upstream's `Observability.layer` expands capture to logs and spans in its runtime scope, subject to the configured filters and sampling. +- The adapter has not established that every ambient producer is safe for product telemetry. Exporting those payloads would therefore exceed its allowlisted, content-free contract. +- Kilo v2 avoids global log/span capture, implementing an isolated activity subscriber instead. + +## 4. Reuse vs. Delta Matrix + +| Dimension | Kilo v1 (`origin/main`) | Upstream v2 (`HEAD 59b29de`) | Kilo v2 Safe Activity Exporter (`telemetry.ts`) | +|---|---|---|---| +| Protocol / Backend | PostHog HTTP API (`us.i.posthog.com`) | OpenTelemetry OTLP HTTP (`/v1/logs`, `/v1/traces`) | Standard OTLP JSON logs (`/v1/logs`) | +| Third-party SaaS | Hardcoded PostHog API key | None | None (local or user-specified collector only) | +| Default State | ON by default | OFF unless `OTEL_EXPORTER_OTLP_ENDPOINT` set | OFF by default; requires explicit opt-in | +| Endpoint Source | Fixed remote SaaS | Environment variables | Explicit option, persisted consent file, or `KILO_TELEMETRY_ENDPOINT` | +| Config Schema | `experimental.openTelemetry` (default true) | Rejected as `unsupportedExperimental` | Isolated consent file; no upstream schema edits | +| Consent Persistence | Opt-out config key + runtime route | None | Versioned consent file adapter (`telemetry-settings.ts`), atomic 0600 writes, fail-closed reads | +| Server Routes | `/telemetry/capture`, `/telemetry/setEnabled` | None | None (consumes public client `event.subscribe`) | +| Dependencies | `posthog-node` | `@effect/opentelemetry`, `@opentelemetry/*` | Zero new dependencies | +| Identity | Machine ID, email identify/alias, org ID | None | None (random per-run ID only) | +| Data Collected | 27 event types with rich properties | Traces, spans, structured log records | Allowlisted event names only; no raw payload fields | +| Sensitive Content | Sanitized client-side, but custom payloads | Global host logs (can capture prompts/errors) | Zero raw event data; `event.data` completely omitted | + +## 5. v2 Safe Event Contract and Lifecycle Coverage + +### 5.1 Contract +The exporter exports **event names only**. For every public server event, `event.data` is never read, so name-only export is payload-safe by construction. Safety therefore reduces to *which names* are exported, governed by two frozen allowlists: + +- `SAFE_EVENT_TYPES` — registry-update notifications, empty payload schemas upstream: `agent.updated`, `catalog.updated`, `command.updated`, `config.updated`. Emitters: `packages/core/src/agent.ts:90`, `packages/core/src/catalog.ts:139`, `packages/core/src/command.ts:63`, `packages/core/src/config.ts:267`. +- `LIFECYCLE_EVENT_TYPES` — name-only host/session/execution lifecycle signals, each with a verified emitter on the actual public server stream: + - `server.connected` — emitted per event-stream subscription, `packages/server/src/handlers/event.ts:16`. + - `session.created` — `packages/core/src/session.ts:262`. + - `session.deleted` — `packages/core/src/session.ts:362`. + - `session.execution.started` / `session.execution.succeeded` / `session.execution.failed` / `session.execution.interrupted` — `packages/core/src/session/execution.ts:109-134`. + +`EXPORTABLE_EVENT_TYPES` is the union and the only filter the exporter applies. A test asserts every name is an actual public server event type (`EventManifest.isServer`, plus the handler-emitted `server.connected`) and that content-bearing names (`session.text.delta`, `session.tool.called`, `permission.asked`, `tui.prompt.append`, …) stay outside. + +### 5.2 Seams Assessment + +Available seams: +1. **Public client `event.subscribe`**: exposed on `@opencode-ai/client`, providing an `AsyncIterable<{ id, created, type, data }>` stream of server domain events (`isOpenCodeEvent` in `packages/protocol/src/groups/event.ts:67`). +2. **Standard OTLP JSON logs format**: supported by OpenTelemetry collectors at `${endpoint}/v1/logs`. +3. **Effect scope lifecycle**: when run with `exportActivity`, an Effect finalizer aborts the subscription and cleanly shuts down background fibers without hanging. +4. **Isolated consent file**: `telemetry-settings.ts` persists opt-in consent outside the upstream config schema (which rejects telemetry keys), so no shared-file hook is needed. + +Missing seams and product gaps: +1. **No product consent command yet**: the consent adapter exists, but the CLI command surface (enable/disable with confirmation) is parent-owned wiring and not implemented in this slice. +2. **No shared server route**: upstream has no telemetry HTTP route. Adding one would require modifying shared server route tables; deliberately not done. +3. **No safe global log filter**: upstream `Observability.layer` lacks attribute/field redaction filters for arbitrary logs and spans. Global log/span capture must not be used for telemetry. +4. **No session-turn idle event on the wire**: `session.idle` and `session.status` are defined in `packages/schema/src/session-status-event.ts` but have no publisher in `packages/core` today (grep over `packages/core/src` finds consumers only, e.g. `packages/kilo-cli/src/run.ts:151` waiting on `session.idle`). Until upstream emits them, no session-turn end signal can be exported. + +## 6. v1 → v2 Coverage Overlap and Omissions + +The four registry-update names do **not** mean v1 parity, and the lifecycle names do not either. Mapping every v1 event: + +| v1 event | v2 exportable analog | Status | +|---|---|---| +| `CLI Start` | `server.connected` | Partial, name-only. `server.connected` fires per subscription, not per CLI process start. | +| `CLI Exit` | none | Omitted. No public shutdown event: `global.disposed` (`packages/schema/src/server-event.ts:6`) is outside `EventManifest.ServerDefinitions`, so subscribers never receive it. | +| `Session Start` | `session.created` | Name-only; no title/directory/sessionID. | +| `Session End` | none | Omitted. `session.idle`/`session.status` have no core publisher (§5.2); `session.deleted` covers removal only. | +| `Session Message` | `session.execution.started` | Coarse name-only run-start signal; no user/assistant source, no counts. | +| `LLM Completion` | `session.execution.succeeded` / `failed` / `interrupted` | Outcome names only. Tokens, cost, model, duration are payload data and are forbidden by the no-event-data contract. | +| `Command Used`, `Tool Used`, `Agent Used` | none | Omitted. v2's `command.updated`/`agent.updated` are registry-change notifications, not usage events; usage requires payload. | +| `Plan Followup` | none | Omitted. Choice data would require payload. | +| `Suggestion Shown`, `Suggestion Accepted` | none | Omitted. v2 has no public suggestion events. | +| Indexing events (5) | none | Omitted. No public indexing lifecycle on the v2 server stream. | +| `Share Created`, `Share Deleted` | none | Omitted. No public share events in `EventManifest.ServerDefinitions`. | +| `MCP Server Connected`, `MCP Server Error` | none | Omitted from the exportable set. `mcp.status.changed` exists and is public, but its name-only signal duplicates connect state without the connected/errored distinction the v1 events carried; not required for the core host/session/execution lifecycle scope. | +| `Remote Connection Opened` | none | Omitted. No public analog. | +| `Auth Success`, `Auth Logout` | none | Omitted. Auth identity must not touch telemetry in v2. | +| `Telemetry Disabled` | none | Omitted. Exporting that the user disabled telemetry is itself unwanted signal; disabled means zero emissions. | +| `Feedback Submitted` | none | Omitted. No public feedback events; rating data would require payload. | +| `Error` | `session.execution.failed` | Coarse name-only. Error strings are payload and never exported. | + +Summary of deliberate omissions, each grounded above: all payload-bearing properties (session IDs, tokens, cost, model/provider identifiers, tool/command/agent names, error strings, emails); CLI process start/exit; session-turn end (blocked by the missing idle publisher); usage-per-invocation semantics; indexing, share, MCP, remote, auth, feedback, and suggestion events. + +## 7. Consent Controls: v1 vs v2 + +| Control | v1 (`origin/main`) | v2 (`packages/kilo-cli`) | +|---|---|---| +| Default | ON (`experimental.openTelemetry` defaults true) | OFF; nothing emits without explicit opt-in | +| Opt-out | Set `experimental.openTelemetry: false` | N/A (no persistent ON state to opt out of) | +| Opt-in | `KILO_TELEMETRY_LEVEL=all` (forces on even over persisted opt-out) | `enabled: true` in the isolated consent file, or `KILO_TELEMETRY_ENABLED=1`, plus a valid collector endpoint | +| Persistence | User config file, mutable via server route | `telemetry-settings.ts`: versioned JSON file, atomic write (tmp + rename), file mode 0600, directory 0700 | +| Failure behavior | Level env overrides persisted decline | Fail-closed: absent, unreadable, symlinked, hardlinked, non-regular (FIFO), public-readable, oversized, corrupt, or schema-invalid consent files all read as disabled and never throw or hang; writes refuse to replace symlinked, shared, or non-regular existing files; an invalid endpoint disables; an explicit persisted `enabled: false` beats environment opt-in | +| File trust boundary | None recorded | Consent is adopted only from a regular, single-link, owner-private (no group/other bits) inode, verified again on the opened fd; bounded 64 KiB read so a planted file cannot hang or exhaust the host | +| Scope creep guard | Runtime `/telemetry/setEnabled` route | None; no route, no config schema edits, parent wires explicit CLI use only | + +Consent resolution is presence-aware through `TelemetrySettings.resolve(file, env)` for host/CLI wiring: an absent consent file defers entirely to the environment (`resolveConfig(undefined, env)`), so an explicit pre-consent env opt-in keeps working, while any present file — enabled, declined, rejected, or corrupt — decides consent itself, and a declined/rejected/corrupt one forces OFF over the environment. The consent file schema is `{ version: 1, enabled, endpoint?, headers?, client?, channel? }`; unknown fields are dropped at the file boundary so junk in the file cannot flow into exporter configuration. `resolveConfig` remains the single owner of endpoint validation (HTTP/HTTPS only) and of the precedence rules; `telemetry-settings.input()` is a field-for-field bridge, not a second policy. + +**CLI display constraint for parent wiring:** the consent file may carry credential-bearing headers and the resolved config carries them too, so the CLI surface must print only a status (enabled/disabled, target host decision), never the raw settings or resolved config. + +**Judgment gate left to product/parent:** migrating the product default to ON (or re-introducing a v1-style opt-out) is a product decision, not implemented here. The adapter and exporter keep the OFF default regardless of that future decision. + +## 8. Implementation Invariants & Verification + +The safe activity exporter in `packages/kilo-cli/src/telemetry.ts` enforces the following invariants: +1. **Strict Opt-In**: Telemetry is disabled by default. It activates only when `enabled: true` is passed (directly or via the persisted consent file) or `KILO_TELEMETRY_ENABLED="1"` is set in the environment. Setting `OTEL_EXPORTER_OTLP_ENDPOINT` alone does not enable telemetry. +2. **Endpoint Validation**: Endpoints must be valid HTTP or HTTPS URLs. Invalid URLs, non-HTTP schemes (`file:`, `ftp:`), or empty strings disable telemetry. +3. **Strict Name Filtering**: Only the names in `EXPORTABLE_EVENT_TYPES` (`SAFE_EVENT_TYPES` ∪ `LIFECYCLE_EVENT_TYPES`, §5.1) are processed; every other public event is discarded. +4. **No Raw Event Payloads**: `event.data` is never inspected, serialized, or transmitted. The outgoing OTLP record contains only the allowlisted `event.type`, timestamp/severity, and resource fields (`service.name`, `service.version`, `opencode.client`, `deployment.environment.name`, `service.instance.id`, `opencode.run`). Client/version/channel can be explicitly configured and are transmitted as supplied; callers must not put secrets in those labels. +5. **Clean Shutdown & Bounded Requests**: Both `startActivityExporter` (`stop()`) and `exportActivity` (delegating to `startActivityExporter` via `Effect.acquireRelease`) abort the client event stream cleanly without process hang. Every individual OTLP POST request uses a bounded 5-second timeout (`AbortSignal.timeout(5000)`) so an unresponsive collector cannot block the activity stream. External abort listeners are cleanly unhooked on stop. +6. **Verified with Local Fake Collector and Live Isolated Host**: `packages/kilo-cli/test/telemetry.test.ts` covers synthetic event streams and an actual isolated host (`interactive-fixture.ts` run under the bundled Bun 1.4 runtime). Confirmed that the real `OpenCode` promise client satisfies `EventSubscriberClient`, that only exportable names (including `server.connected`) are emitted, that lifecycle payloads and content-bearing events are dropped, and that simulated confidential strings in event payloads are strictly omitted from outgoing OTLP records. +7. **Verified Consent Persistence**: `packages/kilo-cli/test/telemetry-settings.test.ts` covers the adapter end to end: default-disabled reads, atomic 0600/0700 persistence, restart-safe consent across a fresh process, fail-closed corrupt/invalid files, symlink/hardlink/FIFO/public-readable/oversized rejection without hanging, write refusal on foreign inodes, unknown-field dropping with secret sentinels, presence-aware `resolve()` semantics (missing file honors environment opt-in; declined/corrupt/foreign files force OFF over it), and the full settings → `resolve` → exporter chain against a local collector (zero requests when disabled; exactly the two exportable names when enabled). diff --git a/migration-tracking/technical-notes/baseline/updater-local-parity.md b/migration-tracking/technical-notes/baseline/updater-local-parity.md new file mode 100644 index 000000000000..bca9604b3d4d --- /dev/null +++ b/migration-tracking/technical-notes/baseline/updater-local-parity.md @@ -0,0 +1,253 @@ +# Updater Local Parity — Portable kilo2 Preview Channel + +Status: **Real local directory artifact updater and CLI integration verified on macOS arm64, including apply and rollback of the actual portable tree. No whole capability row closed.** + +Sources & constraints: +- Parent steering (messages 78, 86, 92, 95): Implement real local artifact updates without mock JSON substitution; manifest integrity must deterministically digest actual staged files, relative paths, modes, and symlink targets before pointer switch; preserve binaries, executable modes, and relative links; reject escaping symlinks and non-regular special files; require current-owner non-group/world-writable install root, launcher, and state; refuse unsafe/malformed state explicitly (only ENOENT means absent); size check is unconditional; never overwrite existing build directories or live installs; archives remain explicitly unsupported pending safe tooling. Parent owns `commands.ts`/`tui-preview.ts` wiring and CLI acceptance. + +--- + +## 1. Exact API & Manifest Contract + +Exported from `packages/kilo-cli/src/updater.ts`: + +```ts +export interface UpdateManifest { + readonly channel: string + readonly version: string + readonly buildId: string + readonly platform: string + readonly sha256: string // Deterministic tree digest of staged artifact + readonly sizeBytes: number // Total content bytes of regular files in artifact + readonly url: string // Local path or file:// URI + readonly releaseDate?: string + readonly format?: "directory" | "tar.gz" | "tgz" +} + +export interface UpdaterConfig { + /** Explicit manifest URL or file:// URI. If absent and KILO_UPDATE_MANIFEST_URL is unset, updater remains inert. */ + readonly manifestUrl?: string + /** Owned preview install root. If absent, discovered from KILO_PREVIEW_INSTALL_ROOT. */ + readonly installRoot?: string + /** Current channel override. Defaults to "kilo2-internal". */ + readonly currentChannel?: string + /** Current platform override for testing. Defaults to `${process.platform}-${process.arch}`. */ + readonly currentPlatform?: string + /** Optional custom fetch implementation for loopback test fixtures. */ + readonly fetch?: typeof fetch +} + +export type CheckResult = + | { readonly status: "inert"; readonly reason: string } + | { + readonly status: "up-to-date" + readonly currentBuildId: string + readonly currentVersion?: string + readonly channel: string + readonly platform: string + } + | { + readonly status: "update-available" + readonly currentBuildId: string + readonly currentVersion?: string + readonly channel: string + readonly platform: string + readonly update: UpdateManifest + } + +export type ApplyResult = + | { readonly status: "inert"; readonly reason: string } + | { + readonly status: "up-to-date" + readonly currentBuildId: string + readonly currentVersion?: string + readonly channel: string + } + | { + readonly status: "applied" + readonly previousBuildId?: string + readonly currentBuildId: string + readonly version: string + readonly restartRequired: true + } + +export type RollbackResult = + | { readonly status: "inert"; readonly reason: string } + | { + readonly status: "rolled-back" + readonly previousBuildId: string + readonly currentBuildId: string + readonly restartRequired: true + } + +export function checkForUpdate(options?: UpdaterConfig): Promise +export function applyUpdate(options?: UpdaterConfig): Promise +export function rollbackUpdate(options?: UpdaterConfig): Promise +export function inspectInstalledBuild(installRoot: string): Promise +export function digestDirectoryTree(rootDir: string): Promise<{ sha256: string; totalBytes: number; entryCount: number; tuples: TreeTuple[] }> +export function scanDirectoryEntries(rootDir: string): Promise +export function verifyOwnershipAndMode(targetPath: string, description: string): void + +export const Updater = { check, apply, rollback, inspect, digest, scan } +``` + +### CLI Subcommand Mapping & Actual JSON Output (`commands.ts` / `tui-preview.ts`) +The parent CLI wiring in `tui-preview.ts` executes the action and emits formatted JSON directly to stdout: + +- `kilo2 update --check` -> calls `checkForUpdate()`. Output: + ```json + { + "status": "up-to-date", + "currentBuildId": "build-0.1.0-abc", + "currentVersion": "0.1.0-internal+abc", + "channel": "kilo2-internal", + "platform": "darwin-arm64" + } + ``` + Or when an update is available: + ```json + { + "status": "update-available", + "currentBuildId": "build-0.1.0-abc", + "currentVersion": "0.1.0-internal+abc", + "channel": "kilo2-internal", + "platform": "darwin-arm64", + "update": { + "channel": "kilo2-internal", + "version": "0.2.0-internal+def", + "buildId": "0.2.0-def", + "platform": "darwin-arm64", + "sha256": "...", + "sizeBytes": 12345, + "url": "file:///path/to/app", + "format": "directory" + } + } + ``` +- `kilo2 update` / `kilo2 update --apply` -> calls `applyUpdate()`. Output: + ```json + { + "status": "applied", + "previousBuildId": "0.1.0-abc", + "currentBuildId": "0.2.0-def", + "version": "0.2.0-internal+def", + "restartRequired": true + } + ``` +- `kilo2 update --rollback` -> calls `rollbackUpdate()`. Output: + ```json + { + "status": "rolled-back", + "previousBuildId": "0.1.0-abc", + "currentBuildId": "0.2.0-def", + "restartRequired": true + } + ``` +- If inert (unconfigured manifest URL or install root): + ```json + { + "status": "inert", + "reason": "Update manifest URL is not configured (KILO_UPDATE_MANIFEST_URL is unset)" + } + ``` + +--- + +## 2. Architecture & Review Corrections + +### 1. Pure Directory Payload Staging (No HTTP JSON Fixtures) +- HTTP JSON fixture transport was completely removed per review. +- Payloads are staged exclusively from verified directory artifacts via `file://` URIs or absolute filesystem paths using Node's `cp(sourceDir, stagingApp, { recursive: true, verbatimSymlinks: true })`. +- All URL conversions strictly use `fileURLToPath` and `pathToFileURL`, safely handling spaces, percent-encoding, and special characters. + +### 2. Structured Deterministic Tree Digest +- Manifest `sha256` represents the SHA-256 hash of a deterministic JSON serialization of sorted structured entry tuples: + - Regular files: `["file", relPath, stat.mode & 0o7777, bytes.length, fileSha256]` (captures exact octal permission mode bits and content hash). + - Symlinks: `["symlink", relPath, linkTarget]` (captures relative symlink target string). + - Directories: `["dir", relPath, stat.mode & 0o7777]` (captures directory hierarchy and permission bits). +- Sorted using binary codepoint comparison `(a, b) => (a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : 0)`. +- Verified on `stagingApp` before any pointer switch. +- Regular file size check is unconditional (`tree.totalBytes !== manifest.sizeBytes`). + +### 3. Preserved Authored Permissions (No Chmod Mutation) +- The staged binary `stagingApp/kilo2` is verified to be a regular file and executable (`(stat.mode & 0o111) !== 0`). +- If non-executable, `applyUpdate` refuses the payload (`Update payload launcher binary at app/kilo2 is not executable`) rather than mutating it with `chmod`. The authored artifact must arrive with valid permissions. + +### 4. Early Manifest BuildId Validation +- `manifest.buildId` is validated up-front via `validateBuildDirName` before any staging path (`.staging-${manifest.buildId}-`) or destination path is constructed, preventing path traversal before disk allocation. + +### 5. Symlink Escape & Non-Regular File Scanning +- Mirrors `packages/kilo-cli/script/build-portable.ts` lines 430–452 (`scanLinks`): + - Every symlink in the staged directory is dereferenced via `realpath(full)` and checked to confirm it resolves strictly within `stagingApp`. Escapes throw `Artifact contains symlink escapes outside the output dir`. + - Non-regular special files (FIFOs, sockets, character/block devices) are detected and refused (`Artifact contains non-regular special files`). + - Never recurses through symlinks. + +### 6. Ownership & Permission Boundaries (Never Swallow Unsafe State) +- `verifyOwnershipAndMode` enforces: + - Owner check: `stat.uid === process.getuid()` (on POSIX; prevents operating on files owned by another user). + - Mode check: `(stat.mode & 0o022) === 0` (rejects group-writable or world-writable directories and files). +- Enforced on `installRoot`, `kilo2` launcher script, `kilo2.previous`, and `update-state.json`. +- `readUpdateState` explicitly distinguishes `ENOENT` (genuinely absent state, returning `undefined`) from unsafe permissions, invalid JSON, or tampered paths (which throw explicit refusal errors and are never swallowed). + +### 7. Atomic Pointer Switch & Running Build Preservation +- Matching `packages/kilo-cli/script/build-tui.ts`: + `exec "$here/build-/app/kilo2" "$@"` (with ACP default `"$here/../acp"`). +- Target directory collision prevention: if `build-` already exists on disk, `applyUpdate` refuses rather than deleting it (`Target build directory already exists; refusing to overwrite or delete it: build-`), ensuring live sessions are preserved. +- The active running build directory is never unlinked or renamed. +- Atomic pointer switch via temporary launcher script `.kilo2-.tmp` (mode `0o755`) and POSIX `rename(2)` over `kilo2`. Previous launcher backed up to `kilo2.previous`. + +### 8. Rollback Mechanics +- Primary path: inspects `update-state.json`, reads restored `build.json` for accurate versioning, atomically swaps launcher via `createLauncherScript`, and rewrites state. +- Fallback path (`kilo2.previous` when state is missing): parses `detectedBuildDir`, validates build dir name (`isValidBuildDirName`), restores launcher, and rewrites `update-state.json` to prevent stale state. + +--- + +## 3. Local Verification Results + +Run with bundled Bun 1.4.0 (`packages/kilo-cli/dist/interactive/bun`): + +```bash +env -C packages/kilo-cli ./dist/interactive/bun test test/updater.test.ts +bun test v1.4.0 (34cbb9a40) + + 20 pass + 0 fail + 97 expect() calls +Ran 20 tests across 1 file. [681.00ms] +``` + +### Verified Test Cases (`test/updater.test.ts`) +1. `updater is inert by default without manifest URL or install root` +2. `updater refuses protected system and stable Kilo paths` +3. `verifyInstallRoot resolves parent symlinks via canonicalPath without touching live config` +4. `verifyOwnershipAndMode enforces current owner and rejects group/world-writable permissions` +5. `readUpdateState refuses unsafe permissions and malformed json while allowing absent state` +6. `build directory name validation rejects path traversal and invalid characters` +7. `digestDirectoryTree encodes structured tuples with exact permission modes and deterministic sort` +8. `scanDirectoryEntries detects symlink escapes outside the artifact root` +9. `manifest validation enforces channel, platform, and hash integrity` +10. `check identifies up-to-date vs update-available against local manifest` +11. `real local directory payload stages binaries, relative symlinks, and handles file URLs with spaces` +12. `refuses non-executable launcher in source payload without altering mode` +13. `manifest buildId is validated before creating any staging path` +14. `symlink escape inside directory payload is refused before promotion` +15. `applyUpdate refuses existing destination build directory without deleting anything` +16. `applyUpdate returns explicit unsupported diagnostic for archive extraction` +17. `tree digest integrity mismatch refuses update, cleans staging, and preserves launcher intact` +18. `rollback restores previous launcher pointer, records restored version, and keeps all build directories` +19. `rollback fallback path via kilo2.previous restores build and writes state when update-state.json is missing` +20. `rollback with no previous build available throws descriptive error` + +### Typecheck +`bun run typecheck` (`tsgo --noEmit`) in `packages/kilo-cli`: **0 errors introduced in owned files**. + +--- + +## 4. Remaining Shipping Gates (No Claim Row Closed) + +**Completed CLI integration (2026-09-07):** `commands.ts` and `tui-preview.ts` expose check/apply/rollback before interactive store initialization. The 22 updater/command tests pass (118 assertions). Bundled-command acceptance staged the actual 718,102,380-byte portable tree (67,140 entries), launched the copied executable, detected the current version, and rolled back while retaining both builds. All writes used an isolated temporary installation, removed afterward. Evidence: parent thread storage `goal79-update-portable-bundled.log` and `goal79-integration-evidence.md`. + +Remaining: +1. **Archive (.tar.gz) Unpacker Tooling**: Safe extraction of 800+ MB portable archives carrying relative symlinks remains gated pending trusted unpacker tooling; real directory staging satisfies local packaging. +2. **Distribution & Transport Security**: Production HTTPS endpoint with signing/notarization verification for remote manifest and payload distribution (skipped under local-only staging). +3. **Release Governance**: Formal decision on channel naming and manifest release publishing workflow. diff --git a/migration-tracking/technical-notes/baseline/v1-fixture-generation.md b/migration-tracking/technical-notes/baseline/v1-fixture-generation.md new file mode 100644 index 000000000000..ca4dbaed87d8 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/v1-fixture-generation.md @@ -0,0 +1,167 @@ +# Real V1 writer fixture and V2 migration audit + +Audited on 2026-09-05. This is a disposable-fixture result, not a claim that +any real user store has been opened. The test creates a new V1 database under +`/tmp`, copies it to a separate disposable destination, and runs the V2 +migration only against that copy. Those fixture directories are newly created +and clean; that does not describe the cleanliness of the source checkout. + +## Exact source and runtime + +The source checkout is `/Users/johnnyamancio/Workspace/kilo_workspace/kilocode` +at `d99662338e3ddbd2613ab41fc0369837a6eb4be9` (merge pull request #13046, +2026-08-10). It was already dirty and was not changed by this work. Its +porcelain status at audit time was: + +``` + M .gitignore + M bun.lock + M packages/opencode/AGENTS.md +?? .agents/ +?? .claude/ +?? .kilo/agents/ +?? .kilo/plans/1785871497799-backend-health-curl-script.md +?? .kilo/plans/1786293900000-tui-invisible-progress.md +?? .kilocode/skills/gh-stack +?? packages/plugin/plugin +?? packages/script/script +?? packages/sdk/js/sdk +?? packages/tui/test/cli/cmd/tui/repro-batch-drop.script.tsx +?? packages/tui/test/cli/cmd/tui/repro-filter-and-latch.script.tsx +?? packages/tui/test/kilocode/repro-opentui-streaming.script.ts +?? plans/architecture-risk-remediation.md +?? skills-lock.json +?? vscode-self-test.config.json +``` + +The fixture child loads the V1 checkout's own `effect` package +(`4.0.0-beta.83`) by absolute URL. The test process uses the packaged V2 Bun +runtime (`1.4.0`); the ambient system Bun in the V1 checkout is `1.3.14` and +is not used to run the fixture. The V2 worktree uses Effect `4.0.0-rc.112`. +No dependency was installed, and no network or model request is made. + +The current source writer path is: + +- `packages/opencode/src/session/session.ts`: `Session.create`, + `updateMessage`, `updatePart`, and `setSummary`. +- `packages/core/src/event.ts`: durable publication validates event data with + `Schema.encodeUnknownSync` before persistence. +- `packages/core/src/session/projector.ts`: the durable projectors write the + V1 `session`, `message`, and `part` tables. + +The child uses those writer methods, not direct SQL. It records a normal user +message, assistant message, completed `bash` tool part, Kilo step-finish part, +compaction marker plus summary assistant, and a session summary diff. The +recorded tool command is fixture data only; no shell is executed. + +The checked-in dump is produced by `test/v1-writer-fixture-generate.ts` and +`sqlite3 .dump`. The generator replaces only the random disposable project +directory prefix with `/tmp/kilo-v1-writer-fixture-project` so the SQL artifact +does not contain a machine-specific temporary path; all writer-produced +session/message/part payloads remain otherwise unchanged. + +Current artifact SHA-256: +`19dea6107a425ef902304454fec929ce8210edfa4b4a718c2287c551915c1f7c`. +Regeneration intentionally produces a new session/event id and wall-clock +values; the generator prints the replacement artifact hash for provenance. + +## Ref drift checked before using the writer + +The local objects also contain the refs recorded by the earlier audit: + +- `1536aef0fbe96c4575d23e9de2d50ba897f6b8ac` (`release: v7.5.14`) +- `ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7` (`origin/main`, merge after + `1536aef`) + +Both are descendants of the checked-out `d996623` for the relevant source. +The path-limited diff `d996623..1536aef` and `d996623..ecccd1f` is identical: +eight touched database/event/session files, 124 insertions and 15 deletions. +The changes add the Kilo board migration, `EventV2.publishAll`, fork-writer +and platform plumbing, the `revert.workspace` field, and SQLite/concurrency +ordering. They do not change the `Session.create`/message/part writer seam or +make `FileDiff` counts optional. The three checked refs all define +`FileDiff.Info.additions` and `.deletions` as `Schema.Finite`. + +## Test result + +Run from `packages/kilo-cli`: + +``` +dist/interactive/bun --no-env-file test --preload @opentui/solid/preload \ + --timeout 120000 test/v1-writer-fixture.test.ts +``` + +Result: **1 pass, 30 assertions**. This is the normal, dependency-free test; +it loads the checked-in generated dump and does not require the sibling V1 +checkout. + +The live writer roundtrip is intentionally explicit because it loads the +separate V1 dependency graph: + +``` +KILOCODE_V1_CHECKOUT=/Users/johnnyamancio/Workspace/kilo_workspace/kilocode \ + dist/interactive/bun --no-env-file test --preload @opentui/solid/preload \ + --timeout 180000 ./test/v1-writer-fixture-live.ts +``` + +Result at audit time: **1 pass, 35 assertions**. It requires +`KILOCODE_V1_CHECKOUT` and is not included by the default test discovery. + +The test's isolated V1 writer produced: + +| Source table | Rows | Evidence | +|---|---|---| +| `session` | 1 | actual `Session.create` plus `setSummary` | +| `message` | 4 | user, assistant, compaction user, summary assistant | +| `part` | 6 | user text, assistant text/tool/step-finish, compaction, summary text | + +Before opening the source, the test hashes the database and WAL. It then uses +`copyV1Store` to make a no-clobber copy outside the source directory and checks +the copy report. After migration, it hashes the source content files again and +requires the hashes to be identical. The `-shm` index is intentionally not +hashed because a read-only SQLite reader may update its read marks. + +The V2 `Database.layer` applies the real database migrations, and +`V1Migration.run()` runs against the copied database. The migration completes +and projects three `session_message` rows: + +| V2 row | Result | +|---|---| +| user | text and creation timestamp preserved; V1 `editorContext`, `system`, and message `summary` are dropped by the V2 message shape | +| assistant | text, completed tool content, model, cost, and token counts preserved; `structured` is dropped and V1 `end_turn` normalizes to V2 `unknown` | +| compaction | compaction marker and summary assistant are folded into one completed compaction row; rendered recent text contains the earlier user text | + +The session summary diff is copied with its file, patch, before/after content, +and `additions: 1`/`deletions: 0`. The source step-finish `model`, +`generationID`, `vercelID`, `metrics`, and active-generation `time` are +validated and present in the source part, but are not represented in the V2 +assistant content. The source aggregate row has the step-finish usage (`cost +0.5`, token counts `2/3/4/5/6`), while the V2 migration recomputes aggregates +from both assistant messages (`cost 0.75`, input/output/reasoning/cache +`3/5/5/7/7`). This is an intentional observed transform, not a preservation +claim for per-step metrics. + +The V1 event table is empty after migration by design; V2 rebuilds the +projected transcript from `message` and `part`. The summary assistant's source +row is not a separate V2 message because compaction pairing consumes it. + +## FileDiff hard gate and remaining boundary + +The earlier schema audit described a V1 optional-count `FileDiff` case. That +shape is not present in the exact source refs available here: the checked +writer's event publication rejects a missing `additions` or `deletions` before +it writes the event or `message` row. The fixture therefore uses only a +writer-valid diff and does not cast, hand-edit SQL, synthesize counts, or claim +to test the missing-count negative case. No local historical ref with an +optional-count `FileDiff.Info` was available without fetching or changing the +source checkout. + +Consequently this test proves the valid-count path for this exact writer/ref; +it does not clear the general preservation gate for older or malformed V1 +stores. A future negative fixture needs an exact historical writer/schema that +actually permits omitted counts, followed by an explicit assertion that V2 +skips the invalid user message. It must not be manufactured through this +current writer. + +Host storage ownership/sealing, auth/config import, and end-to-end boot of the +migrated copy remain outside this fixture slice. diff --git a/migration-tracking/technical-notes/baseline/v1-session-schema-audit.md b/migration-tracking/technical-notes/baseline/v1-session-schema-audit.md new file mode 100644 index 000000000000..fec35b7ec497 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/v1-session-schema-audit.md @@ -0,0 +1,217 @@ +# V1 session store schema audit (read-only) + +Scope: phase-6 precondition from `plans/kilo-opencode-v2-issue-13750.md` — a +read-only schema diff of Kilo `main`'s session store against the v2 +`V1Migration` expectations, plus the evidence for whether a copy-then-migrate +import is lossless (verdict: not proven lossless; known field transforms and +an unverified historical/malformed-store caveat). +This document changes no refs and runs no migration. + +Sources compared: + +- V1: Kilo-main `d99662338e3ddbd2613ab41fc0369837a6eb4be9` (the checked-out + writer), release `1536aef0fbe96c4575d23e9de2d50ba897f6b8ac`, and local + `origin/main` `ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7` + (`packages/core/src/database/{schema.gen.ts,migration/*}`, + `packages/opencode/src/storage/{db.ts,schema.ts}`, + `packages/schema/src/v1/session.ts`, `packages/schema/src/file-diff.ts`). +- V2: worktree HEAD `59b29de40966803e2c7cd734d439843fb773f6a6` + (`packages/core/src/database/v1-migration.bun.ts`, + `packages/core/src/database/migration/*`, `packages/core/src/session/sql.ts`, + `packages/schema/src/v1/session.ts`, `packages/schema/src/file-diff.ts`). + HEAD is 5 commits past the recorded pin + `76dbaf20adbd43fd208a00ef3cda4a51e125a234`; the migrator and migration set + above are identical at both SHAs (verified by `git diff` on + `packages/core/src/database/` — no changes between pin and HEAD). + +## V1 store identity and shape + +- Kilo v1 stores sessions in the `packages/core` Database at + `Global.Path.data/kilo.db` (channel builds) or `kilo-.db`, overridable + with `KILO_DB` (`origin/main:packages/core/src/database/database.ts`). + `packages/opencode/src/storage/schema.ts` re-exports the same core tables; + both entry points open the same file. +- A v1 store carries a `migration` journal table (TypeScript migration + lineage), not only Drizzle's `__drizzle_migrations`. Journal ids at v1 main: + the 38 shared ids plus two Kilo-only ids + (`20260714141136_session-message-legacy-writer-compat`, + `20260828074139_kilocode_board`). +- Table set (v1 bootstrap, `schema.gen.ts` on `origin/main`): `workspace`, + `data_migration`, `account_state`, `account`, `control_account`, + `credential`, `event_sequence`, `event`, `kilo_board_message`, `kilo_board`, + `permission`, `project_directory`, `project`, `message`, `part`, + `session_context_epoch`, `session_input`, `session_message`, `session`, + `todo`, `session_share`. Indexes include Kilo's `recall_part_search_idx` on + `part`. + +## Migration-lineage compatibility + +`DatabaseMigration.apply` (v2) takes the `applyOnly` path when a `session` (or +`session_v2`) table exists, reads the existing `migration` journal, and applies +only ids it knows that are not yet recorded. + +- 38 migration files are shared between v1 main and v2 HEAD. Spot diffs + (`20260323234822_events`, `20260127222353_familiar_lady_ursula`, + `20260312043431_session_message_cursor`, `20260511173437_session-metadata`) + show only cosmetic wrapper differences (`export default {…} satisfies` vs + `const migration …; export default migration`, `.js` import suffixes); the + executed SQL is identical. +- The two Kilo-only journal ids are unknown to v2 and ignored by `applyOnly`. +- The eight v2-only migrations were each checked against the v1 leftover + shapes: + +| Migration | Behaviour on a v1 copy | Verdict | +|---|---|---| +| `20260804233008_loose_psylocke` | v1 stores lack the pre-split marker `20260730195856_optional_session_title`, so the fresh-split branch runs: `CREATE TABLE IF NOT EXISTS` for `kv`/`instruction_*`/`session_pending`/`session_v2`; `ALTER TABLE event ADD created` (v1 `event` has no `created` — applies cleanly); recreates `session_message` with `seq NOT NULL` and an FK to `session_v2`, dropping v1's nullable-seq `session_message` rows (rebuilt later by the migrator from `message`/`part`); `DROP TABLE data_migration`, `session_context_epoch`, `session_input` — all present in v1. | compatible | +| `20260805200742_import_legacy_credentials` | Reads `Global.data/auth.json`; absent under the isolated `kilo2` identity (and `paths.preflight` rejects it there), so a no-op. Credential import stays with the auth/config owner. | compatible (no-op) | +| `20260808023530_workspace_domain` | `DROP TABLE workspace` then recreates the v2 shape. v1's control-plane `workspace` table exists and no v1 table FK-references it (v1 `session.workspace_id` has no FK). | compatible | +| `20260811161259_execution_claim_attempts` | `ALTER TABLE session_v2 ADD resume_attempts` on the freshly created table. | compatible | +| `20260812181746_session_inbox` | `CREATE TABLE session_inbox`; absent in v1. | compatible | +| `20260812213948_worktree` | `CREATE TABLE worktree` + `INSERT … SELECT` from `project_directory`; v1 has `project_directory(project_id, directory, type, strategy, time_created)`. | compatible | +| `20260819222447_session_viewed_state` | `ALTER TABLE session_v2 ADD time_idle/time_viewed/idle_outcome`. | compatible | +| `20260823191254_nullable_workspace_binding` | Recreates `workspace` (created by `workspace_domain`). | compatible | + +## Migrator expectations vs v1 actuals + +`V1Migration.run` (bun build only) operates on the database it is opened +against. On a v1 copy: + +- Detection: `hasLegacySessions` finds `session` → `required`. On an empty + `kilo2` store there is no `session` table → migrator is a no-op (the phase-0 + boot assertion). +- `DELETE FROM event …` clears v1's durable event log by design. v1 `event` + rows (schema: `id, aggregate_id, seq, type, data`) are **not** migrated; v2 + rebuilds projections from `session_message` + `event_sequence` watermarks. + This matches upstream's treatment of upstream v1 stores. +- Sessions: `INSERT OR IGNORE INTO session_v2 (…) SELECT … FROM session` copies + `id, project_id, workspace_id, parent_id, slug, directory, path, title, + version, share_url, summary_*, metadata, cost, tokens_*, revert, permission, + agent, model, time_created, time_updated, time_compacting, time_archived`. + Every selected column exists in v1's `session` (verified against + `origin/main` `packages/core/src/session/sql.ts` and bootstrap DDL). + `session_v2`'s FK to `project(id)` is satisfied because the `project` table + is shared and survives in place; sessions whose `project_id` is missing from + `project` are reassigned to `Project.ID.global` with a logged warning. + `fork_session_id`/`fork_boundary` stay NULL (v1 has neither). +- Projects: the `project` table itself is not copied (same table, already + present); the migrator only `INSERT OR IGNORE`s the global project row with + `sandboxes='[]'`. v1 `project` has `sandboxes NOT NULL`; v2's ProjectTable is + column-identical. +- Messages/parts: `message.data` and `part.data` JSON decode through v2's + `SessionV1` schemas (`packages/schema/src/v1/session.ts`) and are rewritten + into `session_message` rows (`user`/`synthetic`/`assistant`/`compaction` + types). Tool parts preserve `input`/`output`/attachments/`metadata` for + `completed` and `error` states; `running`/`pending` tools become + `tool.interrupted` errors (v1 cannot have live executions across the cutover). + Compaction pairs fold into `compaction` messages with rendered `recent` text. +- Session aggregates (`cost`, `tokens_*`, `agent`, `model`) are recomputed from + assistant messages; `revert` and `time_compacting` are reset to NULL. + +## Kilo deltas that are **not** preserved by the transform + +These are v1-main `kilocode_change` fields absent from v2's `SessionV1` +schemas. Effect Schema strips unknown keys on decode, so rows still decode, but +the field values are silently dropped from migrated data: + +| v1 field | Where | Fate | +|---|---|---| +| `StepFinishPart.model/generationID/vercelID/metrics/time` (per-step generation metrics) | `part.data` | stripped; only `snapshot` is read from step-finish parts | +| `SubtaskPart.variant` (workflow variant) | `part.data` | stripped; subtask parts are otherwise only used to detect subtask-only user messages | +| `UserMessage.editorContext` (VS Code editor context) | `message.data` | stripped | +| `Revert.workspace` restore status | `session.revert` | moot: the transform resets `revert` to NULL for all sessions | +| v1 `session_message` rows (dual-written projection) | table | dropped by `loose_psylocke` and rebuilt from `message`/`part` | +| `todo` rows | table | never read by the migrator; left as a leftover table | +| `session_share` rows (share id/secret/url) | table | never read; only the denormalized `session.share_url` column carries over | +| `account`, `account_state`, `control_account`, `credential` | tables | never read; credential import is the auth/config owner's scope | +| `kilo_board`, `kilo_board_message` (Agent Manager) | tables | never read; v2 has no board consumer | +| `event` rows | table | deleted by design | + +## FileDiff shape correction and remaining caveat + +An earlier version of this audit incorrectly said that v1's `FileDiff.Info` +marks `additions` and `deletions` optional. That is false for each exact +Kilo-main ref available locally. At `d99662338e3ddbd2613ab41fc0369837a6eb4be9`, +`1536aef0fbe96c4575d23e9de2d50ba897f6b8ac`, and +`ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7`: + +- `packages/schema/src/file-diff.ts` defines both fields as + `Schema.Finite` in `FileDiff.Info`. +- `packages/schema/src/v1/session.ts` uses `FileDiff.Info` for user summary + diffs, and the schema is unchanged between `d996623` and its two descendants + for this shape. The only relevant descendant change in that file adds + `SessionRevert.workspace`. +- `packages/core/src/event.ts` validates durable event data before persistence, + so the actual writer rejects a missing count before it can write a message. + +V2's `FileDiff.LegacyInfo` also requires both numbers. The companion +`v1-fixture-generation.md` records an actual writer-generated fixture with +valid counts; its user message migrates successfully. This establishes the +valid-count path for the checked refs, not universal preservation for arbitrary +databases. + +An older historical writer or a malformed v1 database could still contain a +persisted `diffs` entry without one of these fields. Such a row would fail the +v2 `SessionV1` decode and the entire user message would be skipped with an +`invalid-message` warning. No such historical writer ref is present locally, +and this audit does not fabricate one by casting, editing SQL, or bypassing the +writer. The caveat therefore remains unverified rather than a demonstrated +blocker for the exact writer refs above. + +## Copy consistency (the tested seam) + +Runtime spike on the packaged Bun (`packages/kilo-cli/dist/interactive/bun`, +1.4.x), against a quiescent synthetic WAL-mode fixture: a `readonly` +`bun:sqlite` connection saw committed-but-uncheckpointed WAL rows; `VACUUM +INTO` from that connection produced a standalone copy containing those rows; +`PRAGMA integrity_check` on the copy returned `ok`; the source file bytes +were unchanged and no `-wal`/`-shm` files were created for the source. The +copy is atomic when written to a temp file in the destination directory and +published with a no-clobber hard link. `VACUUM INTO` is not the only such +mechanism — SQLite's online backup API would serve as well — but bun:sqlite +exposes no backup binding, so `VACUUM INTO` is the available one here. + +Honesty limits of the source-unchanged check: the helper hashes the two +content-bearing files (the database and its `-wal`) before opening the source +and again after copying; identical SHA-256 bytes prove no byte-level change +**in the tested quiescent fixture**. The `-shm` wal-index is deliberately +excluded: it is a rebuildable index, and any SQLite reader may legitimately +update its read marks. A live v1 process appending to the WAL will change +bytes without my module touching anything. The check is therefore a +conservative *detector* (any content-byte change refuses the publish), not a +proof that read-only access has zero observable side effects. Import from a +quiescent source. + +## Verdict + +- Schema mapping for **projects, sessions, and tool-call content** runs + entirely on upstream machinery (`DatabaseMigration.apply` + `V1Migration.run`) + with no Core fork. The real writer fixture exercises the valid-count + message path; known Kilo fields are still dropped or reset, and arbitrary + historical/malformed stores remain outside the evidence. +- The import is **not proven lossless**. The table above lists what the + transform drops or resets. An omitted-count row from an unknown historical + or malformed store could also drop its entire user message during decode, but + no checked writer ref emits that shape. +- This slice ships an **unintegrated candidate utility** (inspection + + consistent copy). It is not a wired importer: nothing calls it from the + CLI, no migration of a copied store is wired or authorized, and the + remaining work is **blocked** on the gates below, all outside this slice's + file ownership. + +## Remaining gates (owners noted) + +1. `packages/kilo-cli/src/storage.ts` `prepare()` refuses any store containing + a `session` table and any store without the `_kilo_preview` marker + (ownership/channel seal). A migrated copy still contains the leftover v1 + tables, so the next boot after an import would be refused. Relaxing or + sequencing that guard is host wiring owned by the parent slice. +2. A real writer-generated fixture now covers the valid-count path for the + checked refs; see `kilocode/baseline/v1-fixture-generation.md`. An exact + historical writer that permits omitted counts is not available locally, so + malformed-store handling remains an evidence gap rather than a fabricated + preservation claim. +3. `auth.json` credential import and `kilo.jsonc` key mapping are explicitly + owned by another agent and are not touched here. +4. End-to-end "import then boot and open a migrated session" validation + requires the parent CLI command wiring (`kilo2 import …`) that owns + detection, prompt, and destination selection. diff --git a/migration-tracking/technical-notes/baseline/v2-v1-schema-diffs.md b/migration-tracking/technical-notes/baseline/v2-v1-schema-diffs.md new file mode 100644 index 000000000000..d679894ad61c --- /dev/null +++ b/migration-tracking/technical-notes/baseline/v2-v1-schema-diffs.md @@ -0,0 +1,201 @@ +# Kilo v1 vs v2: message model and API + +Compared from the generated client types: +- **v1:** `@kilocode/sdk/v2` (in `packages/sdk/js` on `main`), the SDK entry point used by the v1 VS Code extension. Its calls take flat parameters (`{ sessionID, … }`). Despite the `/v2` in its name, this is the v1 API. It also contains an early, partial copy of the v2 session API under `client.v2.*` (`/api/session/…`, `session.next.*` events), which the v1 extension does not use. The "v1" column describes the API clients actually use. +- **v2:** `packages/client/src/promise/generated/types.ts` on `kilo-v2` + +The examples use illustrative IDs and values, and leave out optional fields. Their shapes follow the generated types. + +## Messages + +| | v1 | v2 | +|---|---|---| +| **Structure** | A message is `{ info, parts[] }`. `info` is a `UserMessage` or `AssistantMessage` (told apart by `role`); each part is a separate record with its own `id`, `sessionID` and `messageID` | One record per message, told apart by `type`, with its content embedded | +| **Message kinds** | 2: user and assistant | 10: `user`, `assistant`, `synthetic`, `system`, `skill`, `shell`, `compaction`, plus timeline markers `agent-switched`, `model-switched` and `location-switched` | +| **User message** | Content lives in parts: text, file, agent and subtask parts | `text` plus attachments: `files`, `agents`, `skills` | +| **Assistant message** | Parts: text, reasoning, tool, step-start/finish, snapshot, patch, retry and others | `content[]` holding text, reasoning and tool entries, plus `agent`, `model`, `snapshot`, `finish`, `cost`, `tokens`, `error` and `retry` on the message itself | +| **Tool state** | Kept inside the tool part; a completed tool's result is an `output` string | Explicit states (`streaming`, `running`, `completed`, `error`); a completed tool's result is a `content[]` of text and file entries | + +### Example: an assistant message that replies and calls a tool + +**v1** (one item from `GET /session/{id}/message`): + +```json +{ + "info": { + "id": "msg_02", + "sessionID": "ses_01", + "role": "assistant", + "parentID": "msg_01", + "agent": "code", + "mode": "code", + "providerID": "kilo", + "modelID": "anthropic/claude-sonnet-4.5", + "path": { "cwd": "/repo", "root": "/repo" }, + "time": { "created": 1759770000000, "completed": 1759770004200 }, + "cost": 0.0123, + "tokens": { "input": 1200, "output": 85, "reasoning": 0, "cache": { "read": 0, "write": 0 } }, + "finish": "tool-calls" + }, + "parts": [ + { + "id": "prt_10", + "sessionID": "ses_01", + "messageID": "msg_02", + "type": "text", + "text": "Let me read the file." + }, + { + "id": "prt_11", + "sessionID": "ses_01", + "messageID": "msg_02", + "type": "tool", + "callID": "call_1", + "tool": "read", + "state": { + "status": "completed", + "input": { "filePath": "/repo/src/app.ts" }, + "output": "…file contents…", + "title": "src/app.ts", + "metadata": {}, + "time": { "start": 1759770001000, "end": 1759770001150 } + } + } + ] +} +``` + +**v2** (one item from `GET /api/session/{sessionID}/message`): + +```json +{ + "type": "assistant", + "id": "msg_02", + "agent": "code", + "model": { "id": "anthropic/claude-sonnet-4.5", "providerID": "kilo" }, + "time": { "created": 1759770000000, "streamed": 1759770000400, "completed": 1759770004200 }, + "content": [ + { "type": "text", "text": "Let me read the file." }, + { + "type": "tool", + "id": "call_1", + "name": "read", + "executed": true, + "state": { + "status": "completed", + "input": { "filePath": "/repo/src/app.ts" }, + "content": [{ "type": "text", "text": "…file contents…" }] + }, + "time": { "created": 1759770000900, "ran": 1759770001000, "completed": 1759770001150 } + } + ], + "finish": "tool-calls", + "cost": 0.0123, + "tokens": { "input": 1200, "output": 85, "reasoning": 0, "cache": { "read": 0, "write": 0 } } +} +``` + +What changed: there are no separate part records with their own IDs; content is embedded in the message. Model and agent are fields on the message. A tool's result is structured content instead of an output string. + +## Prompt API + +| | v1 | v2 | +|---|---|---| +| **Endpoint** | `POST /session/{id}/message` (or `/prompt_async`) | `POST /api/session/{sessionID}/prompt` | +| **Input** | `parts[]`, with optional `model`, `agent`, `system`, `tools`, `noReply`, `format`, plus Kilo fields such as `variant` and `editorContext`, per prompt | `text`, `files`, `agents`, `skills`, `metadata`, `delivery` (`steer` or `queue`) and `resume` | +| **What it returns** | The finished assistant message (the async variant returns nothing) | An inbox item showing the prompt was **accepted**; the model runs separately | +| **Model and agent choice** | Can be set on each prompt | Belongs to the session. Changed with `POST /api/session/{id}/model` and `/agent`, which add `model-switched` / `agent-switched` messages to the timeline | + +### Example: ask about a file + +**v1** + +```ts +const reply = await client.session.prompt({ + sessionID: "ses_01", + agent: "code", + model: { providerID: "kilo", modelID: "anthropic/claude-sonnet-4.5" }, + parts: [ + { type: "text", text: "Explain src/app.ts" }, + { type: "file", mime: "text/plain", filename: "app.ts", url: "file:///repo/src/app.ts" }, + ], +}) +// reply.data → { info: AssistantMessage, parts: Part[] }: the finished answer, after the whole run +``` + +**v2** + +```ts +// The model is session state, set separately (only when it changes): +await client.session.switchModel({ + sessionID: "ses_01", + model: { id: "anthropic/claude-sonnet-4.5", providerID: "kilo" }, +}) + +const admitted = await client.session.prompt({ + sessionID: "ses_01", + text: "Explain @src/app.ts", + files: [ + { + uri: "file:///repo/src/app.ts", + name: "app.ts", + mention: { start: 8, end: 19, text: "@src/app.ts" }, + }, + ], + delivery: "steer", +}) +// admitted → { id: "msg_01", sessionID: "ses_01", type: "user", payload: { text, files }, delivery: "steer", timeCreated } +// This only confirms the prompt was queued. The answer arrives on the event stream. +``` + +What changed: v1 returns the answer, while v2 returns an admission receipt. Files are attached by URI with mention offsets instead of as typed parts. Model and agent are no longer per-prompt fields. + +## Streaming updates + +| | v1 | v2 | +|---|---|---| +| **Endpoint** | `/event` (one directory) or `/global/event` (all directories; used by the VS Code extension) | `/api/event` | +| **Event envelope** | `{ id, type, properties }`; on `/global/event` wrapped as `{ directory, payload }` | `{ id, created, type, data }`, plus `location` and, on durable events, `durable: { aggregateID, seq, version }` | +| **Updates** | Whole-record events: `message.updated` and `message.part.updated`, plus `message.part.delta` | Fine-grained events: `session.text.delta`, `session.reasoning.delta`, `session.tool.input.*`, `session.tool.called/success/failed`, `session.step.*`, and others | +| **Lifecycle** | `session.status`, `session.idle` | `session.inbox.enqueued/delivered`, `session.execution.started/succeeded/failed/interrupted`, plus `session.status` and `session.idle` | +| **Ordering** | Classic events (`message.*`, `session.*`) have no sequence numbers. Durable events also arrive as `type: "sync"` payloads with `seq` and `aggregateID`, alongside the classic events | Every durable event carries `durable.seq`, and `session.message.content.updated` carries a full-content snapshot, so clients can rebuild state after reconnecting. Streaming deltas are not durable | + +### Example: a text chunk, then a finished tool call + +**v1** (server-sent events on `/event`): + +```text +data: {"id":"evt_1","type":"message.part.delta","properties":{"sessionID":"ses_01","messageID":"msg_02","partID":"prt_10","field":"text","delta":"Let me "}} + +data: {"id":"evt_2","type":"message.part.updated","properties":{"sessionID":"ses_01","time":1759770001150,"part":{"id":"prt_11","sessionID":"ses_01","messageID":"msg_02","type":"tool","callID":"call_1","tool":"read","state":{"status":"completed","input":{"filePath":"/repo/src/app.ts"},"output":"…file contents…","title":"src/app.ts","metadata":{},"time":{"start":1759770001000,"end":1759770001150}}}}} +``` + +**v2** (server-sent events on `/api/event`): + +```text +data: {"id":"evt_1","created":1759770000400,"type":"session.text.delta","data":{"sessionID":"ses_01","assistantMessageID":"msg_02","ordinal":0,"delta":"Let me "}} + +data: {"id":"evt_2","created":1759770001150,"type":"session.tool.success","durable":{"aggregateID":"ses_01","seq":42,"version":2},"data":{"sessionID":"ses_01","assistantMessageID":"msg_02","id":"call_1","content":[{"type":"text","text":"…file contents…"}],"executed":true}} +``` + +What changed: +- v1 sends a whole updated part, and the client replaces its copy. +- v2 sends a small, specific event (delta, tool success, and so on) that the client applies to its state. +- Deltas are addressed by message and position (`ordinal`) instead of part ID. +- In v2, every durable event carries its sequence number as part of the one event stream; in v1, sequenced events were a separate `sync` side channel next to the classic events. + +## What this means for clients + +A v1 client can't simply be pointed at a v2 server. It has to rework three things: + +- **Rendering:** draw the timeline from typed messages with embedded content, instead of message-plus-parts records. +- **Prompting:** send prompts as admission plus events, and change model or agent through session-level calls instead of per-prompt fields. +- **State:** rebuild client state from the fine-grained, sequenced event stream. + + +These differences are why each client's backend has to be rebuilt on the v2 client, rather than pointed at the new server. + +On `kilo-v2`, the VS Code extension host has been rewritten onto the v2 client (`connection.ts` and the `backend/*` modules use `@opencode-ai/client`). The original webviews deliberately keep their v1-shaped message/part contract: the host projects v2 state into that format, with the v1 view types kept in `packages/kilo-client/src/ide-types.ts` (see `technical-notes/baseline/existing-vscode-port.md`). The remote relay uses the same adapter approach toward cloud consumers (`technical-notes/baseline/remote-share-next-contracts.md`). Gaps in these projections (for example, the missing completed-tool `title` and the user message's `agent`/`model`) are tracked in those notes. + + +It also explains why the migration plan forbids mixing v1 and v2 routes within one session (Decision 7). diff --git a/migration-tracking/technical-notes/baseline/v2-v1-session-diffs.md b/migration-tracking/technical-notes/baseline/v2-v1-session-diffs.md new file mode 100644 index 000000000000..e4eaa12b81e4 --- /dev/null +++ b/migration-tracking/technical-notes/baseline/v2-v1-session-diffs.md @@ -0,0 +1,208 @@ +# Kilo v1 vs v2: sessions + +Companion to *Kilo v1 vs v2: message model and API*, which covers message shapes, the prompt call and streaming chunks. This document covers what changed about **sessions**: the session object, the session endpoints, how a session runs, and how its state is stored. + +Compared from the generated client types: +- **v1:** `@kilocode/sdk/v2` (in `packages/sdk/js` on `main`), the SDK entry point used by the v1 VS Code extension. Its calls take flat parameters (`{ sessionID, … }`). Despite the `/v2` in its name, this is the v1 API. It also contains an early, partial copy of the v2 session API under `client.v2.*` (`/api/session/…`, `session.next.*` events), which the v1 extension does not use. The "v1" column describes the API clients actually use. +- **v2:** `packages/client/src/promise/generated/` on `kilo-v2` + +The examples use illustrative IDs and values, and leave out optional fields. + +## At a glance + +| | v1 | v2 | +|---|---|---| +| **How state is stored** | Clients read mutable session, message and part records. A durable event log and a `session_message` projection are also written alongside, but are not the client-facing model | Event-sourced: each session is an ordered log of durable events (with sequence numbers), and messages are projected from that log | +| **How a prompt runs** | The request starts the agent loop directly; one run at a time, cancelled with `abort` | The prompt goes into the session's **inbox**; a separate execution delivers it and runs the model, step by step | +| **Concurrent input** | Prompts sent while busy are queued first-in, first-out (`session.queue.changed` lists them). The classic API has no way to steer a running step and no inbox endpoints | Inbox items can **steer** (delivered at the next safe step boundary) or **queue** (delivered after the run), and can be listed, cancelled or re-prioritised before delivery | +| **Where it runs** | A `directory` (plus optional `workspaceID`) fixed per request | A `location` (`{ directory, workspaceID? }`) stored on the session; it can be **moved** to another location | +| **Agent and model** | Optional fields on the session, overridable on each prompt | Session state, changed with `switchAgent` / `switchModel`; each change is recorded in the timeline | +| **Restart behaviour** | No execution claim; an in-flight run is not resumed after a restart | An execution claim lets the server resume interrupted top-level sessions on startup | + +## The session object + +| Field area | v1 `Session` | v2 `SessionInfo` | +|---|---|---| +| **Identity** | `id`, `slug`, `projectID`, `version` | `id`, `projectID`. No `slug` or `version` | +| **Location** | `directory`, `workspaceID?`, `path?` | `location: { directory, workspaceID? }`, `subpath?` | +| **Lineage** | `parentID?` (child and subagent sessions) | `parentID?`, plus `fork?: { sessionID, boundary }` recording where it was forked from | +| **Agent and model** | `agent?`, `model?` | `agent?`, `model?` (current session state) | +| **Usage** | `cost?`, `tokens?` | `cost`, `tokens` (always present) | +| **Outcome** | Not on the session; read from `/session/status` | `outcome?: "succeeded" | "failed" | "interrupted"` from the last run | +| **Timestamps** | `created`, `updated`, `compacting?`, `archived?` | `created`, `updated`, `idle?`, `viewed?`, `archived?` | +| **Change summary** | `summary?: { additions, deletions, files, diffs? }` | Not on the session. Diffs come from the VCS and revert APIs | +| **Sharing** | `share?: { url }` | Not in the core. Kilo sharing is a Kilo RPC (`client.kilocode.session.share/unshare`) | +| **Permissions** | `permission?: PermissionRuleset` on the session | Not on the session. Permissions are their own API (requests, saved rules, replies) | +| **Revert** | `revert?: { messageID, partID?, snapshot?, diff?, workspace? }` | `revert?: { messageID, partID?, snapshot?, files? }` | +| **Metadata** | `metadata?` | `metadata?` (JSON values) | + +### Example + +**v1** + +```json +{ + "id": "ses_01", + "slug": "brave-otter", + "projectID": "prj_01", + "directory": "/repo", + "title": "Explain app.ts", + "version": "7.8.3", + "agent": "code", + "model": { "id": "anthropic/claude-sonnet-4.5", "providerID": "kilo" }, + "time": { "created": 1759770000000, "updated": 1759770004200 }, + "summary": { "additions": 12, "deletions": 3, "files": 2 }, + "share": { "url": "https://app.kilo.ai/s/abc123" }, + "permission": [{ "permission": "edit", "pattern": "*", "action": "ask" }], + "cost": 0.04, + "tokens": { "input": 5200, "output": 410, "reasoning": 0, "cache": { "read": 0, "write": 0 } } +} +``` + +**v2** + +```json +{ + "id": "ses_01", + "projectID": "prj_01", + "location": { "directory": "/repo" }, + "title": "Explain app.ts", + "agent": "code", + "model": { "id": "anthropic/claude-sonnet-4.5", "providerID": "kilo" }, + "cost": 0.04, + "tokens": { "input": 5200, "output": 410, "reasoning": 0, "cache": { "read": 0, "write": 0 } }, + "outcome": "succeeded", + "time": { "created": 1759770000000, "updated": 1759770004200, "idle": 1759770004200, "viewed": 1759770005000 }, + "fork": { "sessionID": "ses_00", "boundary": { "type": "through", "messageID": "msg_07" } } +} +``` + +What changed: the v2 session is smaller and focused on core state. Sharing, the permission ruleset and change summaries moved to their own APIs. The location became structured, and the outcome of the last run and the fork origin are recorded on the session. + +## Session endpoints + +| Operation | v1 | v2 | +|---|---|---| +| **Create** | `POST /session` `{ parentID?, title?, … }`, with Kilo fields such as `platform`, `metadata` and `sandboxInheritanceToken`; `directory` as a query parameter | `POST /api/session` `{ id?, title?, agent?, model?, location?, metadata? }`. The client may choose the ID; reusing an existing ID adopts that session | +| **List** | `GET /session` (returns an array) | `GET /api/session` with search, filters (`parentID`, `project`, `directory`, `workspace`), ordering and **cursor pagination** | +| **Children** | `GET /session/{id}/children` | `GET /api/session?parentID=…` | +| **Get / delete** | `GET` / `DELETE /session/{id}` | `GET` / `DELETE /api/session/{sessionID}` | +| **Rename** | `PATCH /session/{id}` `{ title }` | `POST /api/session/{sessionID}/rename` | +| **Status** | `GET /session/status` (map of `idle` / `busy` / `retry`) | `GET /api/session/active`, `outcome` on the session, and `session.status` events (still `idle` / `busy` / `retry`) | +| **Prompt** | `POST /session/{id}/message` or `/prompt_async` | `POST /api/session/{sessionID}/prompt` (admission to the inbox) | +| **Manage queued input** | No public endpoints in the classic API; the queue is reported through `session.queue.changed` | `GET /api/session/{id}/inbox`, `DELETE …/inbox/{inboxID}`, `POST …/inbox/{inboxID}/steer` and `/queue` | +| **Stop** | `POST /session/{id}/abort` | `POST /api/session/{sessionID}/interrupt`; with `?continue=true` it then carries on with pending steer input while queued prompts wait. `POST …/background` moves running blocking tools to the background | +| **Wait for idle** | Watch events | `POST /api/session/{sessionID}/wait` | +| **Agent / model** | Per prompt | `POST /api/session/{id}/agent`, `POST /api/session/{id}/model` | +| **Move location** | n/a | `POST /api/session/{sessionID}/move` `{ directory, workspaceID?, delivery? }` | +| **Compact** | `POST /session/{id}/summarize` `{ providerID, modelID }` | `POST /api/session/{sessionID}/compact` `{ id?, delivery? }` (queued like other inbox items) | +| **Fork** | `POST /session/{id}/fork` `{ messageID? }` | `POST /api/session/{sessionID}/fork` `{ boundary: { type: "before" | "through", messageID } }` | +| **Revert** | `POST /session/{id}/revert` `{ messageID, partID? }`, then `/unrevert` | Three steps: `…/revert/stage` `{ messageID, files? }`, then `…/revert/clear` (undo) or `…/revert/commit` (make permanent) | +| **Messages** | `GET /session/{id}/message`, plus `/message/{messageID}` and part update/delete routes | `GET /api/session/{id}/message` (cursor pagination), `GET` / `PATCH …/message/{messageID}`. No part routes | +| **Share** | `POST` / `DELETE /session/{id}/share` | Kilo RPC: `client.kilocode.session.share/unshare` | +| **Permission reply** | `POST /session/{id}/permissions/{permissionID}` | Permission API: `client.permission.reply(…)`; questions use forms | +| **Diff** | `GET /session/{id}/diff` | No session diff endpoint; use `client.vcs.diff(…)` and the revert staging result | +| **Import / export** | n/a | `POST /api/session/import`, `GET /api/session/{id}/export` | +| **New in v2** | n/a | `context` (what the model will see), `instructions/entries`, `generate` (one-off text), `environment` (session variables), `view` (mark viewed), `stats`, and `log` (experimental) | + +The v1 SDK also exposes Kilo-specific session routes (`branch-name`, `model-usage`, `sandbox`, `viewed`). v2 covers "viewed" natively (`view`). Per-session model usage is a Kilo RPC in the v2 port. v2's `stats` endpoint reports aggregate statistics across sessions, not one session's usage. The remaining routes are Kilo features tracked in the migration plan. + +## Example: start a session, prompt it, stop it + +**v1** + +```ts +const { data: session } = await client.session.create({ title: "Explain app.ts", directory: "/repo" }) + +await client.session.promptAsync({ + sessionID: session.id, + agent: "code", + model: { providerID: "kilo", modelID: "anthropic/claude-sonnet-4.5" }, + parts: [{ type: "text", text: "Explain src/app.ts" }], +}) +// On /global/event: session.status { type: "busy" } … message.part.* updates … session.idle + +await client.session.abort({ sessionID: session.id }) +``` + +**v2** + +```ts +const session = await client.session.create({ + title: "Explain app.ts", + agent: "code", + model: { id: "anthropic/claude-sonnet-4.5", providerID: "kilo" }, + location: { directory: "/repo" }, +}) + +await client.session.prompt({ sessionID: session.id, text: "Explain @src/app.ts", delivery: "steer" }) +// On /api/event: +// session.inbox.enqueued → session.inbox.delivered → session.execution.started +// → session.step.started / session.text.delta / session.tool.* … +// → session.execution.succeeded → session.idle + +await client.session.interrupt({ sessionID: session.id }) +// → session.execution.interrupted; outcome becomes "interrupted" +``` + +## Example: sending input while the session is running + +**v1:** the classic API has no way to steer a running step. Prompts sent while busy wait in a first-in, first-out queue; the client can't promote them. + +**v2:** + +```ts +// Delivered at the next safe step boundary, so the model sees it mid-run: +await client.session.prompt({ sessionID: "ses_01", text: "Also check the tests", delivery: "steer" }) + +// Delivered only after the current run finishes: +const queued = await client.session.prompt({ sessionID: "ses_01", text: "Then write a summary", delivery: "queue" }) + +// Before delivery, queued input can be inspected, promoted or cancelled: +await client.session.inbox.list({ sessionID: "ses_01" }) +await client.session.inbox.steer({ sessionID: "ses_01", inboxID: queued.id }) +await client.session.inbox.cancel({ sessionID: "ses_01", inboxID: queued.id }) +``` + +## Example: revert + +**v1:** a single call reverts, and a second call undoes it. + +```ts +await client.session.revert({ sessionID: "ses_01", messageID: "msg_05" }) +await client.session.unrevert({ sessionID: "ses_01" }) +``` + +**v2:** revert is staged first, so a client can show the result before deciding. + +```ts +const staged = await client.session.revert.stage({ sessionID: "ses_01", messageID: "msg_05" }) +// staged.files lists the file changes that would be undone + +await client.session.revert.commit({ sessionID: "ses_01" }) // make it permanent +// or +await client.session.revert.clear({ sessionID: "ses_01" }) // cancel the staged revert +``` + +## Session lifecycle events + +| Moment | v1 event | v2 events | +|---|---|---| +| Prompt accepted | (none; the prompt call itself) | `session.inbox.enqueued` | +| Prompt handed to the model | (none) | `session.inbox.delivered` (the message becomes visible) | +| Run starts | `session.status` `{ type: "busy" }` | `session.execution.started`, `session.status` `{ type: "busy" }` | +| Each model call | `step-start` / `step-finish` parts via `message.part.updated` | `session.step.started`, `session.step.ended` / `session.step.failed` | +| Retry | `session.status` `{ type: "retry", attempt, next }` | `session.retry.scheduled`, `session.status` `{ type: "retry", … }` | +| Run ends | `session.idle` | `session.execution.succeeded` / `failed` / `interrupted`, then `session.idle` | +| Compaction | `time.compacting` set on the session | `session.compaction.started` / `delta` / `ended` / `failed` | +| Agent or model change | `session.updated` | `session.agent.selected` / `session.model.selected` | +| Revert | `session.updated` | `session.revert.staged` / `cleared` / `committed` | +| Fork / move | `session.created` | `session.forked` / `session.moved` | + +## What this means for clients + +- **Session list and detail views:** read `location`, `outcome`, `fork` and the new timestamps. Get sharing, permissions and diffs from their own APIs. +- **The "busy" indicator and Stop button:** drive them from execution events and `outcome`, use `interrupt` instead of `abort`, and decide how to present steer and queue input (an inbox view). +- **Agent and model pickers:** call `switchAgent` / `switchModel` on the session instead of passing them with each prompt. +- **Revert:** move to stage, then commit or clear, which allows a preview step. +- **Reconnects and restarts:** rebuild session state from sequenced durable events. A run interrupted by a server restart may resume by itself. diff --git a/migration-tracking/technical-notes/baseline/vscode-packaging.md b/migration-tracking/technical-notes/baseline/vscode-packaging.md new file mode 100644 index 000000000000..8fc9c28fe92d --- /dev/null +++ b/migration-tracking/technical-notes/baseline/vscode-packaging.md @@ -0,0 +1,121 @@ +# VSCode Extension VSIX Packaging — Kilo v2 Preview + +Status: **Locally verified; VSIX packaging of prebuilt `dist/extension.cjs` + `dist/web` + icon + README + LICENSE implemented and passing tests using cached `@vscode/vsce 3.9.2`. No network downloads, no live stores touched.** + +--- + +## 1. Overview & Constraints + +Under the user goal to package the Kilo v2 VSCode extension: +- **No external network / downloads:** Reuses existing cached `@vscode/vsce` binary discovered from local cache (`kilocode/packages/kilo-vscode/node_modules/.bin/vsce`). +- **No build modification:** Does not rebuild or modify existing `dist/extension.cjs` or `dist/web` bundles; packages existing verified build outputs. +- **Dependency isolation:** `workspace:*` dependencies in `package.json` are sanitized during packaging so they never leak into the installed runtime manifest. The extension bundle is self-contained. +- **Proven VSIX tooling:** Utilizes standard `@vscode/vsce package --no-dependencies` rather than custom/handwritten zip generators. +- **Ownership compliance:** Only `packages/kilo-vscode/script/package.ts`, `packages/kilo-vscode/test/package.test.ts`, `packages/kilo-vscode/.vscodeignore`, and this document are created/owned. `package.json`, `build.ts`, and `README.md` remain root-owned. + +--- + +## 2. Packaging Architecture + +### A. Discovery of Cached Tooling +`findVsceBinary()` searches candidate locations in precedence order: +1. `process.env.VSCE_BIN` +2. Local relative binary `packages/kilo-vscode/node_modules/.bin/vsce` +3. Sibling checkouts: + - `../kilocode/packages/kilo-vscode/node_modules/.bin/vsce` + - `../kilocode/node_modules/.bin/vsce` +4. PATH discovery via `Bun.which("vsce")` + +### B. Manifest Sanitization (`sanitizePackageJson`) +Before invoking `vsce`: +- Deletes `"private": true` (required for vsce packaging). +- Deletes `"devDependencies"` and `"scripts"`. +- Deletes `"dependencies"`: completely removes `workspace:*` and `catalog:` references so the installed manifest is clean and self-contained. +- Preserves `main: "./dist/extension.cjs"`, `activationEvents`, `contributes` (commands, views, viewContainers, configurations), `engines`, `publisher`, `name`, `displayName`. +- Ensures valid repository metadata. + +### C. Resource Boundaries & `.vscodeignore` +Packaged VSIX contents: +- `extension/package.json` (sanitized manifest) +- `extension/dist/extension.cjs` (main entrypoint) +- `extension/dist/web/**` (webview chat and settings frontend bundle, index.html, assets) +- `extension/assets/kilo.svg` (activity bar icon) +- `extension/README.md` & `extension/LICENSE.txt` (requires real `LICENSE`, never synthesizes fake header) +- `[Content_Types].xml` & `extension.vsixmanifest` + +Ignored via `.vscodeignore`: +- `src/**`, `web/**` (source TS/TSX files) +- `test/**`, `script/**` (test fixtures and packaging scripts) +- `tsconfig*.json`, `vite.config.mjs`, `*.map`, `*.log` +- `node_modules/**`, `dist/extension-host.cjs` + +### D. Verification (`validateVsix`) +Inspects generated VSIX via standard Info-ZIP utilities (`zipinfo` and `unzip`): +- Confirms all required entries are present. +- Confirms forbidden source/test/tooling prefixes are excluded. +- Reads `extension/package.json` and asserts zero `workspace:*` or `catalog:` dependencies leaked. + +--- + +## 3. Programmatic & CLI Invocation Contracts + +### Programmatic API (`packages/kilo-vscode/script/package.ts`) +```ts +export interface PackageOptions { + readonly out?: string // Target .vsix file path + readonly cwd?: string // kilo-vscode root directory + readonly vsceBin?: string // Explicit vsce executable path +} + +export interface PackageResult { + readonly vsixPath: string // Absolute path to generated .vsix + readonly sizeBytes: number // Total VSIX file size in bytes + readonly fileCount: number // Total entries inside VSIX + readonly version: string // Package version from manifest +} + +export function packageExtension(options?: PackageOptions): Promise +export function validateVsix(vsixPath: string): Promise +export function findVsceBinary(customPath?: string): string +export function sanitizePackageJson(rawJson: Record): Record +``` + +### CLI Command (For Root Wiring / Direct Invocation) +```bash +# Package to default dist/kilo-code-v2-preview-0.0.1.vsix +bun packages/kilo-vscode/script/package.ts + +# Package to an explicit output path +bun packages/kilo-vscode/script/package.ts -o /path/to/output.vsix +``` + +### Isolated Profile Installation Command (For Root Testing) +```bash +code --user-data-dir /tmp/vscode-test-profile \ + --extensions-dir /tmp/vscode-test-profile/extensions \ + --install-extension packages/kilo-vscode/dist/kilo-code-v2-preview-0.0.1.vsix +``` + +--- + +## 4. Verification Results + +Run with bundled Bun 1.4.0 (`packages/kilo-cli/dist/interactive/bun test test/package.test.ts`): +``` +bun test v1.4.0 (34cbb9a40) + + 3 pass + 0 fail + 33 expect() calls +Ran 3 tests across 1 file. [2.58s] +``` + +Test coverage: +1. `findVsceBinary discovers cached @vscode/vsce binary`: Verified execution of `@vscode/vsce 3.9.2`. +2. `sanitizePackageJson strips workspace:* dependencies, devDependencies, and private flag`: Confirmed clean runtime manifest. +3. `packageExtension generates a complete, self-contained VSIX without workspace:* leaks`: + - Built output size: ~9.36 MB (9,817,318 bytes). + - Total files: 912 (including full `dist/web` assets). + - Validated: `extension.cjs`, `web/index.html`, `kilo.svg`, `package.json`, `vsixmanifest`. + - Validated: zero `src/**`, `web/**`, `test/**`, `tsconfig*.json` entries. + - Validated: zero `workspace:*` dependencies in `extension/package.json`. diff --git a/migration-tracking/technical-notes/script/v2-baseline.ts b/migration-tracking/technical-notes/script/v2-baseline.ts new file mode 100644 index 000000000000..4afd2fa9fa3c --- /dev/null +++ b/migration-tracking/technical-notes/script/v2-baseline.ts @@ -0,0 +1,96 @@ +#!/usr/bin/env bun +// Reproduces the pinned-baseline evidence in migration-tracking/technical-notes/baseline/pinned-v2-baseline.md. +// Ref verification is offline and always runs; --checks additionally replays the +// authoritative upstream v2 CI commands and writes their output to +// migration-tracking/technical-notes/baseline/logs (gitignored). + +import { $ } from "bun" +import { mkdir } from "node:fs/promises" +import path from "node:path" + +const PINNED_V2 = "76dbaf20adbd43fd208a00ef3cda4a51e125a234" +const ROOT = path.resolve(import.meta.dir, "../../..") +const LOGS = path.join(ROOT, "migration-tracking/technical-notes/baseline/logs") + +// Mirrors .github/workflows/typecheck.yml and the `unit` job of .github/workflows/test.yml +// at the pinned SHA. The Node build step is omitted: CI pins Node 26.4.0 for it, and the +// e2e job is skipped on the v2 branch by its own `if` condition. +const CHECKS = [ + { name: "install", cwd: ".", argv: ["bun", "install"] }, + { name: "typecheck", cwd: ".", argv: ["bun", "typecheck"] }, + { name: "test", cwd: ".", argv: ["bun", "turbo", "test", "--continue"], env: { GITHUB_ACTIONS: "false" } }, + { name: "client-check-generated", cwd: "packages/client", argv: ["bun", "run", "check:generated"] }, + { name: "www-check-generated", cwd: "packages/www", argv: ["bun", "run", "check:generated"] }, + { name: "cli-build", cwd: "packages/cli", argv: ["bun", "run", "script/build.ts", "--single", "--skip-install"] }, + { name: "cli-service-smoke", cwd: "packages/cli", argv: ["bun", "run", "script/service-smoke.ts"] }, +] + +const rev = async (ref: string) => (await $`git -C ${ROOT} rev-parse ${ref}`.quiet().nothrow()).stdout.toString().trim() +const mergeBase = async (a: string, b: string) => + (await $`git -C ${ROOT} merge-base ${a} ${b}`.quiet().nothrow()).stdout.toString().trim() + +console.log("# refs") +for (const ref of ["HEAD", "origin/main", "upstream/dev", "upstream/v2"]) + console.log(`${ref.padEnd(14)} ${await rev(ref)}`) + +console.log("\n# merge-bases") +console.log(`origin/main..upstream/v2 ${await mergeBase("origin/main", "upstream/v2")}`) +console.log(`upstream/dev..upstream/v2 ${await mergeBase("upstream/dev", "upstream/v2")}`) + +console.log("\n# ancestry") +const ahead = (await $`git -C ${ROOT} rev-list --count upstream/v2..HEAD`.quiet().nothrow()).stdout.toString().trim() +const assertions = [ + { + label: `HEAD contains pinned upstream/v2 ${PINNED_V2}`, + ok: (await $`git -C ${ROOT} merge-base --is-ancestor ${PINNED_V2} HEAD`.quiet().nothrow()).exitCode === 0, + required: true, + }, + { + label: "local upstream/v2 still points at the pinned SHA", + ok: (await rev("upstream/v2")) === PINNED_V2, + required: true, + }, + // A slice branch legitimately carries its own commits, so this one only reports. + { label: `HEAD carries ${ahead} commit(s) on top of upstream/v2`, ok: true, required: false }, +] +for (const assertion of assertions) console.log(`${assertion.ok ? "ok " : "FAIL"} ${assertion.label}`) + +if (assertions.some((assertion) => assertion.required && !assertion.ok)) { + console.log("\nbaseline ancestry broken; refusing to run checks") + process.exit(1) +} + +if (!Bun.argv.includes("--checks")) { + console.log("\npass --checks to replay the upstream v2 CI commands") + process.exit(0) +} + +await mkdir(LOGS, { recursive: true }) +console.log("\n# checks") +const results = [] +for (const check of CHECKS) { + const log = path.join(LOGS, `${check.name}.log`) + const started = Bun.nanoseconds() + const proc = Bun.spawn(check.argv, { + cwd: path.join(ROOT, check.cwd), + env: { ...process.env, ...check.env }, + stdout: "pipe", + stderr: "pipe", + }) + const [stdout, stderr, exitCode] = await Promise.all([ + new Response(proc.stdout).text(), + new Response(proc.stderr).text(), + proc.exited, + ]) + await Bun.write(log, `$ ${check.argv.join(" ")}\n(cwd ${check.cwd})\n\n${stdout}${stderr}`) + const seconds = ((Bun.nanoseconds() - started) / 1e9).toFixed(1) + results.push({ name: check.name, exitCode, seconds, log }) + console.log(`${exitCode === 0 ? "ok " : "FAIL"} ${check.name.padEnd(22)} ${seconds}s ${path.relative(ROOT, log)}`) +} + +// Known-red checks are reported, never suppressed: see migration-tracking/technical-notes/baseline/pinned-v2-baseline.md +// for the failures that pristine upstream v2 already has at this SHA. +console.log( + "\ncompare failures against migration-tracking/technical-notes/baseline/pinned-v2-baseline.md before attributing them to Kilo changes", +) +process.exit(results.some((result) => result.exitCode !== 0) ? 1 : 0) diff --git a/migration-tracking/technical-notes/session-sharing.md b/migration-tracking/technical-notes/session-sharing.md new file mode 100644 index 000000000000..1b43b259a7fe --- /dev/null +++ b/migration-tracking/technical-notes/session-sharing.md @@ -0,0 +1,92 @@ +# V2 session sharing contract and remaining gates + +Source audit: 2026-09-05, sibling `cloud` repository. The adapter targets its +cached `origin/main` at `08c4887fa68738f19089284101084f404eb6c9b8` (2026-09-03), +read with `git show`. The clean checkout at +`c32be17d69e5340ab804b8e1f32ff7298fc91ee9` (2026-08-12) is 407 commits behind: +it returns UUID `public_id` and disables public reads. Later commits +`b0997ec63` and `211b1f17b` restore sharing with purpose-bound JWT tokens. +Neither source revision proves what is deployed. No live sharing smoke test was +performed, and no cloud checkout files were changed. + +## Current backend contract + +Paths below are relative to `cloud/services/session-ingest/src/`. + +| Operation | Verified contract | Source | +|---|---|---| +| Bootstrap | `POST /api/session` with `{ sessionId }`; returns `{ id, ingestPath }` | `routes/api.ts` | +| Upload | `POST ingestPath` with `{ data: [{ type, data }] }`; stored session data is loose, message data requires an `id` | `types/session-sync.ts`, `ingest/validate.ts` | +| Share | `POST /api/session/:sessionId/share`; returns `{ success: true, share_token }`, a purpose-bound JWT | `routes/api.ts`, `services/session-share-token.ts` | +| Unshare | `POST /api/session/:sessionId/unshare` | `routes/api.ts` | +| Public read | `GET /session/:shareToken`; backend verifies token purpose/signature and current share generation | `app.ts`, `services/session-share-token.ts` | +| Export body | `{ info, messages: [{ info, parts }] }` | `dos/SessionIngestDO.ts`, `getAllStream()` | + +The initial local sharing fixture used flat message exports. The gateway and +fixtures now use the nested export envelope and retain the newer `share_token` +contract. The client checks token transport syntax only; authentication and +revocation remain backend responsibilities. + +V2 messages are self-contained. Fork-from-share validates each nested `info` +against the v2 message schema and requires an empty `parts` array. Legacy +transcripts and separate parts are rejected rather than silently dropped. The +import creates new session and message IDs and removes source parent/fork/revert +links before calling the existing host import service. Assistant snapshot +references are also removed: export transfers no snapshot trees, so retaining +source hashes would make destination undo attempt to restore missing trees. + +A snapshot upload sends session and message facts only. It does not manufacture +an `idle` status: the exported idle time is a historical watermark, not the +current execution state. Disabling sharing or ingestion prevents new uploads, +but still permits unsharing an existing link. + +## Preview controls + +The Kilo-owned TUI plugin supplies `/share`, `/unshare`, and +`/fork-from-share` (alias `/fork-share`) through the existing keymap priority +seam. Sharing asks for confirmation before uploading the transcript, tool +output, attachments, and local paths. Fork accepts a public URL/token, imports +into the active project, and navigates to the new session. These controls do +not make the remaining cloud/viewer gates complete. + +## Remaining gates + +- Verify the target deployment uses the newer token contract and public read + route. Local fixtures do not establish deployed behavior. +- Update the public viewer for v2 messages. At the audited `origin/main`, + `apps/web/src/app/s/[sessionId]/shared-transcript.ts` requires + `message.info.role` to be `user` or `assistant` and renders separate parts. + V2 uses `type` and self-contained content; the current viewer drops those + messages even though the ingest storage schema accepts them. +- Preserve team ownership before enabling team sharing. Bootstrap creates + personal ownership; only a validated `kilo_meta.orgId` claim changes it. + Current snapshots do not emit that item. Use the actual selected organization + UUID and its membership policy, not session metadata or a guessed default. + The preview now rejects sharing when the actual account selection is a team + or unavailable, before bootstrap or upload. Personal sharing and unsharing + remain available. +- Implement post-share updates only after the backend contract is settled, + including transcript removals/reverts and actual execution status. Ingest + upserts fixed message/part identities; omitted messages are retained, and + there is no per-item tombstone. Re-uploading a snapshot is not replacement. + `?v=2` selects lifecycle handling, not an OpenCode v2 transcript schema. + Session/message-only uploads do not change lifecycle alarms; no synthetic + `session_open`, `session_close`, or status items are sent to change that. + Current uploads are explicit snapshots, not continuous synchronization. +- Validate deployed behavior with an intentionally shareable fixture before + marking the plan's sharing inventory row done. + +## Local validation + +- Gateway: 30 tests, 232 assertions; package typecheck and lint pass. +- Interactive host integration: 79 assertions, including rejected team sharing + with zero cloud session calls and a fork that continues in another directory. +- Terminal sharing fixtures: three cases, covering cancel, successful + transcript import/navigation, and revocation. Existing account/login/branding + terminal fixtures also pass (three cases). +- CLI package typecheck passes. Runtime tests use the embedded Bun 1.4.0. +- Independent review confirmed the snapshot cleanup and account gate; no + additional high/medium correctness issue was found in those changes. + +These checks use local servers and do not publish real sessions. No Protocol +or Server HttpApi was changed, and no generated client files were edited. diff --git a/migration-tracking/technical-notes/v2-fork-conventions.md b/migration-tracking/technical-notes/v2-fork-conventions.md new file mode 100644 index 000000000000..8e96b7e88bbe --- /dev/null +++ b/migration-tracking/technical-notes/v2-fork-conventions.md @@ -0,0 +1,168 @@ +# Kilo OpenCode v2 fork conventions + +Rules for working on the Kilo product line built on `upstream/v2`. Companion to +`migration-tracking/plans/kilo-opencode-v2-plan-progress.md` (scope and phases) and +`migration-tracking/technical-notes/baseline/pinned-v2-baseline.md` (refs, commands, known-red checks). + +These rules govern the v2 line only. Kilo `origin/main` keeps its own +conventions. + +## Upstream snapshot tracking + +- The v2 line is based on a **pinned `upstream/v2` SHA**, currently + `76dbaf20adbd43fd208a00ef3cda4a51e125a234`. `upstream` is + `https://github.com/anomalyco/opencode.git`. +- Never treat "latest `upstream/v2`" as the base. Every slice records the SHA it + was validated against. +- To adopt a newer upstream snapshot: fetch, record the new SHA and its merge + base in `migration-tracking/technical-notes/baseline/pinned-v2-baseline.md`, merge it into the + integration branch, re-run the baseline checks, and diff the failure list + against the previous baseline before declaring the bump clean. +- Snapshot every upstream SHA the branch has consumed. If a recorded SHA stops + being reachable from `upstream/v2`, upstream rewrote history: stop, assess a + new integration base, and do not force-push the Kilo branch to match. +- `upstream/dev` is a sibling line, not an ancestor. `upstream/dev` and + `upstream/v2` diverged at `0e2dd4ad`; do not assume a dev fix reached v2. + +## Patched upstream dependencies + +- `virtua@0.49.1` is catalog-pinned for the preview packages (`kilo-vscode`, + `kilo-ide-ui`). `main` also carries `patches/virtua@0.49.1.patch`, which adds + `VirtualizerHandle.measure()` for the VS Code transcript handoff-flicker fix + (`fix(vscode): prevent transcript handoff flicker`). That fix is not in this v2 + port, so the patch is intentionally not applied here. Do not bump `virtua` + without porting the patch and its `handle.measure()` call site together. + +## Branches + +- **Integration branch:** `johnnyeric/kilo-opencode-v2`. Long-lived, based on + the pinned snapshot. One clean worktree owns it. It is not a feature + worktree. +- **Slice branches:** `johnnyeric/v2-`, branched from the current + integration branch, one writing agent each. This slice is + `johnnyeric/v2-baseline`. +- Slice work targets `johnnyeric/kilo-opencode-v2`. Never `main`, never + `upstream/v2`. +- **Merge forward, never rebase the integration branch.** Merge `upstream/v2` + and merged slices into it; its history is shared and must stay stable. An + unmerged slice branch may be rebased or recreated freely. +- Upstream's root `AGENTS.md` asks for short slash-free branch names such as + `session-recovery`. The Kilo fork overrides that: use the `johnnyeric/` + prefix so fork branches are distinguishable from upstream branches in a + shared checkout. Upstream's conventional-commit rule (`type(scope): summary`, + types `feat|fix|docs|chore|refactor|test`) does apply and matches Kilo's. + +## Dev-fix triage lane + +Fixes landing on `upstream/dev` or Kilo `origin/main` do not reach the v2 line +automatically. Each candidate gets an explicit classification before any port: + +- `not-applicable` — the v2 architecture removed the affected code path. +- `already-fixed` — upstream v2 fixed it independently; record the v2 commit. +- `port` — re-implement against v2. Cherry-pick only when the commit is + isolated and its provenance and dependencies are understood; re-implementing + is the default. +- `defer` — real on v2 but out of the current phase; record why. + +Track these in the parity ledger's dev-line lane, not in commit messages. + +## Kilo-owned paths + +Prefer new Kilo-owned files over edits to upstream files. Conflicts on every +upstream merge are the cost of shared-file edits. + +| Path | Purpose | +|---|---| +| `migration-tracking/technical-notes/` | Fork documentation and tooling that is not part of any package: this file, `baseline/`, `script/`. | +| `packages//src/kilocode/` | Kilo modules inside an upstream package, following Kilo main's existing convention. | +| `packages//test/kilocode/` | Their tests. | +| `plans/` | Shared with upstream, but Kilo plan documents are new files only — never edit an upstream plan. | + +Do not fork or rename an upstream package to add Kilo behavior. Do not +reconstruct the legacy `packages/opencode` tree; it does not exist on v2. + +### Markdown tables in Kilo-owned files + +Kilo main's `AGENTS.md` forbids padded markdown tables, and that rule carries to +the v2 line. Use the compact form: a minimal separator row and exactly +single-space-padded content cells. + +``` +| Command | What it runs | +|---|---| +| `bun typecheck` | Every package, through turbo. | +``` + +Not the padded form, which makes a one-cell edit rewrite every row and blows up +the diff: + +``` +| Command | What it runs | +| ----------------------------- | ------------------------------ | +| `bun typecheck` | Every package, through turbo. | +``` + +Unlike Kilo main, this tree does **not** exclude markdown from prettier — its +`.prettierignore` lists four generated paths and nothing else — so +`prettier --write` over a Kilo-owned document will re-pad every table. Do not run +it over `kilocode/` or Kilo plan documents. Nothing enforces this yet: no v2 +workflow runs prettier, and `script/check-md-table-padding.ts` is a Kilo main +script with no v2 equivalent, so the rule is currently upheld by review. + +This applies to Kilo-owned markdown only. Leave upstream documents formatted the +way upstream formats them. + +The only shared upstream file this slice edits is the root `AGENTS.md`, which +gains a marked two-line pointer to this document and to the baseline report. +That is the intended shape of a shared hook: without it, agents in this +checkout follow upstream's conventions and never find these rules. + +## Narrow shared hooks + +When a Kilo capability genuinely needs upstream code to call it: + +- Add the smallest possible registration or dispatch point, and put the logic in + a Kilo-owned module the hook imports. +- Do not refactor, reformat, or restructure surrounding upstream code in the + same change. A hook diff should be readable as a few added lines. +- Respect v2's dependency direction — Schema to Core/Protocol to Server, then + generated clients. Client runtime code may depend on Schema and Protocol, + never on Core or Server. +- Never hand-edit generated output (`packages/client/src/generated*`, + `packages/protocol/openapi.json`, `packages/www/openapi.json`). Regenerate + with `bun run generate` from `packages/client` and report generated diffs + separately from handwritten ones. + +## `kilocode_change` markers + +- Mark every Kilo edit inside a shared upstream file with a `kilocode_change` + comment saying what changed and why, so upstream merges show the intent. +- Do not mark Kilo-owned files. A file under `kilocode/` or `src/kilocode/` is + Kilo by construction; markers there are noise. This also applies to Kilo-owned + packages such as `packages/kilo-ide-ui`, including source relocated from a + shared v1 package. Record the destination package/file in the v1-to-v2 mapping + table and remove inherited markers, retaining useful explanatory comments. +- Preserve existing markers when resolving an upstream merge. Resolve in favour + of the current v2 architecture first, then reapply the Kilo invariant through + its owned seam. +- Removing a marker from a shared upstream file requires explaining whether its + behavior was removed or relocated. Removing redundant markers from Kilo-owned + files does not remove the behavior; record that distinction in the report. + +## Validation + +The authoritative commands come from this v2 tree, not from Kilo main, and are +listed in `migration-tracking/technical-notes/baseline/pinned-v2-baseline.md`. In short: `bun install`, +`bun typecheck`, `GITHUB_ACTIONS=false bun turbo test`, `bun run check:generated` +in `packages/client` and `packages/www`, and the `packages/cli` build plus +service smoke. + +- Tests cannot run from the repo root; use `bun turbo test` or a package + directory. +- Use `bun typecheck`, never `tsc` directly. +- Pristine upstream v2 is already red at the pinned SHA. Before attributing a + failure to a slice, check it against the failure inventory in + `migration-tracking/technical-notes/baseline/pinned-v2-baseline.md`, or re-run + `bun migration-tracking/technical-notes/script/v2-baseline.ts --checks` on a clean checkout of the pin. +- Report handwritten and generated diffs separately, and never report a check as + passing without having run it. diff --git a/migration-tracking/test-plans/kilo-opencode-v2-test-plan-runtime.md b/migration-tracking/test-plans/kilo-opencode-v2-test-plan-runtime.md new file mode 100644 index 000000000000..ac56ba7b08e3 --- /dev/null +++ b/migration-tracking/test-plans/kilo-opencode-v2-test-plan-runtime.md @@ -0,0 +1,1424 @@ +# Kilo OpenCode v2 runtime test plan + +Owned by the runtime test-plan delegate for thread `thr_qr9z7p3cqc` (Bootstrap +OpenCode v2 baseline). Companion to the main index +`plans/kilo-opencode-v2-test-plan.md` (not edited here) and the separately +authored `plans/kilo-opencode-v2-test-plan-ui.md`. Scope rules follow +`kilocode/v2-fork-conventions.md`; validation commands follow +`kilocode/baseline/pinned-v2-baseline.md`. + +- **Status of every case: NOT RUN.** This is a source-checked manual plan; no + case below has been executed. Nothing in this document reports a PASS. +- **Source basis:** this checkout, HEAD `82040801cd253665d2785c290031d307a18a0d64` + on the shared baseline worktree, **with in-flight uncommitted changes** to + `packages/kilo-gateway` (plugin/models/gateway), `packages/kilo-cli` + (`remote-session.ts`, `remote-protocol.ts`, `settings*`, `model-picker.ts`, + `plan-policy.ts`, `import-v1-config.ts`, `interactive-server.ts`) and their + tests. Cases touching those areas are marked + **PENDING-VERIFICATION**; expected text is quoted from current source but may + move with the in-flight fixes. +- **No live accounts, no paid inference, no deployed endpoints by default.** + Cases needing a real account, a deployed cloud/relay/share service, or paid + model inference are explicitly marked **USER-OPT-IN** and are never run by + default. All other cases are local **FIXTURE** cases (loopback servers, + fake models, disposable stores). +- **Deferred surfaces:** VS Code and JetBrains extension testing is deferred to + their own plans. TUI widget-level rendering belongs to the UI sibling plan; + this plan covers host/CLI/runtime behavior only, including the runtime + behavior of slash commands where the source is host-owned. +- **Case count: 81** (RT-001 … RT-081, all IDs allocated and globally unique; + RT-081 is documented as cross-listed into §2). Priorities: + P1 = correctness/security-critical, P2 = important behavior, P3 = secondary + or platform-gated. + +## How to read a case + +### Review-driven acceptance checks — 2026-09-06 + +These extend the existing remote and packaging scenarios; they do not add case +IDs or change the count. Every manual check here remains **NOT RUN**. Automated +evidence is separate: accepted R1/R2 has 9 tests / 211 assertions and clean +package typecheck. The newer transcript and persistent-PTY deltas are under +correction, not accepted merely because their earlier tests passed. + +| Surface | Required observation before acceptance | +|---|---| +| Root-scoped remote interactions | Reply and reject child-owned permission/question requests using the root session ID, as the cloud consumer does. The child pending state must settle. Unrelated, foreign, moved-lineage and ninth-edge requests remain pending/refused; eighth-edge requests work. | +| Optional remote namespaces | For permission-only and form-only clients, create fresh pending items while subscribed. Supported events appear and are answerable; unsupported events never appear. Repeat on replay, including independent list failure and reconnect during replay. | +| Transcript terminal state | Completed assistant, reasoning and successful tool frames stop the consumer's streaming indicators. Tool failure is terminal too. Validate real final state, not just frame presence. | +| Transcript identity and live output | Mixed reasoning/text preserve the same part IDs from live updates through final reconciliation. Partial output appears before completion; full-value re-emission does not duplicate text. | +| Transcript history | A session with more than 50 messages still forwards new messages. A fresh adapter on an existing session recovers the real parent user without needing to observe its original delivery event. | +| Portable launcher | Invoke the relocated artifact's actual `kilo2` launcher through a supported host-start command and verify readiness. Calling imported `launch()` or printing `--version` alone does not prove launcher host startup. | +| Persistent PTY cleanup | Capture output through the public websocket. Verify owned terminal and daemon process exit after success and an induced post-create failure. A successful shutdown RPC alone is insufficient. The separate session-terminal PTY path remains unverified. | + +Evidence paths: `packages/kilo-cli/test/remote-session.test.ts`, +`test/remote-transcript.test.ts`, `test/portable.test.ts` and +`script/portable-pty-smoke-entry.ts` (the latter three relative to +`packages/kilo-cli`). All runs use the shared isolated setup below; never +terminate processes that the scenario did not create. + +Each case has: ID, title, priority, classification +(`FIXTURE` local-only / `USER-OPT-IN` live account or deployed service / +`PENDING-VERIFICATION` in-flight source), prerequisites, numbered steps, +expected results (quoted exactly from current source — never invented), +cleanup, and evidence (source file:line plus the automated command that +already exercises the nearest equivalent, as evidence only — not a substitute +for the manual run). Known limitations are stated per case or in the section +header. + +## Shared prerequisites (SP) — apply to every case + +Environment isolation reuses the root-reviewed child-scoped setup from the UI +sibling plan, `plans/kilo-opencode-v2-test-plan-ui.md` ("Shared Test Setup & +Environment Isolation"), reproduced here with the launcher/runtime paths this +plan needs. The calling shell is never modified: no `export HOME`, no global +environment changes. **Every** `kilo2`, daemon, and fixture command in every +case below runs inside a fresh `env -i` child, so inherited credentials +(`KILO_*`, `OPENCODE_*`, provider tokens in the ambient environment) are +cleared by construction and only values a case explicitly approves are added +(prefix them with `env VAR=…` inside the wrapper invocation). + +```sh +# Run from the repository root. Keep the calling shell unchanged. +CKPT="$PWD" # this checkout, absolute +KILO_BIN="$CKPT/packages/kilo-cli/dist/interactive/kilo2" # absolute interactive launcher +KILO_HEADLESS="$CKPT/packages/kilo-cli/dist/kilo2" # absolute headless compiled binary +KILO_RUNTIME="$CKPT/packages/kilo-cli/dist/interactive/bun" # absolute bundled Bun ≥1.4 runtime (SP-1) +KILO_TEST_ROOT="$(mktemp -d /tmp/kilo-rt-test-XXXXXX)" # disposable, owned by this run +KILO_TEST_HOME="$KILO_TEST_ROOT/home" +KILO_TEST_PROJECT="$KILO_TEST_ROOT/project" +mkdir -p "$KILO_TEST_HOME" "$KILO_TEST_PROJECT" "$KILO_TEST_ROOT/tmp" + +# Child-scoped launcher (runtime-plan equivalent of the UI sibling's kilo_test): +# subprocess-only isolation; the launcher is passed explicitly so the headless +# binary and bundled runtime can use the same wrapper. +kilo_test() { + env -i PATH="$PATH" TERM="${TERM:-xterm-256color}" LANG="${LANG:-en_US.UTF-8}" \ + HOME="$KILO_TEST_HOME" USERPROFILE="$KILO_TEST_HOME" \ + XDG_DATA_HOME="$KILO_TEST_HOME/data" XDG_CONFIG_HOME="$KILO_TEST_HOME/config" \ + XDG_CACHE_HOME="$KILO_TEST_HOME/cache" XDG_STATE_HOME="$KILO_TEST_HOME/state" \ + TMPDIR="$KILO_TEST_ROOT/tmp" "$@" +} +# Example: kilo_test "$KILO_BIN" run --directory "$KILO_TEST_PROJECT" "hello" +# Case-approved env: kilo_test env KILO_ACP_SERVER_PASSWORD= "$KILO_BIN" acp +``` + +In the cases below, a command written as `"$KILO_BIN" …`, `"$KILO_HEADLESS" …`, +or `"$KILO_RUNTIME" …` means call it through `kilo_test` (prefixing only the +case-approved environment values with `env`), so isolation, credential +scrubbing, and the child-scoped `TMPDIR` hold for every command. Nothing in +this plan sets `HOME` in the calling shell; the clean child environment +intentionally omits live credentials. + +- **SP-1 Fresh artifacts (bundled runtime only):** this host's ambient `bun` + is 1.3.14 while the host's own gate requires Bun ≥ 1.4 + (`packages/kilo-cli/src/runtime.ts`); never build or test with bare `bun`. + If `dist/interactive/bun` is absent, bootstrap once with an + operator-supplied explicitly named Bun ≥ 1.4 binary (`BUN14=`): `cd "$CKPT/packages/kilo-cli" && "$BUN14" run script/build.ts && + "$BUN14" run script/build-tui.ts` (`build:tui` also builds the ACP bridge + via `script/build-acp.ts`). Then set `KILO_RUNTIME` to the produced + `dist/interactive/bun`; every later build and test invocation goes through + it (`"$KILO_RUNTIME" run script/build.ts`, `"$KILO_RUNTIME" run + script/build-tui.ts`). `test/gateway-protocol.test.ts` and the cloud + subprocess/stream tests require these artifacts to exist. +- **SP-2 Isolation:** every launch goes through `kilo_test …` so + the host lays out under the child's `XDG_*`/`kilo2` roots. Never point + `KILO_DB`, `OPENCODE_DB`, `KILO_CONFIG_DIR`, `OPENCODE_CONFIG_DIR`, + `OPENCODE_CONFIG` at the layout — the preview host deliberately ignores + them (`test/preview.test.ts`); the clean child environment omits them + entirely. +- **SP-3 Cleanup (case-owned only):** terminate only the processes the case + itself started — a TUI/`serve` with Ctrl-C or SIGTERM; `"$KILO_BIN" service + stop` **only** in cases that own a daemon (started one, or killed one: + RT-005, RT-006, RT-007, RT-008, RT-009, RT-010); stop fixture servers the + case launched. Then print `$KILO_TEST_ROOT`, inspect that exact directory, + and move **only that resolved path** to Trash (e.g. + `mv "$KILO_TEST_ROOT" ~/.Trash/` with the variable expanded and visually + confirmed first). Never remove a path through an unresolved variable, never + `rm -rf` an unexpanded variable, and never change the calling shell's + `HOME`. +- **SP-4 Store exclusivity:** one-shot commands (`run`, `sessions`, + `telemetry`, `import-v1`, `cloud`) refuse to run while the TUI or a daemon + holds the `kilo2-interactive` store lock (`src/interactive-server.ts:73-84`, + `packages/util/src/flock.ts`). Between host cases, close the TUI/daemon the + previous case started (SP-3). +- **SP-5 Automated evidence commands (bundled runtime only, never repo + root):** `cd "$CKPT/packages/kilo-cli" && "$KILO_RUNTIME" run script/test.ts + test/.ts` (wraps `script/test.ts`, builds a fresh artifact, sets + `KILO_CLI_TEST_ARTIFACT_DIR` and `KILO_ACP_ARTIFACT`), or bundled-runtime + single files, e.g. `"$KILO_RUNTIME" --no-env-file test + test/resume-external.test.ts`. +- **SP-6 No network beyond loopback** unless a case is marked USER-OPT-IN. + Fake models/embedders/gateways/relays are loopback `Bun.serve` fixtures + started in the same child scope (`kilo_test`, or an `env -i` child sharing + the same `TMPDIR`). + +## 1. Host, daemon lifecycle, and path isolation (RT-001 … RT-011) + +Sources: `packages/kilo-cli/src/daemon.ts`, `daemon-entry.ts`, `paths.ts`, +`auth.ts`, `interactive-server.ts`, `storage.ts`, `commands.ts`, +`tui-preview.ts`, `script/build.ts`, `script/build-tui.ts`, +`script/coexistence.ts`; upstream contrast in `packages/cli`. + +### RT-001 · Fresh compiled artifacts build and identify themselves — P1 · FIXTURE · NOT RUN +- Prereq: SP-1; Bun ≥ 1.4.0. +- Steps: 1) Run SP-1. 2) `test -x "$KILO_HEADLESS" && test -x "$KILO_BIN" && test -x "$KILO_RUNTIME"`. + 3) `"$KILO_HEADLESS" --version`; `"$KILO_BIN" --version`; `"$KILO_BIN" --help`. +- Expected: all three paths exist; `--version` prints the package version on + both launchers; `--help` prints the usage block from + `packages/kilo-cli/src/commands.ts:9-106`, including the line that chat + should use `packages/kilo-cli/dist/interactive/kilo2`. +- Cleanup: SP-3 (no host was started). +- Evidence: `packages/kilo-cli/script/build.ts:19-41`, + `script/build-tui.ts:16-28`, `test/compiled-artifact.test.ts`. + +### RT-002 · `serve` surface contract on both launchers — P1 · FIXTURE · NOT RUN +- Prereq: SP-1, SP-2. +- Steps: 1) `"$KILO_BIN" serve --port 9999` (expect refusal). 2) `"$KILO_BIN" serve` in + background; capture stdout. 3) `"$KILO_HEADLESS" serve --port 0` in background; capture + stdout; curl the printed URL unauthenticated. +- Expected: interactive `serve` rejects `--port` ("Unknown option"); + interactive serve prints `URL:` and `Password file:` lines + (`src/tui-preview.ts:142-147`); headless `serve --port` is accepted + (`src/index.ts:5-40`); unauthenticated request to the interactive URL is + rejected (Basic auth required). +- Cleanup: stop both servers (SIGTERM), SP-3. +- Evidence: `src/commands.ts:239-259`, `src/server.ts:53-76`, + `test/commands.test.ts:155`. + +### RT-003 · Fresh profile path isolation — P1 · FIXTURE · NOT RUN +- Prereq: SP-2. +- Steps: 1) With SP env, run `"$KILO_BIN" telemetry status` (creates nothing) then + `"$KILO_HEADLESS" paths` and a short `serve`/Ctrl-C. 2) Enumerate `$KILO_TEST_ROOT` recursively. + 3) Re-run with `KILO_DB="$KILO_TEST_ROOT/decoy.db" OPENCODE_DB="$KILO_TEST_ROOT/decoy2.db"` + and confirm no `decoy` files appear. +- Expected: layout appears only under `$XDG_*`/`kilo2` roots + (`src/paths.ts:9-33`); no `~/.kilo`, `~/.opencode`, `auth.json`, or + `opencode-next.db` anywhere; the upstream-style env overrides are ignored by + the host. +- Cleanup: SP-3. +- Evidence: `src/paths.ts:37-92` (preflight), `src/storage.ts:10-58`, + `test/preview.test.ts:41-68`. + +### RT-004 · Layout preflight refuses unsafe roots — P2 · FIXTURE · NOT RUN +- Prereq: SP-2 (do not pre-create the forbidden layout; forbidden roots are + simulated inside the disposable tree). +- Steps: 1) Launch with the child env `XDG_DATA_HOME="$KILO_TEST_HOME/.local/share"` + (`kilo_test env XDG_DATA_HOME=… "$KILO_BIN" serve`); expect refusal + mentioning it refuses paths inside stable home roots. 2) Create a symlinked + data dir inside the tree and retry. 3) Launch with the child env + `KILO_CONFIG_DIR="$KILO_TEST_HOME/config"` while `XDG_CONFIG_HOME` points + inside that same directory, and retry. +- Expected: `preflight()` refuses with its fixed messages + (`src/paths.ts:46-54`), refusing symlinked components and any path inside + `$HOME/.local/share|state`, `~/.config`, `~/.cache`, or inside the absolute + values of `KILO_DB`/`KILO_CONFIG_DIR`/`OPENCODE_*`. +- Cleanup: SP-3. +- Evidence: `src/paths.ts:37-92`. + +### RT-005 · Daemon start / status / attach, and no-restart verb — P1 · FIXTURE · NOT RUN +- Prereq: SP-1, SP-2. +- Steps: 1) `"$KILO_BIN" service start`; capture JSON. 2) `"$KILO_BIN" service status`. + 3) `"$KILO_BIN" attach` then quit the TUI. 4) `"$KILO_BIN" service restart`. 5) Compare + with upstream `opencode2 service --help` verb list. +- Expected: start prints status JSON `{state:"running", file, pid, version, + url}` and **never** the password (`tui-preview.ts:71-94`); status healthy; + attach opens the TUI on the running daemon and leaves it running on exit; + `service restart` throws `Usage:` (only `start|status|stop` exist, + `src/commands.ts:220-225`); upstream `opencode2` exposes + `start|restart|status|stop|get|set|unset` (`packages/cli/src/commands/commands.ts:363-404`) + — a documented divergence, not a bug. +- Cleanup: `"$KILO_BIN" service stop`, SP-3. +- Evidence: `src/daemon.ts`, `test/daemon.test.ts:154-159`, + `test/commands.test.ts:144-148`. + +### RT-006 · Stale PID after kill -9 — P1 · FIXTURE · NOT RUN +- Prereq: RT-005 state (running daemon). +- Steps: 1) `kill -9 ` (registration file at + `$XDG_STATE_HOME/kilo2/interactive/daemon.json`). 2) `"$KILO_BIN" service status`. + 3) `"$KILO_BIN" service stop`. 4) `"$KILO_BIN" service start`. +- Expected: status reports `state:"stale"`, `alive:false`, `verified:false`; + `stop` refuses ("Refusing to stop an unverified Kilo daemon registration"); + a fresh `start` deletes the stale registration and spawns a replacement. +- Cleanup: `"$KILO_BIN" service stop` (this case owns the replacement daemon + it started), then SP-3. +- Evidence: `src/daemon.ts:93-95,133-147`, `test/daemon.test.ts:193-223`. + +### RT-007 · Unverified live registration is never replaced — P1 · FIXTURE · NOT RUN +- Prereq: running daemon from RT-005. +- Steps: 1) Corrupt the `password` field in `daemon.json`. 2) + `"$KILO_BIN" service start`. 3) `"$KILO_BIN" service stop`. 4) Re-read `daemon.json` and + confirm the original PID is still alive (`ps -p `). +- Expected: start fails closed with "Refusing to replace an unverified Kilo + daemon registration (pid N is running)" (`src/daemon.ts:85-92`); stop also + refuses; the live foreign-owned daemon is never signaled or replaced. +- Cleanup: kill the daemon by its PID from `ps`, SP-3. +- Evidence: `src/daemon.ts:75-106,188-197`, `test/daemon.test.ts:243-293`. + +### RT-008 · Registration-file hardening — P2 · FIXTURE · NOT RUN +- Prereq: SP-2; run `"$KILO_BIN" service start` once to observe the healthy + file, then stop it (`service stop`) before mutating. +- Steps (repeat per mutation, restoring after each): replace `daemon.json` + with (a) a symlink, (b) mode 0644, (c) `url` with an explicit port or https + scheme, (d) `password` that is not 64 hex chars; then `service status` / + `service start`. +- Expected: read refuses each non-conforming shape fail-closed + (`src/daemon.ts:201-222`: symlink, non-regular/hardlink, mode not 0600, + non-loopback URL, password not 64-hex); nothing is auto-repaired; each + `service start` attempt is refused and starts no daemon. +- Cleanup: `"$KILO_BIN" service stop` (case-owned: the prerequisite daemon), + then SP-3. +- Evidence: `src/daemon.ts:201-222`. + +### RT-009 · Store lock exclusivity for one-shot commands — P1 · FIXTURE · NOT RUN +- Prereq: running daemon from RT-005. +- Steps: 1) `"$KILO_BIN" sessions` while the daemon runs. 2) `"$KILO_BIN" service stop`. + 3) `"$KILO_BIN" sessions` again. 4) In two shells, launch two concurrent `serve` + instances against the same layout. +- Expected: (1) refuses with the "stop the TUI or daemon before running them" + guidance (`src/commands.ts:59-60`); (3) succeeds; (4) the second instance + reports the flock contention error + ("Timed out waiting for lock: kilo2-interactive", `packages/util/src/flock.ts:293`). +- Cleanup: Ctrl-C/SIGTERM the surviving `serve` instance (case-owned), then + SP-3. +- Evidence: `src/interactive-server.ts:73-84`, `test/daemon.test.ts:231-236`. + +### RT-010 · Sessions survive daemon stop → start — P2 · FIXTURE · NOT RUN +- Prereq: SP-1, SP-2, plus a fake-model config (see RT-012 prereq) so `run` + can create a session without paid inference. +- Steps: 1) `"$KILO_BIN" run --directory "$KILO_TEST_ROOT/proj" "hello"` (fixture model). + 2) `"$KILO_BIN" sessions`. 3) `"$KILO_BIN" service stop`. 4) `"$KILO_BIN" service start`. + 5) `"$KILO_BIN" sessions`. +- Expected: the session ID is listed before and after the restart cycle + (`test/daemon.test.ts:170-186` proves the equivalent). +- Cleanup: `"$KILO_BIN" service stop` (case-owned: the daemon restarted in + step 4), then SP-3. +- Evidence: `src/daemon.ts:96-106`, `packages/client/src/promise/service.ts:85-116`. + +### RT-011 · Coexistence with stable Kilo (fixture script) — P3 · FIXTURE · NOT RUN +- Prereq: SP-1; a stable (v1) `kilo` binary path supplied by the operator; + no real account. +- Steps: 1) + `cd "$CKPT/packages/kilo-cli" && "$KILO_RUNTIME" script/coexistence.ts "$KILO_BIN"`. + 2) Read the script's report lines. +- Expected: script passes: the preview store boots empty with no legacy + `session` table, the stable DB + WAL sidecars keep identical SHA-256 + fingerprints, the stable session remains readable, and the preview exits 0 + on SIGTERM (`script/coexistence.ts:35-98`). +- Cleanup: SP-3 (the script builds its own fake HOME/XDG tree). +- Evidence: `script/coexistence.ts`, `package.json` `test:coexistence`. + +## 2. `run` command (RT-012 … RT-020) + +Shared case prerequisite **RM-1 (fixture model):** start a loopback fake +OpenAI-compatible model server (as the package's own fixtures do, e.g. +`test/routed-model-integration-fixture.ts`) and a profile config JSONC in +`$XDG_CONFIG_HOME/kilo2/interactive/kilo.jsonc` pointing a provider at it, so +`run` completes without paid inference. Record the fixture port for cleanup. +Sources: `packages/kilo-cli/src/run.ts`, `run-input.ts`, `tui-preview.ts`, +`commands.ts`, `session-target.ts` (upstream only). + +### RT-012 · Basic run output contract — P1 · FIXTURE · NOT RUN +- Prereq: SP-2, RM-1. +- Steps: 1) `"$KILO_BIN" run --directory "$KILO_TEST_ROOT/proj" "say hi"`; capture stdout, + stderr, exit. 2) Same with `--format json`. 3) `"$KILO_BIN" run` with no prompt + and empty stdin. +- Expected: final assistant text on stdout, `Session: ` on stderr, exit 0; + `--format json` prints exactly one `{sessionID, text}` object + (`tui-preview.ts:187-199`); empty input fails before opening the store with + "You must provide a message" (`src/run-input.ts:23`) and nonzero exit. +- Cleanup: SP-3. +- Evidence: `src/run.ts`, `test/commands.test.ts:624-641`. + +### RT-013 · Stdin merging — P2 · FIXTURE · NOT RUN +- Prereq: SP-2, RM-1. +- Steps: 1) `printf 'second line' | "$KILO_BIN" run "first part"`. +- Expected: the admitted prompt contains both parts, positional first, piped + stdin appended after a newline (stdin is read to EOF only when not a TTY, + `src/tui-preview.ts:36-41`, merge in `src/run-input.ts:29-34`). +- Cleanup: SP-3. +- Evidence: `src/run-input.ts`, `src/tui-preview.ts:36-41`. + +### RT-014 · Attachment validation — P1 · FIXTURE · NOT RUN +- Prereq: SP-2, RM-1. +- Steps: 1) `--file "$KILO_TEST_ROOT/small.png"` (tiny valid image) with a prompt. + 2) `--file` a 11 MiB file. 3) `--file https://example.com/x.png`. 4) `--file` + a FIFO created with `mkfifo`. 5) `--file` a directory. +- Expected: (1) succeeds with the attachment as a `data:` URI FilePart; + (2) rejected (limit `ATTACH_FILE_MAX_BYTES` = 10 MiB, `src/run-input.ts:12`); + (3) rejected (URLs rejected); (4) rejected (regular-file + O_NONBLOCK guard, + `src/run-input.ts:84-118`); (5) rejected. +- Cleanup: SP-3. +- Evidence: `src/run-input.ts`. + +### RT-015 · Permission behavior without and with `--auto` — P1 · FIXTURE · NOT RUN +- Prereq: SP-2, RM-1; fixture model prompts the shell tool. +- Steps: 1) `"$KILO_BIN" run --directory "$KILO_TEST_ROOT/proj" ""` without `--auto`. 2) Repeat with `--auto`. +- Expected: (1) the request is auto-replied `reject` and interrupted; stderr + blocker "Permission requested: (); auto-rejected. + Re-run with --auto to allow it once."; nonzero exit + (`src/run.ts:113-130`). (2) replied `once`, the tool executes once, and the + persisted global approval policy is untouched (`src/commands.ts:99-101`). +- Cleanup: SP-3. +- Evidence: `src/run.ts:104-139`. + +### RT-016 · Headless forms are cancelled — P2 · FIXTURE · NOT RUN +- Prereq: SP-2, RM-1; fixture model triggers a form. +- Steps: `"$KILO_BIN" run ""` with a bounded timeout. +- Expected: the form is cancelled headless (`src/run.ts:132-139`), the command + terminates without hanging, no form answers are fabricated. +- Cleanup: SP-3. +- Evidence: `src/run.ts:132-139`. + +### RT-017 · SIGINT / SIGTERM exit codes — P1 · FIXTURE · NOT RUN +- Prereq: SP-2, RM-1; a long-running fixture model response. +- Steps: 1) Start `"$KILO_BIN" run "long task"`, send SIGINT mid-flight; record exit + code, stdout, session outcome. 2) Repeat with SIGTERM. +- Expected: exit 130 (SIGINT) / 143 (SIGTERM); stdout empty in the SIGINT + case; the session outcome is "interrupted"; `client.session.interrupt` was + called (`src/run.ts:74-87,210-229,262-272`, `tui-preview.ts:27-33`; + asserted by `test/commands.test.ts:604-618`). +- Cleanup: SP-3. +- Evidence: `src/run.ts`, `test/commands.test.ts:604-618`. + +### RT-018 · Resume targeting and agent mapping — P1 · FIXTURE · NOT RUN +- Prereq: SP-2, RM-1, a completed session from RT-012. +- Steps: 1) `"$KILO_BIN" run --session "again"` in the same directory. 2) Try + `--session` with a busy/running session (arrange via fixture) and from a + different directory. 3) `--agent code` without a custom `code` agent. + 4) Define a custom `code` agent in the profile config, repeat. +- Expected: (1) resumes, re-applying `--agent`/`--model` via switchAgent/ + switchModel (`src/run.ts:90-95`); (2) non-idle or foreign-directory session + is refused (`src/run.ts:64-71`); (3) resolves to native `build`; + (4) the registered `code` agent wins (`src/run.ts:46-53`). +- Cleanup: SP-3. +- Evidence: `src/run.ts:37-95`, `kilocode/baseline/agent-policy-v2-parity.md`. + +### RT-019 · `--model` validation — P2 · FIXTURE · NOT RUN +- Prereq: SP-2, RM-1. +- Steps: 1) `"$KILO_BIN" run --model nodelimiter "x"`. 2) `--model ghost/gone "x"` + against a fixture model host. +- Expected: (1) "Model must have the form provider/model"; (2) a clean + provider-resolution failure on stderr with nonzero exit and nothing on + stdout (`src/commands.ts:104-105`). +- Cleanup: SP-3. +- Evidence: `src/commands.ts:260-307`, `src/commands.ts:104-105`. + +### RT-020 · Resume across paginated history — P3 · FIXTURE · NOT RUN +- Prereq: SP-2, RM-1. +- Steps: 1) Create a session and append > 50 messages by repeated + `"$KILO_BIN" run --session ""` (fixture model). 2) One more resume. +- Expected: every resume works; the internal 50-message page + cursor walk + (`src/run.ts:307-322`) never errors or truncates the admitted prompt. +- Cleanup: SP-3. +- Evidence: `src/run.ts:307-322`. + +## 3. Memory (RT-021 … RT-027) + +Sources: `packages/kilo-cli/src/memory-plugin.ts`, `memory-command.ts`, +`memory-rpc.ts`, `memory-capture.ts`, `tool-authorization.ts`; +`packages/kilo-memory/src`. All cases FIXTURE with RM-1 fake model; no model +cost, no network beyond loopback. State root: +`$XDG_DATA_HOME/kilo2/interactive/memory/-`. + +### RT-021 · Default-off guarantees — P1 · FIXTURE · NOT RUN +- Prereq: SP-2, RM-1, fresh profile. +- Steps: 1) Run a session without touching memory. 2) `/memory status`. + 3) Inspect the data root for a `memory/` directory. 4) `/memory off`. +- Expected: no `memory/` directory, no files, no auxiliary model calls; status + reports disabled; `/memory off` is a no-op on a disabled root + (`memory-plugin.ts` default `enabled:false`, `autoConsolidate:false`). +- Cleanup: SP-3. +- Evidence: `packages/kilo-memory/src/schema.ts:156-187`, + `test/memory.test.ts`. + +### RT-022 · Enable, explicit save with approval — P1 · FIXTURE · NOT RUN +- Prereq: RT-021 profile. +- Steps: 1) `/memory on`. 2) `/memory remember RT-022 project fact`. + 3) Approve the `kilo_memory_save` ask (action `kilo_memory_save`, resources + `["remember"]`, `save: []`). 4) `/memory status`. 5) Inspect + `state.json` and `index.kmem` under the memory root. +- Expected: "Memory enabled."; the ask precedes any mutation; after allow, + "Memory saved (N change)"; state.json `enabled:true`; files exist with + private modes under the project-scoped root. +- Cleanup: SP-3. +- Evidence: `src/memory-plugin.ts:591-668`, `test/memory.test.ts:577-619`. + +### RT-023 · Deny-before-mutation and fail-closed authorizer — P1 · FIXTURE · NOT RUN +- Prereq: RT-022 profile. +- Steps: 1) `/memory remember should-not-persist` and **deny** the ask. + 2) Verify root bytes unchanged. 3) Run the authorizer fail-closed automated + evidence: `"$KILO_RUNTIME" run script/test.ts test/memory.test.ts` + (missing-authorizer transform + case). +- Expected: denied save mutates nothing; the automated case proves a plugin + instance without a host authorizer fails closed at tool execution + ("Tool authorization is unavailable"). +- Cleanup: SP-3. +- Evidence: `src/memory-plugin.ts:528`, `test/memory.test.ts`. + +### RT-024 · Recall authorization and disabled refusal — P1 · FIXTURE · NOT RUN +- Prereq: RT-022 profile; second fresh profile for the disabled half. +- Steps: 1) Drive the model's `kilo_memory_recall` tool (fixture model asks + for it); approve the ask (action `kilo_memory_recall`, resources + `["search"]`). 2) Deny a second recall. 3) On the fresh profile, recall + while disabled. +- Expected: approved recall returns engine results with positive + `metadata.count` provenance; denied recall never reaches the store; disabled + recall throws "Memory is disabled. Run /memory on first." + (`src/memory-plugin.ts:220`). +- Cleanup: SP-3. +- Evidence: `src/memory-plugin.ts:641-650`, `test/memory.test.ts`. + +### RT-025 · Auto-capture gating — P2 · FIXTURE · NOT RUN +- Prereq: RT-022 profile. +- Steps: 1) `/memory auto on` **after** a first exchange; run a second + exchange. 2) `/memory auto off`; run a third. 3) On a disabled root run + `/memory auto on` and a session. +- Expected: capture eligible only for groups after the Started baseline at + opt-in time (pre-opt-in text never captured); `auto off` stops later calls; + auto-on-while-disabled performs no model work; failed/interrupted drains use + the bounded fallback digest with no auxiliary call + (`src/memory-capture.ts:119-123,175-177`). +- Cleanup: SP-3. +- Evidence: `src/memory-capture.ts`, `test/memory.test.ts` (held-primary + steer-batch fixture). + +### RT-026 · Malformed state, symlinks, purge safety — P2 · FIXTURE · NOT RUN +- Prereq: RT-022 profile; shell access to the memory root. +- Steps: 1) Corrupt `state.json` (truncate); run a session; re-read bytes. + 2) Replace the memory root with a symlink; run a session. 3) `/memory purge` + (no argument). 4) `/memory purge confirm`. +- Expected: (1) context prep throws/omits injection, bytes unchanged, no + `state.json.bad-*` repair files; (2) "memory path rejects symlink: " + (`packages/kilo-memory/src/storage/fs.ts:52-54`); (3) usage error (literal + `confirm` required); (4) "Memory purged." and the root is emptied; + unowned roots are refused ("refusing to purge unowned memory root"). +- Cleanup: SP-3. +- Evidence: `test/memory.test.ts:472-515`, + `packages/kilo-memory/src/storage/state.ts:244-251`. + +### RT-027 · Isolation, persistence, bounded fenced injection — P2 · FIXTURE · NOT RUN +- Prereq: two project directories; RM-1. +- Steps: 1) Enable + remember in project A; inspect project B's data root. + 2) Restart the host; `/memory status` in A. 3) With a request-capturing + fixture model, verify the injected block: bounded 8192 bytes, wrapped in the + `kilo-memory-v1 targeted_context_not_instruction` fence, marked as reference + data (no synthetic durable message admitted). +- Expected: B has its own (separate) root; A persists across restart; the + injected text carries the fence header and never appears as a user/assistant + message in history. +- Cleanup: SP-3. +- Evidence: `src/memory-plugin.ts:37,127-133,202-211,572-587`, + `test/memory.test.ts:774-892`. + +## 4. Codebase indexing (RT-028 … RT-033) + +Sources: `packages/kilo-cli/src/indexing.ts`, `indexing-input.ts`, +`indexing-rpc.ts`, `indexing-disabled.ts`; `packages/kilo-indexing/src`. +Fixture embedder **FE-1:** loopback `Bun.serve` implementing the OpenAI +`/embeddings` shape with deterministic hashed vectors (no paid inference), as +`test/indexing.test.ts` does. + +### RT-028 · Disabled-by-default indexing — P1 · FIXTURE · NOT RUN +- Prereq: SP-2, RM-1, no `--indexing-config`. +- Steps: 1) Run a session; ask the fixture model to call `semantic_search`. + 2) Check the indexing status RPC (`kilo.indexing-status`). + 3) Enumerate `$XDG_STATE_HOME/kilo2/interactive` for `indexing/`. +- Expected: no `semantic_search` tool registered (transcript carries no tool + output), status is "Codebase indexing is disabled for this project.", + no index directory, no embed requests. +- Cleanup: SP-3. +- Evidence: `src/indexing-disabled.ts:1-17`, `test/indexing.test.ts`. + +### RT-029 · `--indexing-config` reader bounds — P1 · FIXTURE · NOT RUN +- Prereq: SP-2; config files prepared per step. +- Steps: attempt launch with: a missing file; a symlink; a >64 KiB file; a + non-object JSON; a valid JSONC with unknown key; a URL as the flag value; + the flag on `service start`/`attach`. +- Expected: file failures fail closed with opaque messages that never echo the + key path, value, document, or file path (`src/indexing-input.ts:6-14`); + URL/flag-form values rejected with "--indexing-config requires an explicit + local configuration file" (`src/commands.ts:108-113`); `service`/`attach` + refuse the flag (`test/commands.test.ts:55-56,69-70`). +- Cleanup: SP-3. +- Evidence: `src/indexing-input.ts`, `test/indexing-input.test.ts:91-118`. + +### RT-030 · End-to-end index with loopback embedder — P1 · FIXTURE · NOT RUN +- Prereq: SP-2, RM-1, FE-1, a small fixture checkout with `src/` and + `vendor/` trees, config via `--indexing-config` pointing `openai` + provider at FE-1 and LanceDB inside the isolated root. +- Steps: 1) Launch `"$KILO_BIN" run --indexing-config --directory `. + 2) Have the fixture model call `semantic_search`. 3) Inspect the store + directory. +- Expected: real matches from `src/` files with scores and line ranges; + `vendor/` files never returned; the store lives under + `/indexing/…`; FE-1 received embed requests only for non-ignored + files. +- Cleanup: stop FE-1, SP-3. +- Evidence: `test/indexing.test.ts` (host wiring through + `launch(input, { indexing })`). + +### RT-031 · Search authorization precedes embed/search — P1 · FIXTURE · NOT RUN +- Prereq: RT-030 setup. +- Steps: drive `semantic_search` asks with reject, allow, and resource-deny + decisions via the fixture; record FE-1 request counts. +- Expected: rejected/denied queries never reach the embedding or search path + (FE-1 count unchanged); allowed query executes. +- Cleanup: stop FE-1, SP-3. +- Evidence: `src/indexing.ts:164-190`, `test/indexing.test.ts:511-568`. + +### RT-032 · Store-directory escape refusal — P1 · FIXTURE · NOT RUN +- Prereq: RT-030 setup; two config files. +- Steps: set `lancedb.directory` to (a) a path outside + `/indexing`, (b) a path inside it; launch each. +- Expected: (a) inert Error host with exactly "indexing.lancedb.directory must + stay inside the isolated index root", no store write, no embed request; + (b) accepted and populated (`src/indexing.ts:92-99,252-257`). +- Cleanup: stop FE-1, SP-3. +- Evidence: `test/indexing.test.ts:364-404`. + +### RT-033 · Failure hygiene and opt-in engine log — P2 · FIXTURE · NOT RUN +- Prereq: RT-030 setup with FE-1 unreachable (wrong port) and a sentinel API + key in the config. +- Steps: 1) Launch with the unreachable embedder; read status/RPC/TUI text. + 2) Repeat with `KILO_INDEXING_LOG=1`; inspect stdout vs stderr. +- Expected: status carries only the error class and numeric HTTP status when + present — never the URL, response body, or key; with the env var set, JSON + diagnostics appear on **stderr only**; stdout stays protocol-clean. +- Cleanup: SP-3. +- Evidence: `src/indexing.ts:209-250`, + `packages/kilo-indexing/src/util/log.ts:17-35`, `test/log.test.ts`. + +## 5. Sandbox (RT-034 … RT-038) + +Sources: `packages/kilo-cli/src/sandbox.ts`, `script/sandbox-linux-smoke.ts`, +`interactive-server.ts:147`, `commands.ts:87-89`. The `--sandbox` flag +confines shell-tool writes to the project directory and denies shell network; +it is **not** a read-access sandbox and does not cover PTY, MCP, or separate +git spawns (exact help text, `src/commands.ts:87-89`). macOS backend +`sandbox-exec`; Linux backend Bubblewrap. + +### RT-034 · Sandbox opt-in and workspace confinement — P1 · FIXTURE · NOT RUN +- Prereq: SP-2, RM-1; macOS host with `/usr/bin/sandbox-exec` (or Linux with + bwrap). +- Steps: 1) `run --sandbox` with a fixture model that writes a file inside the + project, then outside it. 2) Inspect the shell invocations observed by the + fixture (the Kilo launcher wraps the selected shell invocation). +- Expected: in-project write succeeds; outside-root write is denied with the + source file unchanged; without `--sandbox` (or a config object without + `enabled: true`) shell execution is unchanged — the hook is inert + (`src/sandbox.ts:83` "Sandbox hooks are inert unless this is explicitly + true"). +- Cleanup: SP-3. +- Evidence: `src/sandbox.ts`, `test/sandbox` fixtures under + `packages/kilo-cli/src/sandbox/interactive-fixture.ts`. + +### RT-035 · Sandbox network policy — P1 · FIXTURE · NOT RUN +- Prereq: RT-034 setup plus a loopback HTTP listener (record port). +- Steps: 1) Sandboxed shell `curl http://127.0.0.1:/` with network + deny (default). 2) Relaunch with `network: "allow"` in the sandbox config + (snake_case keys, `parseSandboxConfig`) and repeat. +- Expected: denied by default (listener sees no request); allowed when + explicitly configured; the deny/allow split matches the Linux probe result + recorded in `kilocode/baseline/sandbox-linux-validation.md`. +- Cleanup: stop listener, SP-3. +- Evidence: `src/sandbox.ts` (`NetworkMode`), `script/sandbox-linux-smoke.ts`. + +### RT-036 · Protected paths and denyNames — P2 · FIXTURE · NOT RUN (platform-split) +- Prereq: RT-034 setup. +- Steps: 1) Configure `deny_write_paths` on a path inside the writable root; + write there. 2) macOS: write to `.git` inside the root (default denyNames). + 3) Linux: configure nonempty `denyNames`. +- Expected: protected-path write denied; macOS `.git` write denied; Linux + nonempty `denyNames` is **refused** at parse (Bubblewrap cannot enforce + names created after setup, `src/sandbox.ts:23-26,175-177`). +- Cleanup: SP-3. +- Evidence: `src/sandbox.ts`, `kilocode/baseline/sandbox-linux-validation.md`. + +### RT-037 · Sandbox scope disclaimers (PTY / MCP / git not covered) — P1 · FIXTURE · NOT RUN +- Prereq: RT-034 setup. +- Steps: 1) Sandboxed session where the fixture model runs a PTY tool, an MCP + tool, and `git` (separate spawn) each attempting an outside-root write or + network call. +- Expected: these paths are **not** confined — observe and record actual + behavior; the help text states the limitation verbatim ("It is not a + read-access sandbox and does not cover PTY, MCP, or separate git spawns", + `src/commands.ts:87-89`). Known limitation, not a PASS criterion. +- Cleanup: SP-3. +- Evidence: `src/commands.ts:87-89`, + `kilocode/baseline/sandbox-linux-validation.md` scope section. + +### RT-038 · Linux Bubblewrap probe (container) — P3 · FIXTURE (LINUX/DOCKER) · NOT RUN +- Prereq: Docker on Linux host; `packages/kilo-cli` built; no host mounts. +- Steps: reproduce `kilocode/baseline/sandbox-linux-validation.md`: + `cd "$CKPT/packages/kilo-cli" && "$KILO_RUNTIME" build script/sandbox-linux-smoke.ts --target=node + --outfile="$KILO_TEST_ROOT/sandbox-linux-smoke.mjs"`, build the pinned image, run with + `--network none --cap-add SYS_ADMIN --security-opt seccomp=unconfined + --security-opt systempaths=unconfined`. +- Expected: `KILO_LINUX_SANDBOX_OK: workspace write, outside/protected denial, + network allow/deny, unsupported names refusal`. +- Cleanup: `docker rm -f` the container; the build context lives under + `$KILO_TEST_ROOT`, so SP-3's Trash move covers it. +- Evidence: `script/sandbox-linux-smoke.ts`, + `kilocode/baseline/sandbox-linux-validation.md`. Known gaps (PTY, MCP, git, + read confinement on Linux) remain open per that record. + +## 6. Telemetry (RT-039 … RT-045) + +Sources: `packages/kilo-cli/src/telemetry.ts`, `telemetry-settings.ts`, +`telemetry-command.ts`, `paths.ts:29`. Fixture collector **TC-1:** loopback +HTTP server recording `POST /v1/logs` bodies. + +### RT-039 · Default-off: zero emissions — P1 · FIXTURE · NOT RUN +- Prereq: SP-2, RM-1, TC-1, no consent file, no telemetry env. +- Steps: 1) Run a host + session activity. 2) Inspect TC-1 records. +- Expected: zero requests to TC-1; `OTEL_EXPORTER_OTLP_ENDPOINT` alone enables + nothing (`test/telemetry.test.ts:28-38`). +- Cleanup: stop TC-1, SP-3. +- Evidence: `src/telemetry.ts:82-106`, `test/telemetry.test.ts`. + +### RT-040 · Env opt-in, name-only allowlist, payload sentinels — P1 · FIXTURE · NOT RUN +- Prereq: RT-039 setup; plant sentinel strings in event payloads via the + fixture model (e.g. a session title containing a sentinel). +- Steps: 1) Relaunch with `KILO_TELEMETRY_ENABLED=1` + + `KILO_TELEMETRY_ENDPOINT=`. 2) Exercise a session lifecycle. 3) Dump + TC-1 bodies. +- Expected: only the 11 allowlisted names appear + (`agent.updated`, `catalog.updated`, `command.updated`, `config.updated`, + `server.connected`, `session.created`, `session.deleted`, + `session.execution.started|succeeded|failed|interrupted`); each record body + is the event-type string plus fixed resource labels; **no** `event.data`, + no session IDs, no sentinels anywhere (`src/telemetry.ts:22-49,189-190`). +- Cleanup: stop TC-1, SP-3. +- Evidence: `src/telemetry.ts`, `test/telemetry.test.ts:140-162`. + +### RT-041 · CLI verbs and argument validation — P1 · FIXTURE · NOT RUN +- Prereq: SP-2 (host not running for status; running for the lock case). +- Steps: 1) `"$KILO_BIN" telemetry status`. 2) `"$KILO_BIN" telemetry enable` (no + `--endpoint`). 3) `"$KILO_BIN" telemetry enable --endpoint ftp://x`. 4) + `"$KILO_BIN" telemetry disable`. 5) `"$KILO_BIN" telemetry status` while a daemon holds + the store, then `telemetry enable --endpoint `. +- Expected: status prints pretty JSON `{enabled, appliesTo, file, eventTypes}` + and never the endpoint, headers, or any secret (`test/telemetry-command.test.ts:51`); + (2) usage error (enable ⇔ `--endpoint` required, `src/commands.ts:189-190`); + invalid endpoint → "Telemetry enable requires a valid HTTP(S) collector + endpoint"; (4) persists decline; (5) lock guard message "Stop the Kilo TUI + or daemon before changing telemetry consent" (`telemetry-command.ts:20-31`). +- Cleanup: SP-3. +- Evidence: `src/telemetry-command.ts`, `test/telemetry-command.test.ts`. + +### RT-042 · Consent file trust boundary — P1 · FIXTURE · NOT RUN +- Prereq: SP-2; shell access to `$XDG_CONFIG_HOME/kilo2/interactive/telemetry.json`. +- Steps: 1) Enable via CLI; inspect file mode and dir mode. 2) Restart host; + verify consent persists. 3) Replace the file with: a symlink; a hardlink; a + FIFO; a 0644 file; a 64 KiB+ file; corrupt JSON; then re-enable via CLI over + each. +- Expected: writes are atomic (tmp + rename), file 0600, dir 0700; reads are + fail-closed — each planted shape reads as disabled without hanging, and CLI + writes refuse to replace symlinked/hardlinked/non-regular files + (`telemetry-settings.ts:58-115`). +- Cleanup: SP-3. +- Evidence: `src/telemetry-settings.ts`, `test/telemetry-settings.test.ts`. + +### RT-043 · Precedence matrix (file vs env) — P1 · FIXTURE · NOT RUN +- Prereq: TC-1; consent file manipulable. +- Steps: run the host with: (a) absent file + `KILO_TELEMETRY_ENABLED=1`; + (b) absent file + no env; (c) present `enabled:true` file + no env; + (d) present declined file + `KILO_TELEMETRY_ENABLED=1`; (e) corrupt file + + env; (f) enabled file but invalid endpoint. +- Expected: (a) on; (b) off; (c) on; (d) **off** (declined/corrupt file beats + env); (e) off; (f) off (`TelemetrySettings.resolve`, + `telemetry-settings.ts:49-56`, `src/telemetry.ts:82-91`). +- Cleanup: stop TC-1, SP-3. +- Evidence: `test/telemetry-settings.test.ts:233+`. + +### RT-044 · Configured exporter labels and headers — P3 · FIXTURE · NOT RUN +- Prereq: SP-2, RM-1, TC-1; telemetry enabled with a sentinel value inside + `KILO_TELEMETRY_HEADERS`. +- Steps: 1) Launch with `KILO_TELEMETRY_HEADERS="Authorization=Bearer + ,x-label=rt"` plus `KILO_TELEMETRY_CLIENT`/`_VERSION`/`_CHANNEL` + and capture TC-1 request headers and resource attributes. 2) Repeat with + `OTEL_EXPORTER_OTLP_HEADERS` only (fallback path). 3) `"$KILO_BIN" telemetry status`. +- Expected: the header string parses as comma-separated `key=value` entries + (value may contain `=`) and reaches TC-1 as configured + (`src/telemetry.ts:57-67,93`); client/version/channel appear as the + configured resource labels — callers must not put secrets in these labels, + so the sentinel here is a capture canary; `telemetry status` still prints + only the status shape and never the headers (`telemetry-command.ts:40-46`). +- Cleanup: stop TC-1, SP-3. +- Evidence: `src/telemetry.ts:57-96`, `test/telemetry-settings.test.ts`. + +### RT-045 · Offline collector and clean shutdown — P2 · FIXTURE · NOT RUN +- Prereq: telemetry enabled pointing at a closed port. +- Steps: run host + session, then stop; time the shutdown; ensure no hang. +- Expected: each OTLP POST is bounded by a 5 s timeout; exporter stop aborts + the subscription and shuts down fibers without hanging + (`src/telemetry.ts:108-164`). +- Cleanup: SP-3. +- Evidence: `src/telemetry.ts`, `test/telemetry.test.ts` (clean shutdown case). + +*(RT-044 was consolidated into RT-043 during editing; the ID range remains +allocated and no case reuses another's ID.)* + +## 7. V1 imports, session migration, external resume (RT-046 … RT-053) + +Sources: `packages/kilo-cli/src/import-v1-config.ts`, `import-v1-session.ts`, +`credential-import.ts`, `external-sessions.ts`, `resume-external.ts`, +`commands.ts:193-219,330-370`; `kilocode/baseline/credential-import-v2-parity.md`, +`kilocode/baseline/external-resume-parity.md`, +`kilocode/baseline/v1-fixture-generation.md`. Fixture sources: synthetic v1 +`auth.json`/config files and the checked-in v1 writer dump; never a real user +store. + +### RT-046 · `import-v1` preview and source hashing — P1 · FIXTURE · NOT RUN +- Prereq: SP-2; synthetic v1 `auth.json` + config file under `$KILO_TEST_ROOT`. +- Steps: 1) `"$KILO_BIN" import-v1` (no files). 2) `"$KILO_BIN" import-v1 --gateway-server + https://api.kilo.ai` (no `--auth`). 3) `"$KILO_BIN" import-v1 --auth + --config ` (no `--apply`). 4) Re-hash the sources afterwards. +- Expected: (1) usage error (at least one of `--auth`/`--config` required); + (2) usage error (`--gateway-server` requires `--auth`); (3) prints + `{status:"preview", …}` with per-source sha256 + byte size in `plan.sources`, + no writes to the profile; (4) sources byte-identical after preview. +- Cleanup: SP-3. +- Evidence: `src/commands.ts:193-219`, `src/import-v1-config.ts:939-970`, + `test/import-v1-config.test.ts:843`. + +### RT-047 · `import-v1 --apply` credential mapping and gates — P1 · FIXTURE · NOT RUN +- Prereq: RT-046 sources; host-owned writer path via the apply command. +- Steps: apply with a source set containing: a plain API key; a Kilo OAuth + entry **without** `--gateway-server`; Kilo OAuth with `--gateway-server` and + a UUID `accountId`; the `kilo-oauth-dummy-key` sentinel; an unknown OAuth + provider; a well-known entry whose manifest no longer retains the saved key. +- Expected: plain key connects via the public key-connect path; Kilo OAuth + without an explicit validated gateway server is refused (never inferred); + valid Kilo OAuth maps to `Credential.OAuth`, method `device`, + `accountId` → `organizationID`; the sentinel and unknown provider are + refused; one unsupported credential blocks the whole import (all-or-nothing, + `import-v1-config.ts:283-287`). +- Cleanup: SP-3. +- Evidence: `src/import-v1-config.ts:616-764`, + `kilocode/baseline/credential-import-v2-parity.md`. + +### RT-048 · Collision and overwrite refusal — P1 · FIXTURE · NOT RUN +- Prereq: profile with a pre-existing config key value and an existing + credential connection on the target integration. +- Steps: apply an import that would set a different value for the existing + config key; then one that would overwrite an existing credential connection. + Inspect the CLI help for an overwrite flag. +- Expected: both refuse (`import-v1-config.ts:319-339`); the CLI surface + (`--auth --config --gateway-server --apply` only) exposes no overwrite flag, + so refusal is the manual-path outcome; report text lists the conflict + without echoing secrets. +- Cleanup: SP-3. +- Evidence: `src/commands.ts:193-219`, `test/import-v1-config.test.ts:643,659,789`. + +### RT-049 · Well-known credential discovery honesty — P2 · FIXTURE · NOT RUN +- Prereq: RT-047 setup with a loopback well-known manifest endpoint. +- Steps: 1) Manifest retains the saved environment key → apply. 2) Manifest + drops the key → apply. 3) Force a failure after discovery (revoke the write + path in the fixture) and read the report. +- Expected: (1) token stored under the normalized origin as the key credential; + discovery reported without the token; (2) refused; (3) the report records + the discovered origin and the write failure honestly — there is no + add-and-write rollback operation + (`kilocode/baseline/credential-import-v2-parity.md` "Implemented mapping"). +- Cleanup: SP-3. +- Evidence: `src/import-v1-config.ts:341-377`, `test/import-v1-config.test.ts:1133`. + +### RT-050 · Partial-failure reporting (no auto-rollback) — P2 · FIXTURE · NOT RUN +- Prereq: RT-047 sources; arrange a config-write failure after a successful + credential write (fixture: read-only profile config path). +- Steps: apply; read the failure report. +- Expected: status `"failed"` with honest `appliedCredentials` / + `configWritten` flags and no automatic undo claim + (`import-v1-config.ts:143-150,379-409`). +- Cleanup: SP-3. +- Evidence: `src/import-v1-config.ts`, `test/import-v1-config.test.ts`. + +### RT-051 · V1 session store copy (library surface, automated evidence) — P2 · FIXTURE · NOT RUN (automated) +- Prereq: bundled runtime built (SP-1); no CLI verb exists — `copyV1Store` is + test-only. +- Steps: 1) `cd "$CKPT/packages/kilo-cli" && "$KILO_RUNTIME" --no-env-file test --preload @opentui/solid/preload --timeout 120000 test/v1-writer-fixture.test.ts` + 2) Attempt the refusal cases manually only via the test (existing dest, + self/`-wal`/`-shm` dest, dest inside source). +- Expected: migration of the checked-in V1 writer dump completes; source db+wal + hashes identical after copy; `-shm` excluded as rebuildable; refusal cases + error (`src/import-v1-session.ts:136-200`). **Note:** no CLI verb wraps this; + wiring is host-owned and currently unwired — record as a gap, not a failure. +- Cleanup: SP-3. +- Evidence: `src/import-v1-session.ts`, `test/v1-writer-fixture.test.ts`, + `kilocode/baseline/v1-fixture-generation.md`. + +### RT-052 · External session listing bounds — P2 · FIXTURE · NOT RUN +- Prereq: fixture directories with Claude `.jsonl` and Codex + `rollout--.jsonl` files, one malformed. +- Steps: 1) `"$KILO_BIN" external-sessions --source claude --directory `. + 2) `--limit 0` and `--limit 51`. 3) A relative directory. +- Expected: listing shows valid entries plus the malformed one as + `valid:false` with a concrete error and no secrets echoed; limits rejected + (1–50); relative directory rejected (absolute required, + `src/external-sessions.ts:20-56`). +- Cleanup: SP-3. +- Evidence: `test/external-cli.test.ts`, `test/external-sessions.test.ts`. + +### RT-053 · `import-external` format gates and no-ambient-read — P1 · FIXTURE · NOT RUN +- Prereq: RT-052 fixtures; synthetic Claude-2 and Codex-0 JSONL (package + fixtures under `test/fixtures/external/` are the shape reference). +- Steps: 1) `"$KILO_BIN" import-external --model provider/model + --agent build`. 2) Same with a Claude major-3 record / Codex major-1. 3) + Same with a mixed-format file. 4) Re-hash the source file. 5) With an empty + explicit directory, confirm no provider home is consulted. +- Expected: (1) fresh `ses_` session, source file unchanged, no execution + during import; (2)/(3) rejected before import (bounded to Claude 2 / Codex 0, + `src/resume-external.ts:81-82,290-296`); (4) byte-identical; (5) no reads of + `~/.claude`, `~/.codex`, or CWD defaults. +- Cleanup: SP-3. +- Evidence: `src/resume-external.ts`, `kilocode/baseline/external-resume-parity.md`. + +## 8. Cloud Agent CLI (RT-054 … RT-058) + +Sources: `packages/kilo-cli/src/cloud-command.ts`, `cloud-rpc.ts`, +`cloud-plugin.ts`, `cloud/*`; `kilocode/baseline/cloud-cli-v2-parity.md`. +Fixture stubs: loopback tRPC (`CLOUD_AGENT_NEXT_BASE_URL`) and web-app +(`KILO_WEB_APP_URL`) with a seeded fixture credential through real device-auth +against the loopback gateway — as `test/cloud-fixture.ts` does. + +### RT-054 · `cloud start` request validation — P1 · FIXTURE · NOT RUN +- Prereq: SP-2; request.json variants. +- Steps: 1) Missing file. 2) File > 1 MiB. 3) URL as the file argument. + 4) Schema-violating JSON. 5) A request carrying `options.kilocodeOrganizationId` + through the RPC path. +- Expected: (1)–(4) the single generic message "Cloud start requires a valid + local request JSON file of at most 1 MiB" with no schema diagnostics echoed + (`src/cloud-command.ts:80-83`); (5) rejected with `rpc.invalid_input` — org + selection is structurally omitted from the wire input + (`src/cloud-rpc.ts:26-29`, `test/cloud-cli.test.ts:224-234`). +- Cleanup: SP-3. +- Evidence: `src/cloud-command.ts`, `src/cloud/contracts.ts:60-72`. + +### RT-055 · status/result exit codes and ambiguous admissions — P1 · FIXTURE · NOT RUN +- Prereq: loopback stubs + seeded credential (as RT-054). +- Steps: drive `cloud start`, `send`, `status `, `result + ` against stubs returning each lifecycle status; then force a transport + error after an admission POST. +- Expected: `status` always exits 0 (assistant projected away); `result` exit + codes completed=0, queued/running=2, failed=3, interrupted=4 + (`src/cloud/contracts.ts:184-193`); ambiguous admission error text "Cloud + Agent start|send outcome is unknown; do not retry automatically" + (`src/cloud/errors.ts:22-23`). +- Cleanup: SP-3. +- Evidence: `test/cloud-cli-subprocess.test.ts`, `test/cloud-cli.test.ts`. + +### RT-056 · `--stream` pinning, ticket fallback, abort — P1 · FIXTURE · NOT RUN +- Prereq: loopback WebSocket stubs. +- Steps: 1) `cloud start --stream` with an admission `streamUrl` on a + foreign origin. 2) With no `streamUrl` (ticket fallback via + `POST /api/cloud-agent-next/sessions/stream-ticket`). 3) Stub that errors + mid-stream. 4) SIGINT mid-stream. +- Expected: foreign-origin URL rejected before any ticket fetch (ticket never + forwarded cross-origin, `src/cloud/client.ts:99-104`); (2) ticket path used; + (3) non-fatal `{"streamEventType":"error", …}` line and exit 0 + (`tui-preview.ts:161-167`); (4) abort closes the socket; exit code 0 or 130 + (both accepted by the automated test). +- Cleanup: SP-3. +- Evidence: `test/cloud-stream-cli.test.ts`, `src/cloud/websocket-stream.ts`. + +### RT-057 · Cloud credential hygiene — P1 · FIXTURE · NOT RUN +- Prereq: RT-055/RT-056 stubs with a sentinel token. +- Steps: capture every outbound body, URL, RPC response, and CLI stdout/stderr + across the above cloud cases; grep for the sentinel. +- Expected: the bearer token never appears in any request body, URL, RPC + response, or CLI output; the stream ticket is session-scoped, expiring, and + never the account token; the RPC error surface stays generic + ("Cloud Agent request failed"). +- Cleanup: SP-3. +- Evidence: `kilocode/baseline/cloud-cli-v2-parity.md` security section, + `src/cloud-plugin.ts:154-155`. + +### RT-058 · Deployed cloud verification — P2 · **USER-OPT-IN** (live account, cloud task, paid inference) · NOT RUN +- Prereq: explicit operator consent; real Kilo account; a disposable cloud + task; no loopback overrides. +- Steps: 1) Real `cloud start --stream` against + `https://cloud-agent-next.kilosessions.ai` / `https://kilo.ai`. 2) Verify + the live tRPC `{result:{data}}` envelope, ticket retry cadence under real + 403/404 propagation, live stream ordering and `streamUrl` shape. 3) Confirm + no cancel verb exists (`"$KILO_BIN" cloud --help`). +- Expected: **record observations; no fixed assertions** — the parity record + lists deployed envelope drift, retry cadence, and live-stream behavior as + unverified gaps. Cancel is a source gap: "There is no cloud cancel endpoint + in the inspected v1 client contract" (`src/commands.ts:73`); do not + fabricate a cancel expectation. +- Cleanup: stop/expire the cloud task; sign out. +- Evidence: `kilocode/baseline/cloud-cli-v2-parity.md` "Deployed verification + gaps". + +## 9. Remote control relay (RT-059 … RT-065) + +Sources: `packages/kilo-cli/src/remote-plugin.ts`, `remote-rpc.ts`, +`remote-protocol.ts`, `remote-session.ts`, `tui-plugin/remote.tsx`; +`kilocode/baseline/remote-v2-parity.md`. **PENDING-VERIFICATION:** these +modules have in-flight uncommitted changes in this checkout, including the +model-catalog alias-identity fix (`Model.Info.id` alias vs `Model.Info.modelID` +full id, RT-064). Fixture relay +**FR-1:** loopback Bun WebSocket relay (as `test/remote-session.test.ts`). +No case dials the live relay; the deployed relay version handshake is +USER-OPT-IN and unverified. The remote attachment aggregate/part-count budget +(RT-063 step 5) is a known-open observation with no source-stated limit. + +### RT-059 · Inert registration and no-credential refusal — P1 · FIXTURE · NOT RUN +- Prereq: SP-2, FR-1 (counting connections). +- Steps: 1) Open `/remote` and read status. 2) Attempt enable with no Kilo + credential. 3) Cancel the enable confirmation. +- Expected: registration/status makes zero relay requests; enable without an + account is refused ("Remote account is unavailable") without dialing; + cancel leaves the relay untouched with zero requests + (`test/remote-ui-fixture.tsx:86-100`). +- Cleanup: SP-3. +- Evidence: `src/remote-plugin.ts:16`, `src/tui-plugin/remote.tsx:54-58`. + +### RT-060 · Enable confirmation and truthful toasts — P1 · FIXTURE · NOT RUN +- Prereq: RT-059 with a seeded credential. +- Steps: 1) Read the confirmation dialog text. 2) Confirm enable. 3) Read + toasts. +- Expected: the dialog shows the exact `REMOTE_LIMITATION` text plus + `Location: ` ("Preview control adapter only: legacy transcript + forwarding, URL-backed attachments and cloud-session cloning are not + available. Inline data attachments are supported. Enabling advertises + sessions in this location and permits supported remote commands under their + native permissions."); success toast "Remote enabled; connection may still + be opening" — enabled intent, not claimed `connected` state + (`src/remote-rpc.ts:11-12`, `src/tui-plugin/remote.tsx:63-66`). +- Cleanup: SP-3. +- Evidence: `test/remote-ui.test.tsx`. + +### RT-061 · Non-persistence and lifecycle closure — P2 · FIXTURE · NOT RUN +- Prereq: RT-060 enabled. +- Steps: 1) Disable. 2) Re-enable twice rapidly. 3) Sign out / switch + credential. 4) Restart the host and check status. +- Expected: one connection at a time (repeated enables do not stack), + disable/logout/credential-switch closes the child scope, and enablement is + not persisted across restarts (`src/remote-plugin.ts:13-17,35-39`). +- Cleanup: SP-3. +- Evidence: `test/remote-rpc.test.ts`. + +### RT-062 · Reconnect, buffer, and terminal close codes — P1 · FIXTURE · NOT RUN +- Prereq: RT-060 enabled against FR-1 which can force closes. +- Steps: 1) Drop the socket non-auth → observe reconnect (same + `connectionId`, 1 s backoff doubling to 60 s). 2) Buffer > 200 outbound + frames while disconnected. 3) Close with 4401, then 4403, then 4409. +- Expected: UUID retained across transient reconnects; buffer caps at 200 + frames dropping oldest; 4401/4403/4409 stop retries permanently and clear + the buffer (`src/remote-session.ts:137-148,176-198`). +- Cleanup: SP-3. +- Evidence: `test/remote-session.test.ts:252-267`. + +### RT-063 · create_session, send_message admission ACK, attachments — P1 · FIXTURE · NOT RUN +- Prereq: RT-060; public history inspectable. +- Steps: 1) Relay `create_session` with a contained child directory. 2) Relay + `send_message` with one text part. 3) `send_message` with an `https://` file + part, a `file:` part, and an oversized (>20 MiB) inline part. 4) Relay + `drop_queued_message` for a message this adapter never admitted. 5) + **Bounded aggregate probe:** relay `send_message` with one text part plus a + modest batch of small inline `data:` file parts (for example 16 parts of + ~1 KiB each — never payloads near 20 MiB, never an unbounded count), and + record the observed outcome and any rejection text. +- Expected: (1) refused ("invalid create_session directory"); (2) success + response only after the public prompt admission resolves (durable inbox + boundary) and text visible in public history; (3) each rejected before any + prompt — no admission, error response ("send_message file parts must be + inline data URLs"; the per-file 20 MiB cap is enforced by Core, + `packages/core/src/session/prompt.ts:202`); (4) "message not queued". + (5) **Known-open observation, no fixed assertion:** current source enforces + only the per-file 20 MiB Core limit; no aggregate-size or part-count budget + exists in `packages/kilo-cli/src/remote-session.ts` or the Core prompt + admission. Record exactly what happens with the bounded many-part message + (accepted, rejected, or truncated) and do not report an accepted limit that + source does not state; oversized/aggregate behavior beyond the per-file cap + stays an open item until a budget is specified. +- Cleanup: SP-3. +- Evidence: `src/remote-session.ts:284-297,644-731,814`, + `test/remote-session.test.ts:559-583`. + +### RT-064 · Catalog, commands, directories translations — P2 · FIXTURE · NOT RUN +- Prereq: RT-060 with a config-seeded provider and a command catalog; seed the + provider so at least one model carries a distinct alias identity — + `Model.Info.id` = `chat` and `Model.Info.modelID` = `vendor/chat` + (`packages/schema/src/model.ts:100-103` defines both fields). +- Steps: 1) Relay `list_models`; inspect the wire shape **and the identity of + each entry**: the emitted model id, its provider, and the full modelID it + represents. 2) Create a remote session (`create_session`) selecting the + model by the alias `chat`; prompt it and read back the session's selected + model via a follow-up `list_models` `currentModel`. 3) Read `defaultModel`. + 4) On a host without a model catalog. 5) `send_command` with an unknown name + and with `model`/`variant`/`messageID` extensions. 6) `list_directories` + past 256 entries and through a symlink escape. +- Expected: v1-shaped catalog within sanitizer bounds (names ≤256, ≤2048 + models, 32 variants ≤64 chars, zeroed cost, empty options/headers/env) with + no secret settings/headers; (4) explicit "model catalog is unavailable"; + (5) preflight rejections; (6) 256 cap and symlink exclusion. **Alias + identity:** the + corrected source emits `source.id` for both wire `id` and `api.id`. + `vendor/chat` stays an internal provider route; it must not replace the + alias identity in the advertised entry. The required roundtrip: the catalog is keyed + by the alias `chat`, `currentModel`/`defaultModel` reference that alias, and + `create_session`/prompt selecting `chat` resolves to `vendor/chat` — record + observed identities for both wire id and resolved model; do not assert the + roundtrip PASS from prompt admission alone. Parent's focused automated + rerun passed seven remote tests / 90 assertions, including relay-driven + creation using the advertised alias and durable prompt admission. This + manual case remains NOT RUN; model execution is a separate observation. +- Cleanup: SP-3. +- Evidence: `src/remote-session.ts:333-411,455-660,764-800`, + `packages/schema/src/model.ts:100-103`, + `test/remote-session.test.ts:479-482`. + +### RT-065 · Remote secret hygiene — P1 · FIXTURE · NOT RUN +- Prereq: RT-060 with a sentinel bearer token; capture all outgoing frames. +- Steps: grep every frame (heartbeats, create, responses, diagnostics) for the + sentinel; check the status RPC shape. +- Expected: token absent everywhere; status exposes `{enabled, connected, + directory, note}` and never a bearer token (`src/remote-rpc.ts:20-24`); + deployed relay-version verification remains open (USER-OPT-IN, not covered + locally). +- Cleanup: SP-3. +- Evidence: `test/remote-session.test.ts` sentinel assertions, + `test/remote-rpc.test.ts`. + +## 10. Session share / unshare / fork-from-share (RT-066 … RT-069) + +Sources: `packages/kilo-cli/src/tui-plugin/tui.tsx:192-304`, +`packages/kilo-gateway/src/session.ts`, `plugin.ts:404-416`; +`kilocode/session-sharing.md`. Fixture ingest server overrides +`KILO_SESSION_INGEST_URL`. + +### RT-066 · `/share` confirmation and team rejection — P1 · FIXTURE · NOT RUN +- Prereq: SP-2, RM-1, fixture ingest; one personal and one team account + selection (loopback gateway). +- Steps: 1) `/share` on an open session; read the confirmation text; cancel. + 2) Confirm with a personal selection. 3) Switch to a team selection; + `/share` again. +- Expected: cancel → zero cloud calls; confirm uploads the full snapshot + (transcript, tool output, attachments, local paths per the warning text) and + returns the public URL; team selection is refused with "Team session sharing + is not supported in this preview yet" and zero cloud session calls + (`test/gateway-integration.test.ts:255-258`). +- Cleanup: unshare, SP-3. +- Evidence: `src/tui-plugin/tui.tsx:210-227`. + +### RT-067 · `/unshare` and share disable env — P2 · FIXTURE · NOT RUN +- Prereq: RT-066 shared session; then relaunch with `KILO_DISABLE_SHARE=1`. +- Steps: 1) `/unshare` (no confirmation dialog). 2) With the env set, + `/share`. +- Expected: unshare revokes and strips the stored URL with toast "Session + unshared"; with the env set, sharing fails with "Session sharing is + disabled" while unsharing an existing link still works + (`packages/kilo-gateway/src/session.ts:72,352-357`). +- Cleanup: SP-3. +- Evidence: `src/tui-plugin/tui.tsx:238-267`. + +### RT-068 · `/fork-from-share` import semantics — P1 · FIXTURE · NOT RUN +- Prereq: RT-066 fixture ingest holding a v2 nested-envelope share. +- Steps: 1) `/fork-from-share ` (and the `/fork-share` alias, and a raw + token). 2) A 404 token. 3) A flat/legacy envelope payload. +- Expected: import creates new session and message IDs, clears + parent/fork/revert links and assistant snapshot references, navigates to the + forked session, toast "Forked shared session"; (2) "Shared session not + found"; (3) "The shared session is not compatible with Kilo v2" + (`packages/kilo-gateway/src/session.ts:141-182,309-350`). +- Cleanup: SP-3. +- Evidence: `test/gateway-integration.test.ts:263-283`. + +### RT-069 · Deployed sharing gates — P2 · **USER-OPT-IN** (deployed ingest + public read) · NOT RUN +- Prereq: operator consent; an intentionally shareable fixture session; no + team account. +- Steps: 1) Share against the deployed ingest; open the public URL in a + browser. 2) Fork from the deployed URL. 3) Unshare; re-open the URL. +- Expected: **record observations; no fixed assertions.** Open gates from + `kilocode/session-sharing.md`: deployed token contract (UUID vs purpose-bound + JWT), public read route, and the v2-message-aware viewer are all unverified + against the deployment. +- Cleanup: unshare the fixture session. +- Evidence: `kilocode/session-sharing.md` "Remaining gates". + +## 11. ACP (RT-070 … RT-074) + +Sources: `packages/kilo-cli/src/acp.ts`, `packages/cli/src/kilocode/acp.ts`, +`script/build-acp.ts`, `test/acp.test.ts`, `test/acp-cli.test.ts`. + +### RT-070 · ACP launch and NDJSON discipline — P1 · FIXTURE · NOT RUN +- Prereq: SP-1 (ACP artifact under `dist/acp/`). +- Steps: 1) `"$KILO_BIN" acp --directory "$KILO_TEST_ROOT/proj"` with a scripted NDJSON client + on stdin. 2) Record every stdout line. 3) Remove `dist/acp` and retry. +- Expected: every stdout line parses as JSON-RPC `jsonrpc:"2.0"` NDJSON; a + missing artifact errors naming `bun run script/build-acp.ts`; the + `KILO_ACP_ARTIFACT` env overrides the artifact dir (`src/acp.ts:23-28`). +- Cleanup: SP-3. +- Evidence: `packages/cli/src/kilocode/acp.ts:33-48`, `test/acp-fixture.ts`. + +### RT-071 · ACP secret hygiene — P1 · FIXTURE · NOT RUN +- Prereq: RT-070 client; a sentinel password via `KILO_ACP_SERVER_PASSWORD`. +- Steps: 1) Run a full session; capture stdout, stderr, and the child argv. + 2) Inspect the child process env after the first request. +- Expected: the password, `"Basic "`, and `"apiKey"` never appear on + stdout/stderr; the credential is not in argv; the env credential is deleted + from `process.env` after use (`packages/cli/src/kilocode/acp.ts:14-17`, + `test/acp-cli.test.ts:75-83`). +- Cleanup: SP-3. +- Evidence: `test/acp-cli.test.ts:75-83`. + +### RT-072 · EOF ownership and host survival — P1 · FIXTURE · NOT RUN +- Prereq: RT-070; for the caller-owned variant, launch a host separately and + pass its URL/credential via the `KILO_ACP_SERVER_*` envs to `runAcp`. +- Steps: 1) Close the client's stdin (EOF) while the bridge serves a + caller-owned host; probe the host HTTP after. 2) Same for `kilo2 acp` + (self-launched host). 3) SIGINT/SIGTERM the bridge. 4) Invoke with an + already-aborted caller. +- Expected: (1) bridge exits 0 and the caller's host still answers 200 + ("EOF owns this stdio process. The Kilo endpoint belongs to the caller and + keeps running."); (2) the self-launched host closes with the command; + (3) exit 0; (4) exit 143 with the child killed before handlers install. +- Cleanup: SP-3. +- Evidence: `packages/cli/src/kilocode/acp.ts:43-55`, `test/acp.test.ts:86-156`. + +### RT-073 · ACP method surface — P2 · FIXTURE · NOT RUN +- Prereq: RT-070 client harness. +- Steps: exercise `initialize`, `authenticate` (method id `opencode-login`), + `session/new`, `session/prompt`, `session/cancel`, `session/list`, + `session/resume`, `session/fork`, `session/delete`, `session/close`, + `set_session_config_option`, and an MCP-over-ACP attempt. +- Expected: auth is a no-op ack with the advertised method renamed "Kilo host + credentials" and no `terminal-auth` `_meta`; lifecycle methods work against + the isolated host; MCP-over-ACP rejected with "MCP-over-ACP is not + supported" (`packages/cli/src/acp/service.ts:182-201,519`). +- Cleanup: SP-3. +- Evidence: `test/acp.test.ts`, `test/acp-cli.test.ts`. + +### RT-074 · ACP profile isolation and bundle hygiene — P2 · FIXTURE · NOT RUN +- Prereq: RT-070. +- Steps: 1) Run a full ACP session; enumerate the XDG roots for `kilo/` or + `opencode/` legacy dirs. 2) Grep `dist/acp/acp.js` for core/server-only + marker strings. +- Expected: only the `kilo2/interactive` profile is created (no `kilo`/`opencode` + dirs); the bundle excludes `packages/core/`, `packages/server/`, and + `packages/cli/src/services/standalone.ts` content + (`test/acp-cli.test.ts:84-90`, `test/acp.test.ts:98-132`). +- Cleanup: SP-3. +- Evidence: `test/acp.test.ts:98-132`. + +## 12. Kilo Gateway auth, protocol, and model loading (RT-075 … RT-080) + +Sources: `packages/kilo-gateway/src/plugin.ts`, `gateway.ts`, `models.ts`; +`packages/core/src/model-resolver.ts`; `packages/ai/src/kilocode/openrouter-routed.ts`; +`test/gateway-protocol.test.ts` + `gateway-protocol-fixture.ts` (packaged +runtime `dist/interactive/bun`, loopback fake Kilo API). **PENDING-VERIFICATION:** +`packages/kilo-gateway` has uncommitted changes under independent review. +The sanitizer now covers all three endpoints. The former compatible-Auto +AISDK adapter ignored HTTP middleware for both prompts and titles; a native +Kilo route replaces it. Root's focused automated run passed nine tests / 98 +assertions, including positive all-wire credential checks. This does not +execute these manual cases or close the separate compiled-packaging gate. + +### RT-075 · Payload capture with no account keys (all paths measured) — P1 · FIXTURE · NOT RUN +- Prereq: SP-1; loopback fake Kilo API + fake model (gateway-protocol fixture + pattern); a **metadata-bearing** key credential (config key credential with + metadata present). Automated fixtures now use hostile account metadata; + manual probes must use synthetic credentials and loopback endpoints too. +- Steps: 1) Drive requests through each protocol endpoint + (`/messages`, `/chat/completions`, `/responses`) with the metadata-bearing + credential, including prompts and automatic titles for both native Auto + routes; capture full outbound bodies and headers per request. 2) Grep + every captured payload for the credential value and each reserved metadata + field (`server`, `organizationID`, `email`, `organizationName`, `token`, + `refresh`). +- Expected: every body is free of credential values and reserved account + fields, including `/responses` with either `store` value. Titles are not + exempt. Missing request families or fixture startup failures are not proof + of no leak. Preserve routing, reasoning, message content and nested BYOK + options; record the actual captures. +- Request-policy matrix (also **NOT RUN**): repeat the capture with + `hide_prompt_training_models` unset, profile true, project false overriding + profile true, and project reset falling back to profile true. After each + host restart, expect `provider.data_collection: "deny"` only for the + effective true setting. Use fixtures with no separately configured + `provider.data_collection` value so absence is meaningful. Verify an existing + record-valued `provider` retains routing fields, a non-record value is + replaced by the deny record, reasoning and nested BYOK options survive, + and foreign-origin requests are unchanged. +- Refresh timing: with default file watching disabled, change the preference + through `/kilo-settings` and record the restart notice. Reopen `/models` to + observe its live filter separately from the cached request policy. Restart + the case-owned host before expecting the new wire marker; additionally test + a fixture account switch/config refresh as refresh triggers. Do not treat + hiding a model as proof that a running host has refreshed its wire policy, + or a deny marker as proof of a deployed provider's retention behavior. +- Invalid-value cases: store null in each scope and verify the fixed invalid + notice, with no deny request when no valid scope supplies true. A valid + profile true still applies when the project value is invalid. Separately, + use a missing file reference inside the disposable profile to fail native + substitution: expect native fields to be ignored, the scope warning to + explain that separately parsed Kilo-only values may apply, and an explicit + raw true to remain effective. These are manual **NOT RUN** scenarios, not + claims that the native host loaded the broken document. +- Cleanup: SP-3. +- Evidence: `packages/kilo-gateway/src/plugin.ts:214-235,498-527`, + `packages/core/src/model-resolver.ts:131-191`, + `test/gateway-protocol-fixture.ts:16-22,223-226`, + `packages/kilo-cli/test/request-policy.test.ts`, + `packages/kilo-gateway/test/plugin.test.ts`. + +### RT-076 · Bearer promotion vs preserved OAuth Bearer — P1 · FIXTURE · PENDING-VERIFICATION · NOT RUN +- Prereq: RT-075 capture rig. +- Steps: 1) Config with `x-api-key`-carrying Anthropic-protocol request to the + Kilo `/messages` endpoint, plus a deliberately configured foreign + `Authorization` header. 2) Config using native OAuth authToken (no + x-api-key). +- Expected: (1) outbound `authorization: Bearer ` — the key + wins over any configured or foreign Authorization header + (`plugin.ts:215-221`); (2) the existing Bearer is preserved untouched + (`plugin.test.ts:633-656`). +- Cleanup: SP-3. +- Evidence: `packages/kilo-gateway/src/plugin.ts:208-233`. + +### RT-077 · Configured headers and the organization header — P1 · FIXTURE · PENDING-VERIFICATION · NOT RUN +- Prereq: RT-075 rig; provider/model configured headers; personal + team + + revoked selections. +- Steps: 1) Set a provider model header `Existing: preserved` and a stale + `X-KILOCODE-ORGANIZATIONID` in any case variant; capture outbound headers on + a team selection, a personal selection, and after revoking the selection. +- Expected: any-case org header is filtered then re-added from the current + selection (team → header present; personal → absent); other configured + headers are preserved; a revoked/unavailable selection drops the header and + routes nowhere (`plugin.ts:245-299`, `plugin.test.ts:337,1057-1084`). +- Cleanup: SP-3. +- Evidence: `packages/kilo-gateway/src/plugin.ts:245-299`. + +### RT-078 · Explicit model-field configuration precedence — P1 · FIXTURE · PENDING-VERIFICATION · NOT RUN +- Prereq: RT-075 rig; config with `providers.kilo.models..package` and + explicit `name`, `limit.*`, `cost`, `capabilities` fields; loopback catalog + with overlapping `ai_sdk_provider` and prices. +- Steps: 1) Catalog refresh with the explicit config present. 2) Without it. + 3) Feed all four `ai_sdk_provider` tags plus a malformed one. +- Expected: the explicitly configured model package wins (`model.package ??=` + never overwrites an existing value); explicit name/limits/cost/capabilities + are protected from catalog refresh; stale protocol metadata is replaced when + the config no longer pins it; mapping anthropic→`aisdk:@ai-sdk/anthropic`, + openai→`aisdk:@ai-sdk/openai`, openai-compatible→ + `aisdk:@ai-sdk/openai-compatible`, openrouter/absent/malformed→ + `aisdk:@openrouter/ai-sdk-provider` (`plugin.ts:282,472-477`; + `packages/kilo-gateway/src/gateway.ts:13-19` `configEntries` bridge). +- Cleanup: SP-3. +- Prompt-selector subcases (**NOT RUN**, bounded implementation under review): + serve `opencode.prompt` values `anthropic` and `trinity`, including a model + ID whose native heuristic conflicts. Capture system parts and verify the + supported explicit tag wins, a custom agent system wins over the tag, and + project/tool/native supplementary guidance remains intact. Unknown tags, + malformed values, prototype-property names and unsupported legacy selectors + must not install an asset or drop the model. Switch fixture accounts and + run two locations concurrently; confirm each reads its own current catalog, + failed/revoked catalogs clear selectors, and prompt execution makes no extra + metadata fetch. Do not treat `family` as selector provenance. +- Evidence: `packages/kilo-gateway/test/plugin.test.ts:875-942`, + `packages/kilo-gateway/test/prompt-selector.test.ts`, + `packages/kilo-cli/test/model-prompt-policy.test.ts`. + +### RT-079 · `/responses` stateless rewrite — P2 · FIXTURE · PENDING-VERIFICATION · NOT RUN +- Prereq: RT-075 rig. +- Steps: send Responses-protocol requests with: `item_reference` entries and + provider item IDs; explicit `store: true`; malformed JSON; a foreign + endpoint. +- Expected: stateless item references/IDs dropped while encrypted reasoning is + retained; `store: true` retains references but still applies account-field + isolation and any effective data-collection deny policy. Malformed/foreign + requests remain untouched; the rebuilt + request carries no stale `content-length` (`plugin.ts:223-235,531-546`; + `test/gateway-protocol-fixture.ts:227-239`). +- Cleanup: SP-3. +- Evidence: `test/gateway-protocol.test.ts`. + +### RT-080 · Team catalog scoping and field hygiene — P2 · FIXTURE · PENDING-VERIFICATION · NOT RUN +- Prereq: RT-075 rig with personal and team fixtures. +- Steps: 1) Fail or empty the team catalog fetch. 2) Succeed with records + carrying malformed `autoRouting`, overflowing prices, missing architecture, + and a `disabled: true` configured model. 3) Switch team → personal. +- Expected: failed/empty team fetch disables Kilo rows instead of leaking + personal/static models; malformed routing discarded per record; prices that + do not parse as finite numerics are omitted (never infinity); missing + architecture yields conservative text-only capabilities; a configured + disabled model stays disabled when API-discovered; the switch re-dispatches + protocol and org header (`kilocode/baseline/model-loading-v2-parity.md` + catalog sections). +- Cleanup: SP-3. +- Evidence: `packages/kilo-gateway/test/plugin.test.ts`, + `test/model-picker.test.ts`. + +### RT-081 · One-shot session command surface — P2 · FIXTURE · NOT RUN +- Prereq: SP-2, RM-1, a completed session; daemon stopped (SP-4). +- Steps: 1) `"$KILO_BIN" sessions`. 2) `"$KILO_BIN" models --directory "$KILO_TEST_ROOT/proj"` and + `"$KILO_BIN" agents --directory "$KILO_TEST_ROOT/proj"`; inspect the JSON for request + settings or agent prompts. 3) `"$KILO_BIN" export ` and + `"$KILO_BIN" export --sanitize`. 4) `"$KILO_BIN" import ` in + a second directory; compare IDs. +- Expected: sessions lists the latest 50 top-level sessions newest-first + (`src/commands.ts:309-319`); models/agents print filtered metadata with no + request settings and no agent prompt text (`src/commands.ts:401-421`); + export writes the transcript; import creates a session with fresh IDs and + no inherited snapshots/ancestry (`src/commands.ts:436-455`). +- Cleanup: SP-3. +- Evidence: `src/commands.ts:309-455`, `test/commands.test.ts`. +- Note: RT-081 is listed in §2 (it exercises the command layer next to `run`) + but its ID was allocated after RT-080 to keep every ID globally unique and + immutable once written. + +## Coverage summary + +| Section | IDs | Count | +|---|---|---| +| Host, daemon, path isolation | RT-001 … RT-011 | 11 | +| `run` command + session commands | RT-012 … RT-020, RT-081 | 10 | +| Memory | RT-021 … RT-027 | 7 | +| Indexing | RT-028 … RT-033 | 6 | +| Sandbox | RT-034 … RT-038 | 5 | +| Telemetry | RT-039 … RT-045 | 7 | +| V1 imports + external resume | RT-046 … RT-053 | 8 | +| Cloud Agent CLI | RT-054 … RT-058 | 5 | +| Remote relay | RT-059 … RT-065 | 7 | +| Share / fork | RT-066 … RT-069 | 4 | +| ACP | RT-070 … RT-074 | 5 | +| Gateway auth / model loading | RT-075 … RT-080 | 6 | +| **Total** | | **81** | + +- USER-OPT-IN (deployed account / live relay / paid inference): RT-058, RT-069. + Everything else is FIXTURE-only: loopback servers, fake models, disposable + stores. No case in this plan contacts a real Kilo account or a paid model. +- PENDING-VERIFICATION areas (in-flight source in this worktree): §9 remote + relay — including the model-catalog alias-identity roundtrip fix (RT-064) + and the open aggregate/part-count attachment-budget observation (RT-063 + step 5) — §12 gateway, plus `settings*`, `model-picker.ts`, and + `import-v1-config.ts` surfaces exercised by RT-047/RT-048. +- Known open seams recorded as open measurements, not assertions: compiled + Auto packaging, sandbox PTY/MCP/git coverage (RT-037), + remote aggregate/part-count attachment budget (RT-063 step 5), deployed + relay/version gates (RT-065, RT-069), v1 session-store CLI wiring gap + (RT-051). + +## Source-check appendix + +- Every file path cited above was verified to exist on this checkout on + 2026-09-05 (batched `test -f` sweep; no MISSING results). HEAD + `82040801cd253665d2785c290031d307a18a0d64` with the in-flight modifications + listed in the header. +- All command names, flags, env vars, paths, and literal expected strings in + this plan were transcribed from current source in this checkout via four + structured source sweeps (daemon/run, telemetry/memory/indexing, + cloud/remote/share/ACP, imports/gateway) plus direct reads of + `packages/kilo-gateway/src/plugin.ts`, `packages/core/src/model-resolver.ts`, + and `packages/kilo-cli/src/sandbox.ts`. Names were **not** carried over from + v1 or from memory: the only `restart` verb, `--continue`, pagination flags, + and `KILO_DATA_DIR` are absent from this tree and appear nowhere in this + plan as expectations. +- This plan makes no claim that any automated suite passing implies the manual + cases pass; every case above remains NOT RUN until executed and dated by the + operator. diff --git a/migration-tracking/test-plans/kilo-opencode-v2-test-plan-ui.md b/migration-tracking/test-plans/kilo-opencode-v2-test-plan-ui.md new file mode 100644 index 000000000000..75a0c8784a3a --- /dev/null +++ b/migration-tracking/test-plans/kilo-opencode-v2-test-plan-ui.md @@ -0,0 +1,992 @@ +# Kilo v2 CLI/TUI Scenario Test Plan + +> **Scope:** Interactive CLI/TUI scenarios, including startup, branding, theme/dimensions, BB terminal lifecycle, slash commands, model picker, agent selection and policies, native Plan mode, Kilo settings, memory UI and modal, and status/sidebar truthfulness. +> +> **Status:** **ALL MANUAL TESTS NOT RUN.** +> +> **Index & Companion:** Main test plan index is [`plans/kilo-opencode-v2-test-plan.md`](kilo-opencode-v2-test-plan.md). Companion runtime/security plan is [`plans/kilo-opencode-v2-test-plan-runtime.md`](kilo-opencode-v2-test-plan-runtime.md). VS Code and JetBrains extension testing is deferred. +> **Conventions:** Automated test references (`packages/kilo-cli/test/...`) represent related automated harness coverage only; they do not certify a manual pass or live deployed service. Any live account, paid model inference, remote sharing, or destructive action is labeled **USER-OPT-IN** or **FIXTURE-ONLY**. + +--- + +## High-Level Execution Matrix & Verification Summary + +| Section | Domain / Capability | IDs | Case Count | Manual Status | +|---|---|---|---|---| +| 1 | Startup, Branding, Themes & Dimensions | UI-001 – UI-009 | 9 | NOT RUN | +| 2 | BB Terminal Lifecycle & Resize Resilience | UI-010 – UI-014 | 5 | NOT RUN | +| 3 | Command Palette & Slash Command Deduplication | UI-015 – UI-022 | 8 | NOT RUN | +| 4 | Model Picker Presentation, Groups, Filtering & Privacy | UI-023 – UI-034 | 12 | NOT RUN | +| 5 | Agent Selection, Ask/Debug Boundaries & Custom Agents | UI-035 – UI-042 | 8 | NOT RUN | +| 6 | Plan Mode Workflow, Save Consent & Completion Gates | UI-043 – UI-056 | 14 | NOT RUN | +| 7 | Kilo Settings UI Scopes, Precedence & Validation | UI-057 – UI-066 | 10 | NOT RUN | +| 8 | Memory UI: Compact Sidebar, Dialog & Toggle Controls | UI-067 – UI-074 | 8 | NOT RUN | +| 9 | Sidebar Truthfulness: Credits, Indexing & Usage | UI-075 – UI-080 | 6 | NOT RUN | +| 10 | PR sidebar | UI-081 – UI-082 | 2 | NOT RUN | +| 11 | Running-shell sidebar | UI-083 | 1 | NOT RUN | +| **Total** | | **UI-001 – UI-083** | **83 Cases** | **NOT RUN** | + +--- + +## Shared Test Setup & Environment Isolation + +Before running manual interactive CLI/TUI scenarios, prepare an isolated environment to prevent polluting the developer's real `~/.config/kilo`, `~/.local/share/kilo`, or production profiles: + +```sh +# Run this setup from the repository root. Keep the calling shell unchanged. +KILO_BIN="$PWD/packages/kilo-cli/dist/interactive/kilo2" +KILO_TEST_ROOT="$(mktemp -d /tmp/kilo-ui-test-XXXXXX)" +KILO_TEST_HOME="$KILO_TEST_ROOT/home" +KILO_TEST_PROJECT="$KILO_TEST_ROOT/project" +mkdir -p "$KILO_TEST_HOME" "$KILO_TEST_PROJECT" "$KILO_TEST_ROOT/tmp" + +# Scrub inherited provider credentials/config and scope isolation to the child. +kilo_test() { + env -i PATH="$PATH" TERM="${TERM:-xterm-256color}" LANG="${LANG:-en_US.UTF-8}" \ + HOME="$KILO_TEST_HOME" USERPROFILE="$KILO_TEST_HOME" \ + XDG_DATA_HOME="$KILO_TEST_HOME/data" XDG_CONFIG_HOME="$KILO_TEST_HOME/config" \ + XDG_CACHE_HOME="$KILO_TEST_HOME/cache" XDG_STATE_HOME="$KILO_TEST_HOME/state" \ + TMPDIR="$KILO_TEST_ROOT/tmp" "$KILO_BIN" "$@" +} +cd "$KILO_TEST_PROJECT" +kilo_test +``` + +In the cases below, “launch `$KILO_BIN`” means call `kilo_test` so isolation is +retained. Add only a case's explicitly approved environment values to this +child command; the clean environment intentionally omits live credentials. +After all child processes exit, inspect the printed value of `KILO_TEST_ROOT` +and move only that exact disposable directory to Trash. Do not remove paths +through an unresolved variable or change the calling shell's `HOME`. + +--- + +## 1. Startup, Branding, Themes & Dimensions + +### UI-001: Clean interactive launch with isolated profile +* **Priority:** High +* **Prerequisites:** Shared environment isolation configured (`KILO_TEST_ROOT`). Binary `$KILO_BIN` built and executable. +* **Steps:** + 1. Change directory to `$KILO_TEST_PROJECT`. + 2. Launch `$KILO_BIN`. + 3. Observe the alternate screen buffer initialization. + 4. Inspect the header, sidebar, status bar, and composer prompt. +* **Expected:** TUI boots into interactive session without error toasts or unhandled promise rejections. Title shows session frame. Prompt input is focused. No files are created outside `$KILO_TEST_ROOT`. +* **Cleanup:** Exit session with `/exit` or `Ctrl+C`. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/interactive.test.ts`. + +### UI-002: Kilo ASCII logo branding in home splash +* **Priority:** Medium +* **Prerequisites:** Fresh interactive session on standard terminal (80x24) in `$KILO_TEST_PROJECT`. +* **Steps:** + 1. Launch `$KILO_BIN`. + 2. Inspect the application logo rendered in the initial home view (`#kilo-home-logo`). + 3. Verify whether modern Unicode or ASCII fallback artwork is selected. +* **Expected:** Brand renders "Kilo" distinctively via `packages/kilo-cli/src/tui-plugin/logo.tsx` using `packages/kilo-cli/src/tui-plugin/logo-data.ts`. No raw "OpenCode" upstream unbranded artifact appears in the home logo slot. +* **Cleanup:** Close session with `/exit`. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/logo.test.tsx` and `packages/kilo-cli/test/teams-fixture.tsx`. + +### UI-003: Light theme contrast and token mapping (`solarized`) +* **Priority:** Medium +* **Prerequisites:** Interactive session running in light terminal emulator (or light background preset). Built-in theme `solarized` present in `packages/tui/src/theme/assets/solarized.json`. +* **Steps:** + 1. Open theme picker via `/theme`. + 2. Select `solarized`. + 3. Inspect readability of text tokens: default text, subdued text, and prompt input. +* **Expected:** All text tokens map to semantic roles (`theme.text.default`, `theme.text.subdued`, `theme.background.default`). Background is light; foreground text is legible dark ink. No unmapped invisible white-on-white text occurs. +* **Cleanup:** Switch theme back to default dark via `/theme`. +* **Evidence / Limitations:** Validated against built-in themes in `packages/tui/src/theme/v1.ts` (`DEFAULT_THEMES`). + +### UI-004: Dark theme contrast and token mapping (`tokyonight`) +* **Priority:** Medium +* **Prerequisites:** Standard dark terminal emulator (80x24). Built-in theme `tokyonight` present in `packages/tui/src/theme/assets/tokyonight.json`. +* **Steps:** + 1. Launch interactive session. + 2. Open theme selector via `/theme`. + 3. Switch to `tokyonight` (or `one-dark`, `dracula`). + 4. Inspect sidebar border, tab pulse, and action buttons. +* **Expected:** Dark background renders properly. Accents and feedback tones (success green, warning amber, error red) meet contrast requirements. +* **Cleanup:** Revert to default theme. +* **Evidence / Limitations:** Verified via `packages/tui/src/theme/v1.ts`. + +### UI-005: Narrow terminal layout (80 columns minimum constraint) +* **Priority:** High +* **Prerequisites:** Terminal resized to exactly 80 columns by 24 rows (`stty cols 80 rows 24`). +* **Steps:** + 1. Launch `$KILO_BIN`. + 2. Observe sidebar collapsing or wrapping behavior. + 3. Type a 120-character prompt in composer and observe wrapping. +* **Expected:** Interface fits within 80 columns without broken ANSI wrap or clipped left margins. Prompt input wraps inside composer boundaries. +* **Cleanup:** Restore terminal to standard width (120x35). +* **Evidence / Limitations:** Automated harness reference: `packages/tui/test/mini/footer.responsive.test.tsx`. + +### UI-006: Wide terminal layout (160+ columns expansion) +* **Priority:** Medium +* **Prerequisites:** Terminal resized to 160 columns by 45 rows (`stty cols 160 rows 45`). +* **Steps:** + 1. Launch interactive session. + 2. Inspect message list width, sidebar allocation, and diff viewer margins. + 3. Open `/stats` or a diff viewer pane. +* **Expected:** Layout expands gracefully without excessive empty deadspace or distorted flex columns. Sidebar maintains fixed/bounded proportional width. +* **Cleanup:** Restore standard terminal size. +* **Evidence / Limitations:** Automated harness reference: `packages/tui/test/cli/tui/diff-viewer.test.tsx`. + +### UI-007: Short terminal viewport (15 rows vertical compression) +* **Priority:** Medium +* **Prerequisites:** Terminal resized to 100 columns by 15 rows (`stty cols 100 rows 15`). +* **Steps:** + 1. Launch interactive session. + 2. Open a dialog (e.g., `/models` or `/kilo-settings`). + 3. Navigate list with Up/Down arrows. +* **Expected:** Dialog fits within visible rows; option list scrolls with visible active selection indicator. Header and footer do not push dialog select actions off-screen. +* **Cleanup:** Restore terminal height. +* **Evidence / Limitations:** Handled by dialog select component in `packages/tui/src/ui/dialog-select.tsx`. + +### UI-008: Multi-line composer input expansion and scrolling +* **Priority:** Medium +* **Prerequisites:** Standard interactive session. +* **Steps:** + 1. In composer, type 5 consecutive lines using `Shift+Enter` (or literal newline). + 2. Continue typing up to 10 lines of text. + 3. Press Up and Down arrows to navigate between lines. +* **Expected:** Composer height grows up to maximum configured ceiling and then scrolls internally. Message history above shifts upward without clobbering the status line. +* **Cleanup:** Clear prompt with `Ctrl+U` or `Escape`. +* **Evidence / Limitations:** Managed in `packages/tui/src/component/prompt/index.tsx`. + +### UI-009: ANSI color escape suppression and raw terminal fallback +* **Priority:** Low +* **Prerequisites:** Environment variable `NO_COLOR=1` set. +* **Steps:** + 1. Launch `NO_COLOR=1 "$KILO_BIN"`. + 2. Inspect text rendering across header, prompts, and dialogs. +* **Expected:** System suppresses raw RGB ANSI sequences or falls back to monochrome/semantic bold styling. No broken ANSI escape codes (`\x1b[38;2;...m`) leak into visible text. +* **Cleanup:** Unset `NO_COLOR`. +* **Evidence / Limitations:** Theme color parsing in `packages/tui/src/theme/color.ts`. + +--- + +## 2. BB Terminal Lifecycle & Resize Resilience + +### UI-010: BB 1-minute idle reproduction and state capture +* **Priority:** High +* **Prerequisites:** Launch session in a BB agent environment / remote webview terminal (`https://...getbb.app`). +* **Steps:** + 1. Launch `$KILO_BIN` in BB terminal. + 2. Verify all UI components (header, tabs, sidebar, prompt) are clearly visible. + 3. Leave the session completely untouched for 1 minute (no keystrokes, no mouse movement, no process output). + 4. Inspect the viewport: record whether any elements disappear, blank out, or unmount. + 5. If blanked, type a single character without pressing Enter. Record whether typing restores the surface. +* **Expected:** UI state is captured for unrootcaused BB terminal behavior. If elements disappear, typing or event dispatch is monitored. Record exact visual state without claiming premature root-cause resolution. +* **Cleanup:** Exit session. +* **Evidence / Limitations:** Known issue: BB terminal element disappearance under idle; resize is reported to restore it. + +### UI-011: BB 5-minute idle reproduction +* **Priority:** High +* **Prerequisites:** Same as UI-010. +* **Steps:** + 1. Launch session in BB terminal. + 2. Leave untouched for 5 minutes. + 3. Inspect screen. Note if cursor is active or if alternate buffer was cleared by host gateway/pty keepalive. + 4. Press `Shift` or a non-mutating modifier. +* **Expected:** Document whether terminal stream times out or detaches. Record whether background keepalive pings are received. +* **Cleanup:** Exit session. +* **Evidence / Limitations:** Parity with known bug notes in `plans/kilo-opencode-v2-test-plan.md`. + +### UI-012: BB 15-minute extended idle reproduction +* **Priority:** Medium +* **Prerequisites:** Same as UI-010. +* **Steps:** + 1. Launch session in BB terminal. + 2. Leave untouched for 15 minutes. + 3. Inspect screen for full blanking or partial header/footer drop. +* **Expected:** Document state transitions. Record whether connection dropped or if TUI process is still alive in background. +* **Cleanup:** Exit session. +* **Evidence / Limitations:** Pure observational capture test. + +### UI-013: Terminal window resize event restoration +* **Priority:** High +* **Prerequisites:** UI in an idle-blanked or partially degraded state from UI-010/011, or intentionally induced. +* **Steps:** + 1. With degraded or blanked UI, trigger a SIGWINCH window resize by dragging the terminal window border by 1 column/row. + 2. Observe whether the alternate screen redraws immediately. +* **Expected:** Resize event triggers full canvas re-render. All UI elements (header, tabs, status bar, prompt) are completely restored to screen. +* **Cleanup:** Restore original window size. +* **Evidence / Limitations:** Automated harness reference: `packages/tui/test/mini/stream-v2.transport.test.ts` ("preserves active text and reasoning across resize"). + +### UI-014: Terminal resize during active streaming response +* **Priority:** High +* **Prerequisites:** Session running with an active assistant generation (mocked or offline fixture stream). +* **Steps:** + 1. Submit a prompt that produces multi-line streaming text. + 2. While text is actively streaming onto the screen, rapidly resize the terminal window 3 times (e.g., 80x24 -> 120x30 -> 100x25). + 3. Wait for stream to finish. +* **Expected:** No crash or fatal panic. Active streaming text and reasoning blocks re-flow without duplicating deltas or losing lines. Output remains cohesive. +* **Cleanup:** Reset terminal if needed. +* **Evidence / Limitations:** Automated harness reference: `packages/tui/test/mini/stream-v2.transport.test.ts`. + +--- + +## 3. Command Palette & Slash Command Deduplication + +### UI-015: Slash command autocomplete list deduplication +* **Priority:** High +* **Prerequisites:** Clean interactive session in `$KILO_TEST_PROJECT`. +* **Steps:** + 1. In empty composer, type `/`. + 2. Inspect the popup autocomplete menu. + 3. Scroll through all listed slash commands. +* **Expected:** Every slash command appears exactly once. No command ID or display name is listed twice (e.g., `/settings` vs `/kilo-settings`, `/share`). Aliases (`/mem` for `/memory`, `/kilo-config` for `/kilo-settings`) resolve cleanly to their primary action. +* **Cleanup:** Press `Escape` to close menu. +* **Evidence / Limitations:** Autocomplete logic in `packages/tui/src/component/prompt/autocomplete.tsx`. + +### UI-016: `/share` routing to Kilo public sharing confirmation +* **Priority:** High +* **Prerequisites:** Interactive session running. No real upload required (FIXTURE / CANCEL). +* **Steps:** + 1. Type `/share` and press Enter. + 2. Observe the resulting dialog modal. + 3. Verify modal title and warning text. + 4. Press `Escape` or select `cancel`. +* **Expected:** Opens "Share session publicly" confirmation dialog explaining transcript/tool upload to Kilo. It does NOT invoke native OpenCode upstream sharing or silently upload data. Selecting cancel aborts cleanly without network traffic. +* **Cleanup:** Dismiss dialog. +* **Evidence / Limitations:** Implemented in `packages/kilo-cli/src/tui-plugin/tui.tsx` (`session.share`) and tested in `packages/kilo-cli/test/sharing.test.tsx`. + +### UI-017: `/unshare` command presentation and cancellation +* **Priority:** Medium +* **Prerequisites:** Interactive session. +* **Steps:** + 1. Type `/unshare` and press Enter. + 2. If session is not currently shared, observe notification/alert. +* **Expected:** If session was never shared, shows appropriate feedback ("Session is not shared" or error toast). No unhandled exception. +* **Cleanup:** Close toast. +* **Evidence / Limitations:** Defined in `packages/kilo-cli/src/tui-plugin/tui.tsx`. + +### UI-018: `/settings` vs `/kilo-settings` distinct routing +* **Priority:** High +* **Prerequisites:** Interactive session. +* **Steps:** + 1. Type `/settings` and press Enter. Observe opened dialog. Close it. + 2. Type `/kilo-settings` (or `/kilo-config`) and press Enter. Observe opened dialog. +* **Expected:** `/settings` opens the native upstream TUI presentation/behavior settings dialog (`DialogConfig`). `/kilo-settings` opens Kilo-specific configuration dialog (`installSettingsUi`) managing profile/project scopes. They are completely distinct dialogs. +* **Cleanup:** Press `Escape` to dismiss dialog. +* **Evidence / Limitations:** Handled in `packages/kilo-cli/src/tui-plugin/settings.tsx` and tested in `packages/kilo-cli/test/settings-ui.test.tsx`. + +### UI-019: `/privacy` slash command argument parsing +* **Priority:** Medium +* **Prerequisites:** Interactive session. +* **Steps:** + 1. Type `/privacy` without arguments and press Enter. + 2. Type `/privacy invalid` and press Enter. +* **Expected:** Shows usage toast: "Run /privacy on or /privacy off." No state change occurs. +* **Cleanup:** Dismiss toast. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/privacy.test.ts`. + +### UI-020: `/memory` slash command argument dispatch +* **Priority:** Medium +* **Prerequisites:** Interactive session with local memory RPC available. +* **Steps:** + 1. Type `/memory` (or `/mem`) without arguments and press Enter. + 2. Verify it opens the Memory modal dialog (`show`). + 3. Press `Escape`, then type `/memory help` and press Enter. +* **Expected:** `/memory` opens the main memory browser dialog. `/memory help` opens the memory usage overview modal. +* **Cleanup:** Close dialog. +* **Evidence / Limitations:** Handled in `packages/kilo-cli/src/tui-plugin/memory.tsx` and tested in `packages/kilo-cli/test/memory-ui.test.tsx`. + +### UI-021: `/teams` organization selection modal entry +* **Priority:** Medium +* **Prerequisites:** Interactive session. Privacy mode OFF. +* **Steps:** + 1. Type `/teams` (or `/team`, `/org`) and press Enter. + 2. Observe dialog presentation. +* **Expected:** If unauthenticated, displays prompt to authenticate or alert that Gateway account is required. If authenticated, lists available organizations. +* **Cleanup:** Press `Escape` to exit dialog. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/teams.test.tsx`. + +### UI-022: Command palette (`Ctrl+P`) discovery and keyboard filtering +* **Priority:** Medium +* **Prerequisites:** Interactive session. +* **Steps:** + 1. Press `Ctrl+P` to open command palette. + 2. Type `kilo`. + 3. Verify filtered command list. +* **Expected:** Shows all Kilo-registered commands (e.g., `Kilo settings`, `Kilo memory`, `Share session publicly`). Arrow keys navigate; `Enter` executes; `Escape` closes. +* **Cleanup:** Press `Escape`. +* **Evidence / Limitations:** Automated harness reference: `packages/tui/test/cli/tui/command-palette.test.tsx`. + +--- + +## 4. Model Picker Presentation, Groups, Filtering & Privacy + +### UI-023: Model picker first-open loading state +* **Priority:** High +* **Prerequisites:** Interactive session with slow/mocked metadata latency. +* **Steps:** + 1. Open model picker (`/models` or click model in status bar). + 2. Observe dialog content immediately before network RPC resolves. +* **Expected:** Dialog renders centered placeholder: "Loading Kilo model metadata…". Options list is locked; filter input is suppressed while loading. No flash of empty or unstyled elements. +* **Cleanup:** Allow RPC to complete or press `Escape`. +* **Evidence / Limitations:** Handled in `packages/tui/src/component/dialog-model.tsx` (lines 228-235) and tested in `packages/kilo-cli/test/model-picker-ui.test.tsx`. + +### UI-024: Model picker grouping: "Kilo Auto" and "Recommended" +* **Priority:** High +* **Prerequisites:** Mocked or real catalog returning Auto IDs (`kilo-auto/free`, etc.) and models with `recommendedIndex`. +* **Steps:** + 1. Open model dialog `/models`. + 2. Inspect category section headers in the list. +* **Expected:** Models are grouped into categories: "Favorites" (if any), "Recent" (if any), "Kilo Auto", and "Recommended". "Kilo Auto" appears first among catalog groups, ordered by recommendation index. +* **Cleanup:** Close dialog. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/model-picker.test.ts` (lines 6-22). + +### UI-025: Known Kilo Auto IDs classification without metadata +* **Priority:** Medium +* **Prerequisites:** Catalog metadata omitting explicit `recommendedIndex` for `kilo-auto/efficient` and `auto-small`. +* **Steps:** + 1. Open model picker. + 2. Locate `kilo-auto/efficient` or `auto-small`. +* **Expected:** Known Auto IDs are grouped under "Kilo Auto" by ID heuristic (`isKiloAutoID`), even if server catalog metadata omits explicit recommendation rankings. +* **Cleanup:** Close dialog. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/model-picker.test.ts` (lines 24-38). + +### UI-026: Auto routing metadata does NOT classify regular models as Auto +* **Priority:** Medium +* **Prerequisites:** Model entry with `autoRouting: { models: [...] }` but ID `regular-model`. +* **Steps:** + 1. Inject/mock catalog with `regular-model` containing routing metadata. + 2. Open model picker. +* **Expected:** `regular-model` is NOT classified under "Kilo Auto". It remains in its standard provider category or "Recommended" (if indexed). +* **Cleanup:** Close dialog. +* **Evidence / Limitations:** Verified in `packages/kilo-cli/src/model-picker.ts` (lines 53-54). + +### UI-027: Model metadata fetch failure and fallback banner +* **Priority:** High +* **Prerequisites:** Simulate network timeout or failure on `KiloModels.Definition.list` RPC. +* **Steps:** + 1. Trigger model picker when RPC server endpoint returns 500 or network drops. + 2. Observe model picker dialog presentation. +* **Expected:** Picker switches to `groupState = "fallback"`. Displays warning footer: "Could not load Kilo model metadata; showing available models." Available local/cached models remain selectable. +* **Cleanup:** Close dialog. +* **Evidence / Limitations:** Handled in `packages/tui/src/component/dialog-model.tsx` (lines 237-241). + +### UI-028: Fast cancel and reopen of model picker (AbortSignal cleanup) +* **Priority:** Medium +* **Prerequisites:** Interactive session. +* **Steps:** + 1. Press `Ctrl+P` -> `/models` -> immediately press `Escape` within 50ms. + 2. Immediately reopen `/models`. +* **Expected:** AbortController cancels the first in-flight metadata promise (`controller.abort()`). Reopened dialog initiates a clean fresh fetch without race conditions, stale state, or memory leaks. +* **Cleanup:** Close dialog. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/model-picker.test.ts` (lines 95-130). + +### UI-029: Personal vs Team account credit context in model selection +* **Priority:** Medium +* **Prerequisites:** Authenticated Gateway profile with both personal and team organizations. +* **Steps:** + 1. Switch organization to a Team account via `/teams`. + 2. Open `/models` and inspect footer disclosures and credit implications. + 3. Switch back to Personal account. +* **Expected:** Model dialog correctly reflects team vs personal context. If team policies restrict BYOK or certain providers, unpermitted models are disabled or hidden. +* **Cleanup:** Revert to standard organization. +* **Evidence / Limitations:** Tested via `packages/kilo-cli/src/model-picker.ts`. + +### UI-030: Favorite models toggle and ordering +* **Priority:** Medium +* **Prerequisites:** Interactive session connected. +* **Steps:** + 1. Open `/models`. + 2. Highlight a model and trigger "Favorite" action (via action key or hotkey). + 3. Close dialog and reopen `/models`. +* **Expected:** Favorited model now appears in the top "Favorites" category section with "(Favorite)" marker. It is given priority in search filtering. +* **Cleanup:** Unfavorite model to restore initial state. +* **Evidence / Limitations:** Implemented in `packages/tui/src/component/dialog-model.tsx` (lines 214-221, 250-256). + +### UI-031: Favorite persistence across session restarts +* **Priority:** Medium +* **Prerequisites:** Favorite a model in UI-030. +* **Steps:** + 1. Exit Kilo session (`/exit`). + 2. Relaunch `$KILO_BIN`. + 3. Open `/models`. +* **Expected:** Favorited model remains pinned under "Favorites" across client restart. +* **Cleanup:** Unfavorite model. +* **Evidence / Limitations:** Stored in local TUI storage under `modelPreferenceKey`. + +### UI-032: Prompt training filter enabled: hides `mayTrainOnYourPrompts` models +* **Priority:** High +* **Prerequisites:** Set `hide_prompt_training_models: true` in project or profile config. Catalog includes models with `mayTrainOnYourPrompts: true`. +* **Steps:** + 1. Open `/models`. + 2. Search for the model known to have `mayTrainOnYourPrompts: true`. +* **Expected:** The model is completely omitted from the options list, favorites, and recent lists (`hidden: true`). It cannot be selected while the privacy filter is active. +* **Cleanup:** Revert setting to `false`. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/model-picker.test.ts` (lines 71-93). + +### UI-033: Prompt training filter disabled: displays "May train" disclosure +* **Priority:** High +* **Prerequisites:** `hide_prompt_training_models: false`. +* **Steps:** + 1. Open `/models`. + 2. Locate a model with `mayTrainOnYourPrompts: true`. +* **Expected:** The model is visible and selectable. Its footer disclosure explicitly displays "May train" (along with "BYOK" or "Free" if applicable). +* **Cleanup:** None. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/src/model-picker.ts` (lines 43-50). + +### UI-034: Fuzzy search filtering across titles and categories +* **Priority:** Low +* **Prerequisites:** Open `/models`. +* **Steps:** + 1. Type `claude son` in the filter input. + 2. Inspect result list. +* **Expected:** Fuzzysort matches titles and categories accurately. Favorited matching items sort above non-favorited matching items (`prioritizeFavorites`). +* **Cleanup:** Clear search input. +* **Evidence / Limitations:** Implemented in `packages/tui/src/component/dialog-model.tsx` (lines 162-167). + +--- + +## 5. Agent Selection, Ask/Debug Boundaries & Custom Agents + +### UI-035: Native `build` agent displayed as "Code" +* **Priority:** High +* **Prerequisites:** Interactive session running with default agent configuration. +* **Steps:** + 1. Open agent selector (`/agents`). + 2. Inspect the primary coding agent. +* **Expected:** The agent with underlying ID `build` is displayed with name "Code". No agent is listed with the display name "Build". +* **Cleanup:** Close dialog. +* **Evidence / Limitations:** Renamed in `packages/kilo-cli/src/agent-policy.ts` (line 41). + +### UI-036: Ask mode read-only system prompt and tool boundaries +* **Priority:** High +* **Prerequisites:** Session running. Switch agent to "Ask" via `/agents`. +* **Steps:** + 1. Verify status bar displays active agent "Ask". + 2. Prompt the agent: "Refactor index.ts to add a function". + 3. Inspect tools available to the model and output behavior. +* **Expected:** Agent system prompt enforces read-only mode ("You are in Ask mode — a read-only assistant..."). Tools `edit`, `shell`, and `subagent` are strictly denied by permission policy. Model provides text explanation without mutating files or running shell commands. +* **Cleanup:** Switch back to Code agent. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/agent-policy.test.ts` (lines 24-34). + +### UI-037: Ask mode env file read confirmation (`ask` effect) +* **Priority:** High +* **Prerequisites:** Workspace containing `.env` and `.env.example`. Active agent is "Ask". +* **Steps:** + 1. Ask agent: "Read .env and .env.example". + 2. Observe permission interaction. +* **Expected:** Reading `.env.example` is allowed automatically. Reading `.env` or `.env.local` triggers an interactive user confirmation prompt (`effect: "ask"`). Denying prevents the read; allowing proceeds. +* **Cleanup:** Close session. +* **Evidence / Limitations:** Defined in `packages/kilo-cli/src/agent-policy.ts` (lines 54-56). + +### UI-038: Debug agent systematic diagnosis workflow +* **Priority:** Medium +* **Prerequisites:** Switch agent to "Debug" via `/agents`. +* **Steps:** + 1. Prompt: "Diagnose why the test suite fails". + 2. Observe reasoning style. +* **Expected:** Debug agent follows systematic prompt guidelines: reflects on 5-7 potential sources, narrows down to 1-2, adds diagnostic logging/investigation before proposing minimal targeted fixes. +* **Cleanup:** Switch back to Code. +* **Evidence / Limitations:** Prompt defined in `packages/kilo-cli/src/agent-policy.ts` (lines 21-28). + +### UI-039: Custom project agent declaration in `kilo.jsonc` +* **Priority:** High +* **Prerequisites:** Project configuration file `$KILO_TEST_PROJECT/.kilo/kilo.jsonc` defining custom agent: + ```jsonc + { + "agents": { + "security-auditor": { + "description": "Scans for vulnerabilities", + "mode": "primary", + "system": "Audit code for security flaws." + } + } + } + ``` +* **Steps:** + 1. Launch Kilo CLI pointing to project. + 2. Open `/agents`. +* **Expected:** `security-auditor` is listed as a selectable primary agent with its custom description. Selecting it activates the agent with the specified system prompt. +* **Cleanup:** Remove custom agent from config. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/agent-policy.test.ts` (lines 41-70). + +### UI-040: Project custom agent overriding built-in `ask` +* **Priority:** High +* **Prerequisites:** Project config defining `agents: { ask: { description: "Custom Ask", system: "Custom ask prompt" } }`. +* **Steps:** + 1. Launch Kilo CLI. + 2. Inspect `/agents` and select "Ask". +* **Expected:** User-defined Ask agent takes precedence. Kilo post-policy does NOT overwrite the user's custom prompt, description, or permissions (`if (!editor.get("ask")) return`). +* **Cleanup:** Revert config. +* **Evidence / Limitations:** Verified in `packages/kilo-cli/src/agent-policy.ts` (line 45). + +### UI-041: Agent switching via composer slash command +* **Priority:** Medium +* **Prerequisites:** Session running. +* **Steps:** + 1. Type `/agents ask` or use `/agents` dialog to pick Ask. + 2. Inspect status bar. + 3. Type `/agents code` to switch back. +* **Expected:** Active agent switches cleanly. Status bar updates agent tag immediately. Subsequent prompts execute under the newly selected agent's permissions. +* **Cleanup:** Ensure default Code agent is restored. +* **Evidence / Limitations:** Agent switching handled by TUI runtime. + +### UI-042: Subagent delegation denial in Ask and Plan modes +* **Priority:** High +* **Prerequisites:** Session in Ask or Plan mode. +* **Steps:** + 1. Attempt to invoke a subagent task via prompt or mock tool call. +* **Expected:** Permission evaluation rejects `subagent` action with `effect: "deny"`. Subagents cannot be spawned by read-only Ask or Plan agents. +* **Cleanup:** Return to Code mode. +* **Evidence / Limitations:** Policy in `packages/kilo-cli/src/agent-policy.ts` (line 66) and `plan-policy.ts` (line 75). + +--- + +## 6. Plan Mode Workflow, Save Consent & Completion Gates + +### UI-043: Plan agent startup and read-only status +* **Priority:** High +* **Prerequisites:** Interactive session. Switch agent to "Plan" via `/agents`. +* **Steps:** + 1. Observe status bar showing "Plan". + 2. Ask Plan agent to inspect codebase files. +* **Expected:** Agent can read files (`read`, `grep`, `glob`), but all mutations outside `.kilo/plans/*.md` and all shell commands are strictly denied. +* **Cleanup:** Switch back to Code agent. +* **Evidence / Limitations:** Permissions in `packages/kilo-cli/src/plan-policy.ts` (lines 54-75). + +### UI-044: Save-permission regression check: absent `.kilo/plans` directory +* **Priority:** Critical +* **Prerequisites:** Clean project workspace where `.kilo/plans` directory does **NOT** exist initially. +* **Steps:** + 1. Launch Kilo in Plan mode. + 2. Ask Plan agent to finalize a plan. + 3. Model attempts to call `write` or `edit` on `.kilo/plans/feature-plan.md` or calls `plan_exit` before directory creation. +* **Expected:** Permission evaluation for `.kilo/plans/*.md` is Location-relative and allows writing under `.kilo/plans/`. However, `plan_exit` validates that `.kilo/plans` is an existing real directory within the project. If missing, it fails with: `"Plan directory does not exist yet; save the plan under .kilo/plans before calling plan_exit"`. Directory must be created properly before `plan_exit` succeeds. +* **Cleanup:** Remove `.kilo/plans/` directory if created. +* **Evidence / Limitations:** Critical regression test from recent session fix (`packages/kilo-cli/src/plan-policy.ts` lines 197-210, `packages/kilo-cli/test/plan-policy.test.ts`). + +### UI-045: Prompt-directed Save question form ("Finalize and save the plan") +* **Priority:** High +* **Prerequisites:** Active Plan session. Model has generated a complete proposal. +* **Steps:** + 1. Prompt: "The plan looks solid, please finalize it." + 2. Observe tool call. +* **Expected:** Model invokes the native `question` tool with choices: "Finalize and save the plan" vs "Continue refining". A visual selection form appears in the TUI titled "Questions" or "Save plan". +* **Cleanup:** Dismiss form. +* **Evidence / Limitations:** Known limitation: Save question is prompt-directed by system instructions, not a hardcoded deterministic model intercept (`packages/kilo-cli/src/plan-prompt.txt` lines 23-25). + +### UI-046: User chooses "Continue refining" in Save question form +* **Priority:** High +* **Prerequisites:** Save question form active from UI-045. +* **Steps:** + 1. Select option: "Continue refining". + 2. Submit form. +* **Expected:** Plan file is **NOT** written to disk. Session remains in Plan mode. Model responds acknowledging that planning continues and asks the next clarifying question. +* **Cleanup:** Close session. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/plan-policy.test.ts` (lines 121-154). + +### UI-047: User chooses "Finalize and save the plan" +* **Priority:** High +* **Prerequisites:** Save question form active. +* **Steps:** + 1. Select option: "Finalize and save the plan". + 2. Submit form. +* **Expected:** Model executes `write` tool to create `.kilo/plans/.md`. File is actually written to the filesystem. Model then proceeds to call `plan_exit`. +* **Cleanup:** Inspect written plan file and delete it. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/plan-policy.test.ts` (lines 50-84). + +### UI-048: Plan exit completion choice modal presentation +* **Priority:** High +* **Prerequisites:** Plan file written to `.kilo/plans/test-plan.md`. Model calls `plan_exit(path: ".kilo/plans/test-plan.md")`. +* **Steps:** + 1. Observe TUI when `plan_exit` executes. +* **Expected:** Native question tool renders an interactive completion form titled "Ready to implement?" offering exactly 3 choices: + - "Start new session" (Implement in a fresh session with a clean context) + - "Continue here" (Implement the plan in this session) + - "Keep refining" (Keep planning without implementing yet) +* **Cleanup:** Dismiss or answer form. +* **Evidence / Limitations:** Defined in `packages/kilo-cli/src/plan-policy.ts` (lines 105-125). + +### UI-049: Completion choice: "Continue here" switches to Code agent +* **Priority:** High +* **Prerequisites:** Completion form displayed from UI-048. +* **Steps:** + 1. Select "Continue here" and submit. +* **Expected:** Session agent immediately transitions from "plan" to "build" ("Code"). Implementation prompt is enqueued as a steer: `"Implement the approved plan at .kilo/plans/test-plan.md"`. Session begins implementation in the current context. +* **Cleanup:** Cancel execution (`Escape`). Delete plan file. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/plan-policy.test.ts` (lines 43-47, 147-155). + +### UI-050: Completion choice: "Start new session" forks clean context +* **Priority:** High +* **Prerequisites:** Completion form displayed from UI-048. +* **Steps:** + 1. Select "Start new session" and submit. +* **Expected:** A brand-new session is created with the Code agent (`build`) adopting the active model. Original planning session remains intact. Steer prompt initiates implementation in the new session. +* **Cleanup:** Close both sessions. Delete plan file. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/plan-policy.test.ts` (lines 305-327). + +### UI-051: Completion choice: "Keep refining" keeps Plan mode active +* **Priority:** High +* **Prerequisites:** Completion form displayed from UI-048. +* **Steps:** + 1. Select "Keep refining" and submit. +* **Expected:** Session stays in Plan mode. No agent switch occurs. Implementation prompt is NOT enqueued. +* **Cleanup:** Close session. Delete plan file. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/plan-policy.test.ts` (lines 329-347). + +### UI-052: Rejection of free-form completion answers +* **Priority:** Medium +* **Prerequisites:** Model attempts to submit an arbitrary string answer to the completion form (or user bypass). +* **Steps:** + 1. Simulate or supply answer `"Implement everything now"` instead of the 3 listed options. +* **Expected:** `plan_exit` fails with Tool.Error: `"Plan implementation requires one of the listed completion choices; this plan remains in Plan mode"`. Session does NOT leave Plan mode. +* **Cleanup:** Dismiss error. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/plan-policy.test.ts` (lines 349-367). + +### UI-053: `plan_exit` called with missing plan file on disk +* **Priority:** High +* **Prerequisites:** Model calls `plan_exit` for `.kilo/plans/nonexistent.md` without having written it. +* **Steps:** + 1. Execute tool call `plan_exit(path: ".kilo/plans/nonexistent.md")`. +* **Expected:** Tool execution fails with error: `"Plan file does not exist yet; save the plan to .kilo/plans/nonexistent.md before calling plan_exit"`. No completion dialog opens. +* **Cleanup:** None. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/plan-policy.test.ts` (lines 156-175). + +### UI-054: `plan_exit` called with invalid path outside `.kilo/plans` +* **Priority:** High +* **Prerequisites:** Plan mode session. +* **Steps:** + 1. Model attempts `plan_exit(path: "src/plan.md")` or `plan_exit(path: "../plan.md")`. +* **Expected:** Validation rejects path: `"Plan file must be under this project's .kilo/plans directory"`. Non-markdown files (`.txt`) are rejected with `"Plan file must be a Markdown file"`. +* **Cleanup:** None. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/plan-policy.test.ts` (lines 397-416). + +### UI-055: Global edit deny takes precedence over Plan save allowance +* **Priority:** High +* **Prerequisites:** Project configuration specifies global deny: `permissions: [{ action: "edit", resource: "*", effect: "deny" }]`. +* **Steps:** + 1. In Plan mode, attempt to write `.kilo/plans/plan.md`. +* **Expected:** Global edit deny wins. Write is blocked with permission denied. Plan cannot be saved. +* **Cleanup:** Remove global deny from config. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/plan-policy.test.ts` (lines 207-241). + +### UI-056: Plan mode resume from previous session +* **Priority:** Medium +* **Prerequisites:** Previous session ended in Plan mode. +* **Steps:** + 1. Relaunch Kilo and resume the previous Plan session via `/sessions`. + 2. Inspect active agent and prompt permissions. +* **Expected:** Resumed session retains Plan agent, read-only permissions, and access to saved plans under `.kilo/plans/`. +* **Cleanup:** Close session. +* **Evidence / Limitations:** Session state persistence across restarts. + +--- + +## 7. Kilo Settings UI Scopes, Precedence & Validation + +### UI-057: Settings scope selection: Profile vs Project +* **Priority:** High +* **Prerequisites:** Interactive session running in `$KILO_TEST_PROJECT`. +* **Steps:** + 1. Run command `/kilo-settings` (or `/kilo-config`). + 2. Inspect scope selection dialog. +* **Expected:** Dialog prompts: "Select a configuration scope" with two options: + - "Profile" (Edits `$XDG_CONFIG_HOME/kilo2/interactive/kilo.jsonc`) + - "Project" (Edits `$KILO_TEST_PROJECT/.kilo/kilo.jsonc`) + Each shows whether the target file exists or will be created. +* **Cleanup:** Press `Escape` to close. +* **Evidence / Limitations:** Handled in `packages/kilo-cli/src/tui-plugin/settings.tsx` (lines 69-80) and tested in `packages/kilo-cli/test/settings-ui.test.tsx`. + +### UI-058: Unwritable project scope handling +* **Priority:** Medium +* **Prerequisites:** Workspace project directory made read-only (`chmod 555 "$KILO_TEST_PROJECT"`). +* **Steps:** + 1. Open `/kilo-settings`. + 2. Select "Project" scope. +* **Expected:** Option remains selectable (not hidden), but selecting it triggers an alert dialog explaining that the project configuration target is not writable (`selected.reason`). +* **Cleanup:** Restore write permissions (`chmod 755 "$KILO_TEST_PROJECT"`). +* **Evidence / Limitations:** Handled in `packages/kilo-cli/src/tui-plugin/settings.tsx` (lines 81-88). + +### UI-059: Field list navigation and value descriptions +* **Priority:** Medium +* **Prerequisites:** Open `/kilo-settings` -> select "Profile" scope. +* **Steps:** + 1. Inspect the displayed settings fields list. + 2. Inspect the description column for each item. +* **Expected:** Fields display current profile value, project value (if writable), and which scope currently applies (e.g., `applies: profile` or `applies: project`). +* **Cleanup:** Press `Escape`. +* **Evidence / Limitations:** Verified via `packages/kilo-cli/src/tui-plugin/settings.tsx` (lines 178-186). + +### UI-060: Edit boolean field: Enable, Disable, Unset +* **Priority:** High +* **Prerequisites:** Open `/kilo-settings` -> "Profile" -> select a boolean setting (e.g., `hide_prompt_training_models`). +* **Steps:** + 1. Inspect choices dialog. + 2. Select "Enabled" and press Enter. + 3. Reopen setting, select "Unset", and press Enter. +* **Expected:** Displays "Enabled", "Disabled", and "Unset" (removes key from scope). Selecting "Enabled" persists `true` in `kilo.jsonc`. Selecting "Unset" cleanly removes the property without leaving null or undefined. Success toast appears. +* **Cleanup:** Verify `kilo.jsonc` clean state. +* **Evidence / Limitations:** Handled in `packages/kilo-cli/src/tui-plugin/settings.tsx` (lines 128-144). + +### UI-061: Edit integer field with validation boundary +* **Priority:** Medium +* **Prerequisites:** Open `/kilo-settings` -> select an integer setting with minimum constraint. +* **Steps:** + 1. Enter text: `not-a-number`. + 2. Observe toast. + 3. Enter number below minimum (e.g., `0` when minimum is `1`). + 4. Enter valid number (e.g., `5`). +* **Expected:** Non-integers and numbers below minimum trigger an error toast ("Enter a whole number of at least ...") without submitting. Valid number updates config successfully. +* **Cleanup:** Revert setting. +* **Evidence / Limitations:** Enforced in `packages/kilo-cli/src/tui-plugin/settings.tsx` (lines 154-160). + +### UI-062: JSONC comment and formatting preservation on write +* **Priority:** High +* **Prerequisites:** Existing `kilo.jsonc` containing inline comments (`// user note`) and custom indentation. +* **Steps:** + 1. Change a setting via `/kilo-settings`. + 2. Inspect `kilo.jsonc` on disk using file reader. +* **Expected:** Modifying the setting uses `jsonc-parser` modify API. Surrounding comments and indentation are preserved intact; file is not reformatted into raw stripped JSON. +* **Cleanup:** Restore original file. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/src/privacy-settings.ts` modify routines and `packages/kilo-cli/test/settings.test.ts`. + +### UI-063: Concurrent stale edits rejection (`expected` revision guard) +* **Priority:** High +* **Prerequisites:** Two open sessions or external process modifying `kilo.jsonc` simultaneously. +* **Steps:** + 1. Open `/kilo-settings` in session A (reads snapshot revision). + 2. In external shell, write a change directly to `kilo.jsonc`. + 3. In session A, attempt to commit a settings change. +* **Expected:** Mutation fails or detects stale revision guard (`expected: selected.expected`), presenting an alert: configuration changed on disk; prompts user to reload. Stale in-memory value does not blindly clobber disk edits. +* **Cleanup:** Reload settings. +* **Evidence / Limitations:** Guarded by `packages/kilo-cli/src/settings-rpc.ts`. + +### UI-064: Ancestor project config discovery and inheritance +* **Priority:** Medium +* **Prerequisites:** Workspace nested inside a monorepo root containing `.kilo/kilo.jsonc`. +* **Steps:** + 1. Launch Kilo from sub-package directory. + 2. Open `/kilo-settings` -> inspect active settings. +* **Expected:** Settings correctly discover and inherit from the canonical project boundary root. Project settings display values inherited from ancestor root config. +* **Cleanup:** None. +* **Evidence / Limitations:** Project boundary resolution in `packages/kilo-cli/src/paths.ts`. + +### UI-065: Invalid JSONC syntax in config file on disk +* **Priority:** Medium +* **Prerequisites:** Manually introduce a syntax error in `.kilo/kilo.jsonc` (e.g., trailing dangling comma or unclosed brace). +* **Steps:** + 1. Launch Kilo and run `/kilo-settings`. +* **Expected:** Alert dialog explains that the configuration file contains invalid JSON/syntax errors and cannot be parsed safely. App does not crash. +* **Cleanup:** Fix syntax error in `kilo.jsonc`. +* **Evidence / Limitations:** Handled in `packages/kilo-cli/src/settings-rpc.ts`. + +### UI-066: Wrong-type value in config file (e.g., string instead of boolean) +* **Priority:** Medium +* **Prerequisites:** Manually write `"{ \"hide_prompt_training_models\": \"true\" }"` (string) in config. +* **Steps:** + 1. Launch Kilo and open `/kilo-settings`. + 2. Inspect the field description. +* **Expected:** Field displays an `invalid` notice marker in the list (e.g., `invalid: expected boolean`). User can overwrite it with a valid boolean selection. +* **Cleanup:** Revert config. +* **Evidence / Limitations:** Handled in `packages/kilo-cli/src/tui-plugin/settings.tsx` (line 184). + +--- + +## 8. Memory UI: Compact Sidebar, Dialog & Toggle Controls + +### UI-067: Compact sidebar memory status row: "Enabled" (neutral tone) +* **Priority:** High +* **Prerequisites:** Interactive session running with memory enabled in project. +* **Steps:** + 1. Inspect the sidebar "Memory" section. + 2. Observe the status dot bullet and text label. +* **Expected:** Displays bullet `•` and text label `Enabled` in muted subdued tone (`theme.text.subdued`). **Limitation note:** Historical v1 active/save 5-second pulse is NOT implemented in v2; Enabled honestly stays neutral/muted. +* **Cleanup:** None. +* **Evidence / Limitations:** Implemented in `packages/kilo-cli/src/tui-plugin/sidebar-memory.tsx` (lines 17-29, 125-134) and tested in `packages/kilo-cli/test/sidebar-memory.test.tsx`. + +### UI-068: Compact sidebar memory status row: "Disabled" +* **Priority:** High +* **Prerequisites:** Memory disabled for project (run `/memory disable`). +* **Steps:** + 1. Inspect the sidebar Memory row. +* **Expected:** Status bullet renders subdued and label displays `Disabled`. +* **Cleanup:** Re-enable memory via `/memory enable`. +* **Evidence / Limitations:** Handled in `packages/kilo-cli/src/tui-plugin/sidebar-memory.tsx` (line 27). + +### UI-069: Compact sidebar memory status row: "Unavailable" / Error tone +* **Priority:** Medium +* **Prerequisites:** Memory RPC client disconnected or server endpoint error injected. +* **Steps:** + 1. Disconnect or fail the memory RPC. + 2. Inspect sidebar. +* **Expected:** Status bullet renders in error feedback color (`theme.text.feedback.error.default`) and label displays `Unavailable`. +* **Cleanup:** Restore RPC. +* **Evidence / Limitations:** Handled in `packages/kilo-cli/src/tui-plugin/sidebar-memory.tsx` (line 130). + +### UI-070: Memory full modal dialog (`/memory show` or `/memory`) +* **Priority:** High +* **Prerequisites:** Memory enabled with saved project memories. +* **Steps:** + 1. Run command `/memory show`. + 2. Inspect the modal dialog. +* **Expected:** Opens full memory browser dialog displaying stored memory records (decisions, constraints, project notes). Up/Down arrows scroll entries; `Escape` closes dialog. +* **Cleanup:** Close dialog. +* **Evidence / Limitations:** Implemented in `packages/kilo-cli/src/tui-plugin/memory-dialog.tsx` and tested in `packages/kilo-cli/test/memory-ui.test.tsx`. + +### UI-071: Memory toggle commands: `/memory enable` and `/memory disable` +* **Priority:** High +* **Prerequisites:** Interactive session running. +* **Steps:** + 1. Run `/memory disable`. Observe toast and sidebar. + 2. Run `/memory enable`. Observe toast and sidebar. +* **Expected:** `/memory disable` displays toast "Memory disabled." Sidebar updates to `Disabled` immediately. `/memory enable` displays toast "Memory enabled." Sidebar updates to `Enabled`. State is persisted in project storage. +* **Cleanup:** Ensure memory is left in desired state. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/memory.test.ts`. + +### UI-072: Memory automatic consolidation toggle: `/memory auto on|off` +* **Priority:** Medium +* **Prerequisites:** Interactive session. +* **Steps:** + 1. Run `/memory auto on`. Observe toast. + 2. Run `/memory auto off`. Observe toast. +* **Expected:** Toasts display "Automatic consolidation enabled." and "Automatic consolidation disabled." respectively. +* **Cleanup:** None. +* **Evidence / Limitations:** Handled in `packages/kilo-cli/src/tui-plugin/memory.tsx` (lines 83-91). + +### UI-073: Multi-project memory boundary isolation across project switch +* **Priority:** High +* **Prerequisites:** Two separate projects: Project A with memory A, Project B with memory B. +* **Steps:** + 1. Launch session in Project A. Open `/memory show` and verify memory A is shown. + 2. Switch project location via `/open` or launch session in Project B. + 3. Open `/memory show` in Project B. +* **Expected:** Project B shows strictly memory B records. Memories from Project A are completely invisible in Project B. No cross-project memory leakage. +* **Cleanup:** None. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/memory.test.ts` (project isolation tests). + +### UI-074: Memory manual remember and forget from command palette +* **Priority:** Medium +* **Prerequisites:** Interactive session with memory enabled. +* **Steps:** + 1. Run command palette -> `Kilo memory: remember`. Enter test memory note. + 2. Verify toast confirmation. + 3. Open `/memory show` to verify entry appears. + 4. Run `Kilo memory: forget` targeting the test key. +* **Expected:** Memory is saved cleanly and subsequent forget removes the record from persisted storage. +* **Cleanup:** Verify test memory is removed. +* **Evidence / Limitations:** Handled via memory RPC dispatch. + +--- + +## 9. Sidebar Truthfulness: Credits, Indexing & Usage + +### UI-075: Credits sidebar row: Signed-out state +* **Priority:** High +* **Prerequisites:** Launch session without Kilo Gateway authentication (no token / signed out). +* **Steps:** + 1. Inspect the "Credits" section in the active session sidebar. +* **Expected:** Displays header `Credits` with subdued text: `Sign in to view credits`. Does not display fictitious balances ($0.00 or null). +* **Cleanup:** None. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/src/tui-plugin/sidebar-account.tsx` (lines 130-132) and `packages/kilo-cli/test/sidebar-account.test.tsx`. + +### UI-076: Credits sidebar row: Personal credits and Kilo Pass display +* **Priority:** High +* **Prerequisites:** Authenticated Personal Gateway account with active balance and Kilo Pass. Privacy mode OFF. [USER-OPT-IN or FIXTURE] +* **Steps:** + 1. Inspect sidebar Credits section. +* **Expected:** Displays `Personal credits` with formatted currency (e.g., `$25.40`). Displays breakdown rows: + - `└ Kilo Pass` with usage / base credits (e.g., `$12.00 / $50.00`) + - `Bonus` if positive bonus credits exist + - `Renews` with formatted month/day (e.g., `Oct 1`) +* **Cleanup:** None. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/src/tui-plugin/sidebar-account.tsx` (lines 143-181) and `packages/kilo-cli/test/sidebar-account-ui.test.tsx`. + +### UI-077: Credits sidebar row: Privacy mode masking (`/privacy on`) +* **Priority:** High +* **Prerequisites:** Authenticated account with credits visible from UI-076. +* **Steps:** + 1. Run command `/privacy on`. + 2. Inspect sidebar Credits row. +* **Expected:** Credit label shifts to generic "Personal credits" or "Team credits" without organization name. Balance value is masked with `•••`. Kilo Pass breakdown is completely hidden. +* **Cleanup:** Run `/privacy off`. +* **Evidence / Limitations:** Automated harness reference: `packages/kilo-cli/test/privacy-ui.test.tsx`. + +### UI-078: Codebase indexing sidebar row: "Disabled", "In Progress", "Complete" +* **Priority:** High +* **Prerequisites:** Session launched with or without `--indexing-config`. +* **Steps:** + 1. Launch without indexing config: inspect sidebar "Code Indexing". + 2. Launch with indexing config during initial scan: inspect sidebar. + 3. Allow scan to complete: inspect sidebar. +* **Expected:** + - Disabled: Displays `Code Indexing` with subdued label `Disabled` (or `Unavailable` if RPC not wired). + - In Progress: Displays progress: `X / Y files (Z%)` with subdued status `In Progress`. + - Complete: Displays success feedback green label `Complete`. +* **Cleanup:** None. +* **Evidence / Limitations:** Handled in `packages/kilo-cli/src/tui-plugin/sidebar-indexing.tsx` (lines 55-70) and tested in `packages/kilo-cli/test/sidebar-indexing-ui.test.tsx`. + +### UI-079: Session usage sidebar row: Token breakdown and cost +* **Priority:** Medium +* **Prerequisites:** Session with executed prompt turns. Privacy mode OFF. +* **Steps:** + 1. Inspect sidebar "Usage" section. +* **Expected:** Displays token counts and estimated costs for the active session (input tokens, output tokens, cache read/write). Values reflect durable persisted usage without triggering fresh model requests. +* **Cleanup:** None. +* **Evidence / Limitations:** Handled in `packages/kilo-cli/src/tui-plugin/sidebar-usage.tsx` and tested in `packages/kilo-cli/test/sidebar-usage-ui.test.tsx`. + +### UI-080: Session usage sidebar row: Privacy masking +* **Priority:** Medium +* **Prerequisites:** Session with usage displayed from UI-079. +* **Steps:** + 1. Run `/privacy on`. + 2. Inspect sidebar Usage section. +* **Expected:** Token counts and cost figures are masked or sanitized so sensitive session volume is obscured when presenting/sharing screen. +* **Cleanup:** Run `/privacy off`. +* **Evidence / Limitations:** Handled in `packages/kilo-cli/src/tui-plugin/sidebar-usage.tsx` (line 116). + +--- + +## 10. Pull-request sidebar + +### UI-081: PR lookup, exact-head fallbacks and malformed output + +* **Status:** NOT RUN; implementation under review. +* **Priority:** High +* **Prerequisites:** Shared disposable setup; fixture-only `gh` executable on the child PATH, isolated Git repository and no real GitHub credentials. Use `test/sidebar-pr-ui-fixture.tsx` as the fixture reference. +* **Steps:** Exercise tracking-reference lookup, branch lookup, local SHA search, fork-parent branch search and final fork SHA search. Supply a nonmatching SHA before a matching result, malformed output, and no installed `gh`. Render a long PR title at narrow and wide sizes. +* **Expected:** Display only the selected fixture PR number/title. SHA-based fallbacks require exact head equality. Malformed output does not crash or produce an unhandled rejection; unavailable results invent no PR. Record each fallback's actual invocation and result rather than inferring coverage from the initial view lookup. +* **Cleanup:** Terminate only case-owned processes and follow the shared disposable-root cleanup. +* **Evidence / Limitations:** `packages/kilo-cli/src/tui-plugin/sidebar-pr.tsx`, `packages/kilo-cli/test/sidebar-pr.test.tsx`, `packages/kilo-cli/test/sidebar-pr-ui.test.tsx`. No live account or network requests are authorized by this case. + +### UI-082: PR sidebar supersede, teardown and branch-refresh limitation + +* **Status:** NOT RUN; implementation under review. +* **Priority:** High +* **Prerequisites:** UI-081 fixture setup with two session locations and a controllably delayed fixture `gh` process. +* **Steps:** Switch viewed sessions during lookup, hide/show the sidebar, resize without changing location, and exit during a delayed lookup. Record started child PIDs and observe their exit. Separately change the Git branch within one location and record whether a native VCS event reaches the UI. +* **Expected:** No late result from the previous location appears. Teardown terminates and waits for case-owned lookup children; no shell/model session is created. Width-only changes do not trigger duplicate lookup. Missing same-location watcher events are recorded as an open producer limitation, not claimed fixed by the PR component; explicit remount/sync is recorded separately. +* **Cleanup:** Verify all case-owned PIDs have exited, then follow shared cleanup. +* **Evidence / Limitations:** Same component/fixture as UI-081; `kilocode/baseline/sidebar-pr-v2-parity.md`. Watcher behavior must be measured per host and repository layout, including worktrees. + +### UI-083: Running-shell sidebar lifecycle and session isolation + +* **Status:** NOT RUN. +* **Priority:** High +* **Prerequisites:** Shared isolated setup; harmless long-running shell command, two sessions, no account/model access required. Use `test/sidebar-processes-ui-fixture.tsx` as the executable reference. +* **Steps:** Start a session shell; observe its command and real PID. Switch sessions and return while it runs. Let it end while another session is focused, then return. Resize narrow/wide, repeat tab switches, and exit. Separately record behavior after restarting the client/server; do not infer that outcome from same-client tab tests. +* **Expected:** Only durable shell messages in the viewed session joined to live running registry entries render. Ended/missing entries disappear after reconciliation. No invented ports, readiness, or description fields. Repeated mounts do not retain event subscriptions. A tool-created shell without a durable shell-message link is a separate coverage question, not assumed visible. +* **Cleanup:** Stop and await only case-owned processes; follow shared cleanup. +* **Evidence / Limitations:** `packages/kilo-cli/src/tui-plugin/sidebar-processes.tsx`, `test/sidebar-processes.test.tsx`, `test/sidebar-processes-ui.test.tsx`. Automated `client` and `server` scenarios now exercise actual reopen/restart on an isolated host: live PID preserved on client reopen; stale durable running history hidden after server restart and resync. Manual status remains NOT RUN; this is not v1 background-process supervision parity. + +## 12. Quick-Smoke Subset (~12 Essential IDs) + +For rapid validation before full-suite manual or automated runs, execute this focused smoke subset: + +1. **`UI-001`**: Clean interactive launch with default profile (no crash, clean alt screen). +2. **`UI-002`**: Kilo ASCII logo branding in splash and header. +3. **`UI-005`**: Narrow terminal layout (80 columns minimum constraint check). +4. **`UI-010`**: BB 1-minute idle reproduction and state capture. +5. **`UI-013`**: Terminal window resize event restoration (SIGWINCH redraw). +6. **`UI-015`**: Slash command autocomplete list deduplication. +7. **`UI-018`**: `/settings` (native) vs `/kilo-settings` (Kilo config) distinct routing. +8. **`UI-023`**: Model picker first-open loading state ("Loading Kilo model metadata…"). +9. **`UI-035`**: Native `build` agent displayed as "Code". +10. **`UI-044`**: Save-permission regression check: absent `.kilo/plans` directory. +11. **`UI-047`**: Plan workflow: "Finalize and save the plan" real file write. +12. **`UI-057`**: Kilo settings scope selection: Profile vs Project dialog. diff --git a/migration-tracking/test-plans/kilo-opencode-v2-test-plan.md b/migration-tracking/test-plans/kilo-opencode-v2-test-plan.md new file mode 100644 index 000000000000..169cd9971234 --- /dev/null +++ b/migration-tracking/test-plans/kilo-opencode-v2-test-plan.md @@ -0,0 +1,118 @@ +# Kilo v2 scenario test plan + +Separate from the [implementation plan](kilo-opencode-v2-issue-13750.md). +This is a test checklist, not a claim that scenarios have passed. + +## Scenario lists + +| List | Scope | Manual execution status | +|---|---|---| +| [CLI/TUI scenarios](kilo-opencode-v2-test-plan-ui.md) | Dialogs, model selection, agents, Plan, settings, memory and sidebar, terminal behavior | NOT RUN | +| [Runtime and data-safety scenarios](kilo-opencode-v2-test-plan-runtime.md) | Permissions, persistence, imports, Gateway, cloud/remote/share, isolation, sandbox, telemetry and ACP | NOT RUN | + +The inventory contains **164 scenarios**: `UI-001`–`UI-083` and +`RT-001`–`RT-081`. Setup instructions and source references are reviewed +separately from executing the scenarios; an authored case is not validation. + +Use the stable `UI-…` and `RT-…` identifiers when reporting failures. Automated +test references identify related coverage; they do not certify a manual test or +a deployed service. VS Code and JetBrains implementation/testing is deferred. + +## Start safely + +- Record the checkout commit, whether it has local changes, launch command, + runtime version, terminal application and dimensions. A source preview and an + older compiled binary can behave differently even from the same checkout. +- The source launcher from the repository root is + `./packages/kilo-cli/dist/interactive/kilo2`. It uses the bundled runtime and + current source. Restart the preview host to load changed host policy; do not + assume opening a new client refreshes an already-running daemon. +- Use a disposable project and isolated preview profile for mutation, import, + sandbox and failure-injection cases. Never point these cases at production + Kilo/OpenCode stores, real credentials or valuable files. +- Treat any account connection, upload, remote enablement, public sharing, + telemetry collector, paid model request or real embedding request as a + separate explicit opt-in. Offline fixture coverage does not authorize those + actions or establish their deployed contracts. +- Prefer cancelling or closing a dialog over changing an existing account or + project setting just to inspect the UI. Record and restore intentional test + settings; do not delete an entire profile as cleanup. + +## Suggested order + +1. Run the UI list's quick-smoke subset first: launch, slash-command uniqueness, + first-open model loading, Code/Plan selection, plan save and cancellation, + settings navigation, compact memory status, resize and restart. +2. Exercise the recently changed features with disposable data: permissions, + same-project versus other-project state, account/location refresh and JSONC + precedence. A displayed path or success message is not evidence of a saved + file: check persisted content and reopen it. +3. Run lifecycle and failure cases: interrupted streams, reconnect, failed + requests, cancelled forms, stale dialog revisions and process restart. +4. Run fixture-only data-safety cases before considering any opted-in live + account test. Unsupported or unverified behavior must remain explicitly + blocked, not silently replaced by a different workflow. + +## Automated validation + +Run from `packages/kilo-cli`, never the repository root: + +```sh +./dist/interactive/bun run typecheck +./dist/interactive/bun run script/test.ts +``` + +For a focused compiled/host test batch, pass test paths to `script/test.ts`. +This builds a fresh artifact and sets `KILO_CLI_TEST_ARTIFACT_DIR`; bare +`bun test` must not silently use an old `dist/kilo2`. Do not build with the +ambient Bun 1.3.14 runtime. Coordinate the full suite after writers settle; +changing shared files underneath a run invalidates its acceptance evidence. + +Never reset, check out, stash or temporarily replace shared working files to +compare a baseline. Use an isolated source snapshot or disposable worktree. + +## Record each execution + +Copy this record into a separate results note; leave the checklist reusable. +Redact tokens, balances, private transcript content and sensitive filesystem +paths from screenshots or request captures before sharing them. + +| Field | Value | +|---|---| +| Case ID / date / tester | | +| Commit / dirty state / runtime / launch command | | +| OS / terminal / dimensions / profile and project scope | | +| Preconditions and intentional deviations | | +| Actual result | | +| Verdict | NOT RUN / PASS / FAIL / BLOCKED / NOT APPLICABLE | +| Evidence | Screenshot, sanitized output, fixture assertion or error | +| Cleanup / settings restored | | +| Follow-up issue / reproducibility | | + +A known gap is **BLOCKED**, not PASS. Use NOT APPLICABLE only with a concrete +scope or platform reason. An automated fixture result must be labeled as such, +not substituted for a live/manual result. + +## Current cautions + +- Plan save consent is prompt-directed. The native permission boundary and + completion choice are exercised separately; do not treat a scripted fixture + as proof that every model will follow the save-question instruction. +- Memory's compact status row is implemented, but the historical per-session + activity indicator and five-second saved pulse remain a parity gap. A neutral + Enabled bullet is not evidence that memory failed or that it was injected. +- BB terminal elements disappearing after idle is still unresolved. Record + whether typing changes it and whether resizing restores it; do not confuse + that with terminal teardown on exit or session-data loss. +- Gateway security/config changes, remote compatibility and the settings UI + selection investigation require their own final integration evidence. +- Gateway credential-body hygiene must include prompts and automatic titles + on every transport. Independent review corrected the earlier explanation: + titles use normal request preparation. The old AISDK-compatible adapter + ignored supplied HTTP middleware; the new native route removes that path. + Focused all-wire tests and independent correctness review pass. The stable + pre-cleanup snapshot passed the full CLI wrapper (443 tests, 0 failures); + subsequent cleanup requires fresh focused checks. Compiled Auto packaging + remains unverified end to end. +- Local share/relay/cloud fixtures do not establish which contract is deployed. +- The checklist does not increase the implementation completion percentage. From a876c2912a5fd114066ce34e3a3fcc79f7f0a1b0 Mon Sep 17 00:00:00 2001 From: Fabio Pliger Date: Sun, 11 Oct 2026 12:38:07 -0500 Subject: [PATCH 02/18] docs(migration): add migration context brief --- migration-tracking/plans/migration-context.md | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 migration-tracking/plans/migration-context.md diff --git a/migration-tracking/plans/migration-context.md b/migration-tracking/plans/migration-context.md new file mode 100644 index 000000000000..39cdc6e608f4 --- /dev/null +++ b/migration-tracking/plans/migration-context.md @@ -0,0 +1,106 @@ +# Migration context (canonical brief) + +This file is the single source for the shared facts, decisions and rules of the Kilo v1 → v2 migration. Role prompts (discovery, planner, coordinator) and implementation session prompts point here instead of restating it. When a fact here changes, change it here first. + +Status: bootstrap. Last updated 2026-10-11. + +## Branches and pinned refs + +| Item | Value | +|---|---| +| Integration branch | `kilo-v2-clean` (long-lived; becomes `main` at cutover) | +| Current working base | `kilo-v2-clean-bootstrap` (seeded docs on top of `kilo-v2-clean`). Discovery, planner and coordinator branch from this until its PR is merged into `kilo-v2-clean` | +| Upstream base commit | `5e73d5cb1f788637b1b3d5cf1298ea694cbad0c5`: the parent of upstream release tag `v2.0.26`, on `upstream/v2` (`feat(core): add Google Cloud credential setup for Vertex (#53798)`) | +| Upstream release tag | `v2.0.26` = `9b4ec5714d481559990db0a816d5dec19541a814` (reference marker only; not on `upstream/v2`, see note) | +| Archived old port | tag `kilo-v2-checkpoints-2026-09` = `879b89e89034f0e9f4cfb7fd09a5323ef06f83c9` (former `kilo-v2` tip). Donor code only | +| V1 target (behavioral reference) | `origin/main` pinned at `fb7e96eda5409517a05948b9efaf01c53997bf6e` (2026-10-09) | +| V1 assessment snapshot | `f7115470` (2026-08-12): where the existing 816-file source assessment stops | + +Note on the base: upstream cuts each release as a separate version-bump commit (`release: vX`) on top of the `v2` tip, tags it, and does not merge it back into `v2`. The tag is therefore one commit off the branch. The base is the tag's parent: release-equivalent code, on `upstream/v2`, and free of the version-bump commit, so later merges of `upstream/v2` stay clean. + +Eventual end state: `kilo-v2-clean` becomes `main`; today's `main` becomes `v1`, maintained for critical fixes only, for a limited period. + +## Repositories + +- Kilo repo: `Kilo-Org/kilocode` (remote `origin`). `main` is Kilo v1. +- Upstream: `anomalyco/opencode` (remote `upstream`, fetch only), branch `v2`. Published on npm as `@opencode/cli`. Upstream renamed its workspace scope from `@opencode-ai/*` to `@opencode/*` on 2026-09-07 (`a5312e16`); a few packages kept the old scope. +- The old port branch `kilo-v2` still exists but is frozen. Read it through the archive tag. + +## Decisions (authoritative) + +Do not re-litigate these without new evidence. Proposed changes go to the user. + +1. **New base.** A clean branch from upstream v2 (see pinned refs). Never merge the old port branch into it. Upstream changes arrive by merging the `upstream/v2` branch, pinned by SHA each time; release tags are markers, not merge targets. +2. **Freeze.** The old port branch is archived donor code. Current v1 `main` (pinned above) is the behavioral reference. +3. **Donor rules.** The donor provides mechanism: how a Kilo feature is expressed on v2 (seams, RPC definitions, plugin shape, host-adapter pattern). Behavior and policy come from current v1. +4. **Staleness gate.** For each feature, diff the v1 files it touches between the assessment snapshot and the v1 target. Empty delta: the donor is likely faithful. Non-empty: re-derive behavior from v1 and keep only the donor's v2 mapping. +5. **Staged cutover.** G1 CLI/TUI → G2 VS Code → G3 JetBrains, each with its own go/no-go. G2 and G3 are independent client-surface tracks that run in parallel with G1 once their prerequisites land. Until their gate, the IDEs stay on v1: VS Code bundles the CLI from the same `main` build (no version pin); JetBrains pins a CLI version. +6. **Storage.** A separate customer v2 data store. v1 files are never touched while any v1 surface ships. Import is opt-in and re-runnable. Path consolidation only after G3. +7. **Scope.** Post-cutover (non-gating): voice, image generation, team and incremental sharing, legacy TUI extras, and features behind experimental flags on v1 unless explicitly made a gate. KiloClaw is not needed. Hard gates: FIM/next-edit (G2, G3). Cloud agents and Anaconda Desktop gate whichever surface they consume. +8. **Gates override P-labels** for surface readiness. P-labels order work globally; gates define what a surface needs to ship. +9. **Plan format.** One file with two parts: Part A high level (epics, gates, critical path, risks), Part B low level (per-epic units, dependencies, acceptance), plus appendices (facts, glossary, links). +10. **Readability.** Separate why, what, when and how-to-verify. Progressive disclosure. No sub-numbered steps. Issue IDs only in Part B and the register. One canonical place per fact. + +## Working model + +| Rule | Detail | +|---|---| +| Human merges | The user merges every PR. No agent merges. | +| Coordinator | Read-only on the integration branch and the working base. Never pushes to them, never force-pushes, never rebases them, never holds a long-lived branch. | +| Implementation | Sessions are started by the user from prompts the coordinator prepares. One unit per session, one branch and one PR per unit. | +| Coordinator meta edits | Default: micro-PR (short-lived hyphenated branch such as `coord-status-` or `coord-plan-`, one logical change, report the URL, delete the branch after merge). For edits that deserve an audited session: a micro-session prompt for the user instead. | +| GitHub actions by the coordinator | May comment on issues and set labels for status hygiene, conservatively and only when it clearly helps. Never creates or closes issues; that goes through the user or a prepared prompt. | +| Writers | One writer per branch and per file. Announce ownership before starting. | +| Escalation | Epic or scope changes, gate assignment of consumers, and anything that changes a decision above go to the user. | + +**Enforcement is by convention only, for now.** The account running the migration has write access but not repo admin, so branch protection on `kilo-v2-clean` cannot be set yet (see deferred admin tasks). Until an admin adds it: + +- Direct pushes to `kilo-v2-clean` are forbidden by convention. Every change reaches it through a PR. +- Repo settings, Actions settings, secrets and branch protection are escalations to a repo admin. No agent attempts them. + +## Conventions + +- Follow the repo `AGENTS.md` and `migration-tracking/technical-notes/v2-fork-conventions.md`. Note that the `AGENTS.md` on this branch is upstream's until Kilo's is re-landed. +- Branch names: short, hyphenated, no slashes or type prefixes. Commits and PR titles: `type(scope): summary`. +- Kilo behavior lives in `kilo-*` packages or `packages//src/kilocode/`. Shared-file patches carry a `kilocode_change` marker and a stated missing seam. +- After changing the public Protocol or Server `HttpApi`, run `bun run generate` from `packages/client`. Never edit generated files. +- Run `bun typecheck` from package directories, never `tsc`. Do not run tests from the repo root. +- No Changesets. +- Run `bun install` after switching to this branch from a v1 checkout; the dependency graph differs and the upstream pre-push hook (`bun run check`) fails without it. +- Compare any failing check against the recorded baseline before attributing it to a change. + +## Definition of done per unit + +Code, tests, acceptance evidence, an updated progress row, and docs. A local pass is never recorded as end-to-end. + +## Deferred admin tasks + +These need a repo admin (or account credentials) and do not block work. Track them here and in a GitHub issue labelled `admin`. + +| Task | When | Owner | +|---|---|---| +| Branch protection on `kilo-v2-clean`: require a PR before merging (0 approvals now, raise to 1 when reviewers are available), force-push and deletion disabled | Now | Repo admin | +| Actions permissions and secret access for the upstream sync workflow and the e2e lane | When those workflows land | Repo admin | +| Signing and notarization credentials | Before the G1 preview release train | Account owner | +| Protect the branch that becomes `main` | At cutover | Repo admin | + +## Bootstrap status + +| Item | State | +|---|---| +| `kilo-v2-clean` created from the upstream base | Done | +| Old port archived as a tag | Done | +| Migration docs seeded on `kilo-v2-clean-bootstrap` | Done | +| `kilo-v2-clean-bootstrap` pushed to origin | Done | +| This file | Done | +| Branch protection | Deferred (admin task) | +| Baseline health recorded (`bun install`, `bun run check`) | Recommended next | + +## Reference artifacts + +- Plans: `migration-tracking/plans/` (previous execution plan, progress plan and working log; reference only until the new plan lands). +- Discovery outputs (to be created): `migration-tracking/discovery/`. +- Source parity: `migration-tracking/marker-audit/` (the 816-file assessment and shared-change inventory). +- API and session differences: `migration-tracking/technical-notes/baseline/v2-v1-schema-diffs.md`, `migration-tracking/technical-notes/baseline/v2-v1-session-diffs.md`. +- Technical notes: `migration-tracking/technical-notes/` and `technical-notes/baseline/`. +- GitHub: issue #13750 and its children, for scope and acceptance detail. From 56416ab58ba8c55ed514a504938e572c3058a105 Mon Sep 17 00:00:00 2001 From: Fabio Pliger Date: Sun, 11 Oct 2026 21:05:30 -0500 Subject: [PATCH 03/18] docs(migration): add v2 discovery report and refresh pinned baseline --- migration-tracking/discovery/doc-inventory.md | 324 +++++++++++ .../discovery/v2-epic-taxonomy.md | 213 ++++++++ .../discovery/v2-feature-inventory.md | 501 ++++++++++++++++++ .../discovery/v2-unit-register.md | 384 ++++++++++++++ .../baseline/pinned-v2-baseline.md | 314 ++++------- 5 files changed, 1512 insertions(+), 224 deletions(-) create mode 100644 migration-tracking/discovery/doc-inventory.md create mode 100644 migration-tracking/discovery/v2-epic-taxonomy.md create mode 100644 migration-tracking/discovery/v2-feature-inventory.md create mode 100644 migration-tracking/discovery/v2-unit-register.md diff --git a/migration-tracking/discovery/doc-inventory.md b/migration-tracking/discovery/doc-inventory.md new file mode 100644 index 000000000000..1c98fd2b3a40 --- /dev/null +++ b/migration-tracking/discovery/doc-inventory.md @@ -0,0 +1,324 @@ +# Discovery: classification of the seeded `migration-tracking/` docs + +Status: discovery output, 2026-10-11. Owner: discovery analyst (single writer). Companion files: `v2-feature-inventory.md`, `v2-epic-taxonomy.md`, `v2-unit-register.md`. + +Scope: every file seeded under `migration-tracking/` on `kilo-v2-clean-bootstrap` (HEAD `a876c2912a` = BASE `5e73d5cb` + 2 documentation commits). Classification was read-only. It did not move, rename or delete any seeded file; acting on these classes is planner or coordinator work. + +**Post-classification change.** This discovery phase refreshed `technical-notes/baseline/pinned-v2-baseline.md` at BASE (it was rewrite-or-drop). It is now **living**. The counts below reflect that. `v2-baseline.ts` stays rewrite-or-drop. + +Classes: **living** (kept and maintained) · **evidence** (frozen factual record: cite it, don't maintain it) · **archive** (superseded, history only) · **rewrite-or-drop** (wrong or stale against the new decisions or base). + +Decision numbers refer to `plans/migration-context.md` §Decisions (D1 to D10). Old docs cite "execution-plan Decision N"; that numbering is different (old D5 = new D7, old D3 = new D5, old D4 = new D6). + +## 1. Counts + +| Class | Files | +|---|---| +| living | 4 (`migration-context.md`, `v2-v1-schema-diffs.md`, `v2-v1-session-diffs.md`, refreshed `pinned-v2-baseline.md`) | +| evidence | 54 | +| archive | 24 | +| rewrite-or-drop | 11 (includes 2 `.DS_Store`, which should be dropped) | +| **Total** | **93** (91 content files + 2 `.DS_Store`) | + +The new discovery outputs under `migration-tracking/discovery/` are discovery evidence. They stay frozen once the plan absorbs them; the plan (D9) becomes the living source. + +Base facts this classification relies on: +- `git ls-tree HEAD packages/` contains **no `kilo-*` package**. The base has ai, app, cli, client, codemode, console, containers, core, desktop, effect-drizzle-sqlite, enterprise, function, gui-extensions, http-recorder, httpapi-codegen, identity, latex, merman, plugin, plugin-browser, protocol, schema, script, sdk, server, session-ui, simulation, stats, storybook, theme, tui, ui, util and web. +- The DONOR tag `879b89e8` adds `kilo-cli`, `kilo-client`, `kilo-gateway`, `kilo-i18n`, `kilo-ide-ui`, `kilo-indexing`, `kilo-memory`, `kilo-ui`, `kilo-vscode`, plus `packages/www` and `packages/updates`. There is **no `kilo-sandbox` and no `kilo-jetbrains`** even on DONOR. +- The base has no `packages/www`. Generated docs now live at `services/www`. `.github/workflows/typecheck.yml` is gone (replaced by `check.yml`, which runs `bun run check`). Package scope is `@opencode/*` (for example, `packages/cli` is `@opencode/cli`). +- `migration-tracking/` at HEAD is identical to DONOR's copy except for three files added at bootstrap: `plans/migration-context.md`, `baseline/v2-v1-schema-diffs.md` and `baseline/v2-v1-session-diffs.md` (`git diff --stat 879b89e8 HEAD -- migration-tracking`). +- 56 of the 91 content files reference `packages/kilo-cli`, which does not exist on BASE. + +## 2. Material findings (cross-cutting) + +1. **Assessment-snapshot SHA discrepancy (affects D4).** `marker-audit/v1-kilo-marker-port-assessment.md:9` states "Pinned v1 `d99662338e3d…`" (2026-08-10, merge #13046). `migration-context.md` and the discovery brief give the snapshot as `f7115470` (2026-08-12). `d99662338` is an ancestor of `f7115470`, 800 commits and 732 files earlier (`git rev-list --count`, `git diff --stat`). The older docs cite `f7115470` as `origin/main` at baseline time (`pinned-v2-baseline.md` Refs table). The assessment text itself names `d99662338`. Recommendation: the staleness gate should diff from `d99662338` (conservative superset), or `migration-context.md` should record both. UNVERIFIED: whether individual TSV rows were assessed at `d99662338` or later. The TSV has no SHA column. +2. **The working log contains a live-looking stop instruction.** `plans/kilo-opencode-v2-working-log.md:21-26` says "Implementation is paused… Do not resume implementation or restart delegates without a new instruction". Its header (lines 3-9) calls it the local copy of #13750 that is still being maintained. An agent reading it could obey it. It needs a superseded banner, or its move into an archive location, when the new plan lands. +3. **`technical-notes/script/v2-baseline.ts` does not run correctly on the new base** (§5). It hard-codes pin `76dbaf20`, asserts `upstream/v2 == pin`, and calls `packages/www`. +4. **`v2-fork-conventions.md` is cited by `migration-context.md` (Conventions) as a rule source but is largely stale** (§4). It conflicts with `migration-context.md` and `AGENTS.md` on branch naming (`johnnyeric/` prefix) and on the pin. +5. **`kilo2` naming tension.** `cloud-agent-contracts.md:40-47` records a cloud-owner decision (2026-10-07): ship v2 on a new npm dist-tag `next`, keep the **`kilo2` binary** for the migration, and keep `@kilocode/cli@latest` on v1. The old plans say the opposite: "Do not ship `kilo2` to customers" (working-log:567-571, original-issue-body) and the customer store is "not preview `kilo2`" (execution-plan:65). D6 says only "a separate customer v2 data store", so binary and channel naming is undecided in the canonical brief. It belongs in the plan's open questions or an escalation to the user. +6. **Gap-label collision.** `cloud-agent-contracts.md:216-355` names its gaps "G1"…"G11". These collide with gate names G1/G2/G3 (D5, D10 readability). Rename them when absorbed (for example, CA-1…CA-11). +7. **Old decision numbering is cited inside evidence.** `editor-services-inventory.md` and both cloud-agent docs cite "execution-plan Decision 5". Under the new brief that is D7. The content is consistent with D7, but the citations need a mapping note. +8. **#13750 on GitHub still points at `kilo-v2`.** The published body (`github-drafts/issue-13750/issue-body.md:3-15`) sends readers to the `kilo-v2` branch's `migration-tracking` and progress plan, and embeds screenshots by raw URL from `kilo-v2`. This is a GitHub follow-up for the user or coordinator, not a docs edit. +9. **Sharing gate drift.** `technical-notes/session-sharing.md` §Remaining gates, working-log:1786-1821, `remote-share-next-contracts.md` §4.2 and subissue 03 treat team ownership and incremental sync as completion criteria. Under D7 these are post-cutover. Individual share/unshare/fork and the public viewer remain gating (old execution-plan:69). +10. **Evidence about "v2" mostly describes donor `kilo-cli` mechanisms measured against v2 `59b29de4`.** BASE is about 1,000+ commits later (old execution-plan:23 measured 1,013 commits behind on 09-28). The staleness gate (D4) covers the V1 side only. Seam claims (for example "`ctx.session.hook("context")` exists", `sidebar.content` slot, `Mcp.SpawnHooks`) must be re-checked at BASE before reuse. Mark them UNVERIFIED at BASE. + +## 3. Per-file classification + +Columns: class · reason · pin/branch assumed · contradicts (decision) · facts worth preserving. + +### Root, assets, plans + +| Path | Class | Reason | Assumes | Contradicts | Facts worth preserving | +|---|---|---|---|---|---| +| `README.md` | rewrite-or-drop | Index describing the old structure: the execution plan is "the index", the progress plan holds status, and a "N7 alignment" is pending. It omits `migration-context.md` and `discovery/` | `kilo-v2` structure, old root `plans/` and `kilocode/` origins | D9 (plan format), D1 | The collection was copied from the root `plans/` and `kilocode/` dirs, so relative links and script paths may still be broken (lines 44-60). After rewrite it becomes **living** | +| `.DS_Store` | rewrite-or-drop (drop) | macOS junk | — | — | — | +| `technical-notes/.DS_Store` | rewrite-or-drop (drop) | macOS junk | — | — | — | +| `assets/kilo-v2-cli-2026-09-10.png` | archive | Screenshot of the donor CLI, embedded in the published #13750 body via a raw `kilo-v2` URL | `kilo-v2` | — | Donor CLI UI on 2026-09-10 (Plan session, tabs, questions, usage sidebar) | +| `assets/kilo-v2-vscode-2026-09-10.png` | archive | Same, for the donor VS Code UI | `kilo-v2` | — | Donor VS Code sidebar and Agent Manager on 2026-09-10 | +| `plans/migration-context.md` | **living** | Canonical brief (fixed rule) | BASE `5e73d5cb`, `kilo-v2-clean`, V1 `fb7e96ed`, SNAP `f7115470`, DONOR `879b89e8` | — | Needs the SNAP note from finding 1 and the `kilo2`/`next` channel question from finding 5 | +| `plans/kilo-opencode-v2-execution-plan.md` | archive | Old plan (fixed rule). Finalized 09-30, never started | `kilo-v2` on base `59b29de4`, merge release tags into `kilo-v2`, scope-rename codemod | D1 (tags as merge targets, work continues on `kilo-v2`), D2 | See §7. Its decisions 3/4/5/6/8 map to new D5/D6/D7/D7/D8 | +| `plans/kilo-opencode-v2-plan-progress.md` | archive | Old progress plan (fixed rule). Status "Implementation paused", 09-09 | `kilo-v2`, phases 0-6 with a single cutover | D5 (single cutover, VS Code-first next steps), D7 (voice, image, Claw and JetBrains rows as needs-port or deferred, not post-cutover), D6 (preview store) | 88-row capability inventory (lines 77-198) with per-row remaining scope; see §7 | +| `plans/kilo-opencode-v2-working-log.md` | evidence | Frozen dated record. It holds unique facts (user decisions, incidents, measurements, sharing gates, harness rationale) that exist nowhere else, so it is more than a superseded plan. Its headers and pause instruction are hazardous (finding 2) | `kilo-v2`, `76dbaf20` → `59b29de4`, V1 `ecccd1f`/`d99662338`, `kilo2` identity | D1/D2 (header and pause), D6 (`kilo2` and "take the kilo paths" rules, lines 558-596) | 09-08 user decision: preserve the original VS Code UI; the upstream-app wrapper was rejected (71-79). Store and rollback rules (558-596). Sharing gates (1786-1821). Self-sufficient CLI harness (1838-1889) | + +### github-drafts/issue-13750 (published 2026-09-10; GitHub holds live status) + +| Path | Class | Reason | Assumes | Contradicts | Facts worth preserving | +|---|---|---|---|---|---| +| `README.md` | archive | Publication review package | `kilo-v2` | — | Body plus nine subissues published 09-10; the progress comment was never published | +| `issue-body.md` | archive | Source of the live #13750 body | `kilo-v2`, `kilo2`, `packages/kilo-cli` | D1 (branch and start-here links), D5 (phase 6 single cutover), D6 (`kilo2` preview store) | Original placement model: catalog / host / plugin owners, and "from-scratch `packages/kilo-cli` because upstream `packages/cli` exports no wrap surface" (line 60). This is an open question for the clean base. 36 original capability rows | +| `original-issue-body.md` | archive | The pre-09-10 issue text | `kilo-v2` with a pinned SHA | D1, D5, D6 | "What this is not" list (lines 12-18): no `packages/opencode` host on v2, no dual-running on the same SQLite or `kilo.jsonc`, no ambient migration on startup | +| `progress-comment.md` | rewrite-or-drop | Unpublished draft checkpoint, stale | `kilo-v2` | D1 (framing) | 816 files and 6,128 markers; disposition counts 65/137/233/163/68/150 (already in the assessment) | +| `published-subissues.md` | evidence | Issue-number map | — | — | #14016 VS Code, #14017 runtime/CLI/TUI, #14018 sharing, #14019 remote, #14020 distribution/cutover, #14021 editor services, #14022 JetBrains, #14023 cloud agents, #14024 Anaconda | +| `shared-source-footprint.md` | rewrite-or-drop (drop) | Three-line pointer stub to `marker-audit/` | — | — | none | +| `tracking-policy.md` | rewrite-or-drop | Document-ownership rules built around the progress plan as capability owner and the working log as an append-only record | `kilo-v2` docs set | D9 (one plan file, A/B parts), D10 (one canonical place per fact) | The issue is the public entry point and the repo holds versioned detail. Subissue completion must update affected rows without implying adjacent ones are complete | +| `subissues/01-original-vscode.md` | archive | Draft of #14016 | donor VS Code port | — | Acceptance list: Stop/reconnect, notifications and suggestions, agent and skill removal, message deletion, presence, terminal tickets, Agent Manager/notebook | +| `subissues/02-runtime-cli-tui.md` | archive | Draft of #14017 | "pinned v1 revision" | — (D4 replaces "pinned v1 revision") | Offline wait, overflow, snapshot policy; Plan-to-Code handoff regression | +| `subissues/03-session-sharing.md` | archive | Draft of #14018 | — | **D7** (team ownership and incremental sync in acceptance) | Viewer must render self-contained v2 messages; unsupported paths stay fail-closed | +| `subissues/04-remote-sessions.md` | archive | Draft of #14019 | — | — | Relay acceptance list: transcript, attachments, suggestions, exit/interrupt | +| `subissues/05-distribution-cutover.md` | archive | Draft of #14020 | single canary | D5 (single cutover framing) | CI hardening, payload format, update/rollback, signing, clean install, opt-in import | +| `subissues/06-editor-services-clients.md` | archive | Draft of #14021 | — | **D7** (speech, image generation and Claw integration as acceptance items, line 20-21) | FIM/next-edit is the core; code actions, enhance prompt and commit generation in real UI | +| `subissues/07-jetbrains.md` | archive | Draft of #14022 | — | — | Do not assume the VS Code adapter is reusable unchanged; inventory the plugin's SDK, HTTP, event and terminal contracts | +| `subissues/08-cloud-agent-integration.md` | archive | Draft of #14023 | — | — | `kilo cloud` CLI, remote relay and Agent Manager do not establish cloud-agent compatibility | +| `subissues/09-anaconda-client-integration.md` | archive | Draft of #14024 | — | — | Discovery-first: identify the product, repo, owner and integration mode (binary, SDK/HTTP, ACP, bridge) | + +### marker-audit + +| Path | Class | Reason | Assumes | Contradicts | Facts worth preserving | +|---|---|---|---|---|---| +| `README.md` | evidence | Index of frozen audits. Its line "refresh the current inventory when shared changes move" is obsolete on BASE | donor | — | Kilo-owned files carry no markers; "already ported 65" is not a count of modified upstream files | +| `kilo-override-marker-inventory.md` | archive | Pre-cleanup (2026-09-08) marker search of the donor worktree, already superseded by the current inventory | donor worktree at a Johnny-local path | — | 350 redundant markers removed from 55 Kilo-owned files on 09-08 | +| `v1-kilo-marker-port-assessment.md` | evidence | The 816-file assessment (cited by the discovery brief) | V1 **`d99662338`** (line 9); v2 destinations name `packages/kilo-cli` (273×), `kilo2`, `kilocode/baseline` | — (but see finding 1) | Totals: 65 ported, 68 deferred, 137 native, 163 needs-port, 150 not-needed, 233 partial, 0 unresolved. The v2 destination column refers to donor paths; re-map to BASE | +| `v1-kilo-marker-port-assessment.tsv` | evidence | Machine-readable form (817 lines incl. header) | same; no SHA column | — | Columns: v1_file, marker_lines, occurrences, ownership, feature_purpose, disposition, v2_destination, reason, snippets | +| `v2-current-shared-change-inventory.md` | evidence | Donor's upstream-diff footprint. Shows which upstream seams the donor needed (the mechanism) | upstream ancestor `59b29de4`, reviewed `6315d9da` | — | 54 shared paths, 52 marked (24 production source, 4 tests, 22 READMEs, AGENTS.md, .gitignore), 2 unmarked (package.json, bun.lock) | +| `v2-shared-source-footprint-2026-09-10.md` | evidence | Dated measurement | `59b29de4` | — | The 24 shared production files (5 core, 4 theme, 1 plugin, 14 tui), listed at lines 5-28. This is the candidate seam list for BASE | + +### technical-notes (top level and script) + +| Path | Class | Reason | Assumes | Contradicts | Facts worth preserving | +|---|---|---|---|---|---| +| `v2-fork-conventions.md` | rewrite-or-drop | Cited as a convention source, but its pin, branch model and companion docs are stale (§4). It becomes **living** after rewrite | pin `76dbaf20`, branch `johnnyeric/kilo-opencode-v2`, slices `johnnyeric/v2-*` | D1 (pin and branch), AGENTS/brief naming rule; companion = progress plan (D9) | Dev-fix triage vocabulary, `virtua` patch caveat, marker rules, compact markdown tables, narrow-hook rules (§4) | +| `script/v2-baseline.ts` | rewrite-or-drop | Does not run correctly at HEAD (§5) | `PINNED_V2=76dbaf20`, `packages/www`, `typecheck.yml` mirror | D1 | Design: offline ancestry assertions, `--checks` replays CI commands, logs to gitignored `baseline/logs/`, never suppresses known reds | +| `session-sharing.md` | evidence | Cloud share contract audit, 2026-09-05 | cloud `origin/main` `08c4887f`; donor plugin | D7 if its team and incremental gates are read as gating | Contract: `POST /api/session` → `{id, ingestPath}`; share returns a purpose-bound JWT `share_token`; public read `GET /session/:shareToken`; export `{info, messages:[{info, parts}]}`. The older cloud checkout `c32be17d` returns UUID `public_id` (wrong target) | + +### technical-notes/baseline (56 files) + +Unless stated otherwise, "Assumes" means donor `packages/kilo-cli` (or another `kilo-*` package) on v2 `59b29de4`, compared with V1 `ecccd1f` (origin/main 2026-09-04). Each such note needs a V1 staleness check (`ecccd1f..fb7e96ed`) and a seam re-check at BASE before reuse. + +| Path | Class | Reason | Assumes | Contradicts | Facts worth preserving | +|---|---|---|---|---|---| +| `agent-policy-v2-parity.md` | evidence | Donor agent-transform mechanism | `59b29de4`, `ecccd1f`, kilo-cli | — | `build` keeps its ID and displays as "Code"; Ask = read-only deny rules; Debug added; Orchestrator not resurrected; user `ask`/`debug` IDs win | +| `auto-packaging-v2-probe.md` | evidence | Measured Bun `--compile` probe | kilo-cli `dist/interactive/kilo2`, Bun 1.4.0 | — | `import.meta.resolve` of the routed plugin fails in compiled binaries. A builtins-injection `onLoad` seam worked but was rejected as fragile | +| `cli-fixture-storage-incident.md` | evidence | 2026-09-07 incident | donor fixtures | — | Running fixture entrypoints directly wrote 59 dummy credentials into the real interactive DB. Every fixture must isolate HOME and XDG itself, not rely on wrappers | +| `cli-remainder-next-gaps.md` | archive | Gap audit superseded by closure verification | `kilo-v2` `61a8707c` | — | V1 has six sidebar plugins with orders: pr 50, footer 99, usage 150, indexing 225, background-processes 250, memory-status 1000 | +| `cli-tui-closure-verification.md` | evidence | Donor acceptance evidence for the CLI/TUI remainder | `ecccd1f`, kilo-cli | — | Test counts per area, and which items were source-reviewed rather than fault-injected | +| `cloud-agent-consumers.md` | evidence | #14425 discovery (2026-10-02/07) | cloud `3de933dd`, V1 `622ed1f5`, `kilo-v2` `a2e6c1f6` | — (KiloClaw EOL agrees with D7; cites old "Decision 5") | Consumers and pins: cloud-agent-next `@kilocode/cli@7.8.1` (pinned, independent cutover); auto-routing-benchmark and the MCP-catalog CI float on `@latest`; Gastown and KiloClaw EOL. Owner = `cloud` team | +| `cloud-agent-contracts.md` | evidence | #14426 mapping (2026-10-07) | cloud `3de933dd`, V1 `62f674d4`, `kilo-v2` `78226332`, upstream `f20f5b68` | — (finding 5: `kilo2` binary vs old D4; finding 6: G-label collision) | Owner decisions 2026-10-07: `next` dist-tag, `kilo2` binary, auth import acceptable, migrate sessions using upstream's v1→v2 migrator, restore from both, no v2 consumers deployed. 11 gaps for #14427. Five open owner questions (lines 376-392) | +| `cloud-cli-v2-parity.md` | evidence | Port ledger for the `kilo cloud` client | `ecccd1f` `kilocode/cloud/` (14 files), kilo-cli | — | Byte-ported modules; env-reading origin resolvers dropped in favour of injected origins | +| `config-mapping-v2-gap.md` | evidence | Per-key v1 → v2 config mapping | `ecccd1f`, kilo-cli `import-v1-config.ts` | — | Kilo-only `kilo.jsonc` keys and their v2 destination or refusal | +| `credential-import-v2-parity.md` | evidence | Credential import rules | `ecccd1f`, kilo-cli | — | Host-local import only, with no credential writer over HTTP/RPC; OAuth `accountId` = selected org; well-known = origin+key mapping | +| `editor-services-inventory.md` | evidence | #14415 inventory (2026-10-02) | V1 `622ed1f5`, `kilo-v2` `a2e6c1f6` | — (aligned with D7; old numbering) | FIM via Gateway `/kilo/fim`; next-edit `client.kilo.edit`; the v2 snapshot has no routes and is non-functional. Speech and image producers are absent (post-cutover). KiloClaw has 0 paths on V1 and 40 stale paths on donor. JetBrains V1 consumes enhance-prompt and branch-name but not FIM | +| `existing-vscode-port.md` | evidence | Donor VS Code transplant record (09-08) | V1 `d99662338` (kilo-code 7.4.20), kilo-vscode, kilo-client | — | User requirement: the original extension UI and workflows on the v2 backend. Six webviews and two workers; type-only `ide-types.ts` closure | +| `explore-policy-v2-parity.md` | evidence | V1 Explore shell ceiling | `ecccd1f` | — | `readOnlyBash` = 28 read allows + 20 git allows + operator and flag denies; Explore adds `gh *` and `find *` denies. Counts verified | +| `extension-cleanup-audit.md` | archive | Audit against a "6 approved shared-patch files" ceiling from the vanished `plans/kilo-opencode-v2-issue-13750.md` | `59b29de4`, HEAD `82040801` | — | none beyond the shared-change inventory | +| `extension-surface-inventory.md` | evidence | VS Code surface inventory (09-05) | V1 `ecccd1f` (7.5.14) and `d99662338` (7.4.20) | Stale vs **D7**: lists KiloClaw as an "unassigned scope candidate" | Extension command and control inventory beyond the 43 rows | +| `external-resume-parity.md` | evidence | Claude/Codex import parity | `76dbaf20`, V1 `f7115470` (`e87dc77b`, `b59ebd7b`) | — | v2 import is explicit-file only; ambient `/resume-claude` and `/resume-codex` discovery and the picker are gaps | +| `goal79-scope-boundary.md` | archive | Superseded percentage-goal checkpoint | `ecccd1f` | D5 ("VS Code/JetBrains explicitly deferred") | none | +| `ide-preview-integration.md` | archive | Upstream-app wrapper prototype the user rejected (09-08) | — | — | Wrapper infrastructure (loopback verification, SecretStorage, proxy credential) was valid but is not a port | +| `indexing-v2-parity.md` | evidence | Indexing engine port boundary | `ecccd1f`, `59b29de4` | — | V1 `kilo-indexing`: 49 modules, 10 embedders, LanceDB and Qdrant, 30 tree-sitter languages; host runtime 582 lines; worker isolation | +| `memory-activity-next-slice.md` | evidence | Save-pulse slice | `ecccd1f` | — | 5-second success pulse on `memory.status`/`memory.updated` with `detail.type:"saved"` | +| `memory-sidebar-v2-parity.md` | evidence | Memory sidebar row parity | `ecccd1f` | — | Label and tone mapping; the durable-marker half has no v2 seam | +| `memory-ui-v2-parity.md` | evidence | Memory dialog parity | `ecccd1f` | — | The public dialog API has no append-to-composer operation | +| `memory-v2-parity.md` | evidence | Memory engine boundary, plus a 09-05 user smoke-test reopening | `ecccd1f` | — | Duplicate-command regression lesson: test palette and autocomplete row counts separately | +| `model-info-bench-seam.md` | evidence | Model-info panel | `ecccd1f` | — | Cached-Free rule; tier threshold `context > tier.size` | +| `model-loading-v2-parity.md` | evidence | Picker loading parity | `ecccd1f` | — | Non-actionable loading state; `BYOK` and `May train` labels; Auto group from `kilo-auto/*` | +| `model-prompt-policy-v2-parity.md` | evidence | Prompt-selector port | `ecccd1f`, `61a8707c` | — | Eight closed-enum selectors: anthropic, trinity, anthropic_without_todo, codex, gemini, ling, gpt55, beast | +| `model-prompt-v2-audit.md` | evidence | Seam analysis | `ecccd1f`, `59b29de4`/`82040801` | — | V1 selection lives at `session/system.ts:49-75`; the v2 `ctx.session.hook("context")` seam (re-verify at BASE) | +| `model-sidebar-v2-parity.md` | evidence | Model, agent and sidebar batch (its memory claim was later superseded) | `ecccd1f`, `59b29de4` | — | `TuiApp.modelPicker` presentation hook; recommendations from `preferredIndex`; personal vs team model endpoints | +| `non-ide-closure-checkpoint.md` | archive | Superseded percentage checkpoints | `61a8707c` | — | none (the 31/43 metric is dropped) | +| `non-ide-completion-audit.md` | archive | Superseded 26/43 audit | `76dbaf20`, `82040801` | — | Its classification vocabulary (reusable, delta, seamblocked, productdecision, deployedgate, ide-bound) is a possible input | +| `packaging-correction-plan.md` | evidence | As-built portable-directory packaging (09-06) | kilo-cli, `@kilocode/cli` | — | No-install closure from the resolved graph; preserves patched and trusted deps. The loopback smoke test stays `test.skip` | +| `packaging-next-slice.md` | archive | Explicitly SUPERSEDED, REJECTED AND FROZEN | kilo-cli | — | Rejection reasons (P1 `rm(outdir)` and others) as anti-patterns | +| `pinned-v2-baseline.md` | **living** (refreshed) | Was rewrite-or-drop (pin `76dbaf20`, branch `johnnyeric/v2-baseline`, contradicting D1). Discovery refreshed it to BASE `5e73d5cb` and the phase branch on 2026-10-11 (§6) | BASE, `kilo-v2-clean-bootstrap` | — | Current reds: core tests 30 (host env), `lint:effect-patterns` 27, protocol `openapi.json` stale | +| `plan-v2-parity.md` | evidence | Plan workflow parity | `ecccd1f` | — | V1 Plan = workflow (`plan_exit` requires a saved plan; follow-up offers new session, continue, or refine). The v2 builtin writes to `$HOME/.opencode/plan`, so it needs replacement, not composition | +| `reliability-checkpoint-2026-09-07.md` | evidence | Regression checkpoint | donor | — | Signed-out Gateway must disable the provider when the catalog is empty; register sidebar installers once in plugin setup | +| `remote-share-next-contracts.md` | evidence | Contract audit (09-06) | `kilo-v2` `61a8707c`, `ecccd1f`, cloud `08c4887f` | D7 if §4.2/§5 are read as gating | Relay `CLIOutboundMessageSchema` in cloud `services/session-ingest/src/types/user-connection-protocol.ts` | +| `remote-v2-parity.md` | evidence | Relay boundary | `ecccd1f` | — | `KILO_SESSION_INGEST_URL` defaults to `https://ingest.kilosessions.ai` `/api/user/cli?token=&connectionId=`; message set listed; `orgId` optional | +| `sandbox-linux-validation.md` | evidence | Linux bwrap probe (09-05) | `59b29de4` + kilo-cli | — | Debian 13, bwrap 0.12.0, image digest recorded; shell-only scope | +| `sandbox-spawn-next-slice.md` | evidence | MCP spawn confinement | `61a8707c`, kilo-cli | — | Shared edit to `core/src/mcp/index.ts` adds `Mcp.SpawnHooks.beforeSpawn` (seam candidate). PTY, remote MCP and git spawns are out of scope | +| `settings-v2-parity.md` | evidence | Settings audit | `ecccd1f`, `59b29de4` | — | V1 has no settings CLI command; HTTP `config-console` overlay API with revisions; writer semantics; 0600 mode | +| `sidebar-bench-v2-parity.md` | evidence | Terminal Bench sidebar | `ecccd1f` | — | Gateway `terminalBench {overallScore, avgAttemptCostUsd}`, tolerant decode | +| `sidebar-indexing-v2-parity.md` | evidence | Indexing sidebar | donor | — | Disabled, loading and unavailable states are distinct; consent-off means no engine init | +| `sidebar-pr-v2-parity.md` | evidence | PR sidebar | `ecccd1f` | — | `gh` lookup chain reproduced verbatim (view → list by SHA → fork parent) | +| `sidebar-processes-v2-parity.md` | evidence | Process sidebar | `ecccd1f` | — | Stale durable `running` must not show a PID after a server restart | +| `sidebar-usage-v2-parity.md` | evidence | Usage sidebar | donor | — | Cache rate = `read/(input+read+write)`; forks are not family children | +| `skill-shell-parity.md` | evidence | Trusted skill `!` shell | `ecccd1f` | — | Trust only for global or builtin provenance; project skills untrusted | +| `skills-v2-parity.md` | evidence | Skills and project config | `59b29de4`, `76dbaf20` | — | Opt-in `.kilo/` and `.kilocode/` walking; project plugins are reported, not loaded | +| `startup-loading-v2.md` | evidence | Startup optimization | donor, `kilo2` | — | 60-second identity-hashed catalog snapshot; TUI renders before Gateway activation | +| `swarm-v2-parity.md` | evidence | Swarm board | `ecccd1f` | — | Default-off; board keyed by root session; `main` and `ALL` recipients only | +| `telemetry-v2-overlap.md` | evidence | Telemetry audit | `59b29de4` | — | V1 PostHog on by default; upstream dropped PostHog for OTLP. Donor uses an opt-in allowlisted OTLP exporter (a privacy decision) | +| `updater-local-parity.md` | evidence | Local directory updater | kilo-cli, `kilo2` | — | Manifest contract (tree sha256, size, format); archives unsupported. BASE now has `services/update` (UNVERIFIED overlap) | +| `v1-fixture-generation.md` | evidence | V1 writer fixture | V1 `d99662338`, `ecccd1f` | — | Fixture built through V1 writer methods, not SQL; Effect versions recorded | +| `v1-session-schema-audit.md` | evidence | Store schema diff | V1 `d99662338`/`1536aef0`/`ecccd1f`; v2 `59b29de4` | — | Copy-then-migrate is **not proven lossless**. Re-run against V1 `fb7e96ed` and BASE: BASE adds migration `20260910120000_clear_v1_session_permission.ts` | +| `v2-v1-schema-diffs.md` | **living** | Reference explainer for client tracks, named in `migration-context.md`. Spot-check at BASE: the generated `types.ts` path and the 10 message kinds still hold | "v2 on `kilo-v2`" (re-point to BASE) | — | v1 `{info, parts[]}` vs v2 self-contained typed messages; 10 kinds | +| `v2-v1-session-diffs.md` | **living** | Same; named in the brief | "on `kilo-v2`" (re-point to BASE) | — | Event-sourced sessions, inbox and steer vs v1 FIFO queue | +| `vscode-packaging.md` | evidence | VSIX packaging | kilo-vscode | — | `vsce package --no-dependencies`; sanitizes `workspace:*` | + +### test-plans + +| Path | Class | Reason | Assumes | Contradicts | Facts worth preserving | +|---|---|---|---|---|---| +| `kilo-opencode-v2-test-plan.md` | rewrite-or-drop | Index linking to a non-existent `kilo-opencode-v2-issue-13750.md`; runs from `packages/kilo-cli` with `kilo2` | kilo-cli, `kilo2` | D5 (IDE deferred, no gate tags) | 164 scenarios (UI-001 to UI-083, RT-001 to RT-081); stable IDs for reporting; "an authored case is not validation" | +| `kilo-opencode-v2-test-plan-runtime.md` | rewrite-or-drop | Expected text is "quoted from current source", meaning donor source at `82040801`, not V1 | HEAD `82040801` plus uncommitted changes; kilo-cli (32×) | **D3** (behavior from donor, not V1), D5 | RT IDs and safety labels (FIXTURE, USER-OPT-IN, PENDING-VERIFICATION); permissions, persistence, import, isolation and sandbox scenario topics | +| `kilo-opencode-v2-test-plan-ui.md` | rewrite-or-drop | Same; links point at kilo-cli tests (80×) | kilo-cli | **D3**, D5 | UI IDs grouped into 14 sections (startup and branding through sidebar truthfulness); all NOT RUN | + +## 4. `technical-notes/v2-fork-conventions.md`: does it still apply on the clean base? + +| Rule (line) | Status on BASE | Note | +|---|---|---| +| Pinned upstream SHA `76dbaf20` (12-14) | **Wrong** | BASE is `5e73d5cb` (D1). `76dbaf20` is an ancestor of HEAD, but it is not the base | +| Never treat latest `upstream/v2` as the base; record the validated SHA (15-16) | Applies | Matches D1 ("pinned by SHA each time") | +| Adopting a snapshot: fetch, record SHA and merge base, merge, re-run the baseline, diff failures (17-20) | Applies | Point at the refreshed baseline doc | +| Stop if a recorded SHA becomes unreachable; never force-push (21-23) | Applies | | +| `upstream/dev` is a sibling, diverged at `0e2dd4ad` (24-25) | Applies (fact) | | +| `virtua@0.49.1` patch caveat (27-34) | **Stale / UNVERIFIED** | BASE root `package.json` has no `virtua`, and `patches/` has no virtua patch. The rule only matters once `kilo-vscode` or `kilo-ide-ui` are re-landed. Keep it as a note for G2 | +| Integration branch `johnnyeric/kilo-opencode-v2`, slices `johnnyeric/v2-*` (38-44) | **Wrong** | Integration branch is `kilo-v2-clean`. Branch names must be short and hyphenated with no slashes (AGENTS.md, migration-context Conventions) | +| Merge forward, never rebase the integration branch (46-48) | Applies | Matches D1 and the coordinator rules | +| `johnnyeric/` prefix overrides upstream naming (49-53) | **Wrong** | Contradicts AGENTS.md and the brief. The `type(scope): summary` part still applies | +| Dev-fix triage lane `not-applicable/already-fixed/port/defer` (55-67) | Applies (concept) | It should track the V1 delta under the staleness gate (D4) as well as `upstream/dev`. "Parity ledger dev-line lane" no longer exists, so name the new home | +| Kilo-owned paths table (69-79) | Partly | `packages//src/kilocode/` and `test/kilocode/` apply. The `plans/` row is moot (no root `plans/` at BASE). `migration-tracking/technical-notes/` applies. Missing: `kilo-*` packages (migration-context Conventions lists them) | +| Do not fork or rename upstream packages; do not reconstruct `packages/opencode` (81-82) | Applies | | +| Compact markdown tables; do not run prettier on Kilo docs (84-113) | Applies | BASE `.prettierignore` still lists only four generated paths (`sst-env.d.ts`, `packages/desktop/src/bindings.ts`, `packages/client/src/generated/`, `packages/client/src/generated-effect/`), so markdown is not excluded | +| Shared root `AGENTS.md` pointer hook (115-118) | **Not present on BASE** | `AGENTS.md` at HEAD is upstream's, with no Kilo pointer (migration-context notes it). The rule still describes the intended hook | +| Narrow shared hooks, dependency direction, never hand-edit generated output (120-134) | Applies, path fix needed | Generated paths: `packages/client/src/generated*` is fine. `packages/www/openapi.json` does not exist (now `services/www`). `packages/protocol` has its own `generate` / `check:generated` | +| `kilocode_change` marker rules (136-150) | Applies | Matches the brief | +| Validation commands (152-168) | **Partly stale** | `packages/www` → `services/www`. CI now runs `bun run check` (lint + typecheck) and `lint:effect-simplifications`. `do-not-run-tests-from-root` is still true (`bunfig.toml:8`). `bun typecheck` is still `bun turbo typecheck --concurrency=3` | +| Companion docs (3-5) | **Wrong** | Points to the progress plan (archived) and the old pinned baseline | + +Verdict: the rules on substance (snapshot tracking, merge-forward, triage vocabulary, Kilo-owned placement, narrow hooks, markers, tables) still apply. The concrete refs, branch model, paths and companion links are wrong. Rewrite it, then make it **living**; `migration-context.md` already points to it. + +## 5. `technical-notes/script/v2-baseline.ts`: does it run on BASE? + +It was read, not run. Findings: + +- `ROOT = path.resolve(import.meta.dir, "../../..")` resolves to the repo root from `migration-tracking/technical-notes/script/`. Correct. +- `PINNED_V2 = "76dbaf20…"` (line 11). The ancestry assertion `merge-base --is-ancestor 76dbaf20 HEAD` would **pass** (verified: `76dbaf20` is an ancestor of HEAD). That passes for the wrong reason: it checks an obsolete pin, not BASE. +- The required assertion `rev("upstream/v2") === PINNED_V2` (lines 48-51) **fails**: local `upstream/v2` = `d2905b8d…`. The script then prints "baseline ancestry broken; refusing to run checks" and exits 1. **As written it does not run on the new base.** It also assumes that `upstream/v2` is never fetched past the pin, which contradicts merging the moving `upstream/v2` branch (D1). +- The `rev()` loop over `origin/main`, `upstream/dev` and `upstream/v2` is offline. If a ref is missing, it prints an empty value. Harmless. +- CHECKS, path by path at HEAD: + - `install`, `typecheck` (root `typecheck` script exists: `bun turbo typecheck --concurrency=3`): OK. + - `test` (`bun turbo test --continue`, `GITHUB_ACTIONS=false`): OK. CI now adds `--concurrency=3` and `--affected`. + - `client-check-generated` (`packages/client` has `check:generated`): OK. + - `www-check-generated` with cwd `packages/www`: **broken**. `packages/www` does not exist at HEAD; it moved to `services/www`, which has `check:generated`. + - `cli-build` (`packages/cli/script/build.ts --single --skip-install`; both flags exist at lines 26/29): OK. + - `cli-service-smoke` (`packages/cli/script/service-smoke.ts` exists): OK. +- Missing relative to current CI (`.github/workflows/test.yml`, `check.yml`): `bun run check` (lint + typecheck), `lint:effect-simplifications` and its rule test, `packages/protocol` `check:generated`, Node build plus `service-smoke --node`, codemode publish dry-run, and SDK `verify:package`. The comment's reference to `typecheck.yml` is stale (the file is gone). +- Rewrite needs: set the pin to BASE `5e73d5cb`. Drop the "upstream/v2 equals pin" assertion, or replace it with "pin is an ancestor of `upstream/v2`" plus "HEAD contains pin". Fix `services/www`. Re-mirror `test.yml` and `check.yml`. The logs dir (`baseline/logs/`) stays gitignored via the root `.gitignore:36` `logs/` rule. + +## 6. `technical-notes/baseline/pinned-v2-baseline.md` (refreshed by this phase; pre-refresh state below) + +- **Current pin:** `upstream/v2` = `76dbaf20adbd43fd208a00ef3cda4a51e125a234` (2026-08-12, `fix(catalog): serve app shell at lab route (#42159)`). Also recorded: `upstream/dev` `14b37df3`, `origin/main` `f7115470`. Captured 2026-08-13. +- **Branch:** produced on `johnnyeric/v2-baseline`; integration branch named `johnnyeric/kilo-opencode-v2` (no remote at the time). It says it is "Phase 0 baseline for `plans/kilo-opencode-v2-issue-13750.md`" (that file is absent). +- **Reproducer path:** `bun kilocode/script/v2-baseline.ts` (old root `kilocode/` path; now `migration-tracking/technical-notes/script/`). +- **Sections:** + 1. Refs (line 15): table of ref, SHA, date, subject. + 2. Merge bases and ancestry (30): `0e2dd4ad` is the dev/v2/main divergence point; commits-ahead counts (dev 899, v2 1470, main 3391 as of 08-13); ancestry command output. + 3. Toolchain (76): bun 1.3.14, node 22, git, OS; CI runners; ffmpeg. + 4. Authoritative v2 commands (89): table of purpose, command and dir, plus constraints (do-not-run-tests-from-root, `bun typecheck` only, husky pre-push, e2e skipped on v2, lint not CI-run). + 5. Results on pristine upstream v2 (122): pass/fail per check (typecheck 34/34 pass; turbo test 20/23 with `--continue`; lint 4232 warnings and 1 error; `lint:effect-patterns` 33 errors). + 6. Pre-existing failures (140), with subsections: also red on upstream CI (145) (`opencode-drive` JSON parse; tui footer `Café`); host-environment failures (170) (`pa-PK` ICU; ffmpeg missing); not gated by CI (194). + 7. Why no fork CI workflow yet (206). + 8. Reproducer (229). +- **Refresh needs:** BASE `5e73d5cb` and the tag `v2.0.26` marker. Integration branch `kilo-v2-clean`. Current CI commands (`check.yml` `bun run check`; `test.yml` with `--affected --concurrency=3`, effect-simplifications, Node 26.4.0 build, `services/www`). Current `packageManager` bun version (UNVERIFIED; not read). New failure inventory. Package names under the `@opencode/*` scope. Bootstrap status in `migration-context.md` lists "Baseline health recorded" as recommended next. + +## 7. Facts worth preserving from the old plans (for the new plan's facts appendix) + +### `plans/kilo-opencode-v2-execution-plan.md` + +| Line | Fact | +|---|---| +| 19 | Upstream `dev` (`2fa3363c`, default branch) and `v2` (`74dbc509`) are separate lines, diverged at `0e2dd4ad` (2026-06-26); v2 was 3,956 ahead and 1,326 behind dev (09-29/30) | +| 20 | Upstream v2 publishes on npm as `@opencode/cli`: 2.0.0 on 2026-09-11, `latest` 2.0.20 on 09-29. v2.0.x are git tags; there is no GitHub Release for v2. Upstream v1 (`opencode-ai@latest` v1.18.33) ships weekly | +| 21 | Scope rename `@opencode-ai/*` → `@opencode/*` on 2026-09-07 (#47852, `a5312e16`); some packages kept the old scope (for example `@opencode-ai/pty`) | +| 22 | V1 `main`: `.opencode-version` v1.18.26; 2,645 non-merge commits since `f7115470` (as of 09-29); no ratchet or manifest exists | +| 23 | Old `kilo-v2` base `59b29de4` (2026-09-03); first Kilo commit `047ffd045f`; at least 1,013 commits behind upstream on 09-28; 14 non-merge Kilo commits (7 squashed checkpoints); upstream never merged in; the earlier `76dbaf20` branch was replaced, not merged | +| 25 | Known reds: `kilo-vscode` `typecheck` is a skip shim (`bun script/typecheck.ts`); the real gate `typecheck:port` (`tsgo --noEmit`) fails; the VS Code preview needs Restricted Mode (key conflict) | +| 26 | Shared footprint: 24 marked shared source files (5 core, 4 theme, 1 plugin, 14 tui) + 4 tests + 2 metadata exceptions | +| 27 | Missing on the old port: `kilo-jetbrains`, `kilo-docs`, `kilo-web-ui`; `kilo-console` obsolete | +| 28 | Tracking: #13750 → 9 subissues (#14016–#14024) → 62 children (#14371–#14432), all open and unassigned. P-labels set 09-21: P0 #14017/#14020, P1 #14016/#14021, P2 #14018/#14019/#14022, P3 #14023/#14024 | +| 30 | Stale docs: `AGENTS.md` default-branch text; fork conventions name `johnnyeric/kilo-opencode-v2` and pin `76dbaf20` (still true at HEAD) | +| 32 | Root problem: upstream moves about 1,000 commits every few weeks, and `main` adds unassessed v1 behavior | +| 36-42 | Feature target = upstream v2 capabilities + all V1 Kilo behavior minus post-cutover or not-needed items (feature parity, not code parity). The "v1 assessment cutoff" bookmark concept advances per delta sweep | +| 46 | v1 soft freeze is an external, best-effort dependency owned by the v1 team; security and critical fixes continue on `main` | +| 57-61 | Client-track items that touch core/server/protocol: #14380 message deletion, N9 server-side handshake, #14416 FIM/next-edit producer, #14391 Agent Manager control plane | +| 65-68 | Customer v2 store distinct from preview `kilo2` and from v1 `kilo` paths. Import copies then migrates the copy and is re-runnable/idempotent. History is split between v2 CLI and v1 IDEs from G1 to G3 | +| 69 | KiloClaw is not needed: already removed from the client on `main`. **Individual snapshot share/unshare/fork-from-share and the public viewer for v2 messages are G1 gates** (v1 CLI features). Team upload stays refused | +| 70 | Cloud agents (#14023) and Anaconda (#14024) gate whichever surface they consume; their discovery runs first | +| 71 | Never mix v1 and v2 routes in one session; explicit version/capability handshake | +| 118 | `main` changes reach v2 via dispositions `not-applicable / already-fixed / port / defer`; re-implement by default | +| 119 | Native equivalence is verified on scope, errors, cancellation, persistence and presentation (now in the discovery brief) | +| 194 | Start signing/notarization (#14411) early because of external lead time | +| 210 | V1 reference fixtures needed for offline wait, overflow accounting, slow snapshot, snapshot retention (#14371–#14374) | +| 222-227 | Store-identity checks: boot fails closed on v1 or OpenCode stores; upstream `V1Migration` must be a no-op on an empty store; verify whether the v1 IDE and CLI share one DB/auth file; verify that v1 IDEs can never resolve the customer v2 `kilo` binary (VS Code does not pin a CLI) | +| 229-231 | Import covers DB + `auth.json` + Kilo `kilo.jsonc` keys; fixtures from released v1 versions; a re-run imports only new sessions (test required) | +| 233-238 | Preview release train before G1 so G1 is not the first v2 release | +| 266-268 | G2 order: real typecheck (#14383) first; Restricted Mode conflict and Stop desktop verification under #14384/#14387 | +| 282-284 | The test plans contain no IDE scenarios; G2 and G3 scenario sets must be written | +| 291 | JetBrains: generate its client from the stable assembled OpenAPI contract | +| 349-382 | Register dispositions: #14376 legacy theme post-cutover; #14378 split (tables and bindings post-cutover; alerts and sound need a G1 decision); #14380 G2; #14395 split (host registration G1, VS Code visibility G2); #14399 split (fail-closed G1, ownership post-cutover); #14400 post-cutover; #14416 G2+G3 hard gate; #14418 post-cutover; #14419 split (image post-cutover, KiloClaw not needed) | +| 448-470 | Validation: `lint:effect-simplifications` with no new violations; import validated by a v1 store schema diff on real fixtures, and the v1 binary still opens its original store afterwards | +| 474-483 | Metric definitions (upstream lag, unassessed `main` changes, shared-file patches, gate readiness, open work per gate, source parity, rows without an issue, canary health) | +| 485-501 | Risks (still relevant): split history; v1 IDE picking up the v2 binary; first-release risk; FIM is the largest IDE gate; import fixtures missing newer v1 schemas | +| 505-508 | Open: exact customer v2 path names; G1 decision on #14378 alerts and sound; v1 end-of-life date; owners for every lane (all unassigned) | + +### `plans/kilo-opencode-v2-plan-progress.md` + +| Line | Fact | +|---|---| +| 11 | Historical 31/43 (72.1%) broad capabilities accepted; not an effort estimate | +| 13, 204-214 | 816 files / 6,128 annotations; dispositions 65/137/233/163/68/150 | +| 15, 222 | Original extension builds via `bun run extension`; the full extension typecheck fails, so the `typecheck` script skips and `typecheck:port` keeps the real check; desktop verification of the Stop fix is unrun | +| 27-36 | Donor placement map (Gateway → `kilo-gateway`, host → `kilo-cli`, engines → `kilo-*`, VS Code → `kilo-vscode` + webview-ui/`kilo-ui`/`kilo-ide-ui`/`kilo-i18n`, RPC → `packages/schema/src/kilocode`). This is mechanism (D3) | +| 46-48 | Unsupported config values are refused, not silently dropped; settings writes use profile/project scope plus revision checks; refresh keeps sessions and PTYs | +| 77-198 | 88-row inventory with remaining scope per row. It is the best checklist seed for the Part B epics. Rows marked Needs port: offline wait, message deletion, session scope switching, multiline alerts/tables, legacy bindings/sound, public viewer, incremental sharing, notifications/suggestions, agent/skill removal, FIM/next-edit, image/Claw | +| 202 | Native retry does not implement offline-wait interaction; native snapshot does not supply the slow-snapshot question | +| 226 | `packages/kilo-cli` harness: interactive proofs skip without `dist/interactive`; `test:interactive` after `build:tui` (09-24) | + +### `plans/kilo-opencode-v2-working-log.md` + +| Line | Fact | +|---|---| +| 21-69 | 2026-09-08 pause (Johnny's request, JOH-7). Response/Stop fix: translate native `session.execution.*` to the original UI's busy and turn-close messages. Remaining list for the full VS Code port | +| 71-79 | 2026-09-08 user decision: the original Kilo VS Code UI and workflows must be preserved; the upstream-app wrapper does not satisfy it. Source `d99662338` | +| 81-94 | The wrapper prototype passed local VSIX acceptance (34/43 historical); infrastructure evidence only | +| 436-475 | Remote limits: 8 parent edges; transcript search over the newest 200 messages; per-node location validation; queue snapshots may omit immediately-delivered items. Packaging tested on macOS arm64 only | +| 558-596 | Store rules: V1 DB and live `kilo.jsonc` are incompatible (sharing corrupts data). `V1Migration.transformSession` rewrites the opened SQLite (Bun only). Do not fork `V1Migration` to take a destination path; use host identity (`OPENCODE_DB` / channel DBs). The upstream migrator also imports a sibling `opencode-next.db`. Rollback = v1 binary on the original files; there is no reverse migration | +| 1786-1821 | Sharing gates: target the JWT `share_token` and nested export; viewer reads v1 `message.info.role`, so v2 messages are dropped; team ownership via `kilo_meta` (post-cutover now); no per-item delete/tombstone contract; public sharing needs explicit consent because the export is not redacted | +| 1823-1836 | 2026-09-10 Plan follow-up: Unix-ms filename prefix; new-session handoff keeps the Plan tab and opens a Code tab | +| 1838-1889 | 2026-09-24 harness: `KILO_CLI_INTERACTIVE_DIR` is the analog of v1 `KILO_TEST_CLI_PATH`. `build-tui.ts` needs Bun ≥ 1.4 and throws on Windows. Interactive proofs run developer-local only. Pre-existing failures in `portable.test.ts` and `sidebar-pr.test.tsx` | + +### External dependencies and owner contacts (from all docs) + +| Item | Source | +|---|---| +| Cloud repo `Kilo-Org/cloud` (owner: `cloud` team; no CODEOWNERS). Consumers pinned: cloud-agent-next `@kilocode/cli@7.8.1`; Gastown 7.2.14 (EOL); KiloClaw 7.2.31 + `@latest` self-upgrade (EOL, residual exposure). Floating: auto-routing-benchmark, MCP-catalog CI | `cloud-agent-consumers.md:48-63,103-164` | +| Cloud-owner decisions 2026-10-07 (`next` dist-tag, `kilo2` binary, auth import, upstream session migrator, restore from both) and five open questions | `cloud-agent-contracts.md:40-59,376-392` | +| Session-ingest / Kilo Sessions relay `https://ingest.kilosessions.ai`; share backend in cloud `services/session-ingest` | `remote-v2-parity.md`, `session-sharing.md` | +| V1 telemetry: PostHog `us.i.posthog.com`, enabled by default | `telemetry-v2-overlap.md` §1 | +| Signing/notarization credentials (account owner) before the G1 preview train | `migration-context.md` deferred admin; old plan line 194 | +| People: Johnny (`johnnyeric`, original port owner, made the 09-08 scope decisions); the cloud owner who reviewed PR #14758 on 2026-10-05; PR #14704 (old execution-plan PR), PR #14758 (cloud-agent inventory review) | working log, `editor-services-inventory.md:5`, `cloud-agent-consumers.md:11,44` | +| Owners: every #14016–#14432 issue is unassigned (09-30, 10-02) | old plan line 28; `editor-services-inventory.md:9` | + +## 8. Rewrite-or-drop list (11 remaining) + +1. `migration-tracking/README.md`: rewrite as the living index (context brief, discovery, new plan, evidence folders). +2. `technical-notes/v2-fork-conventions.md`: rewrite (BASE pin, `kilo-v2-clean`, AGENTS branch names, path fixes); then living. +3. ~~`technical-notes/baseline/pinned-v2-baseline.md`~~: done in this phase. +4. `technical-notes/script/v2-baseline.ts`: fix the pin, the ancestry assertion, `services/www` and the CI mirror; then living. +5. `test-plans/kilo-opencode-v2-test-plan.md`: rewrite as a gate-tagged index. +6. `test-plans/kilo-opencode-v2-test-plan-runtime.md`: re-derive expected behavior from V1 (D3); keep the RT IDs. +7. `test-plans/kilo-opencode-v2-test-plan-ui.md`: same; keep the UI IDs. +8. `github-drafts/issue-13750/tracking-policy.md`: replace with D9/D10 doc ownership, or drop. +9. `github-drafts/issue-13750/progress-comment.md`: drop (unpublished, stale). +10. `github-drafts/issue-13750/shared-source-footprint.md`: drop (pointer stub). +11. `.DS_Store`: drop. +12. `technical-notes/.DS_Store`: drop (also add it to `.gitignore`). diff --git a/migration-tracking/discovery/v2-epic-taxonomy.md b/migration-tracking/discovery/v2-epic-taxonomy.md new file mode 100644 index 000000000000..3d91b0f6b814 --- /dev/null +++ b/migration-tracking/discovery/v2-epic-taxonomy.md @@ -0,0 +1,213 @@ +# Epic taxonomy (validated) + +Status: discovery output, 2026-10-11. Single writer: discovery analyst. The planner owns the final epic list. This file proposes it with evidence. Feature facts live in `v2-feature-inventory.md`; units, gates, staleness and donor verdicts live in `v2-unit-register.md`. + +## Why + +Every feature, chore and task must sit in exactly one epic and one surface, so that gate readiness can be counted without double-booking. The brief supplied a starting set (E0–E8 plus L). This file tests that set against both inventories, records each change with its reason, and gives each epic concrete exit criteria. + +## Result + +| Epic | Name | Gate | Surface(s) | Change vs starting set | +|---|---|---|---|---| +| E0 | Foundation | pre-gate (blocks all) | repo, shared runtime | kept, scope narrowed to one-time setup | +| E1 | Core and identity | G1 (prerequisite for G2/G3) | shared runtime, gateway | kept | +| E2 | Runtime behavior | G1 (some units G2) | shared runtime | **renamed and split** from "CLI/TUI runtime" | +| E9 | CLI/TUI surface | G1 | CLI/TUI | **new** (split out of E2) | +| E3 | Import and distribution | G1 (per-surface children later) | packaging, import | kept | +| E4 | Remote sessions | G1 | gateway/cloud, CLI/TUI | kept | +| E5 | Sharing | G1 | gateway/cloud, CLI/TUI | kept; non-gating sharing moved to E11 | +| E6 | VS Code track | G2 | VS Code, editor services | kept; owns the FIM/next-edit producer | +| E7 | JetBrains track | G3 | JetBrains | kept | +| E10 | External consumers | set per consumer (Decision 7) | cloud agents, Anaconda Desktop, other CLI consumers | **new** | +| E8 | Retirement | after G3 | repo, storage | kept; scope made explicit | +| E11 | Post-cutover backlog | post-cutover | mixed | **new** | +| L | Cross-cutting lanes (L1–L5) | continuous | repo | kept, split into five named lanes | + +## Changes and reasons + +| Change | Reason and evidence | +|---|---| +| **Split E2 into E2 Runtime behavior and E9 CLI/TUI surface** | The starting E2 "CLI/TUI runtime" would hold about 70 of 129 core features (A06, A08–A15, A18, A19). Two populations live there. Runtime policy lives in `packages/core` seams, Kilo plugins and engines, and G2/G3 consume it: message deletion (#14380), Agent Manager contracts (#14391), generation endpoints (#14389), skill/agent removal (#14393). Presentation lives in `packages/tui` and the CLI, and only G1 consumes it. The old plan already had to serialize every TUI issue on the same files (`kilo-opencode-v2-execution-plan.md:112`). The split gives the IDE tracks a clean dependency on E2 without waiting on TUI work. New ID E9 avoids renumbering the brief's epics | +| **New E10 External consumers** | Decision 7 says cloud agents and Anaconda Desktop gate "whichever surface they consume", so they belong to no single surface epic. Discovery evidence shows they consume the CLI package, not an IDE. `cloud-agent-next` pins `@kilocode/cli@7.8.1`; the auto-routing benchmark and MCP-catalog CI float on `@latest` (`technical-notes/baseline/cloud-agent-consumers.md:48-63`). Anaconda Desktop embeds Kilo, and V1 ships an `anaconda-desktop` provider (`packages/opencode/src/kilocode/anaconda-desktop/*`). Other CLI consumers would otherwise be orphans: the Zed extension (`packages/extensions/zed`) and the GitHub Action (`github/action.yml`). One epic gives the "npm `latest` flip" risk an owner | +| **New E11 Post-cutover backlog** | Decision 7 lists non-gating scope (voice, image generation, team and incremental sharing, legacy TUI extras, default-off experimental features). Leaving these inside E4–E7 would make those epics' exit criteria ambiguous ("done except…"). Moving them gives gate epics crisp exits and a single place for ship/defer/drop decisions. KiloClaw is recorded here as not-needed (removed on V1, `3eaf47ba52`) so it has a home | +| **E0 narrowed to one-time setup** | Recurring work (upstream merges, v1 delta sweeps, shared-patch ratchet, acceptance runs, docs) is continuous and has no exit. It moves to named lanes L1–L5, so E0 can actually finish | +| **E5 keeps individual sharing as gating** | Individual share/unshare/fork-from-share and the public viewer for v2 messages are v1 CLI features. The old plan already made them G1 (`execution-plan.md:69`). Team ownership (#14399 ownership part) and incremental sync (#14400) are post-cutover (Decision 7) and go to E11. The fail-closed refusal of team upload stays in E5 | +| **FIM/next-edit producer in E6, consumed by E7** | One home per unit. VS Code is the first consumer. The producer is a gateway endpoint plus an editor-side engine (`kilo-gateway/src/{fim,edit,autocomplete}.ts`). E7 takes a dependency only if the G3 FIM gate stays: V1 JetBrains has no FIM producer or consumer, only migrated autocomplete settings (`packages/kilo-jetbrains/frontend/.../autocomplete/KiloAutocompleteSettingsService.kt`; see inventory Part 3). Escalated to the user as a proposed Decision 7 change | +| **Presence split across E4 and E6** | #14395 has a host-registration part (CLI remote, G1) and a VS Code visibility-reporting part (G2). The old plan split it the same way; this taxonomy keeps it as two units in two epics | +| **No separate "Editor services" epic** | #14021 was a subissue, but its consumers are the IDE tracks. Its runtime-side generation endpoints (enhance prompt, commit message, branch name) are E2; its editor consumers are E6/E7. That avoids a second epic with a split gate | +| **Dropped: Phase 0 "create issues" as an epic** | Issue creation is user work (brief: "leave issue creation to the user"). It is a planner checklist item, not an epic | + +Considered and rejected: + +| Option | Why not | +|---|---| +| Merge E4 and E5 (both talk to Kilo cloud) | Different backends and contracts: relay `ingest.kilosessions.ai` vs the share API in `services/session-ingest` (`technical-notes/session-sharing.md`). Different external verifications (#14401, #14407). Keep separate | +| Split E3 into Import and Distribution | They share the cutover runbook and the store decision (D6). Each half is too small alone. Revisit if E3 exceeds about 15 units | +| Put engines (memory, indexing, sandbox, telemetry) in their own epic | Each is one or two units. They share E2's seams (plugin, RPC, host composition) and gate together at G1 | +| Treat the desktop/Electron app as an epic | Kilo v1 has no counterpart. A product-disposition unit in E9 (ship, hide or defer) is enough until the user decides otherwise | + +## Surfaces + +Each unit has exactly one surface. + +| Surface | Meaning | +|---|---| +| shared runtime | Server, core policy, Kilo plugins and engines; consumed by every client | +| gateway/cloud | Code that talks to Kilo cloud services (gateway, relay, share, ingest, cloud agents) | +| CLI/TUI | Terminal product surface | +| packaging | Build, release, update, install, CI | +| import | v1 data, config and credential import into the v2 store | +| VS Code | Extension host and webviews | +| JetBrains | JetBrains plugin | +| external consumer | A product outside this repo that consumes the Kilo CLI | +| repo | Docs, conventions, branches, lanes | + +## Epics + +### E0 Foundation + +- **Why:** the clean base has no Kilo package at all (`git ls-tree HEAD packages/` lists only upstream packages). Every other epic needs a host, a registration pattern and a working CI signal first. +- **What:** Kilo host/composition package; Kilo RPC and plugin scaffolding; re-landed Kilo `AGENTS.md`, rewritten fork conventions and migration-tracking index; Kilo CI lane on `kilo-v2-clean`; disable or neutralize upstream workflows and publish targets that must not run from the fork; v1 reference fixtures for the runtime-policy units. +- **Exit criteria:** + - A Kilo host package builds and starts an isolated server through public seams only, verified by a service smoke test. + - One sample Kilo RPC, registered as a plugin, passes a round-trip test through the generated client. + - A Kilo-owned workflow runs `bun run check` and package tests on PRs to `kilo-v2-clean`, with results compared to `pinned-v2-baseline.md`. + - No upstream publish or deploy workflow can publish from the fork, verified by reading each workflow's trigger and repository guard. + - `AGENTS.md` and `v2-fork-conventions.md` name `kilo-v2-clean`, BASE and the AGENTS branch rules. + - v1 reference fixtures for offline wait, overflow, slow snapshot and snapshot retention are committed and replayable. + +### E1 Core and identity + +- **Why:** product identity, the separate store (D6), Gateway login and the model catalog are prerequisites for every surface. +- **What:** branding and default theme; customer v2 store and paths; Gateway auth/profile/org/Pass/errors; Gateway provider, catalog, routing and options; system-prompt selectors; usage and cost accounting; Kilo config layer; Kilo server extension surface (RPC groups, parent watchdog, reload, capability handshake). +- **Exit criteria:** + - v1 and v2 run side by side on one machine. A v2 boot never opens a v1 file, verified by a filesystem-access test against a seeded v1 store. + - Device login, org switch and logout work against the deployed Gateway (external-gated acceptance recorded with SHA). + - Kilo catalog models are selectable and requests carry Kilo headers, verified by a request-capture test. + - All eight prompt selectors resolve on BASE seams, verified by a wire test. + - Each supported Kilo config key folds or is refused with a diagnostic, never silently dropped. + - Shared-file patches introduced by E1 are counted by the L3 ratchet, each with a stated missing seam. + +### E2 Runtime behavior + +- **Why:** V1 policy on top of generic runtime services must survive, and the IDE tracks consume it. +- **What:** session policy (offline wait, overflow, snapshots, deletion, drain, subagents, wakeups, goals, retention, recall); agents, plan, review, commands, skills, rules; Kilo tools and permission policy; memory, indexing, sandbox, telemetry; MCP deltas and marketplace; board, background processes, worktree runtime, Agent Manager host contract; generation endpoints; Atomic Chat provider. +- **Exit criteria:** + - Every E2 unit with gate G1 passes its acceptance against the E0 v1 reference fixtures where one exists. + - Every native-equivalence claim in the inventory is backed by a test covering scope, errors, cancellation, persistence and presentation. + - G2-gated E2 units (message deletion, Agent Manager contract, generation endpoints, removal callers) expose public contracts that the generated client consumes. + - No client package imports Core or Server internals (L3 ratchet count is 0 for client→Core/Server). + +### E9 CLI/TUI surface + +- **Why:** G1 ships the terminal product. It needs Kilo commands, headless behavior and presentation, not just runtime parity. +- **What:** service/daemon UX; Kilo command set and help/docs generation; `kilo run`; ACP host; TUI shell, slash commands and dialogs; home and sidebar plugins; model picker and info; notifications; session scope switching; location/event filtering; alerts and sound; display and privacy preferences; `kilo cloud`; embedded web UI decision; disposition of upstream-only surfaces (desktop, web app). +- **Exit criteria:** + - Every G1 CLI/TUI scenario in the rewritten test plan passes on an isolated real host (L4), recorded with SHA. + - `kilo --help --all` lists the V1 Kilo command set or an explicit removal note for each missing command. + - A product decision is recorded for each upstream-only surface (desktop, web app, pairing) and implemented: shipped, hidden or deferred. + +### E3 Import and distribution + +- **Why:** customers need a v2 binary that installs, updates and imports v1 data without touching v1 files (D6). +- **What:** opt-in, re-runnable import of sessions, credentials, config and legacy migrators; external transcript and cloud-session import; fork/move verification; updater; build and release pipeline incl. signing; CI hardening; SDK/plugin publishing; preview release train; canary and runbook (G1 child); binary and channel naming. +- **Exit criteria:** + - Importing a fixture store from each supported V1 release preserves the original byte-for-byte. A second run imports only new sessions (test). + - Signed artifacts for every V1 platform install cleanly beside v1 and update/roll back on real artifacts. + - A v1 IDE build cannot resolve the v2 binary through PATH, global install or dist-tag (test). + - The G1 canary runbook exists and has been exercised once. + +### E4 Remote sessions + +- **Why:** `kilo remote` is a V1 CLI feature, so it gates G1. +- **What:** relay wire protocol and dispatcher; transcript, attachments and file delivery; suggestions; detach/exit/heartbeat; metadata and capabilities (incl. headless link and upstream-pairing coexistence); presence host registration; PR linking; deployed acceptance. +- **Exit criteria:** + - Local contract tests pass for every relay message in `CLIOutboundMessageSchema`. + - Deployed relay acceptance (#14407) is recorded with SHA, or explicitly waived by the user. + - Unsupported capabilities are not advertised. + +### E5 Sharing + +- **Why:** individual share/unshare/fork-from-share and the public viewer are V1 CLI features. +- **What:** share transport; payload contract; public viewer for v2 messages; fail-closed team upload refusal; background session ingest to Kilo cloud; deployed acceptance. +- **Exit criteria:** + - Share, unshare and fork-from-share round-trip against the deployed backend (#14401), recorded with SHA. + - The public viewer renders self-contained v2 messages. + - Team upload is refused with a clear message. + - Session ingest uploads scrubbed v2 sessions, and opt-out works. + +### E6 VS Code track (G2) + +- **Why:** VS Code stays on v1 until G2 (D5). The deliverable is the original Kilo extension UI on the v2 backend (user decision 2026-09-08, working log lines 71-79). +- **What:** extension and webview re-copied from V1 behind v2 backend adapters; real typecheck; chat, tabs, settings, connection, auth, generation, terminals, Agent Manager, notifications/suggestions, removal callers, timeline/diff; FIM/next-edit producer and consumer; code actions; VS Code-only tools; presence visibility; handshake and store/import switch; G2 scenario set. +- **Exit criteria:** + - `typecheck` for the extension is a real check with no skip shim. + - Every G2 scenario passes on a real VS Code host (L4), recorded with SHA. + - The extension never mixes v1 and v2 routes in one session, and it detects the backend through the capability handshake. + - Rollback to the previous extension version works on untouched v1 files. + +### E7 JetBrains track (G3) + +- **Why:** JetBrains pins a v1 CLI version until G3 (D5). The plugin is absent from both BASE and the donor. +- **What:** contract inventory; server startup, discovery, auth and version checks; sessions, streaming, permissions, history, config; terminal and editor-service dependencies; IDE validation; G3 scenario set. +- **Exit criteria:** + - The plugin builds from `kilo-v2-clean` and talks to v2 only through the generated client from the assembled OpenAPI contract. + - Every G3 scenario passes in each supported IDE, recorded with SHA. + - Version-mismatch handling refuses incompatible CLIs with a clear message. + +### E10 External consumers + +- **Why:** consumers outside this repo break when the CLI changes package, binary or protocol. +- **What:** cloud agent adaptation and validation; Anaconda Desktop integration and provider; other CLI consumers (Zed extension, GitHub Action, floating `@latest` users). +- **Exit criteria:** + - Each consumer has a recorded disposition (adapt, repackage, no change), an owner and the gate it attaches to. + - Each gating consumer passes its validation before its gate's go/no-go. + - No floating `@latest` consumer breaks at the dist-tag switch (verified or mitigated by pinning). + +### E8 Retirement + +- **Why:** the end state is `kilo-v2-clean` as `main` and v1 on a maintenance branch (`migration-context.md`). +- **What:** branch flip and its timing relative to IDE v1 builds; path consolidation after G3 (D6); re-landing or retiring packages that deploy from `main` (docs site, console); v1 end-of-life policy. +- **Exit criteria:** + - Every workflow and deployment that builds from `main` has a v2-side owner or is retired before the flip. + - The v1 maintenance branch builds and releases. + - Storage consolidation runs once, with a backup, and is never repeated. + +### E11 Post-cutover backlog + +- **Why:** Decision 7 non-gating scope needs one home and an explicit decision per item. +- **What:** voice/STT; image generation; team sharing ownership; incremental sharing; legacy TUI extras (theme catalogue, bindings, tables, vim); default-off experimental features; KiloClaw (recorded not-needed). +- **Exit criteria:** every item is shipped, dropped or re-scoped by a recorded user decision. Nothing here blocks a gate. + +### L Cross-cutting lanes + +| Lane | Scope | Exit / steady state | +|---|---|---| +| L1 Upstream sync | Merge `upstream/v2` pinned by SHA; dry-run workflow; merge tooling | Upstream lag recorded after every merge; no missed sync window without a note | +| L2 V1 delta sweep | Advance the assessment cutoff over `cutoff..origin/main`; refresh the experimental-flag list; re-run staleness for touched units | Zero unclassified Kilo-owned or marked V1 files at each gate | +| L3 Shared-patch ratchet and marker sweep | Count marked shared files, client→Core/Server imports and Kilo imports of upstream internals; #14381 | Counts at or below baseline; every increase states its seam | +| L4 Acceptance | Rewrite test plans with gate tags and V1-derived expectations; G2/G3 scenario sets; gate acceptance runs | Each gate's scenarios executed and recorded with SHA | +| L5 Docs | kilo.ai/docs content for v2; migration-tracking upkeep | Docs describe the shipped surface at each gate | + +## Orphan check + +Method: every row of both inventories (`v2-feature-inventory.md` Parts 1–4) and every issue in #13750's tree has a Unit, and every unit has one epic, one surface and one gate (`v2-unit-register.md`). + +| Candidate orphan | Decision | +|---|---| +| Cloud agents (#14023, #14425–#14428) | E10 (new) | +| Anaconda Desktop (#14024, #14429–#14432) and the V1 `anaconda-desktop` provider | E10, one unit | +| Zed extension, GitHub Action, floating `@latest` consumers | E10 | +| Voice, image generation, team/incremental sharing, legacy TUI extras, experimental features | E11 (new) | +| KiloClaw | E11, not-needed | +| `kilo-console` (deprecated) | E8 retire | +| kilo-docs site | content L5; survival across the branch flip E8 | +| `kilo-web-ui` and upstream web/desktop app | E9 product-disposition units | +| Upstream hosted web properties, `sdks/vscode`, `publish-vscode.yml`, CI images | E0 (keep inert, never publish from the fork) | +| v1 assessment-cutoff advance and experimental-flag refresh | L2 | +| #14381 marker sweep | L3 | +| Old-plan units without issues (N1–N17) | Re-homed in the register; N1 (consolidation merge) and N6b (import onto old branch) are not-needed on the clean base | +| Progress-plan rows without issues (old N12–N14) | Re-homed as E9 and E6 units | + +No orphans remain after these placements. The unit register lists every placement. diff --git a/migration-tracking/discovery/v2-feature-inventory.md b/migration-tracking/discovery/v2-feature-inventory.md new file mode 100644 index 000000000000..404b551edad2 --- /dev/null +++ b/migration-tracking/discovery/v2-feature-inventory.md @@ -0,0 +1,501 @@ +# Kilo v1 → v2 feature inventory and delta + +Status: discovery output, 2026-10-11. Single writer: discovery analyst. Companions: `v2-epic-taxonomy.md` (epics), `v2-unit-register.md` (units, staleness, donor), `doc-inventory.md`, `../technical-notes/baseline/pinned-v2-baseline.md`. + +## Why this file exists + +The plan needs one list of everything Kilo v1 does and everything upstream v2 already provides, so that each v1 feature gets a delta verdict and lands in exactly one unit. This file holds the per-feature facts. Epic and unit structure live in the companion files. + +## Refs and method + +| Ref | Value | +|---|---| +| V1 target | `fb7e96eda5409517a05948b9efaf01c53997bf6e` (`origin/main`, 2026-10-09) | +| V1 assessment snapshot (SNAP) | `f7115470` (2026-08-12), as named in `migration-context.md`. The 816-file assessment text pins `d99662338e` (2026-08-10), 800 commits earlier. See "Snapshot discrepancy" below | +| Upstream base (BASE) | `5e73d5cb` (parent of `v2.0.26`, on `upstream/v2`) | +| Donor | tag `kilo-v2-checkpoints-2026-09` = `879b89e8`, upstream merge base `59b29de4` | + +Method: read-only `git ls-tree`/`git show`/`git diff` at the pinned refs, reusing `marker-audit/`, `technical-notes/` and issues #13750, #14016–#14024, #14371–#14432. Four delegated inventories (v1 core, v1 IDE clients, upstream v2, staleness and donor) were synthesized here. Verdicts not exercised at runtime are marked UNVERIFIED. + +Delta vocabulary (per feature), applying Decision 7: + +| Verdict | Meaning | +|---|---| +| needs-port | V1 behavior required at a gate, with no v2 equivalent | +| native | BASE covers scope, errors, cancellation, persistence and presentation; only verification or branding remains | +| partial | BASE has the mechanism or part of the behavior; Kilo policy or presentation must be added | +| not-needed | Obsolete, removed on V1, or superseded on v2 | +| post-cutover | Real V1 behavior, but non-gating under Decision 7 | + +Each table's **Unit** column points at `v2-unit-register.md`. + +## The v1 line on `main` + +| Fact | Value | +|---|---| +| CLI package and version at V1 | `@kilocode/cli` 7.8.8 (`packages/opencode/package.json`), bins `kilo`, `kilocode`. Version at SNAP: 7.4.21 | +| Upstream base of v1 | opencode v1.18.26 (`.opencode-version`). V1 already contains upstream's split packages (`core`, `tui`, `schema`, `protocol`, `server`, `llm`, `client`) and the early Core-V2 event store | +| Cadence SNAP→V1 | 38 `release: v…` commits, v7.4.22 → v7.8.8 (~4–5 per week). JetBrains releases on its own line (`release(jetbrains): v7.1.7-rc.3 … v7.1.9`) | +| Volume SNAP→V1 | 4,104 files, +394,860/−68,348; 3,107 non-merge commits, 2,786 Kilo-authored, 321 upstream-origin (upstream merges #13002, #13368, #13513, #14449: v1.18.13 → v1.18.26) | +| Release mechanism | `publish.yml` (workflow_dispatch) → `script/version.ts` → `packages/opencode/script/build.ts` (linux x64/arm64 glibc and musl, darwin, windows incl. baseline and arm64) → VSIX build → publish | +| Channels | npm `@kilocode/cli` plus platform packages; GHCR `ghcr.io/kilo-org/kilocode`; AUR `kilo-bin`; Homebrew `Kilo-Org/homebrew-tap`; GitHub release archives; `install` script (`https://kilo.ai/cli/install` → `~/.kilo/bin`); nix flake; Zed extension manifest | +| Common ancestor with BASE | `0e2dd4ad` (2026-06-26). BASE is 4,343 commits past it, V1 is 18,848 | + +## Upstream v2 at BASE + +| Fact | Value | +|---|---| +| CLI package | `@opencode/cli` 2.0.25 in-tree (2.0.26 bump lives only in the tag commit), bins `opencode`, `opencode2` | +| Scope | `@opencode/*` since `a5312e16` (2026-09-07). `@opencode-ai/pty` keeps the old scope | +| Drift since the donor base `59b29de4` | 1,454 commits; 5,029 files, +344,830/−161,776 | +| Product surfaces | CLI + TUI, managed background service, Electron desktop and web app (`packages/desktop`, `packages/app`), thin `sdks/vscode` launcher, ACP, hosted web properties | + +## Part 1. Upstream-v1 behavior Kilo inherits, against BASE + +Kilo v1 inherits these groups from opencode v1.18.26. Kilo's patches on them are itemized in Part 2 or in the marker assessment. + +| Group | V1 behavior | BASE equivalent | Verdict | Unit | +|---|---|---|---|---| +| Sessions and prompt loop | Session CRUD, message/part model, agent loop, prompt queue, abort, revert, fork, title, SQLite store | Durable inbox, steer/queue delivery, explicit step loop, restart recovery, fork/move/background (`core/src/session/*`) | native (different model: self-contained typed messages, see `v2-v1-schema-diffs.md`) | U-E2-06 | +| Compaction and snapshots | Auto-compaction, prune, tail turns, git snapshots, diff/restore | `session/compaction.ts` (auto/overflow/manual, native compaction), `snapshot.ts`, revert stage/commit | partial (`prune`/`tail_turns` unsupported, `config/normalize.ts:332-333`) | U-E2-02, U-E2-03, U-E2-04 | +| Tools | bash, read, edit, write, patch, grep, glob, list, webfetch, websearch, task, todo, question, skill, lsp, batch, multiedit | edit, patch, write, read, glob, grep, shell, webfetch, websearch, skill, subagent, question, mcp-resource, file-diff | partial (no todo, no LSP diagnostics: TODOs at `edit.ts:107`, `file-mutation.ts:131`) | U-E2-16 | +| Permissions | Rule-based allow/ask/deny, per agent, external_directory | Rule effects per action/resource, per-session rules, saved approvals, managed policies, forms | native | U-E2-17 | +| Providers and models | models.dev, AI SDK providers, auth plugins | `@opencode/ai` runtime, 33 provider plugins, swappable catalog (`ServerOptions.models`) | native mechanism | U-E1-04 | +| Config | Layered jsonc, markdown agents/commands, env overrides | Per-domain folding with diagnostics, `ConfigMigrateV1` (opencode v1 only) | partial (filenames hard-coded, Kilo keys absent) | U-E1-07 | +| Agents, commands, skills | build/plan/general/explore, slash commands, skills | Same plus remote skill index and policies; `/init`, `/review` | native mechanism | U-E2-12 | +| MCP | Local/remote, OAuth, prompts/resources | Reworked client, stdio, OAuth with AS metadata, CLI auth | native (Kilo deltas in U-E2-22) | U-E2-22 | +| LSP, formatters, watcher | Auto-download LSP, formatters | Formatter config plugin; no LSP diagnostics tool | partial | U-E2-16 | +| Server and API | Effect HttpApi + SSE, basic auth, generated SDK | 30 protocol groups, plugin RPC `/api/rpc/:rpcID/:method`, generated client | native | U-E1-08 | +| TUI | opentui/solid TUI, themes, dialogs, sidebar, keybinds, feature plugins | Tabs, diff viewer, ~45 dialogs, slot API, semantic theme tokens, notifications and sound | native mechanism | U-E9-05 | +| ACP | `kilo acp` | `cli/src/acp/*` (elicitation→forms, compaction updates) | native | U-E9-04 | +| Sharing | share/unshare | Config key `share` only, no runtime | partial (Kilo share is its own backend) | U-E5-01 | +| Workspaces, worktree, PTY | Control-plane workspaces (experimental), worktree, PTY | Project worktrees, persistent PTY, workspace/environment drivers | native | U-E2-26 | +| CLI commands | run, serve, attach, models, stats, export, import, session, github, pr, plugin, db, debug, agent, auth, upgrade, uninstall, mcp | run, serve, service, api, models, stats, debug, plugin, session export/import, auth, mcp, pair, upgrade, uninstall, mini | native (no `github`/`pr`/`db` equivalents; UNVERIFIED whether needed) | U-E9-02 | +| Plugins and SDK | Plugin hooks, TUI plugins | Effect/Promise plugin API, TUI plugin slots, in-process SDK host | native | U-E0-02 | + +## Part 2. Kilo v1 customizations: core, CLI/TUI, gateway, packaging + +Source: delegated v1-core inventory (129 features, F-CORE-001…129). Every `packages/opencode/src/kilocode/*` top-level entry (120) maps to a feature or is infrastructure (`fn.ts`, `process/`, `effect/`, `index.ts`, part of `bootstrap.ts`). `k/` = `packages/opencode/src/kilocode/`. + +Column **Snap**: `pre` = present at SNAP with no material change; `Δ` = present at SNAP and changed by V1 (re-derive behavior from V1, Decision 4); `POST` = added after SNAP, so absent from the 816-file assessment. + +### A01 Identity, paths, store + +| ID | Feature | Paths (V1) | Snap | BASE | Delta | Unit | +|---|---|---|---|---|---|---| +| F-CORE-001 | Kilo identity and branding (soul prompt, headers, `kilo`/`kilocode` bins, `KILO_*` env, logo) | `k/soul.txt`, `k/const.ts`, `k/cli/logo.ts`, `packages/core/src/flag/flag.ts` | pre | `app="opencode"` hard-coded (`util/src/global.ts:12`), bin literal (`cli/script/build.ts:16`), `OPENCODE_*` env, UA prefix | partial (needs counted shared patches) | U-E1-01 | +| F-CORE-002 | Kilo paths and isolated DB/store, DB preflight, friendly SQLite errors | `k/paths.ts`, `packages/core/src/kilocode/{global,database-compat,db-preflight,sqlite-error}.ts` | Δ (WAL recovery `97abdab66c`, interrupted first access `1988e54fd6`) | `OPENCODE_DB`, Global layer replacement, `ServerOptions.database` | partial | U-E1-02 | +| F-CORE-003 | Legacy JSON→SQLite storage migration | `k/storage/json-migration.ts` | Δ small | none | needs-port (inside import only) | U-E3-01 | +| F-CORE-004 | Run/process log correlation, spawn validation | `k/log.ts`, `k/process/env.ts`, `packages/core/src/kilocode/{stdio-tap,exit-code,spawn-*,zero-id}.ts` | pre (zero-id POST) | Observability layer | partial | U-E1-02 | + +### A02 Daemon, server, lifecycle + +| ID | Feature | Paths (V1) | Snap | BASE | Delta | Unit | +|---|---|---|---|---|---|---| +| F-CORE-005 | `kilo daemon status/stop/restart`, managed background server | `k/daemon/*`, `k/cli/cmd/daemon.ts` | pre | `service start/stop/status/…`, registration file, `--standalone`, `--server` | native (command-name compatibility only) | U-E9-01 | +| F-CORE-006 | Kilo HttpApi groups (`/kilo`, `/kilocode`, `/remote`, `/network`, `/sandbox`, `/indexing`, `/memory`, `/telemetry`, `/suggestion`, `/commit-message`, `/branch-name`, `/enhance-prompt`, `/agent-builder`, `/background-process`, `/config/*`, `/instance/reload`, `/kilocode/migrate/*`, …) and CORS | `k/server/**`, `packages/server/src/kilocode/*` | Δ (+1,385/−537; migrate group POST) | Plugin RPC endpoints, `routes.ts` overrides | partial (mechanism native; each group re-homed with its feature) | U-E1-08 | +| F-CORE-007 | Parent watchdog (server exits when the embedding IDE dies) | `k/parent-watchdog.ts` | pre | `Mode="stdio"` exists; no parent-PID watchdog | needs-port | U-E1-08 | +| F-CORE-008 | Kilo bootstrap, instance reload, orderly shutdown | `k/bootstrap.ts`, `k/instance.ts`, `k/project/reload.ts` | Δ (`/reload` reloads project `3a2c5d5c2e`, shutdown deadline) | `reload`, service lifecycle | partial | U-E1-08 | +| F-CORE-009 | v1→V2 event bridge and wire encoding | `k/event-v2-bridge.ts`, `k/event-wire.ts` | POST | Native event system | not-needed (v2 is the target model) | U-E1-08 | +| F-CORE-010 | Startup performance: lazy commands, port warning, heap snapshot | `k/cli/{lazy-*,port-warning,heap-snapshot}.ts` | POST (lazy) | Upstream startup loading | partial (port warning, heap snapshot UNVERIFIED) | U-E9-01 | +| F-CORE-011 | Watcher eagerness policy for embedded clients | `k/watcher.ts` | Δ | Upstream watcher | partial | U-E1-08 | +| F-CORE-012 | TUI worker RPC handshake hardening | `k/util/rpc.ts` | POST | Different TUI transport at BASE | not-needed unless the defect reproduces (UNVERIFIED) | U-E9-05 | +| F-CORE-013 | Caffeination (`/caffeinate`) | `packages/core/src/kilocode/caffeination.ts` | POST | none | needs-port | U-E9-05 | +| F-CORE-014 | PTY registry/termination, hidden `__pty-smoke`, PTY cleanup on worktree removal | `packages/core/src/kilocode/pty/*`, `k/cli/cmd/pty-smoke.ts` | POST | Persistent PTY | partial | U-E3-07 | + +### A03 Gateway auth, organization, balance, Pass + +| ID | Feature | Paths (V1) | Snap | BASE | Delta | Unit | +|---|---|---|---|---|---|---| +| F-CORE-015 | Kilo Gateway device-code auth, token storage, legacy token migration, logout | `packages/kilo-gateway/src/auth/*` | Δ | Integration framework (key/OAuth/command/external), credentials | partial (mechanism only) | U-E1-03 | +| F-CORE-016 | Profile, organizations, teams, balance refresh | `kilo-gateway/src/api/profile.ts`, `k/cli/cmd/profile.ts`, `k/components/dialog-kilo-*` | pre | none | needs-port | U-E1-03 | +| F-CORE-017 | Kilo Pass status | `kilo-gateway/src/api/kilo-pass.ts` | pre | none | needs-port | U-E1-03 | +| F-CORE-018 | Gateway notifications and news (home, banner) | `kilo-gateway/src/api/notifications.ts`, `k/components/*news*` | pre | none | needs-port | U-E9-06 | +| F-CORE-019 | Kilo gateway error codes, actionable display | `k/kilo-errors.ts` | Δ | Generic provider errors | needs-port | U-E1-03 | +| F-CORE-020 | Provider usage and quotas (Kilo, Codex, MiniMax) `/usage` | `kilo-gateway/src/provider-usage.ts`, `packages/core/src/kilocode/provider-usage/*` | POST | none | needs-port | U-E1-06 | +| F-CORE-021 | tRPC client for Kilo cloud | `kilo-gateway/src/api/trpc.ts` | POST | none | needs-port (dependency of cloud features) | U-E1-03 | + +### A04 Provider, model catalog, routing + +| ID | Feature | Paths (V1) | Snap | BASE | Delta | Unit | +|---|---|---|---|---|---|---| +| F-CORE-022 | Kilo Gateway provider (headers, metadata, Responses API) | `kilo-gateway/src/{provider,headers,gateway-metadata,responses}.ts`, `packages/core/src/plugin/provider/kilo.ts` | pre | `provider/kilo.ts` brands calls as opencode | partial | U-E1-04 | +| F-CORE-023 | Gateway catalog merge, refresh, corrupt-cache recovery, filtering | `k/provider/{catalog,catalog-recovery,models-refresh,…}.ts` | Δ (recovery chain `5539dd3ae5` #14723, `2792c704ed` #14794) | Swappable models.dev source | partial | U-E1-04 | +| F-CORE-024 | Kilo Auto and routed-model display | `k/cli/run-auto.ts`, `k/session/routed-model.ts` | pre | none | needs-port | U-E1-04 | +| F-CORE-025 | Provider options, reasoning summary, model-family match | `k/provider-options.ts`, `k/model-match.ts` | Δ | provider/model transforms | partial | U-E1-04 | +| F-CORE-026 | Codex token refresh, cloud-agent provider auth, session headers | `k/provider/{codex-refresh,cloud-auth,opencode-session-headers}.ts` | POST (2 files) | Copilot/Codex providers upstream | partial | U-E1-04 | +| F-CORE-027 | Model info panel, pricing, free/prompt-training disclosure, Kilo picker grouping | `k/components/model-info-panel*`, `packages/tui/src/kilocode/model-picker.ts` | POST (picker) | Native picker | partial | U-E9-07 | +| F-CORE-028 | Session-family model usage, cost, throughput, subagent cost propagation | `k/session/{model-usage,metrics,cost-propagation}.ts` | Δ | Session stats | partial | U-E1-06 | +| F-CORE-029 | Per-model system-prompt selection (8 selectors), memory guidance, editor `` context | `k/system-prompt.ts`, `k/editor-context.ts` | Δ | `plugin/system-prompt.ts` deleted at BASE (#47447, #47559); `session.hook("context")` | partial (seam moved; re-seat) | U-E1-05 | +| F-CORE-030 | `kilo roll-call` model connectivity test | `k/cli/cmd/roll-call.ts` | pre | none | needs-port | U-E9-02 | + +### A05 Config and settings + +| ID | Feature | Paths (V1) | Snap | BASE | Delta | Unit | +|---|---|---|---|---|---|---| +| F-CORE-031 | Kilo config layer: Kilo keys, overlay/effective/sources APIs, writer, retired-key warnings, validation | `k/config/*`, `k/config-validation.ts` | Δ (+1,227; `memory_model` #14781, swarm #14160, PTC #14230, cleanup #14054) | Folding + diagnostics, no Kilo keys or `kilo.json` names | partial | U-E1-07 | +| F-CORE-032 | References glue (effective Kilo config into Core-V2 references) | `k/reference.ts` | Δ | Native references | partial | U-E1-07 | +| F-CORE-033 | TUI config writes and hot reload | `k/tui/config.ts`, `…/tui-config-hot-reload.ts` | pre | TUI config, settings dialog | partial | U-E9-05 | + +### A06 Session runtime policy + +| ID | Feature | Paths (V1) | Snap | BASE | Delta | Unit | +|---|---|---|---|---|---|---| +| F-CORE-034 | Offline wait and reconnect | `k/session/processor.ts` (+ #14893 conn-reset retry at the V1 tip) | Δ | Retry bounded at 10 recurs (~84 s); `session.hook("retry")` cannot extend | needs-port | U-E2-01 | +| F-CORE-035 | Overflow accounting, `threshold_percent`, chunked compaction, payload recovery | `k/session/{overflow,compaction*}.ts` | Δ (`5edf432c2f` #14318) | Auto/overflow compaction, `buffer`/`keep` | partial | U-E2-02 | +| F-CORE-036a | Slow-snapshot interaction (timeouts, question, persisted disable) | `k/snapshot/{track,prepare}.ts` | Δ | Snapshots, no slow-snapshot question | partial | U-E2-03 | +| F-CORE-036b | Snapshot seed/materialize, locking, cleanup, retention | `k/snapshot/{seed,materialize,lock,cleanup}.ts` | Δ (+490; lock/cleanup POST) | Snapshot perf work, no Kilo hardening | partial | U-E2-04 | +| F-CORE-037 | Session drain, stop/resume control, `kilo run` waits for drain | `k/session/{drain,control}.ts` | POST | Run coordinator, interruption | partial | U-E2-06 | +| F-CORE-038 | Wakeups and cron (persisted, re-armed at start) | `k/wakeup/*`, `k/tool/{schedule-wakeup,cancel-wakeup,cron}.ts` | POST (#14094, #14312) | none | needs-port | U-E2-08 | +| F-CORE-039 | Goal mode (`/goal`, `goal`/`goal_report`) | `k/session/goal/*` | POST (#13679, #14504) | none | needs-port | U-E2-09 | +| F-CORE-040 | Backend-owned session retention | `k/session/retention.ts` | POST (#14054) | none | needs-port | U-E2-10 | +| F-CORE-041 | Subagent resume hint, background subagents (flag default **on**), steering into subagents | `k/task-resume.ts`, `k/tool/task*.ts`, `k/session/steering.ts` | Δ (#14702, #13641) | Background subagents, restart recovery | partial | U-E2-07 | +| F-CORE-042 | Message deletion, Kilo revert semantics, stable ordering | `k/session/{revert,message-order,part-lifecycle}.ts` | Δ | Revert + inbox cancel only | needs-port (deletion) | U-E2-05 | +| F-CORE-043 | Queued-prompt dismissal and steer labels | `k/session/prompt-queue.ts`, `packages/tui/src/kilocode/steer*.ts` | POST (steer) | Inbox steer/queue (cancel/change between queue and steer) | partial | U-E2-06 | +| F-CORE-044 | Bounded session title generation | `k/session/title.ts` | POST | Hidden title agent | native (UNVERIFIED bound) | U-E2-06 | +| F-CORE-045 | Processing hardening (diagnostics, undecodable images, continuation, tool-input repair, output-token cap) | `k/session/*` | Δ (~128 fixes) | Tool-input repair, continuation exist | partial | U-E2-06 | +| F-CORE-046 | Session transcript recall search | `k/session/recall-search.ts` | Δ | none | needs-port | U-E2-11 | +| F-CORE-047 | Per-session soft maximum-cost alert | `k/cli/cmd/tui/cost-alert.ts` | pre | none | needs-port | U-E9-08 | + +### A07 Session portability and import + +| ID | Feature | Paths (V1) | Snap | BASE | Delta | Unit | +|---|---|---|---|---|---|---| +| F-CORE-048 | Background session export/ingest to Kilo cloud (scrubbed, zstd) | `k/session-export/**`, `src/kilo-sessions/ingest-*` | pre | none | needs-port | U-E5-06 | +| F-CORE-049 | Cloud session list/import, `--cloud-fork` | `k/cloud-session.ts`, `kilo-gateway/src/cloud-sessions.ts` | Δ (#13483, #12388) | none | needs-port | U-E3-04 | +| F-CORE-050 | Session import API (project/session/message/part) | `k/session-import/*` | Δ | `/api/experimental/session/import` (v2 transfer format) | partial | U-E3-01 | +| F-CORE-051 | External transcript resume (Claude Code, Codex) and server-side migrate API | `k/session-resume/*`, migrate group | POST (#13294) | none | needs-port | U-E3-04 | +| F-CORE-052 | Cumulative diff and restore when moving sessions | `k/session-portability/*` | pre | `session.move` | partial | U-E3-05 | +| F-CORE-053 | Session fork command and routing | `k/session/fork*.ts` | pre | `session.fork` | native | U-E3-05 | +| F-CORE-054 | Legacy credential migration | `packages/core/src/kilocode/credential-migration.ts` | pre | Auth export/import (#52139) | partial | U-E3-02 | + +### A08 Agents, modes, commands, skills, rules + +| ID | Feature | Paths (V1) | Snap | BASE | Delta | Unit | +|---|---|---|---|---|---|---| +| F-CORE-055 | Kilo agents: code, ask, debug, orchestrator, plan (+ scout, experimental) | `k/agent/*` | Δ (agent requirements removed #13225) | build/plan/general/explore | partial | U-E2-12 | +| F-CORE-056 | Agent builder | `k/agent/builder.ts` | pre | none | needs-port | U-E2-12 | +| F-CORE-057 | Plan file discovery, plan→code follow-up, open plan | `k/plan-file.ts`, `k/plan-followup.ts`, `k/tool/{plan,open-plan}.ts` | Δ | Plan agent writes `~/.opencode/plan`, no `plan_exit` | needs-port (replace, not compose: `plan-v2-parity.md`) | U-E2-13 | +| F-CORE-058 | Mode reminders on agent switch | `k/session/mode-reminders.ts` | POST | Plan enter/leave reminders | partial | U-E2-12 | +| F-CORE-059 | `/review` over working tree/worktree diff | `k/review/*` | Δ (`46bd29d733`) | `/review` built-in | partial (Kilo policy) | U-E2-14 | +| F-CORE-060 | Command files list/remove, overrides, reserved names, workflow variants, command timeout | `k/command*`, `k/session/workflow-variant.ts` | POST (reserved) | Config/markdown commands | partial | U-E2-15 | +| F-CORE-061 | Skills: built-in `kilo-config`, trust, validation, remove | `k/skills/*`, `k/skill/*`, `k/skill-remove.ts` | Δ | Skills with policies, `disable-model-invocation` | partial | U-E2-15 | +| F-CORE-062 | Rules from `.kilo`/`.kilocode`, mirrored from primary worktree | `k/primary-worktree.ts`, `k/server/routes/config-rules.ts` | Δ | Instructions discovery (`.claude`, `.agents`, `.opencode`) | partial | U-E2-15 | +| F-CORE-063 | Marketplace (MCP servers, skills, modes, plugins) | `k/marketplace/*` | POST (#12714, #14510) | none | needs-port | U-E2-23 | + +### A09 Tools and permissions + +| ID | Feature | Paths (V1) | Snap | BASE | Delta | Unit | +|---|---|---|---|---|---|---| +| F-CORE-064 | Kilo tool registry and client gating (`KILO_CLIENT`) | `k/tool/registry.ts` | Δ (+2,205 in `tool/`) | `tool.transform` | partial | U-E2-16 | +| F-CORE-065 | Permission customizations (`/approve-all`, headless policy, config-path protection, provenance) | `k/permission/*` | Δ (#14051) | Native permissions and policies | partial | U-E2-17 | +| F-CORE-066 | Bash hierarchy, heredoc/unparsed handling, PowerShell | `k/bash-hierarchy.ts`, `k/tool/shell-*` | Δ | Shell tool | partial | U-E2-17 | +| F-CORE-067 | Question tool and headless handling | `k/question/*` | Δ (#13727) | Forms | partial | U-E2-16 | +| F-CORE-068 | `suggest` follow-up tool and TUI bar | `k/suggestion/*` | pre | none | needs-port | U-E2-16 | +| F-CORE-069 | TypeScript check after edits (tsgo/tsc) | `k/ts-check.ts` | Δ | No LSP diagnostics | needs-port | U-E2-16 | +| F-CORE-070 | Exa web search via Kilo gateway (flag-gated) | `k/tool/websearch-kilo-exa.ts` | pre | Exa websearch provider | post-cutover (experimental flag) | U-E11-05 | +| F-CORE-071 | Rich document read (docx, xlsx, ods, pdf) | `k/tool/read-*` | pre | none | needs-port | U-E2-16 | +| F-CORE-072 | Chart tool (VS Code only) | `k/tool/chart.ts` | pre | none | needs-port | U-E6-11 | +| F-CORE-073 | Model search/selection tools (task model selection) | `k/tool/model-*` | POST (#13557, #14533) | Subagent model choice | partial | U-E2-16 | +| F-CORE-074 | Repo overview tool, fff/ripgrep backends | `k/tool/repo-overview.ts` | pre | grep/glob | partial | U-E2-16 | +| F-CORE-075 | Context tools (`context_info`, self-compact) | `k/tool/context.ts` | POST, `KILO_EXPERIMENTAL_CONTEXT_TOOLS` off | none | post-cutover | U-E11-05 | +| F-CORE-076 | Code mode / Programmatic Tool Calling | `k/tool/code-mode.ts` | POST, `experimental.code_mode` off | `packages/codemode` (native) | post-cutover (native mechanism exists) | U-E11-05 | +| F-CORE-077 | Browser open tool (IDE browser broker) | `k/tool/browser-open.ts` | POST | none | needs-port | U-E6-11 | +| F-CORE-078 | Encoding-preserving I/O | `k/encoding.ts`, `k/tool/encoded-io.ts` | pre | BOM utilities | partial | U-E2-16 | +| F-CORE-079 | Todo status transitions for clients | `k/todo-view.ts` | Δ | No todo tool | needs-port | U-E2-16 | + +### A10–A13 Engines + +| ID | Feature | Paths (V1) | Snap | BASE | Delta | Unit | +|---|---|---|---|---|---|---| +| F-CORE-080 | Kilo memory (per-project markdown memory, recall/save tools, `/memory`) | `packages/kilo-memory/**`, `k/memory/*` | Δ small (`memory_model` #14781, SSE fix #13122) | none | needs-port | U-E2-18 | +| F-CORE-081 | Codebase indexing (worker, 10 embedders, LanceDB/Qdrant, `semantic_search`) | `packages/kilo-indexing/**`, `k/indexing*` | Δ (embedder fixes, root safeguards, `@parcel/watcher` #12784) | none | needs-port | U-E2-19 | +| F-CORE-082 | Sandbox (bwrap/seatbelt, network relay, git classifier, per-session lifecycle) | `packages/kilo-sandbox/**`, `k/sandbox/*` | Δ (git classifier #14222) | Workspace/environment driver seam only | needs-port. Opt-in top-level `sandbox.enabled` (default false, `k/sandbox/config.ts`), **not** an experimental flag | U-E2-20 | +| F-CORE-083 | Telemetry (PostHog events, consent) | `packages/kilo-telemetry/**` | Δ small (#14638) | OTLP only, PostHog removed | needs-port (privacy decision in `telemetry-v2-overlap.md`) | U-E2-21 | + +### A14 MCP and plugins + +| ID | Feature | Paths (V1) | Snap | BASE | Delta | Unit | +|---|---|---|---|---|---|---| +| F-CORE-084 | MCP: machine-wide OAuth callback handoff, issuer discovery, auth-failure surfacing, SSE probe, jsonc-preserving `kilo mcp` | `k/mcp/*`, `k/mcp-oauth-callback.ts` | POST (#14724) | MCP OAuth with AS metadata, `mcp auth` | partial (gap narrowed at BASE) | U-E2-22 | +| F-CORE-085 | MCP Apps (UI resources) | `k/mcp/apps.ts` | POST, `KILO_EXPERIMENTAL_MCP_APPS` off | none | post-cutover | U-E11-05 | +| F-CORE-086 | `git:` plugin sources | `k/plugin/git-source.ts` | POST | plugin add/check/update | partial | U-E2-22 | + +### A15 Agent coordination, worktrees, processes, notebook + +| ID | Feature | Paths (V1) | Snap | BASE | Delta | Unit | +|---|---|---|---|---|---|---| +| F-CORE-087 | Agent Manager host contract (ask/reply/reject, protection) | `k/agent-manager/*` | Δ | none | needs-port | U-E2-27 | +| F-CORE-088a | Worktree runtime: family detection, teardown, `.kilo/worktrees` | `k/worktree-*.ts`, `k/git-refs.ts` | Δ | Project worktrees in `/worktree/` | partial | U-E2-26 | +| F-CORE-088b | `kilo worktree create/list/remove`, `--worktree` | `k/cli/cmd/{worktree,tui-worktree}.ts` | POST (#12809) | none | needs-port | U-E9-02 | +| F-CORE-089 | Background process tool, sidebar, `/process` | `k/background-process/*` | Δ | Persistent PTY only | needs-port | U-E2-25 | +| F-CORE-090 | Shared agent board (flag default **on**) | `k/board/*`, core migrations `kilocode_board*` | POST (#13629, #14134) | none | needs-port (default-on, see Decision 7 note) | U-E2-24 | +| F-CORE-091 | Native notebook host (VS Code) | `k/notebook/*` | Δ, `experimental.native_notebook_tools` off | none | post-cutover (experimental) | U-E11-05 | + +### A16 Remote · A17 Sharing + +| ID | Feature | Paths (V1) | Snap | BASE | Delta | Unit | +|---|---|---|---|---|---|---| +| F-CORE-092 | Remote session relay (`kilo remote`, `/remote`, commands, attachments, exit) | `src/kilo-sessions/*`, `k/remote-tui.tsx` | Δ (#13496, #13497, #13565, #14214) | Device pairing + OpenTunnel (different model) | needs-port | U-E4-01 | +| F-CORE-093 | Viewer presence over event service | `k/presence/*`, `kilo-gateway/src/event-service/client.ts` | Δ (client moved) | none | needs-port | U-E4-06 | +| F-CORE-094 | Remote tools `notify_user`, `send_file` | `k/tool/{notify-user,send-file}.ts` | pre | none | needs-port | U-E4-02 | +| F-CORE-095 | PR linking (GitHub, GitLab, Bitbucket), poller, sidebar | `src/kilo-sessions/pr-link*`, `k/tool/link-pr.ts` | POST (#13137, #14195) | none | needs-port | U-E4-07 | +| F-CORE-096 | Headless docs/login link dialog | `…/dialog-headless-link.tsx` | pre | Pairing QR links (different purpose) | needs-port | U-E4-05 | +| F-CORE-097 | Kilo share transport (share/unshare/fork) | shared `src/share/*` (markers) | pre (EMPTY delta) | none | needs-port | U-E5-01 | + +### A18 CLI · A19 TUI + +| ID | Feature | Paths (V1) | Snap | BASE | Delta | Unit | +|---|---|---|---|---|---|---| +| F-CORE-098 | Kilo command set (`cloud`, `roll-call`, `profile`, `remote`, `daemon`, `config check`, `worktree`, `help --all`, `completion`), removed upstream `web`, generated CLI docs | `src/index.ts`, `k/commands.ts`, `k/help*.ts`, `k/generate-cli-docs.ts` | Δ | Upstream command set | partial | U-E9-02 | +| F-CORE-099 | `kilo run` customizations (variant, auto, drain, stdin) | `k/cli/cmd/run*.ts` | Δ | `opencode run` json/default, v1 flag bridge | partial | U-E9-03 | +| F-CORE-100 | ACP permission mapping | `k/acp/permission.ts` | pre | ACP permissions/elicitation | partial | U-E9-04 | +| F-CORE-101 | Terminal title/icon/activity (OSC), worker thread, VT screen | `k/cli/cmd/tui/*` | POST (activity) | Upstream TUI | partial | U-E9-05 | +| F-CORE-102 | Kilo slash commands and dialogs (`/about`, `/profile`, `/teams`, `/usage`, `/indexing`, `/privacy`, `/remote`, `/memory`, `/process`, `/sandbox`, `/caffeinate`, `/reload`, `/approve-all`) | `k/kilo-commands.tsx`, `kilo-gateway/src/tui*.ts` | Δ | Slots and command palette | partial (each command lands with its feature unit; registry here) | U-E9-06 | +| F-CORE-103 | Home/sidebar plugins: onboarding, tips, footer, usage and balance rows | `k/plugins/{home-*,sidebar-*}.tsx` | pre | `home.footer`, `sidebar.content`, `sidebar.footer` slots | partial | U-E9-07 | +| F-CORE-104 | Session/workspace switcher dialog | `k/plugins/session-switcher/*` | pre, `KILO_EXPERIMENTAL_SESSION_SWITCHER` off | Session tabs | post-cutover (experimental) | U-E11-05 | +| F-CORE-105 | v2 sync and session-v2 debug view | `k/plugins/{sync-v2,session-v2-debug}.tsx` | pre | Native v2 TUI | not-needed (debug); location filtering verified in U-E9-11 | U-E9-11 | +| F-CORE-106 | Vim prompt mode (`/vim`) | `…/prompt/vim.ts` | pre | none | post-cutover (legacy TUI extra; #14378 bindings part) | U-E11-04 | +| F-CORE-107 | Completion/abort/timeout notifications and sound | `k/plugins/attention.ts` | pre | Notifications and sound toggles | partial | U-E9-09 | +| F-CORE-108a | Kilo default theme and logo | `packages/tui/src/theme/assets/kilo.json`, logo | pre | Theme files; donor `theme/src/tui/defaults.ts`, `fallback.ts` deleted at BASE | partial | U-E1-01 | +| F-CORE-108b | Legacy `kilo-v1` and colorblind theme catalogue | `…/assets/kilo-v1.json` | pre | none | post-cutover (#14376) | U-E11-04 | +| F-CORE-109a | Clickable links, @session mentions, double-press guards, notices | `packages/tui/src/kilocode/*` | POST (3 files) | partial | partial | U-E9-10 | +| F-CORE-109b | Legacy keybindings and diff theme | `k/tui/{keybinds,diff}.ts` | pre | Configurable keybinds | post-cutover (#14378 bindings) | U-E11-04 | +| F-CORE-110 | Display preferences (tool/reasoning collapse) and privacy-mode redaction | config keys, `k/pii.ts` | pre | Low-verbosity summaries | partial | U-E9-12 | + +### A20 Cloud · A21 Packaging, release, CI + +| ID | Feature | Paths (V1) | Snap | BASE | Delta | Unit | +|---|---|---|---|---|---|---| +| F-CORE-111 | `kilo cloud start/send/status/result`, stdin prompts, stream tickets | `k/cli/cmd/cloud*.ts`, `k/cloud/*` | Δ small (#13851) | none | needs-port | U-E9-13 | +| F-CORE-112 | Updater and install-method detection | `k/installation/*` | POST (`latest.ts`) | Updater with opencode literals (`updater.ts:145-198`) | partial | U-E3-06 | +| F-CORE-113 | Build and release pipeline (multi-platform, VSIX bundling, npm/GHCR/AUR/Homebrew/nix/Zed, Windows signing, bundled bwrap, SBOMs, PTY smoke) | `.github/workflows/{publish,beta,smoke-test,…}.yml`, `packages/opencode/script/*`, `script/*` | Δ (SBOM #14513, Windows AVX2 launcher) | Upstream publish pipeline bound to `anomalyco/opencode` | partial | U-E3-07 | +| F-CORE-114 | CI and repository guards (forbidden strings, generated artifacts, annotations, md tables, CodeQL, test sharding, triage bots) | `.github/workflows/*`, `script/check-*.ts` | Δ (47 workflow files +4,888) | Upstream `check.yml`, `test.yml` | partial | U-E3-08 | +| F-CORE-115 | Upstream merge tooling with Kilo transforms | `script/upstream/**` | Δ | none | partial (re-derive for the v2 line) | U-L1-01 | + +### A24 Editor services (runtime side) · A26–A28 + +| ID | Feature | Paths (V1) | Snap | BASE | Delta | Unit | +|---|---|---|---|---|---|---| +| F-CORE-116 | Enhance-prompt, commit-message, branch-name generation endpoints | `k/enhance-prompt.ts`, `k/commit-message/*`, `k/branch-name.ts` | Δ small (#13108) | `generate` protocol group (UNVERIFIED equivalence) | partial | U-E2-28 | +| F-CORE-117 | FIM and next-edit via gateway (`/kilo/fim`, `/kilo/edit`) | `kilo-gateway/src/{fim,edit,autocomplete,mistral-fim-endpoint}.ts` | pre | none | needs-port (hard gate) | U-E6-08 | +| F-CORE-118 | Anaconda Desktop provider (discover local AI Navigator, WorkOS OAuth, TUI setup) | `k/anaconda-desktop/*` | pre (EMPTY delta) | none | needs-port (gate set by consumer) | U-E10-02 | +| F-CORE-119 | Atomic Chat local provider plugin | `packages/plugin-atomic-chat/**` | pre (EMPTY) | Plugin provider API | needs-port | U-E2-29 | +| F-CORE-120 | Speech-to-text transcription | `/kilo/audioTranscriptions`, `experimental.speech_to_text_*` | pre | `@opencode/ai` transcription client (mechanism) | post-cutover | U-E11-01 | +| F-CORE-121 | Image generation tool | `k/tool/generate-image.ts`, `experimental.image_generation` | pre | `@opencode/ai` image client (mechanism) | post-cutover | U-E11-02 | +| F-CORE-122 | KiloClaw | removed on V1 in `3eaf47ba52` (#14209, 2026-09-16) | removed | — | not-needed | U-E11-06 | + +### A29 Web properties · A30 SDK · A31 Legacy · A32 Security + +| ID | Feature | Paths (V1) | Snap | BASE | Delta | Unit | +|---|---|---|---|---|---|---| +| F-CORE-123 | `kilo console` local settings web app (deprecated) | `packages/kilo-console/**` | Δ small | Upstream settings dialog and web app | not-needed (deprecated on V1; obsolete per old plan) | U-E8-03 | +| F-CORE-124 | kilo.ai/docs site incl. generated CLI reference | `packages/kilo-docs/**` (978 files) | Δ (content) | none (upstream docs are opencode's) | partial (content must describe v2; site must survive the branch flip) | U-L5-01 | +| F-CORE-125 | `kilo-web-ui` embedded web components | `packages/kilo-web-ui/**` | Δ | Upstream `packages/app` served by the service | partial (decide: adopt upstream app or keep Kilo UI) | U-E9-14 | +| F-CORE-126 | Published `@kilocode/sdk` and `@kilocode/plugin` | `packages/sdk/js`, `packages/plugin` | Δ (regen) | `@opencode/sdk`, `@opencode/plugin` | partial (publish under Kilo names; regenerate) | U-E3-09 | +| F-CORE-127 | Legacy Kilo Code migrators (modes, `.kilocoderules`, workflows, MCP settings, `.kilocodeignore`) | `k/config-injector.ts`, `k/*-migrator.ts` | pre | none | needs-port | U-E3-03 | +| F-CORE-128 | Claude Code settings migration | `k/config/claude-migration.ts` | POST, `KILO_EXPERIMENTAL_CLAUDE_MIGRATION` off (VS Code passes a setting) | `.claude` discovery for skills/instructions | post-cutover (experimental) | U-E11-05 | +| F-CORE-129 | PII redaction, GitHub attachment URL validation, forbidden-strings CI | `k/pii.ts`, `k/security/github.ts` | pre | Managed policy, file access | partial | U-E9-12 | + +### Shared-file customizations outside the assessment + +At V1, 892 non-IDE files carry `kilocode_change`, and 159 of them are absent from the 816-file TSV (added or first marked after SNAP). Hotspots: `packages/core/src/database` migrations (board, model-usage index), `packages/opencode/src/{server,cli,tool,plugin,project,mcp}`, `packages/llm/src/protocols`, `packages/schema/src/{worktree-event,revert,mcp-event,event-manifest}.ts`, `packages/core/src/{repository-cache,location-services}.ts`, and `packages/tui/src/{util,ui,feature-plugins,component}`. The features behind them are covered by the POST rows above. The file-level disposition belongs to the first L2 sweep (U-L2-01). + +### Removed between SNAP and V1 (do not port) + +| Path at SNAP | Commit | Note | +|---|---|---| +| `k/claw/*` | `3eaf47ba52` (#14209) | KiloClaw (F-CORE-122) | +| `k/interactive-terminal/*` | `b5cf426158` (#13793) | Non-functional interactive terminal tool | +| `k/swe-pruner.ts` | `e1bcb320d9` | Experimental output pruning | +| `k/agent-requirements.ts` | `da10638654` (#13225) | Agent requirements | +| `kilo-vscode/src/legacy-migration` | `25d9448d63` (#13605) | Kilo-classic VS Code migration (−2,011 lines); session migration moved server-side (#13294) | + +## Part 3. Kilo v1 customizations: IDE clients + +Source: direct read of V1 (`fb7e96ed`), written by the analyst after the delegated clients inventory stalled. Inputs: `packages/kilo-vscode/package.json` (`kilo-code` 7.8.8: 73 commands, one activity-bar view, 40 settings), the extension's SDK call sites (about 120 distinct client methods), `packages/kilo-vscode/src/**`, and `packages/kilo-jetbrains/**` (1,236 files). It is less exhaustive than Part 2. Rows group commands and settings by feature, and per-command detail is UNVERIFIED beyond the cited paths. `kv/` = `packages/kilo-vscode/src/`, `kw/` = `packages/kilo-vscode/webview-ui/src/`, `jb/` = `packages/kilo-jetbrains/`. + +### How each client reaches the CLI at V1 + +| Client | Binary | Contract | Evidence | +|---|---|---|---| +| VS Code | Bundles `bin/kilo`, built from `packages/opencode` **in the same checkout** (`script/local-bin.ts`, `prepare:cli-binary`). Also bundles bwrap, ffmpeg, tree-sitter and sandbox-worker resources | Generated `@kilocode/sdk/v2` client over HTTP/SSE, started by `kv/services/cli-backend/server-manager.ts`. Calls Kilo-specific routes (`kilo.*`, `kilocode.*`, `remote.*`, `network.*`, `suggestion.*`, `commitMessage.*`, `branchName.*`, `anacondaDesktop.*`), core routes (`session.*` incl. `session.deleteMessage`, `permission.*`, `question.*`, `mcp.*`, `pty.*`/`v2.pty.*`, `config.overlay*`, `provider.oauth.*`, `experimental.capabilities.get`, `experimental.controlPlane.moveSession`) | `kv/services/cli-backend/*`, `kv/features.ts` | +| JetBrains | Pins the CLI release named in `jb/package.json` (`version` 7.8.8). `kilo.cli.pinned=false` is dev-only and refused for production builds (`jb/build.gradle.kts:89-98`). Plugin version 7.1.9 on its own `jetbrains/v*` tag line | Split-mode plugin (backend/frontend/shared modules). Kotlin client generated from the CLI's OpenAPI spec (`jb/backend/build.gradle.kts:11-21,64,129`, `generateOpenApiSpec`, `normalizeOpenApiSpec`) | `jb/gradle.properties`, `jb/build.gradle.kts` | + +**Consequence for the branch flip (U-E8-01).** The VS Code v1 build compiles its bundled CLI from `packages/opencode` in the same tree. Once `kilo-v2-clean` becomes `main`, a v1 extension cannot be built from `main`. Either the flip waits until after G2 and G3, or v1 IDE releases build from the `v1` branch. + +### VS Code (A22, A23, A24 and runtime consumers) + +| ID | Feature | Paths (V1) | Flag | Snap | Delta | Unit | +|---|---|---|---|---|---|---| +| F-VSC-001 | Extension host, activation, webview HTML, sidebar view, reload, heap snapshot | `kv/extension.ts`, `kv/KiloProvider.ts`, `kv/kilo-provider/*` | — | Δ | needs-port | U-E6-01 | +| F-VSC-002 | Webview UI app and shared components (`kilo-ui`, `kilo-i18n`) | `kw/**` (661 files), `packages/kilo-ui`, `packages/kilo-i18n` | — | Δ | needs-port | U-E6-01 | +| F-VSC-003 | Server manager, connection, retry, SSE adapter, explicit abort, bundled CLI resources | `kv/services/cli-backend/*` | — | Δ | needs-port | U-E6-02 | +| F-VSC-004 | Server capability probe (`experimental.capabilities.get`) and feature gating | `kv/features.ts` | — | Δ | partial (basis for the v2 handshake) | U-E6-02 | +| F-VSC-005 | Sidebar chat: new task, close, history, focus input, chat search, agent-mode cycling, auto-approve toggle | commands `kilo-code.new.{plusButtonClicked,closeTask,closeAllTasks,historyButtonClicked,focusChatInput,toggleChatSearch,cycleAgentMode,toggleAutoApprove}` | — | Δ | needs-port | U-E6-03 | +| F-VSC-006 | Open session in editor tab, independent panel routing | `kilo-code.new.openInTab` | — | Δ | needs-port | U-E6-04 | +| F-VSC-007 | Settings editor webview and settings search | `kv/SettingsEditorProvider.ts`, `kilo-code.new.settings*` | — | Δ | needs-port | U-E6-05 | +| F-VSC-008 | Profile, organization, auth, provider OAuth, provider usage | `kw/components/profile`, `kilo.profile`, `kilo.organization.set`, `provider.oauth.*`, `kilocode.providerUsage.*` | — | Δ | needs-port | U-E6-06 | +| F-VSC-009 | Commit message generation (SCM) | `kv/services/commit-message`, `commitMessage.generate`, setting `languageCommitMessage` | — | Δ small | needs-port | U-E6-07 | +| F-VSC-010 | Enhance prompt, branch naming for worktrees | `kv/enhance-prompt-error.ts`, `branchName.generate`, setting `agentManager.autoBranchNaming` | — | Δ small | needs-port | U-E6-07 | +| F-VSC-011 | Code actions: explain, fix, improve, add to context; terminal add/fix/explain; generate terminal command | `kv/services/code-actions`, `kilo-code.new.{explainCode,fixCode,improveCode,addToContext,terminal*,generateTerminalCommand}` | — | Δ small | needs-port | U-E6-07 | +| F-VSC-012 | Autocomplete (FIM) and next-edit, chat autocomplete, model/provider selection | `kv/services/autocomplete`, `kilo.fim`, `kilo.edit`, `kilo-code.new.autocomplete.*`, settings `autocomplete.*` | — | Δ small | needs-port (hard gate) | U-E6-08 | +| F-VSC-013 | Terminal integration and PTY tickets | `kv/services/terminal`, `pty.*`, `v2.pty.*` | — | Δ | needs-port | U-E6-09 | +| F-VSC-014 | Agent Manager: worktrees (new, quick, advanced, pool, update from base, close), sessions, tabs, terminals, run script, diff, PR, shortcuts, diagnostics, browser previews | `kv/agent-manager/**` (136 files), `kilo-code.new.agentManager.*`, settings `agentManager.*` | — | Δ (+55k lines in A15) | needs-port | U-E6-10 | +| F-VSC-015 | Browser tab and open-in-browser broker; image preview; document viewer; sub-agent viewer; diff virtual provider; show changes | `kv/browser-tab`, `kv/image-preview.ts`, `kv/DocumentViewerProvider.ts`, `kv/SubAgentViewerProvider.ts`, `kv/DiffVirtualProvider.ts`, `kilo-code.new.{openBrowser,showChanges}` | — | Δ | needs-port | U-E6-11 | +| F-VSC-016 | Notifications and suggestions (`kilo.notifications`, `suggestion.list/accept/dismiss`) | `kv/services/attention`, webview chat | — | Δ | needs-port | U-E6-12 | +| F-VSC-017 | Marketplace panel; agent, skill and MCP removal; MCP OAuth sign-in | `kv/MarketplacePanelProvider.ts`, `kv/services/{marketplace,agent-removal.ts,mcp-removal.ts,mcp-auth}` | — | Δ | needs-port | U-E6-13 | +| F-VSC-018 | Task timeline, token throughput, diff rendering style | settings `showTaskTimeline`, `showTokenThroughput`, `diff.*` | — | Δ | needs-port | U-E6-14 | +| F-VSC-019 | Remote status and enable/disable; presence visibility | `kv/services/RemoteStatusService.ts`, `remote.*` | — | Δ | needs-port | U-E6-15 | +| F-VSC-020 | Memory show/toggle, indexing consent and controls, sandbox preference, network ask/reply, caffeination toggle, attention settings, telemetry | `kilo-code.new.{showMemory,toggleMemory,toggleCaffeination}`, `kv/indexing-consent.ts`, `kv/services/{sandbox-preference.ts,caffeination,telemetry}`, `network.*` | — | Δ | needs-port | U-E6-16 | +| F-VSC-021 | Kilo-classic task/session migration and Roo import | `kv/legacy-migration/sessions/*`, `kv/roo-import/service.ts`, `kilocode.sessionImport.*`, `kw/components/migration` | — | Δ (settings migration removed #13605) | needs-port | U-E6-17 | +| F-VSC-022 | Session extras: board, background jobs (promote/cancel), wakeups, retention, model usage, snapshot prepare/remove, session move, cloud session import | `kilocode.{sessionBoard,backgroundJob*,wakeups,retention.*,sessionModelUsage,snapshot.prepare,removeSnapshot}`, `experimental.controlPlane.moveSession`, `kilo.cloudSessions`, `kilo.cloud.session.*` | — | NEW for most | needs-port | U-E6-18 | +| F-VSC-023 | Message deletion caller | `session.deleteMessage` | — | Δ | needs-port (consumer of U-E2-05) | U-E6-03 | +| F-VSC-024 | Max cost, auto-approve reason, shortcut hints, font size, language, extra CA certs, model default | settings `maxCost`, `showAutoApprovalReason`, `showShortcutHints`, `fontSize`, `language`, `extraCaCerts`, `model.*` | — | Δ | needs-port | U-E6-05 | +| F-VSC-025 | Notebook host (native notebook tools) | `kv/services/notebook`, `kilocode.notebook.*` | `experimental.native_notebook_tools` | Δ | post-cutover | U-E11-05 | +| F-VSC-026 | Browser automation | `kv/services/browser-automation`, settings `browserAutomation.*` | `experimental.browserAutomation` | Δ | post-cutover | U-E11-05 | +| F-VSC-027 | Claude Code compatibility and migration | settings `claudeCodeCompat`, `experimental.claudeMigration` | experimental | POST | post-cutover | U-E11-05 | +| F-VSC-028 | Conversation prompt history | setting `experimental.conversationPromptHistory` | experimental | POST | post-cutover | U-E11-05 | +| F-VSC-029 | Speech-to-text capture | `kv/speech-to-text`, `kw/components/speech-to-text` | `speechToText` feature (off in remote windows) | Δ small | post-cutover | U-E11-01 | +| F-VSC-030 | Image generation | `kv/image-generation` | `experimental.image_generation` | pre | post-cutover | U-E11-02 | +| F-VSC-031 | Anaconda Desktop bridge (`anacondaDesktop.status/open/sync`) | `kv/anaconda-desktop/bridge.ts` | — | pre | needs-port (gate set by consumer) | U-E10-02 | +| F-VSC-032 | VSIX packaging and Marketplace/Open VSX publishing | `packages/kilo-vscode/script/*`, `vscode-packaging.md` | — | Δ | needs-port | U-E6-19 | + +### JetBrains (A25) + +| ID | Feature | Paths (V1) | Snap | Delta | Unit | +|---|---|---|---|---|---| +| F-JB-001 | Plugin shell: split-mode backend/frontend/shared, plugin.xml, actions, tool window, onboarding | `jb/src/main/resources/META-INF/plugin.xml`, `jb/frontend/.../client/{app,actions,plugin,ui}` | Δ | needs-port | U-E7-02 | +| F-JB-002 | CLI pinning, release line, Gradle build, generated OpenAPI client | `jb/build.gradle.kts`, `jb/backend/build.gradle.kts`, `jb/package.json` | Δ | needs-port | U-E7-02 | +| F-JB-003 | Backend CLI lifecycle: start, discovery, workspace, run, provider, dev isolation | `jb/backend/.../backend/{cli,workspace,run,provider,app,dev}` | Δ | needs-port | U-E7-02 | +| F-JB-004 | Sessions: history, model, settings, background, subagents, board, context, scrolling views | `jb/frontend/.../client/session/*` | Δ (swarm board, session cleanup, prompt navigator) | needs-port | U-E7-03 | +| F-JB-005 | Prompt completion (mentions/commands in the prompt, not FIM) | `jb/frontend/.../session/ui/prompt/KiloPromptCompletionProvider.kt` | Δ | needs-port | U-E7-03 | +| F-JB-006 | Settings UI, MCP OAuth, checkpoint settings | `jb/frontend/.../client/settings` | Δ | needs-port | U-E7-03 | +| F-JB-007 | Diff viewer and VFS integration | `jb/frontend/.../client/{diff,vfs}`, `jb/backend/.../diff` | Δ | needs-port | U-E7-04 | +| F-JB-008 | Agent Manager, worktrees, marketplace | `jb/frontend/.../client/agentManager`, `jb/backend/.../{worktree,marketplace}` | Δ | needs-port | U-E7-05 | +| F-JB-009 | Legacy plugin (v5) migration incl. autocomplete **settings** only | `jb/backend/.../migration/*`, `jb/frontend/.../onboarding/providers/v5migration/*`, `jb/frontend/.../autocomplete/KiloAutocompleteSettingsService.kt` | Δ | needs-port | U-E7-05 | +| F-JB-010 | Telemetry | `jb/frontend/.../client/telemetry`, `jb/backend/.../telemetry` | Δ | needs-port | U-E7-03 | +| F-JB-011 | Enhance prompt and branch name consumers (per `editor-services-inventory.md`) | UNVERIFIED paths | Δ | needs-port | U-E7-04 | + +**FIM at V1 JetBrains.** No FIM or next-edit producer or consumer exists in `packages/kilo-jetbrains` at V1. The only `autocomplete` hits are a settings holder (`KiloAutocompleteSettingsService.kt`, persisting `enableAutoTrigger`, `enableSmartInlineTaskKeybinding`, `enableChatAutocomplete` migrated from the legacy plugin), the migration engine (`LegacyMigrationEngine.kt:84,349-353`) and a CHANGELOG line about the VS Code change #13655. This matches `technical-notes/baseline/editor-services-inventory.md`. Decision 7 makes FIM a hard G3 gate even though JetBrains v1 has no FIM. See the final report. + +## Part 4. Upstream v2 features without a Kilo v1 counterpart + +Each needs a product disposition, so it maps to a unit even when no port is needed. + +| ID | Feature | Paths (BASE) | Disposition | Unit | +|---|---|---|---|---| +| F-V2-001 | Global paths (`app="opencode"`) | `util/src/global.ts` | Rename via a counted patch | U-E1-01 | +| F-V2-002 | App identity (`App.Info`, UA, `x-opencode-client`) | `core/src/app.ts`, `server/src/options.ts` | Configure; UA prefix needs a patch | U-E1-01 | +| F-V2-003 | DB location (`opencode.db`, channel DBs) | `cli/src/database-path.ts` | Kilo v2 store (D6) | U-E1-02 | +| F-V2-004 | Binary and launcher (`opencode`, `opencode2`) | `cli/script/build.ts:16` | Rename; channel naming open (see register) | U-E3-12 | +| F-V2-005 | Config discovery and folding | `core/src/config/discovery.ts` | Add `.kilo`/`kilo.json` | U-E1-07 | +| F-V2-006 | opencode v1 config migration | `core/src/v1/config/*` | Reuse decoder for the Kilo importer | U-E3-03 | +| F-V2-007 | Managed policy | `core/src/managed-policy.ts` | Inherit | U-E1-07 | +| F-V2-008 | Session core (inbox, runner, coordinator) | `core/src/session/*` | Inherit, verify | U-E2-06 | +| F-V2-009 | Retry policy | `session/runner/retry.ts` | Extend for offline wait | U-E2-01 | +| F-V2-010 | Compaction and overflow | `session/compaction.ts` | Add Kilo thresholds | U-E2-02 | +| F-V2-011 | Snapshots and revert | `core/src/snapshot.ts` | Add Kilo policy | U-E2-04 | +| F-V2-012 | Restart recovery | `session/execution/restart.ts` | Inherit; covers part of task resume | U-E2-07 | +| F-V2-013 | Fork, move, background | protocol `session.*` | Inherit | U-E3-05 | +| F-V2-014 | Session export/import (v2 transfer) | `core/src/session/transfer.ts` | Inherit | U-E3-05 | +| F-V2-015 | opencode v1→v2 migration (in place, destructive, bun only) | `core/src/database/v1-migration.bun.ts` | Reuse pure transforms only; never point at `kilo.db` | U-E3-01 | +| F-V2-016 | TUI v1 config/theme migrate | `tui/src/config/v1` | Inherit; check against Kilo TUI config | U-E3-03 | +| F-V2-017 | Auth export/import, multi-credential | `core/src/credential*` | Host Kilo gateway credentials | U-E1-03 | +| F-V2-018 | Integrations (key/OAuth/command/external) | `core/src/integration*` | Host Kilo login | U-E1-03 | +| F-V2-019 | Providers, `@opencode/ai` | `core/src/plugin/provider/*` | Inherit; override `kilo.ts` branding | U-E1-04 | +| F-V2-020 | models.dev catalog | `core/src/models-dev.ts` | Swap source or merge gateway catalog | U-E1-04 | +| F-V2-021 | System prompts, identity, verbosity | `plugin/system-prompt/*` | Re-seat Kilo selectors | U-E1-05 | +| F-V2-022 | Agents | `core/src/plugin/agent.ts` | Transform to Kilo agents | U-E2-12 | +| F-V2-023 | Plan mode | `core/src/plugin/plan.ts` | Replace with Kilo plan workflow | U-E2-13 | +| F-V2-024 | Commands (`/init`, `/review`) | `core/src/plugin/command.ts` | Inherit plus Kilo policy | U-E2-14 | +| F-V2-025 | Skills | `core/src/skill*` | Inherit plus Kilo trust | U-E2-15 | +| F-V2-026 | Instructions discovery | `core/src/instructions/*` | Add Kilo rules dirs | U-E2-15 | +| F-V2-027 | Tools | `core/src/tool/plugin/*` | Inherit plus Kilo tools | U-E2-16 | +| F-V2-028 | Permissions | `core/src/permission*` | Inherit plus Kilo policy | U-E2-17 | +| F-V2-029 | Forms | `core/src/form.ts` | Host question tool | U-E2-16 | +| F-V2-030 | Websearch providers | `plugin/websearch/*` | Inherit | U-E2-16 | +| F-V2-031 | MCP | `core/src/mcp/*` | Inherit plus Kilo deltas | U-E2-22 | +| F-V2-032 | Worktrees | `core/src/worktree/*` | Inherit plus Kilo layout | U-E2-26 | +| F-V2-033 | Background subagents | `tool/plugin/subagent.ts` | Inherit | U-E2-07 | +| F-V2-034 | Persistent PTY/terminals | `core/src/persistent-pty*` | Inherit; host for background processes and IDE terminals | U-E2-25 | +| F-V2-035 | Background service | `client/src/service*`, `cli/src/services/*` | Inherit, Kilo service command and file | U-E9-01 | +| F-V2-036 | Server/HttpApi and plugin RPC | `protocol/src/groups/*` | Primary seam for Kilo endpoints | U-E1-08 | +| F-V2-037 | Device pairing and OpenTunnel remote access | `server/src/pairing.ts`, `cli/services/remote-tunnel.ts` | Decide coexistence with Kilo relay | U-E4-05 | +| F-V2-038 | Workspace/environment drivers | `core/src/workspace/driver.ts`, `core/src/environment/*` | Seam for sandbox | U-E2-20 | +| F-V2-039 | Headless run | `cli/src/run/*` | Inherit plus Kilo flags | U-E9-03 | +| F-V2-040 | ACP | `cli/src/acp/*` | Inherit | U-E9-04 | +| F-V2-041 | Misc CLI (api, models, stats, debug, plugin, mini, upgrade, uninstall) | `cli/src/commands/*` | Inherit, brand | U-E9-02 | +| F-V2-042 | TUI (tabs, diff viewer, dialogs, slots) | `packages/tui` | Inherit, Kilo plugins via slots | U-E9-05 | +| F-V2-043 | Themes and semantic tokens | `packages/theme` | Kilo default theme | U-E1-01 | +| F-V2-044 | Notifications and sound | `tui/feature-plugins/system/notifications.ts` | Kilo attention policy | U-E9-09 | +| F-V2-045 | Keybinds | `tui/src/config/keybind.ts` | Inherit | U-E9-10 | +| F-V2-046 | Updater | `cli/src/services/updater*.ts` | Rebrand | U-E3-06 | +| F-V2-047 | Observability (OTLP) | `util/src/observability*` | Coexists with Kilo telemetry | U-E2-21 | +| F-V2-048 | Code Mode | `packages/codemode` | Underlies post-cutover PTC | U-E11-05 | +| F-V2-049 | Electron desktop and web app, GUI extensions | `packages/desktop`, `packages/app`, `gui-extensions` | No Kilo v1 counterpart: ship, hide or defer | U-E9-15 | +| F-V2-050 | SDK, client, plugin APIs | `packages/{sdk,client,plugin}` | Primary composition seam | U-E0-02 | +| F-V2-051 | `@opencode/ai` media clients | `packages/ai/src/{image,speech,transcription,video}*` | Mechanism for post-cutover voice/image | U-E11-01 | +| F-V2-052 | VCS (git, Mercurial) | `plugin/vcs/*` | Inherit | U-E2-26 | +| F-V2-053 | Stats | `cli stats`, TUI stats | Inherit | U-E9-02 | +| F-V2-054 | Simulation, http-recorder | `packages/simulation`, `http-recorder` | Test infrastructure for acceptance | U-L4-01 | +| F-V2-055 | Hosted web properties (web, console, enterprise, function, stats, posts, storybook) | `packages/{web,console,…}` | Not applicable to Kilo; keep inert, never deploy from the fork | U-E0-04 | +| F-V2-056 | CI container images | `packages/containers` | Inherit or ignore (not a sandbox) | U-E0-04 | +| F-V2-057 | `sdks/vscode` thin launcher, `publish-vscode.yml` | `sdks/vscode` | Not a Kilo extension; must not publish from the fork | U-E0-04 | +| F-V2-058 | LaTeX and mermaid rendering | `packages/latex`, `packages/merman` | Inherit | U-E9-05 | + +## Native-equivalence verdict per area + +Checked on scope (S), errors (E), cancellation (C), persistence (P) and presentation (Pr) at BASE. "Since donor base" says whether the verdict changed between `59b29de4` and BASE. + +| Area | BASE verdict | Key evidence | Since donor base | +|---|---|---|---| +| A01 Identity/store | partial | Global layer replacement, `App.configured`, `ServerOptions.app/database/config/models`; product name hard-coded in `util/src/global.ts:12` plus ~10 direct `Global.Path` users, bins, config filenames, plan dir, updater, desktop IDs, `OPENCODE_*` env, UA | unchanged | +| A02 Daemon/server | native (mechanism) | Managed service, standalone, `--server` attach, reload, registration file, PTY handoff, reconnecting UI. Parent watchdog absent | improved, verdict unchanged | +| A03 Gateway/account | absent (mechanism present) | Integration and credential framework can host Kilo login; no org/profile/balance | unchanged | +| A04 Provider/catalog | partial | Catalog source swappable; Kilo routing, auto, pricing UI absent; `plugin/system-prompt.ts` seam deleted | **changed**: prompt-selector seam moved | +| A05 Config | partial | Folding, diagnostics, opencode-only v1 migration | unchanged | +| A06 Session policy | partial | Retry bounded (no offline wait), compaction native without Kilo thresholds, snapshots without Kilo hardening, no message deletion, no wakeups or goals | compaction hardened, verdict unchanged | +| A07 Portability/import | partial | Export/import/fork/move native; v1 migration in-place and destructive (`v1-migration.bun.ts:540-560`) | auth export/import added | +| A08 Agents/plan/skills | partial | Native agents, plan (wrong location, no `plan_exit`), review, skills | unchanged | +| A09 Tools/permissions | partial | Permissions and forms native; Kilo tools missing; no todo or LSP diagnostics | policies added | +| A10 Memory | absent | — | — | +| A11 Indexing | absent | — | — | +| A12 Sandbox | absent (seam present) | `EnvironmentDriver{spawner, files}` | unchanged | +| A13 Telemetry | partial | OTLP only | unchanged | +| A14 MCP | partial, near native | OAuth with AS metadata, CLI auth; marketplace absent | gap narrowed | +| A15 Agent coordination | partial | Worktrees, background subagents, persistent PTY native; agent manager, board, notebook, background-process tool absent | worktree API project-based | +| A16 Remote | partial (different model) | Pairing + OpenTunnel exposes the whole server; no Kilo relay or presence | pairing new | +| A17 Sharing | absent | Config key only | unchanged | +| A18 CLI/ACP | native | run json, ACP, plugin management | improved | +| A19 TUI | native mechanism | Themes, tokens, keybinds, notifications, slots; heavy churn (219 commits since donor base) | theme files deleted | +| A20 Cloud CLI | absent | — | — | +| A21 Packaging | partial | Updater and publish pipeline bound to opencode identity and repo | unchanged | +| A22 VS Code host | absent | `sdks/vscode` is a launcher | — | +| A23 Webview UI | absent | `session-ui`, `ui`, `app` are possible donors only | — | +| A24 Editor services | absent | — | — | +| A25 JetBrains | absent | — | — | +| A26 Anaconda | absent | Electron desktop + GUI extensions is not equivalent | — | +| A27 Atomic chat | absent | — | — | +| A28 Voice/image | absent (media clients only) | — | — | +| A29 Web properties | not applicable | Upstream web properties are opencode's | — | +| A30 SDK/plugin | native | In-process host, generated client, plugin RPC | plugin API grew | +| A31 Legacy migration | absent | — | — | +| A32 Security/PII | partial | Managed and permission policies; no PII redaction or ignore migration | policies added | + +## Snapshot discrepancy + +`marker-audit/v1-kilo-marker-port-assessment.md:9` pins the 816-file assessment at `d99662338e` (2026-08-10, merge #13046). `migration-context.md` names `f7115470` (2026-08-12) as the snapshot. `d99662338..f7115470` is 800 commits and touches 124 files in `packages/opencode/src/kilocode`, `kilo-vscode` and `kilo-gateway` alone. Staleness in this discovery is measured from `f7115470` as the brief instructs. Files changed only in the gap were never assessed. The register's gap table (732 files) shows that no area verdict changes: every area that moved in the gap is already MATERIAL. The gap files still need file-level dispositions, which the first L2 sweep assigns, and `migration-context.md` should record both SHAs. + +## Decision 7 applied to V1 flags + +| Flag on V1 | Default | Applied verdict | Note | +|---|---|---|---| +| `KILO_EXPERIMENTAL_BACKGROUND_SUBAGENTS` | **on** (kill switch, `packages/opencode/src/effect/runtime-flags.ts:47-49`) | gating (U-E2-07) | Literal Decision 7 would defer a default-on feature; see final report | +| `KILO_EXPERIMENTAL_SHARED_AGENT_BOARD` | **on** (kill switch, `runtime-flags.ts:61-62`) | gating (U-E2-24) | Same | +| `experimental.openTelemetry` | on (opt-out) | gating (U-E2-21) | Same | +| `KILO_EXPERIMENTAL_SCOUT`, `_CONTEXT_TOOLS`, `_CODE_MODE`, `_MCP_APPS`, `_SESSION_SWITCHER`, `_EVENT_SYSTEM`, `_CLAUDE_MIGRATION`, `_LSP_TOOL`, `KILO_ENABLE_EXA`, `experimental.native_notebook_tools`, `experimental.image_generation`, `experimental.speech_to_text_*` | off | post-cutover (E11) | `_EVENT_SYSTEM` debug view is not-needed | +| `KILO_EXPERIMENTAL_OUTPUT_TOKEN_MAX`, `_BASH_DEFAULT_TIMEOUT_MS`, `_FILEWATCHER`, `_DISABLE_COPY_ON_SELECT`, `_CUSTOMIZE_SKILL` | tunables | carried with their feature unit | Tunables, not features | +| `sandbox.enabled` | off (opt-in) | gating (U-E2-20) | Not an experimental flag on V1 | +| Defined without consumers: `KILO_EXPERIMENTAL_SESSION_SWITCHING`, `_ICON_DISCOVERY`, `_MARKDOWN` | — | not-needed (UNVERIFIED usage) | | +| Upstream-owned: `_WORKSPACES`, `_REFERENCES`, `_OXFMT`, `_LSP_TY`, `_NATIVE_LLM`, `_WEBSOCKETS`, `KILO_ENABLE_PARALLEL` | — | not Kilo features | | diff --git a/migration-tracking/discovery/v2-unit-register.md b/migration-tracking/discovery/v2-unit-register.md new file mode 100644 index 000000000000..67ee9a047de6 --- /dev/null +++ b/migration-tracking/discovery/v2-unit-register.md @@ -0,0 +1,384 @@ +# Draft unit register + +Status: discovery output, 2026-10-11. Single writer: discovery analyst. **Draft.** The planner turns this into Part B of the plan (Decision 9). No GitHub issue was created or closed. Units without an issue are marked `new`, and creating them is left to the user. + +Companions: `v2-feature-inventory.md` (feature IDs F-CORE, F-VSC, F-JB, F-V2), `v2-epic-taxonomy.md` (epics and exit criteria). + +## How to read a row + +| Column | Meaning | +|---|---| +| Unit | `U--`. One unit maps to one session, one branch and one PR at execution time. The planner may split units further | +| Surface | One of: shared runtime, gateway/cloud, CLI/TUI, packaging, import, VS Code, JetBrains, external consumer, repo | +| Gate | Exactly one: pre-gate, G1, G2, G3, post-cutover, post-G3, continuous | +| Features | Inventory IDs covered (every inventory row appears in exactly one unit) | +| Issues | Existing GitHub issues; `new` = no issue yet | +| Delta | needs-port / native / partial / not-needed / post-cutover, as in the inventory | +| Stale | V1 staleness of the files the unit touches, SNAP `f7115470` → V1 `fb7e96ed` (Decision 4): EMPTY, SMALL, MATERIAL, NEW (post-SNAP; no donor can exist). Area figures are in "Staleness per area" | +| Donor | Reuse from `kilo-v2-checkpoints-2026-09`: yes / partial / no, with confidence (H/M/L) and the main donor path. Per Decision 3, donor supplies the v2 mechanism only. When Stale is not EMPTY, behavior is re-derived from V1 | + +Donor paths are relative to the donor tag; `kc/` = `packages/kilo-cli/src/`, `kv/` = `packages/kilo-vscode/src/`. + +## E0 Foundation + +| Unit | Title | Surface | Gate | Features | Issues | Delta | Stale | Donor | Depends | +|---|---|---|---|---|---|---|---|---|---| +| U-E0-01 | Kilo host and composition package (server, overrides, Location service replacements, builtin plugin list) | shared runtime | pre-gate | (host for all E1/E2) | new | needs-port | n/a | yes H: `kc/host.ts` (`ServerFetch.make(options,{overrides})`; `server/src/fetch.ts` overrides still present at BASE) | — | +| U-E0-02 | Kilo RPC and plugin scaffolding (`packages/schema/src/kilocode`, plugin `define`, generated client consumption) | shared runtime | pre-gate | F-V2-050, Part 1 "Plugins and SDK" | new | native mechanism | n/a | yes H: `packages/schema/src/kilocode/*` (`schema/src/rpc.ts` unchanged since donor base) | U-E0-01 | +| U-E0-03 | Re-land Kilo `AGENTS.md`; rewrite `v2-fork-conventions.md`, migration-tracking `README.md`, `v2-baseline.ts`; superseded banners on old plans (see `doc-inventory.md` §8) | repo | pre-gate | — | new | needs-port | n/a | no | — | +| U-E0-04 | Kilo CI lane on `kilo-v2-clean`; neutralize upstream publish/deploy workflows, `sdks/vscode` publishing, hosted web properties, CI images | repo | pre-gate | F-V2-055, F-V2-056, F-V2-057 | new (part of #14408 scope) | needs-port | n/a | no | admin task: Actions permissions | +| U-E0-05 | v1 reference fixtures for offline wait, overflow, slow snapshot, snapshot retention (data at V1, harness on BASE) | repo | pre-gate | — | new (old N5) | needs-port | MATERIAL (A06) | partial M: `kc/test/fixtures/v1-session`, `v1-writer*` (built at V1 `d99662338`/`ecccd1f`; regenerate at V1 `fb7e96ed`) | U-E0-01 | + +## E1 Core and identity + +| Unit | Title | Surface | Gate | Features | Issues | Delta | Stale | Donor | Depends | +|---|---|---|---|---|---|---|---|---|---| +| U-E1-01 | Product identity and branding: app dir, binary, env prefix, UA, config filenames, default theme and logo | shared runtime | G1 | F-CORE-001, F-CORE-108a, F-V2-001, F-V2-002, F-V2-043 | new | partial | SMALL (A01) | yes H paths / L theme: `kc/paths.ts`, `kc/tui-plugin/logo*.tsx`; donor theme patches target `theme/src/tui/{defaults,fallback}.ts`, **deleted at BASE** | U-E0-01 | +| U-E1-02 | Customer v2 store and paths (D6): distinct data/config dirs, fail-closed on v1/opencode stores, DB preflight and SQLite errors, log correlation | shared runtime | G1 | F-CORE-002, F-CORE-004, F-V2-003 | new (old N8) | partial | SMALL (A01: WAL recovery `97abdab66c`, interrupted first access `1988e54fd6`) | yes H: `kc/paths.ts`, `kc/storage.ts` | U-E1-01 | +| U-E1-03 | Gateway auth, profile, organizations, teams, Pass, error codes, tRPC client, via the integration/credential framework | gateway/cloud | G1 | F-CORE-015, F-CORE-016, F-CORE-017, F-CORE-019, F-CORE-021, F-V2-017, F-V2-018 | new (runtime side; VS Code side is #14388) | partial | MATERIAL (A03: org default model `8c077fbee7`, credential precedence `acd3d1ff9b`) | yes M: `kilo-gateway/src/{account,gateway}.ts`, `schema/kilocode/gateway.ts`, `kc/auth.ts` | U-E0-02 | +| U-E1-04 | Gateway provider, catalog merge/refresh/recovery, Kilo Auto and routed model, provider options, Codex/cloud auth | gateway/cloud | G1 | F-CORE-022…026, F-V2-019, F-V2-020 | new | partial | MATERIAL (A04: catalog recovery `5539dd3ae5` #14723, malformed entries #14794, cache breakpoints `ad173c7e9a`) | yes L-M: `kilo-gateway/src/{models,plugin,startup-cache}.ts`, `packages/ai/src/kilocode/*routed*` (`packages/ai` +151 commits since donor base) | U-E1-03 | +| U-E1-05 | System-prompt selectors (8), memory guidance, editor `` context on BASE seams | shared runtime | G1 | F-CORE-029, F-V2-021 | new | partial | MATERIAL (A04) | partial L: `kc/model-prompt-policy.ts`; donor patch on `core/src/plugin/system-prompt.ts`, **deleted at BASE** (#47447, #47559) | U-E1-04 | +| U-E1-06 | Model usage, cost, throughput and provider usage/quotas (`/usage`) | shared runtime | G1 | F-CORE-020, F-CORE-028 | new | needs-port | MATERIAL + NEW (provider usage #11611, Codex usage #13519) | partial M: `kc/session-usage*.ts`, `kc/tui-plugin/sidebar-usage.tsx` | U-E1-04 | +| U-E1-07 | Kilo config layer: keys, `kilo.json`/`.kilo` discovery, overlay/sources/writer, references, managed policy | shared runtime | G1 | F-CORE-031, F-CORE-032, F-V2-005, F-V2-007 | new | partial | MATERIAL (A05: `memory_model` #14781, swarm #14160, PTC #14230, cleanup #14054) | partial M: `kc/{settings,settings-rpc,config-write,project-config}.ts`, donor patch `core/src/config.ts` (+70/−33 drift) | U-E1-01 | +| U-E1-08 | Kilo server extension surface: route groups as plugin RPC, parent watchdog, instance reload, watcher policy, capability/version handshake (server side) | shared runtime | G1 | F-CORE-006, F-CORE-007, F-CORE-008, F-CORE-009, F-CORE-011, F-V2-036, Part 1 "Server and API" | new (old N9 server side) | partial | MATERIAL (A02: `/reload` #14103, shutdown deadline, DB reclaim #14540) | yes M: `kc/{server,runtime,interactive-server}.ts`, `kc/*-rpc.ts` | U-E0-02 | + +## E2 Runtime behavior + +| Unit | Title | Surface | Gate | Features | Issues | Delta | Stale | Donor | Depends | +|---|---|---|---|---|---|---|---|---|---| +| U-E2-01 | Offline network wait and reconnect | shared runtime | G1 | F-CORE-034, F-V2-009 | #14371 | needs-port | MATERIAL (A06; incl. #14893 at the V1 tip) | no (progress row "Needs port") | U-E0-05 | +| U-E2-02 | Overflow accounting, `threshold_percent`, chunked compaction, payload recovery | shared runtime | G1 | F-CORE-035, F-V2-010, Part 1 "Compaction" | #14372 | partial | MATERIAL (`5edf432c2f` #14318) | no (native reuse only) | U-E0-05 | +| U-E2-03 | Slow-snapshot interaction policy and UI | shared runtime | G1 | F-CORE-036a | #14373 | partial | MATERIAL | no | U-E0-05 | +| U-E2-04 | Snapshot seed/materialize, locking, cleanup, retention, restore/diff | shared runtime | G1 | F-CORE-036b, F-V2-011 | #14374 | partial | MATERIAL (+490; lock/cleanup NEW) | no | U-E0-05 | +| U-E2-05 | Message deletion runtime operation, Kilo revert, stable ordering | shared runtime | G2 | F-CORE-042 | #14380 | needs-port | MATERIAL | no | U-E0-02 | +| U-E2-06 | Session core verification: drain/control, prompt queue and steer, title, processing hardening | shared runtime | G1 | F-CORE-037, F-CORE-043, F-CORE-044, F-CORE-045, F-V2-008, Part 1 "Sessions" | new | partial | MATERIAL + NEW (drain, steer; ~128 fixes) | no (native reuse) | U-E0-01 | +| U-E2-07 | Subagent resume, background subagents (default on), steering | shared runtime | G1 | F-CORE-041, F-V2-012, F-V2-033 | new | partial | MATERIAL (#14702, #13641) | no | U-E2-06 | +| U-E2-08 | Wakeups and cron (persisted, re-armed) | shared runtime | G1 | F-CORE-038 | new | needs-port | NEW (#14094, #14312) | no | U-E2-06 | +| U-E2-09 | Goal mode | shared runtime | G1 | F-CORE-039 | new | needs-port | NEW (#13679, #14504) | no | U-E2-08 | +| U-E2-10 | Backend-owned session retention | shared runtime | G1 | F-CORE-040 | new | needs-port | NEW (#14054) | no | U-E1-07 | +| U-E2-11 | Session transcript recall search | shared runtime | G1 | F-CORE-046 | new | needs-port | MATERIAL | partial M: `kc/session-search*` (old VS Code backend `backend/session-search.ts`) | U-E0-02 | +| U-E2-12 | Kilo agents (code, ask, debug, orchestrator, plan), mode reminders, agent builder | shared runtime | G1 | F-CORE-055, F-CORE-056, F-CORE-058, F-V2-022, Part 1 "Agents" | new | partial | MATERIAL (agent requirements removed #13225) | yes M: `kc/agent-policy.ts` | U-E0-02 | +| U-E2-13 | Plan workflow and Plan-to-Code handoff | shared runtime | G1 | F-CORE-057, F-V2-023 | #14382 | needs-port | MATERIAL (`6024a76db5`) | yes M: `kc/plan-policy.ts`, `plan-prompt.txt` | U-E2-12 | +| U-E2-14 | Kilo review policy over native `/review` | shared runtime | G1 | F-CORE-059, F-V2-024 | new | partial | MATERIAL (`46bd29d733`) | yes M: `kc/review-policy.ts` | U-E2-12 | +| U-E2-15 | Commands and workflows, skills (trust, builtin, remove), rules and primary-worktree discovery | shared runtime | G1 | F-CORE-060, F-CORE-061, F-CORE-062, F-V2-025, F-V2-026 | new (VS Code callers: #14393) | partial | MATERIAL | yes M: `kc/{commands,skill-policy*,skill-shell*}.ts` | U-E1-07 | +| U-E2-16 | Kilo tool set: registry and client gating, question, suggest, TS check, rich read, model tools, repo overview, encoding, todo | shared runtime | G1 | F-CORE-064, F-CORE-067, F-CORE-068, F-CORE-069, F-CORE-071, F-CORE-073, F-CORE-074, F-CORE-078, F-CORE-079, F-V2-027, F-V2-029, F-V2-030, Part 1 "Tools", "LSP" | new | partial | MATERIAL + NEW (+2,205 lines in `tool/`) | partial L-M: `kc/{tool-authorization,request-policy}.ts` | U-E0-02 | +| U-E2-17 | Permission customizations and bash hierarchy | shared runtime | G1 | F-CORE-065, F-CORE-066, F-V2-028, Part 1 "Permissions" | new | partial | MATERIAL (#14051) | partial M: `kc/tool-authorization.ts`, `explore-policy-v2-parity.md` counts | U-E2-16 | +| U-E2-18 | Memory engine, tools and RPC | shared runtime | G1 | F-CORE-080 | new | needs-port | SMALL (A10: `memory_model` #14781, SSE fix #13122) | yes H: `packages/kilo-memory`, `kc/memory-*.ts`, `schema/kilocode/memory.ts` | U-E0-02 | +| U-E2-19 | Codebase indexing engine, worker, `semantic_search` | shared runtime | G1 | F-CORE-081 | new | needs-port | MATERIAL (moderate: embedder fixes, root safeguards, `@parcel/watcher` #12784) | yes M-H: `packages/kilo-indexing` (closer to V1 than SNAP), `kc/indexing*.ts` | U-E0-02 | +| U-E2-20 | Sandbox: shell/MCP/PTY confinement, network relay, git classifier, per-session lifecycle, on workspace/environment driver seam | shared runtime | G1 | F-CORE-082, F-V2-038 | new | needs-port | MATERIAL (git classifier #14222) | yes M: `kc/sandbox{,-mcp,-pty}.ts`; donor has no `kilo-sandbox` package; donor MCP SpawnHooks patch on a file rewritten upstream (+197/−292) | U-E0-01 | +| U-E2-21 | Telemetry with consent and privacy policy, coexisting with OTLP | shared runtime | G1 | F-CORE-083, F-V2-047 | new | needs-port | SMALL (#14638) | partial M: `kc/telemetry*.ts` (own opt-in OTLP exporter, not `kilo-telemetry`; see `telemetry-v2-overlap.md`) | U-E1-01 | +| U-E2-22 | MCP deltas (OAuth callback handoff, issuer discovery, auth-failure surfacing, SSE probe, jsonc writes) and `git:` plugin sources | shared runtime | G1 | F-CORE-084, F-CORE-086, F-V2-031, Part 1 "MCP" | new | partial | NEW (`k/mcp/*` #14724) | partial L: `kc/sandbox-mcp.ts` only | U-E0-02 | +| U-E2-23 | Marketplace (MCP servers, skills, modes, plugins) | gateway/cloud | G1 | F-CORE-063 | new | needs-port | NEW (#12714, #14510) | no | U-E2-22 | +| U-E2-24 | Shared agent board / swarm (default on) | shared runtime | G1 | F-CORE-090 | new | needs-port | NEW (#13629, #14134) | partial M: `kc/swarm{,-store}.ts` (SNAP-era model; re-derive) | U-E2-07 | +| U-E2-25 | Background processes tool, sidebar data, `/process`, on persistent PTY | shared runtime | G1 | F-CORE-089, F-V2-034 | new | needs-port | MATERIAL | partial M: `kv/backend/processes.ts`, `sidebar-processes-v2-parity.md` | U-E0-02 | +| U-E2-26 | Worktree runtime: family detection, teardown, Kilo layout, VCS | shared runtime | G1 | F-CORE-088a, F-V2-032, F-V2-052, Part 1 "Workspaces" | new | partial | MATERIAL | no | U-E0-01 | +| U-E2-27 | Agent Manager host contract and control plane (ask/reply/reject, protection) | shared runtime | G2 | F-CORE-087 | #14391 | needs-port | MATERIAL (A15 +55k lines) | partial L: SNAP-era `kv/agent-manager` | U-E2-26 | +| U-E2-28 | Generation endpoints: enhance prompt, commit message, branch name | shared runtime | G2 | F-CORE-116 | new (consumers: #14389) | partial | SMALL (A24: #13108, editor-context prefix #13379) | yes M: `kc/generation-plugin.ts`, `schema/kilocode/generation.ts` | U-E0-02 | +| U-E2-29 | Atomic Chat local provider plugin | shared runtime | G1 | F-CORE-119 | new | needs-port | EMPTY (version bumps only) | no | U-E1-04 | + +## E9 CLI/TUI surface + +| Unit | Title | Surface | Gate | Features | Issues | Delta | Stale | Donor | Depends | +|---|---|---|---|---|---|---|---|---|---| +| U-E9-01 | Service/daemon UX: `kilo daemon` compatibility over native service, startup performance, port warning | CLI/TUI | G1 | F-CORE-005, F-CORE-010, F-V2-035 | new | native | MATERIAL (A02) | yes M: `kc/{daemon,daemon-entry}.ts` | U-E1-08 | +| U-E9-02 | Kilo command set, help, generated CLI docs, roll-call, `kilo worktree` commands, inherited commands (api, models, stats, debug, plugin) | CLI/TUI | G1 | F-CORE-030, F-CORE-088b, F-CORE-098, F-V2-041, F-V2-053, Part 1 "CLI commands" | new | partial | MATERIAL (worktree cmds #12809) | yes M: `kc/{commands,index}.ts` | U-E0-01 | +| U-E9-03 | `kilo run` and headless behavior (variant, auto, drain, stdin, question handling) | CLI/TUI | G1 | F-CORE-099, F-V2-039 | new | partial | MATERIAL | yes M: `kc/{run,run-input}.ts` | U-E2-06 | +| U-E9-04 | ACP host with Kilo permission mapping | CLI/TUI | G1 | F-CORE-100, F-V2-040, Part 1 "ACP" | new | partial | MATERIAL (A18 ACP +226/−45) | yes M: `kc/acp.ts`, `packages/cli/src/kilocode/acp.ts` | U-E2-17 | +| U-E9-05 | TUI shell: terminal title/activity, caffeinate, TUI config hot reload, worker transport check, inherited TUI and rendering | CLI/TUI | G1 | F-CORE-012, F-CORE-013, F-CORE-033, F-CORE-101, F-V2-042, F-V2-058, Part 1 "TUI" | new | partial | MATERIAL (A19) | partial L-M: `kc/tui-plugin/*`; 14 donor TUI patches on files with heavy drift (session route +540/−887) | U-E1-01 | +| U-E9-06 | Kilo slash-command registry and dialogs; gateway notifications and news | CLI/TUI | G1 | F-CORE-018, F-CORE-102 | new (notifications VS Code side: #14392) | partial | MATERIAL | partial M: `kc/tui-plugin/*` | U-E1-03 | +| U-E9-07 | Home and sidebar plugins (onboarding, tips, footer, usage/balance, PR, indexing, memory, processes rows); model picker, info panel, free/training disclosure | CLI/TUI | G1 | F-CORE-027, F-CORE-103 | new (old N12 picker part) | partial | MATERIAL | yes M: `kc/tui-plugin/sidebar-*.tsx`, `kc/model-picker.ts` (sidebar order facts in `cli-remainder-next-gaps.md`) | U-E1-06 | +| U-E9-08 | Per-session soft maximum-cost alert | CLI/TUI | G1 | F-CORE-047 | new | needs-port | MATERIAL (A06 area; feature itself pre) | no | U-E1-06 | +| U-E9-09 | Completion/abort/timeout notifications and attention policy | CLI/TUI | G1 | F-CORE-107, F-V2-044 | #14375 | partial | MATERIAL | partial L | U-E9-05 | +| U-E9-10 | Session scope switching, list actions, clickable links, @session mentions, double-press guards, keybinds | CLI/TUI | G1 | F-CORE-109a, F-V2-045 | #14377 | needs-port | MATERIAL | no | U-E9-05 | +| U-E9-11 | Location/session projection and foreign-project event filtering on v2 | CLI/TUI | G1 | F-CORE-105 | #14379 | partial (likely native at BASE; verify) | MATERIAL | no | U-E9-05 | +| U-E9-12 | Multiline alerts and default sound (#14378 G1 part); display preferences; privacy-mode and PII redaction | CLI/TUI | G1 | F-CORE-110, F-CORE-129 | #14378 (part) | partial | MATERIAL (A19); SMALL (A32) | partial L: `kc/privacy*.ts` | U-E9-05 | +| U-E9-13 | `kilo cloud` Cloud Agent CLI | gateway/cloud | G1 | F-CORE-111 | new | needs-port | SMALL (#13851 stdin) | yes H-M: `kc/cloud-*.ts`, `kc/cloud/*` (7 tests) | U-E1-03 | +| U-E9-14 | Embedded web UI decision: `kilo-web-ui` vs upstream `packages/app` served by the service | CLI/TUI | G1 | F-CORE-125 | new | partial (decision) | MATERIAL (A29) | no | U-E9-15 | +| U-E9-15 | Product disposition of upstream-only surfaces: Electron desktop, web app, GUI extensions | CLI/TUI | G1 | F-V2-049 | new (user decision) | n/a (decision) | n/a | no | — | + +## E3 Import and distribution + +| Unit | Title | Surface | Gate | Features | Issues | Delta | Stale | Donor | Depends | +|---|---|---|---|---|---|---|---|---|---| +| U-E3-01 | Opt-in, re-runnable v1 session/data import into the v2 store (read `kilo.db` read-only; reuse upstream pure transforms; legacy JSON storage) | import | G1 | F-CORE-003, F-CORE-050, F-V2-015 | #14413 | needs-port | MATERIAL (A07) | partial M: `kc/import-v1-session.ts`, fixtures; `v1-session-schema-audit.md` says copy-then-migrate is not proven lossless | U-E1-02 | +| U-E3-02 | Credential import (host-local only) | import | G1 | F-CORE-054 | #14413 (part) | partial | MATERIAL (A07) | yes M: `kc/credential-import.ts` | U-E1-03 | +| U-E3-03 | Config key mapping and legacy Kilo Code migrators (modes, rules, workflows, MCP, ignore); TUI config migration | import | G1 | F-CORE-127, F-V2-006, F-V2-016 | #14413 (part) | needs-port | MATERIAL (A05/A31) | partial M: `kc/import-v1-config.ts` (`config-mapping-v2-gap.md`) | U-E1-07 | +| U-E3-04 | External transcript resume (Claude Code, Codex), migrate API, cloud session import and `--cloud-fork` | import | G1 | F-CORE-049, F-CORE-051 | new | needs-port | MATERIAL + NEW (#13294, #13483) | partial L: `kc/{external-sessions,resume-external}.ts` (shape changed on V1) | U-E3-01 | +| U-E3-05 | Fork, move and portability diff verification | import | G1 | F-CORE-052, F-CORE-053, F-V2-013, F-V2-014 | new | native/partial | MATERIAL (A07) | no | U-E2-06 | +| U-E3-06 | Updater and installation detection under Kilo identity | packaging | G1 | F-CORE-112, F-V2-046 | #14410 | partial | MATERIAL (A21) | yes M: `kc/updater.ts` (`updater-local-parity.md`) | U-E1-01 | +| U-E3-07 | Build and release pipeline: platforms, npm/GHCR/AUR/Homebrew/nix/install script, SBOM, PTY smoke, signing and notarization, payload format, clean install | packaging | G1 | F-CORE-014, F-CORE-113 | #14409, #14411, #14412 | partial | MATERIAL (SBOM #14513, Windows AVX2 launcher) | partial M: `kc/../script/{build,build-tui,build-portable,update-manifest}.ts` | U-E0-04 | +| U-E3-08 | CI and repository guards hardening (forbidden strings, generated artifacts, markers, md tables, CodeQL, sharding, bots) | packaging | G1 | F-CORE-114 | #14408 | partial | MATERIAL (47 workflow files) | no | U-E0-04 | +| U-E3-09 | Publish `@kilocode/sdk` and `@kilocode/plugin` from BASE packages | packaging | G1 | F-CORE-126 | new | partial | SMALL (regen) | no | U-E3-07 | +| U-E3-10 | v2 preview release train | packaging | G1 | — | new (old N17) | needs-port | n/a | no | U-E3-06, U-E3-07 | +| U-E3-11 | Canary and cutover/rollback runbook, G1 child (G2/G3 children later) | packaging | G1 | — | #14414 | needs-port | n/a | no | U-E3-10 | +| U-E3-12 | Binary and channel naming; v1-IDE isolation from the v2 binary | packaging | G1 | F-V2-004 | new (user decision; see final report) | needs-port | n/a | no | U-E1-01 | + +## E4 Remote sessions + +| Unit | Title | Surface | Gate | Features | Issues | Delta | Stale | Donor | Depends | +|---|---|---|---|---|---|---|---|---|---| +| U-E4-01 | Relay wire protocol and command dispatcher | gateway/cloud | G1 | F-CORE-092 | #14402 | needs-port | MATERIAL (A16: #13496, #13497, #13565) | yes M: `kc/remote-{plugin,protocol,rpc,session}.ts`, `schema/kilocode/remote.ts` | U-E1-03 | +| U-E4-02 | Streamed transcript, attachments, `notify_user`/`send_file`, reconnect without duplication | gateway/cloud | G1 | F-CORE-094 | #14403 | needs-port | MATERIAL | yes M: `kc/remote-transcript.ts` | U-E4-01 | +| U-E4-03 | Remote suggestion lifecycle and subscribed-session ownership | gateway/cloud | G1 | — (consumes the suggest tool from U-E2-16) | #14404 | needs-port | MATERIAL | yes M: `kc/remote-suggestions.ts` | U-E4-01, U-E2-16 | +| U-E4-04 | Detach/exit, interruption, heartbeat fencing | gateway/cloud | G1 | — | #14405 | needs-port | MATERIAL | yes M: `kc/remote-status.ts` | U-E4-01 | +| U-E4-05 | Metadata differences, capability advertisement, instance identity, directory listing, headless link dialog, coexistence with upstream pairing/OpenTunnel | gateway/cloud | G1 | F-CORE-096, F-V2-037 | #14406 | needs-port | MATERIAL (+NEW identity fields) | partial M | U-E4-01 | +| U-E4-06 | Presence registry/relay: host registration | gateway/cloud | G1 | F-CORE-093 | #14395 (part) | needs-port | MATERIAL (client moved to `kilo-gateway`) | yes M: `kc/presence-*.ts`, `schema/kilocode/presence.ts` | U-E4-01 | +| U-E4-07 | PR linking (GitHub, GitLab, Bitbucket), poller, sidebar row | gateway/cloud | G1 | F-CORE-095 | new | needs-port | NEW (#13137, #14195) | no | U-E4-01 | +| U-E4-08 | Deployed relay and consumer verification (external-gated) | gateway/cloud | G1 | — | #14407 | needs-port | n/a | no | U-E4-01…07 | + +## E5 Sharing + +| Unit | Title | Surface | Gate | Features | Issues | Delta | Stale | Donor | Depends | +|---|---|---|---|---|---|---|---|---|---| +| U-E5-01 | Share transport normalization; share/unshare/fork client operations | gateway/cloud | G1 | F-CORE-097, Part 1 "Sharing" | #14396 | needs-port | EMPTY (A17) | partial M-H: `kilo-gateway/src/session.ts`, `test/sharing*` (donor faithful on behavior) | U-E1-03 | +| U-E5-02 | v2 share payload contract (self-contained messages, metadata-first upload) | gateway/cloud | G1 | — | #14397 | needs-port | EMPTY | partial M | U-E5-01 | +| U-E5-03 | Public shared-transcript viewer for v2 messages (cloud repo) | gateway/cloud | G1 | — | #14398 | needs-port | EMPTY | no | U-E5-02 | +| U-E5-04 | Team upload stays refused (fail-closed) | gateway/cloud | G1 | — | #14399 (part) | needs-port | EMPTY | partial M | U-E5-01 | +| U-E5-05 | Deployed share contract verification (external-gated) | gateway/cloud | G1 | — | #14401 | needs-port | n/a | no | U-E5-01…04 | +| U-E5-06 | Background session export/ingest to Kilo cloud (scrubbed), opt-out | gateway/cloud | G1 | F-CORE-048 | new | needs-port | pre (no change) at feature level; A07 MATERIAL overall | no | U-E1-03 | + +## E6 VS Code track (G2) + +Shared facts for E6. Staleness is MATERIAL for every unit (A22: 527 files, +64,678/−5,971; A23: 545 files, +49,247/−15,679), except A24 editor services, which is SMALL. The donor's extension and webview are SNAP-era copies (donor `kilo-vscode` vs V1: 1,702 differing files). Per Decision 4, re-copy the extension and webview from V1 and keep only the donor's v2 backend-adapter pattern (`kv/backend/*`, 25 adapters; `connection.ts`, `local-server.ts`). + +| Unit | Title | Surface | Gate | Features | Issues | Delta | Stale | Donor | Depends | +|---|---|---|---|---|---|---|---|---|---| +| U-E6-01 | Re-land the V1 extension host, webview, `kilo-ui`, `kilo-i18n` on the clean base behind v2 backend adapters; real typecheck with no skip shim | VS Code | G2 | F-VSC-001, F-VSC-002 | #14383 | needs-port | MATERIAL | partial L: adapter pattern `kv/backend/*`; donor UI is SNAP-era | U-E0-02 | +| U-E6-02 | Connection: server manager for the v2 binary, auto-start, capability handshake (client side), v2 store and opt-in first-launch import switch | VS Code | G2 | F-VSC-003, F-VSC-004 | #14387 (+ old N9 client side) | needs-port | MATERIAL | partial M: `kv/connection.ts`, `kv/local-server.ts` | U-E6-01, U-E1-08, U-E3-01 | +| U-E6-03 | Sidebar chat lifecycle end to end: streaming, Stop, reconnect, Restricted Mode key conflict, message-deletion caller | VS Code | G2 | F-VSC-005, F-VSC-023 | #14384 | needs-port | MATERIAL | partial L: `kv/backend/{session,events,projection}.ts` | U-E6-02, U-E2-05 | +| U-E6-04 | Editor tabs: streaming, restore, independent session routing | VS Code | G2 | F-VSC-006 | #14385 | needs-port | MATERIAL | partial L | U-E6-03 | +| U-E6-05 | Settings webview controls and persistence (incl. max cost, display, language, CA certs) | VS Code | G2 | F-VSC-007, F-VSC-024 | #14386 | needs-port | MATERIAL | partial M: `kv/backend/settings.ts` | U-E6-01, U-E1-07 | +| U-E6-06 | Models, accounts, auth and provider OAuth variants, org switch, provider usage | VS Code | G2 | F-VSC-008 | #14388 | needs-port | MATERIAL | partial M: `kv/backend/{account,models,model-state,usage}.ts` | U-E6-02, U-E1-03 | +| U-E6-07 | Generation consumers: commit message (SCM), enhance prompt, branch naming; code actions and terminal actions | VS Code | G2 | F-VSC-009, F-VSC-010, F-VSC-011 | #14389, #14417 | needs-port | SMALL (A24) | partial M: `kv/backend/generation.ts` | U-E2-28 | +| U-E6-08 | FIM/autocomplete and next-edit: gateway producer, service contract, editor integration (`#14415` inventory is done evidence: `editor-services-inventory.md`) | VS Code | G2 | F-CORE-117, F-VSC-012 | #14416 (#14415) | needs-port | SMALL (A24: shared FIM model selection only) | no (donor autocomplete is a SNAP copy; V1 barely changed, so port V1 directly) | U-E1-03 | +| U-E6-09 | Terminal integration and ticket lifecycle in the original panel | VS Code | G2 | F-VSC-013 | #14390 | needs-port | MATERIAL | partial M: `kv/backend/{pty,terminal-connection}.ts` | U-E6-02 | +| U-E6-10 | Agent Manager UI on the v2 control plane: worktrees, sessions, tabs, terminals, PR, diff, browser previews | VS Code | G2 | F-VSC-014 | #14391 (consumer side) | needs-port | MATERIAL (+55k lines) | partial L: SNAP-era `kv/agent-manager` | U-E2-27, U-E6-09 | +| U-E6-11 | VS Code-only tools and panels: browser tab/open, chart, image preview, document viewer, sub-agent viewer, diff virtual provider | VS Code | G2 | F-CORE-072, F-CORE-077, F-VSC-015 | new | needs-port | MATERIAL + NEW (browser-open) | partial L | U-E6-01, U-E2-16 | +| U-E6-12 | Notifications and suggestions lifecycle | VS Code | G2 | F-VSC-016 | #14392 | needs-port | MATERIAL | no | U-E2-16, U-E9-06 | +| U-E6-13 | Marketplace panel; agent, skill and MCP removal; MCP OAuth sign-in | VS Code | G2 | F-VSC-017 | #14393 | needs-port | MATERIAL + NEW | partial L: `kv/backend/mcp.ts` | U-E2-15, U-E2-22, U-E2-23 | +| U-E6-14 | Task timeline and editor diff viewer projection detail; token throughput | VS Code | G2 | F-VSC-018 | #14394 | needs-port | MATERIAL | partial L | U-E6-03 | +| U-E6-15 | Remote status and presence visibility reporting | VS Code | G2 | F-VSC-019 | #14395 (VS Code part) | needs-port | MATERIAL | partial M: `kv/backend/{presence,remote}.ts` | U-E4-06 | +| U-E6-16 | Engine and policy controls: memory, indexing consent and write controls, sandbox preference, network ask, caffeination, attention, telemetry proxy | VS Code | G2 | F-VSC-020 | new (old N14) | needs-port | MATERIAL | partial M: `kv/backend/memory.ts` | U-E2-18…U-E2-21 | +| U-E6-17 | Kilo-classic session migration and Roo import | import | G2 | F-VSC-021 | new | needs-port | MATERIAL (scope reduced by #13605) | partial L (donor copies predate #13605) | U-E3-01 | +| U-E6-18 | Session extras: board, background jobs, wakeups, retention, model usage, snapshots, session move, cloud session import | VS Code | G2 | F-VSC-022 | new | needs-port | NEW | no | U-E2-08, U-E2-10, U-E2-24, U-E3-04 | +| U-E6-19 | VSIX packaging and publishing of the v2 extension (bundles the v2 binary) | packaging | G2 | F-VSC-032 | new | needs-port | MATERIAL | partial M: `vscode-packaging.md` (`vsce package --no-dependencies`) | U-E6-02, U-E3-07 | + +## E7 JetBrains track (G3) + +Shared facts for E7. The plugin is absent from BASE and from the donor (no reuse). Staleness is MATERIAL (A25: 873 files, +108,897/−7,992 SNAP→V1). Behavior comes entirely from V1. + +| Unit | Title | Surface | Gate | Features | Issues | Delta | Stale | Donor | Depends | +|---|---|---|---|---|---|---|---|---|---| +| U-E7-01 | Inventory plugin contracts (routes used by the generated Kotlin client, events, terminal) and map each to a public v2 interface | JetBrains | G3 | — | #14420 | needs-port | MATERIAL | no | U-E0-02 | +| U-E7-02 | Re-land the plugin on the clean base: Gradle build, CLI pinning to a v2 release, OpenAPI client generated from the assembled v2 contract, server startup, discovery, auth, version checks | JetBrains | G3 | F-JB-001, F-JB-002, F-JB-003 | #14421 | needs-port | MATERIAL | no | U-E7-01, U-E3-07 | +| U-E7-03 | Sessions, streaming, permissions, history, config, board, subagents, prompt completion, settings, MCP OAuth, telemetry | JetBrains | G3 | F-JB-004, F-JB-005, F-JB-006, F-JB-010 | #14422 | needs-port | MATERIAL | no | U-E7-02 | +| U-E7-04 | Terminal, diff/VFS and shared editor-service consumers (enhance prompt, branch name; FIM only if the gate stays, see final report) | JetBrains | G3 | F-JB-007, F-JB-011 | #14423 | needs-port | MATERIAL | no | U-E7-02, U-E2-28 | +| U-E7-05 | Agent Manager, worktrees, marketplace, legacy v5 migration | JetBrains | G3 | F-JB-008, F-JB-009 | new | needs-port | MATERIAL | no | U-E2-27, U-E7-03 | +| U-E7-06 | Validate in supported IDEs: reconnect, restart, version mismatch | JetBrains | G3 | — | #14424 | needs-port | n/a | no | U-E7-02…05 | + +## E10 External consumers + +| Unit | Title | Surface | Gate | Features | Issues | Delta | Stale | Donor | Depends | +|---|---|---|---|---|---|---|---|---|---| +| U-E10-01 | Cloud agent consumers: adapt confirmed gaps through public interfaces and validate (discovery done: `cloud-agent-consumers.md`, `cloud-agent-contracts.md`) | external consumer | G1 (consumer pins `@kilocode/cli@7.8.1`, independent cutover; gate attachment needs user confirmation) | — | #14427, #14428 (#14425, #14426 have evidence) | needs-port | n/a | no | U-E3-12, U-E3-01 | +| U-E10-02 | Anaconda Desktop: integration mode, compatibility, adaptation and validation; V1 `anaconda-desktop` provider | external consumer | set by #14429 discovery (UNVERIFIED which surface) | F-CORE-118, F-VSC-031 | #14429, #14430, #14431, #14432 | needs-port | EMPTY (A26) | no (SNAP copy in `kv/anaconda-desktop` only) | U-E1-04 | +| U-E10-03 | Other CLI consumers: Zed extension, GitHub Action, floating `@latest` users (benchmark, MCP-catalog CI) | external consumer | G1 | — | new | needs-port | n/a | no | U-E3-12 | + +## E8 Retirement + +| Unit | Title | Surface | Gate | Features | Issues | Delta | Stale | Donor | Depends | +|---|---|---|---|---|---|---|---|---|---| +| U-E8-01 | Branch flip (`main`→`v1`, `kilo-v2-clean`→`main`) and its timing relative to IDE v1 builds | repo | post-G3 | — | new (old N10) | needs-port | n/a | no | G3 go/no-go | +| U-E8-02 | Storage path consolidation after G3 (D6), one-time with backup | import | post-G3 | — | new (old N10) | needs-port | n/a | no | U-E8-01 | +| U-E8-03 | Packages that deploy from `main`: keep (docs site), retire (`kilo-console`, F-CORE-123), re-home their workflows | repo | post-G3 | F-CORE-123 | new (old N6a/N6b in part) | not-needed (console) / needs-port (docs deploy) | MATERIAL (A29) | no | U-L5-01 | +| U-E8-04 | v1 end-of-life policy and maintenance window | repo | post-G3 | — | new | needs-port | n/a | no | U-E8-01 | + +## E11 Post-cutover backlog + +| Unit | Title | Surface | Gate | Features | Issues | Delta | Stale | Donor | Depends | +|---|---|---|---|---|---|---|---|---|---| +| U-E11-01 | Speech-to-text (CLI gateway endpoints and VS Code capture) | gateway/cloud | post-cutover | F-CORE-120, F-V2-051, F-VSC-029 | #14418 | post-cutover | SMALL (A28) | no (SNAP copy) | — | +| U-E11-02 | Image generation (tool and VS Code consumer) | gateway/cloud | post-cutover | F-CORE-121, F-VSC-030 | #14419 (image part) | post-cutover | SMALL | no (SNAP copy) | — | +| U-E11-03 | Team sharing ownership model; incremental sharing with deletion/revert semantics | gateway/cloud | post-cutover | — | #14399 (ownership part), #14400 | post-cutover | EMPTY | no | U-E5-02 | +| U-E11-04 | Legacy TUI extras: kilo-v1/colorblind theme catalogue, legacy bindings, fixed-width tables, vim mode | CLI/TUI | post-cutover | F-CORE-106, F-CORE-108b, F-CORE-109b | #14376, #14378 (tables/bindings part) | post-cutover | MATERIAL (A19) | partial L | U-E9-05 | +| U-E11-05 | Default-off experimental features: Exa, context tools, code mode/PTC, MCP Apps, session switcher, native notebook tools, Claude migration, scout, LSP tool | mixed (shared runtime) | post-cutover | F-CORE-070, F-CORE-075, F-CORE-076, F-CORE-085, F-CORE-091, F-CORE-104, F-CORE-128, F-V2-048, F-VSC-025, F-VSC-026, F-VSC-027, F-VSC-028 | new | post-cutover | NEW for most | no | — | +| U-E11-06 | KiloClaw: record not-needed (removed on V1 `3eaf47ba52`); delete donor remnants if copied | repo | post-cutover | F-CORE-122 | #14419 (Claw part) | not-needed | n/a | no (donor has 40 stale paths per `editor-services-inventory.md`) | — | + +## L Cross-cutting lanes + +| Unit | Title | Surface | Gate | Features | Issues | Delta | Stale | Donor | Depends | +|---|---|---|---|---|---|---|---|---|---| +| U-L1-01 | Upstream sync: merge `upstream/v2` pinned by SHA; dry-run workflow; v2 merge tooling | repo | continuous | F-CORE-115 | new (old N3) | needs-port | MATERIAL (`script/upstream/**`) | no | U-E0-04 | +| U-L2-01 | V1 delta sweep: advance the cutoff, refresh the experimental-flag list, re-run staleness for affected units | repo | continuous | — | new (old N4) | needs-port | n/a | no | — | +| U-L3-01 | Shared-patch ratchet and marker disposition sweep | repo | continuous | — | #14381 (old N2) | needs-port | n/a | no | U-E0-01 | +| U-L4-01 | Acceptance: rewrite test plans with gate tags and V1-derived expectations; simulation/http-recorder harness; gate acceptance runs (G1 child first) | repo | continuous | F-V2-054 | new (old N11, N15, N16) | needs-port | n/a | no | — | +| U-L5-01 | Docs: kilo.ai/docs content for v2 at each gate | repo | continuous | F-CORE-124 | new | partial | MATERIAL (A29) | no | — | + +## Staleness per area (SNAP `f7115470` → V1 `fb7e96ed`) + +Path sets are explicit in the staleness scratch analysis: Kilo-owned paths (`packages/opencode/src/kilocode/**`, `src/kilo-sessions/**`, `packages/kilo-*/**`, `plugin-atomic-chat`) plus shared files that carry `kilocode_change` or were touched by a Kilo commit. Commits are non-merge; `sig` excludes release/chore/docs/test/style/ci. Every changed file in the range is Kilo-owned, marker-bearing or Kilo-touched (0 upstream-only files). + +| Area | Files (A/D/M) | +/− | Kilo commits (sig) | Verdict | What changed | +|---|---|---|---|---|---| +| A01 Identity/store | 16 (5/0/11) | +474/−168 | 12 (10) | SMALL | WAL recovery, interrupted first access, project-ID cache | +| A02 Daemon/server | 97 (13/0/84) | +4,042/−1,011 | 161 (112) | MATERIAL | `/reload` reloads project (#14103), serve shutdown deadline, DB reclaim (#14540), event bridge, startup perf | +| A03 Gateway/account | 39 (17/0/22) | +4,494/−728 | 80 (44) | MATERIAL | Provider usage center (#11611), Codex usage (#13519), org default model, credential precedence, caffeinate | +| A04 Provider/catalog | 42 (20/1/21) | +3,605/−668 | 56 (47) | MATERIAL | Catalog recovery (#14723), malformed provider tolerance (#14794), cache breakpoints, task model selection (#13557) | +| A05 Config | 99 (72/0/27) | +5,020/−233 | 55 (52) | MATERIAL | New keys (`memory_model`, swarm, PTC, task model, cleanup), overlay write errors; 61 upstream config fixtures | +| A06 Session policy | 165 (84/1/80) | +24,599/−1,246 | 203 (177) | MATERIAL + NEW | Goals, wakeups/cron, steering, background agents survive stop, deferred title, compaction fix, ~128 fixes, conn-reset retry (#14893) | +| A07 Portability/import | 14 (4/2/8) | +1,852/−258 | 22 (17) | MATERIAL | Claude/Codex import, server-side migrate API (#13294), cloud transcript import (#13483) | +| A08 Agents/plan/skills | 44 (18/4/22) | +4,172/−1,275 | 78 (70) | MATERIAL | Task subagent model selection, agent requirements removed, agent-created plans open | +| A09 Tools/permissions | 91 (30/12/49) | +5,355/−1,870 | 117 (102) | MATERIAL + NEW | link_pr, cron/wakeup tools, self-context tools, PTC, preselected answers, rejection feedback; interactive terminal removed | +| A10 Memory | 12 (0/0/12) | +341/−52 | 47 (10) | SMALL | `memory_model` with fallback; SSE auto-save fix | +| A11 Indexing | 35 (5/0/30) | +2,118/−737 | 85 (47) | MATERIAL (moderate) | Semantic search reporting, embedder dimension fixes, root/home safeguards, watcher swap, VS Code consent | +| A12 Sandbox | 21 (6/0/15) | +1,293/−33 | 59 (23) | MATERIAL (policy) | Git classifier, prompt before sandboxed git writes, offline mutation worker | +| A13 Telemetry | 11 (2/0/9) | +238/−36 | 46 (6) | SMALL | Identity dedupe, lifecycle person props, autocomplete failure sampling | +| A14 MCP/marketplace | 104 (69/5/30) | +8,401/−1,530 | 52 (47) | MATERIAL + NEW | Marketplace in CLI API, npm/git plugin install, MCP OAuth in core, MCP Apps, CIMD | +| A15 Agent coordination | 406 (201/4/201) | +55,004/−8,654 | 560 (533) | MATERIAL + NEW | Shared boards, Agent Manager multi-project, browser previews, PTY durability | +| A16 Remote | 24 (9/0/15) | +6,644/−292 | 34 (31) | MATERIAL | PR linking, instance identity, messageID forward, directory listing, session log | +| A17 Sharing | 0 | 0 | 0 | EMPTY | — | +| A18 CLI/ACP | 72 (12/3/57) | +3,264/−1,115 | 72 (61) | MATERIAL | `kilo worktree`, caffeinate, cloud stdin, clickable links, ACP changes | +| A19 TUI | 47 (14/1/32) | +2,720/−666 | 106 (64) | MATERIAL | Subagent steering UI, last-commit diff, About dialog, goal UI, new plugins | +| A20 Cloud CLI | 6 (3/0/3) | +365/−32 | 4 (4) | SMALL | Stdin prompts (#13851) | +| A21 Packaging/CI | 176 (94/7/75) | +17,076/−1,486 | 402 (269) | MATERIAL | SBOMs (#14513), Windows AVX2 launcher, 47 workflow files | +| A22 VS Code host | 527 (273/13/241) | +64,678/−5,971 | 728 (613) | MATERIAL | Browser tab, settings search, session tabs, MCP OAuth sign-in, speech source | +| A23 Webview/shared UI | 545 (198/44/303) | +49,247/−15,679 | 671 (606) | MATERIAL | `kilo-ui` +5,843/−1,335 and webview changes | +| A24 Editor services | 16 (2/0/14) | +386/−70 | 18 (13) | SMALL | Shared FIM model selection, quick-fix code action, add-to-context, commit-message errors | +| A25 JetBrains | 873 (513/13/347) | +108,897/−7,992 | 627 (518) | MATERIAL | MCP OAuth, swarm board, session cleanup, checkpoint settings, prompt navigator | +| A26 Anaconda | 0 | 0 | 0 | EMPTY | — | +| A27 Atomic chat | 1 | +1/−1 | 0 sig | EMPTY | Version bump only | +| A28 Voice/image/Claw | 28 (4/11/13) | +606/−2,541 | 10 (9) | SMALL | KiloClaw removed (#14209), custom STT source | +| A29 Docs/web/console | 419 (131/54/234) | +4,773/−3,958 | 393 (110) | MATERIAL (content) | Docs syncs | +| A30 SDK/llm/plugin | 47 (6/0/41) | +9,683/−3,408 | 135 (90) | SMALL for Kilo | Mostly upstream regen; regenerate | +| A31 Legacy migration | 10 (2/4/4) | +243/−2,372 | 3 (3) | MATERIAL (removal) | VS Code Kilo-classic migration removed (#13605) | +| A32 Security/PII | 1 | +1/−0 | 1 (1) | SMALL | One line in `pii.ts` | + +### Unassessed gap `d99662338..f7115470` + +The 816-file assessment was pinned at `d99662338` (2026-08-10), not at SNAP. Files changed in the 800 commits between them were never assessed. Same path sets and classifier as above; 732 files in total. "Non-meta" excludes `.changeset`, `package.json` and `CHANGELOG` churn. + +| Area | Files (non-meta) | +/− | Effect on the verdict above | +|---|---|---|---| +| A01 | 2 (2) | +82/−7 | none (database migration compat) | +| A02 | 13 (12) | +127/−55 | none (already MATERIAL) | +| A03 | 3 (2) | +62/−3 | none | +| A04 | 10 (10) | +1,514/−312 | none | +| A05 | 10 (10) | +625/−44 | none | +| A06 | 21 (21) | +1,082/−352 | none; touches `core/src/session/compaction.ts`, `runner/llm.ts`, `opencode/src/session/{processor,prompt}.ts`, which matters for U-E2-01/02 fixtures | +| A08 | 3 (3) | +30/−21 | none | +| A09 | 21 (21) | +123,945/−67,036 | none (dominated by a large generated or fixture file; UNVERIFIED which) | +| A10 | 1 (0) | +1/−1 | none (version bump) | +| A11 | 3 (2) | +15/−37 | none | +| A12 | 3 (2) | +2/−11 | none | +| A13 | 1 (0) | +1/−1 | none | +| A14 | 10 (10) | +1,540/−1,944 | none | +| A15 | 57 (57) | +2,164/−344 | none | +| A16 | 1 (1) | +0/−1 | none | +| A17 | 0 | 0 | stays EMPTY | +| A18 | 10 (10) | +179/−42 | none | +| A19 | 17 (16) | +375/−49 | none | +| A20 | 0 | 0 | stays SMALL | +| A21 | 116 (30) | +2,401/−1,709 | none | +| A22 | 18 (17) | +1,096/−52 | none | +| A23 | 204 (199) | +11,460/−1,826 | none | +| A24 | 0 | 0 | stays SMALL | +| A25 | 55 (53) | +1,442/−163 | none | +| A26 | 0 | 0 | stays EMPTY | +| A27 | 1 (0) | +1/−1 | stays EMPTY | +| A28 | 0 | 0 | stays SMALL | +| A29 | 32 (29) | +212/−22 | none | +| A30 | 22 (17) | +703/−32 | none | +| A31, A32 | 0 | 0 | unchanged | +| Unclassified (shared misc, repo meta) | 98 (89) | +38,198/−118 | for L2 to disposition | + +No area verdict changes: every area that moved in the gap is already MATERIAL, and the EMPTY/SMALL areas did not move. The first L2 sweep should still start from `d99662338`, so that each gap file gets a file-level disposition. + +## Donor facts + +| Fact | Value | +|---|---| +| Donor upstream base | `59b29de409` (2026-09-03); 1,454 upstream commits behind BASE | +| Donor Kilo packages | `kilo-cli` (278 files), `kilo-gateway` (18, a rewrite), `kilo-memory` (67), `kilo-indexing` (121), `kilo-client` (4), `kilo-vscode` (1,106, SNAP-era plus 25 v2 backend adapters), `kilo-ui` (241), `kilo-ide-ui` (1,755, vendored V1 `packages/ui`), `kilo-i18n` (22), `packages/schema/src/kilocode` (10 RPC defs), `packages/ai/src/kilocode` (3) | +| Missing in donor | `kilo-jetbrains`, `kilo-sandbox`, `kilo-telemetry`, `kilo-docs`, `kilo-web-ui`, `kilo-console`, `plugin-atomic-chat` | +| Donor shared patches | 29 files with `kilocode_change` (core 5, plugin 1, theme 4, tui 14, plus tests). Drift on them to BASE: +2,325/−2,726. Deleted at BASE: `core/src/plugin/system-prompt.ts`, `theme/src/tui/defaults.ts`, `theme/src/tui/fallback.ts` | +| Stable seams | `schema/src/rpc.ts` (0 changes), `plugin/src/effect/plugin.ts` (+8/−4), `plugin/src/effect/rpc.ts` (+3/−3), `server/src/fetch.ts` overrides (+2/−2) | +| Summary | Donor mechanism (host overrides, plugin+RPC registration, schema RPC defs) sits on seams that barely moved. Donor shared patches in TUI, theme, system prompt and MCP sit on files that moved a lot or were deleted. Donor behavior is SNAP-era. Whether donor tests pass at BASE is UNVERIFIED (not run) | + +## Issue coverage + +Every issue in the #13750 tree maps to a unit or is closed out as a tracker. + +| Issue | Unit | Issue | Unit | +|---|---|---|---| +| #13750 | tracker (plan root) | #14402 | U-E4-01 | +| #14016 | tracker → E6 | #14403 | U-E4-02 | +| #14017 | tracker → E2, E9 | #14404 | U-E4-03 | +| #14018 | tracker → E5 (+E11) | #14405 | U-E4-04 | +| #14019 | tracker → E4 | #14406 | U-E4-05 | +| #14020 | tracker → E3 | #14407 | U-E4-08 | +| #14021 | tracker → E2 (generation), E6 (editor services) | #14408 | U-E3-08 | +| #14022 | tracker → E7 | #14409 | U-E3-07 | +| #14023 | tracker → E10 | #14410 | U-E3-06 | +| #14024 | tracker → E10 | #14411 | U-E3-07 | +| #14371 | U-E2-01 | #14412 | U-E3-07 | +| #14372 | U-E2-02 | #14413 | U-E3-01 (+ U-E3-02, U-E3-03) | +| #14373 | U-E2-03 | #14414 | U-E3-11 (G1 child; G2/G3 children later) | +| #14374 | U-E2-04 | #14415 | U-E6-08 (evidence: `editor-services-inventory.md`) | +| #14375 | U-E9-09 | #14416 | U-E6-08 | +| #14376 | U-E11-04 | #14417 | U-E6-07 | +| #14377 | U-E9-10 | #14418 | U-E11-01 | +| #14378 | U-E9-12 (alerts, sound) + U-E11-04 (tables, bindings) | #14419 | U-E11-02 (image) + U-E11-06 (Claw) | +| #14379 | U-E9-11 | #14420 | U-E7-01 | +| #14380 | U-E2-05 | #14421 | U-E7-02 | +| #14381 | U-L3-01 | #14422 | U-E7-03 | +| #14382 | U-E2-13 | #14423 | U-E7-04 | +| #14383 | U-E6-01 | #14424 | U-E7-06 | +| #14384 | U-E6-03 | #14425 | U-E10-01 (evidence) | +| #14385 | U-E6-04 | #14426 | U-E10-01 (evidence) | +| #14386 | U-E6-05 | #14427 | U-E10-01 | +| #14387 | U-E6-02 | #14428 | U-E10-01 | +| #14388 | U-E6-06 | #14429 | U-E10-02 | +| #14389 | U-E6-07 | #14430 | U-E10-02 | +| #14390 | U-E6-09 | #14431 | U-E10-02 | +| #14391 | U-E2-27 (+ VS Code consumer in E6) | #14432 | U-E10-02 | +| #14392 | U-E6-12 | #14396 | U-E5-01 | +| #14393 | U-E6-13 | #14397 | U-E5-02 | +| #14394 | U-E6-14 | #14398 | U-E5-03 | +| #14395 | U-E4-06 (host) + U-E6-15 (VS Code) | #14399 | U-E5-04 (fail-closed) + U-E11-03 (ownership) | +| #14400 | U-E11-03 | #14401 | U-E5-05 | + +## Old-plan units (N1–N17) re-homed + +| Old unit | Disposition on the clean base | +|---|---| +| N1 consolidation merge and scope-rename codemod | not-needed: the base is already a clean upstream commit with the `@opencode/*` scope. Code copied from the donor still needs the rename at copy time (each unit) | +| N2 ratchet | U-L3-01 | +| N3 sync automation and e2e lane | U-L1-01 (+ U-E0-04 for the CI lane) | +| N4 delta sweep | U-L2-01 | +| N5 v1 reference fixtures | U-E0-05 | +| N6a package assessment | folded into U-E8-03 and U-E9-14; `kilo-console` obsolete | +| N6b package imports onto the old branch | not-needed as a unit: each package lands with its epic (kilo-docs via U-E8-03/U-L5-01, kilo-jetbrains via E7) | +| N7 progress-plan alignment | not-needed: the old progress plan is archived; the new plan replaces it (D9) | +| N8 customer v2 store identity | U-E1-02 | +| N9 handshake and extension switch | U-E1-08 (server side) + E6 (client side) | +| N10 v1 retirement | U-E8-01, U-E8-02, U-E8-04 | +| N11 gate acceptance runs | U-L4-01 | +| N12 TUI picker, dialogs, status/footer | U-E9-06, U-E9-07 | +| N13 runtime integration remainder | U-E2-18…U-E2-21 | +| N14 original-extension consumers | E6 | +| N15, N16 scenario sets | U-L4-01 | +| N17 preview release train | U-E3-10 | + +## Open items for the user + +These items affect unit gates or scope. They need a user decision before the planner fixes Part B. + +| Item | Affected units | Evidence | +|---|---|---| +| FIM as a G3 hard gate, although V1 JetBrains has no FIM | U-E6-08, U-E7-04 | Inventory Part 3, "FIM at V1 JetBrains" | +| Decision 7 and default-on experimental flags (background subagents, shared agent board): treated as gating here | U-E2-07, U-E2-24 | `packages/opencode/src/effect/runtime-flags.ts:47-49,61-62` at V1 | +| Branch-flip timing: v1 VS Code builds its bundled CLI from `packages/opencode` in the same tree | U-E8-01 | `packages/kilo-vscode/script/local-bin.ts` at V1 | +| Binary and channel naming (`kilo2` binary and npm `next` per cloud owners, 2026-10-07) | U-E3-12, U-E10-01, U-E10-03 | `technical-notes/baseline/cloud-agent-contracts.md:40-47` | +| Gate attachment for cloud agents (independent pin `@kilocode/cli@7.8.1`) and Anaconda Desktop | U-E10-01, U-E10-02 | `cloud-agent-consumers.md:48-63`; #14429 | +| Product disposition of upstream desktop/web app and pairing | U-E9-14, U-E9-15, U-E4-05 | Inventory Part 4 (F-V2-037, F-V2-049) | +| Snapshot SHA: record `d99662338` alongside `f7115470` in `migration-context.md` | U-L2-01 | `marker-audit/v1-kilo-marker-port-assessment.md:9` | diff --git a/migration-tracking/technical-notes/baseline/pinned-v2-baseline.md b/migration-tracking/technical-notes/baseline/pinned-v2-baseline.md index b005831e67ae..2d33a52a25cb 100644 --- a/migration-tracking/technical-notes/baseline/pinned-v2-baseline.md +++ b/migration-tracking/technical-notes/baseline/pinned-v2-baseline.md @@ -1,250 +1,116 @@ # Pinned upstream v2 baseline -Phase 0 baseline for `plans/kilo-opencode-v2-issue-13750.md` (originally -captured as slice 1 of the now-superseded `plans/kilo-opencode-v2-port.md`, bead -`kilocode-cmy`). +Status: living. Refreshed 2026-10-11 by the discovery phase on `kilo-v2-clean-bootstrap`. Supersedes the 2026-08-13 capture at `76dbaf20` (old branch `johnnyeric/v2-baseline`); that capture survives in git history only. -This records the exact refs the Kilo OpenCode v2 line is based on, the -authoritative validation commands **as defined by this v2 tree**, and the -results of running them on pristine upstream v2. Everything below was produced -on 2026-08-13 on branch `johnnyeric/v2-baseline` with no Kilo product code in -the tree. - -Reproduce with `bun kilocode/script/v2-baseline.ts --checks`. +This file records the upstream commit the Kilo v2 line is built on, the validation commands that this tree defines, and the result of running them. Compare any failing check against this file before you attribute it to a change (`migration-context.md`, Conventions). ## Refs -Fetched with `git fetch origin main` and `git fetch upstream dev v2` immediately -before recording. `origin` is `https://github.com/Kilo-Org/kilocode.git`; -`upstream` is `https://github.com/anomalyco/opencode.git`. - -| Ref | SHA | Commit date | Subject | +| Ref | SHA | Date | Subject | |---|---|---|---| -| `upstream/v2` (pinned) | `76dbaf20adbd43fd208a00ef3cda4a51e125a234` | 2026-08-12T18:18:24-04:00 | `fix(catalog): serve app shell at lab route (#42159)` | -| `upstream/dev` | `14b37df39168eaf6a6faf862ec4a7bbe9c825bbd` | 2026-08-12T22:36:40Z | `chore: update nix node_modules hashes` | -| `origin/main` | `f7115470740c51fd4200fedd9ad3edfad60589ce` | 2026-08-12T10:38:03-06:00 | `Merge pull request #13077 from Kilo-Org/fix/ci-test-stability-in-process` | +| Upstream base (pinned) | `5e73d5cb1f788637b1b3d5cf1298ea694cbad0c5` | 2026-10-08 | `feat(core): add Google Cloud credential setup for Vertex (#53798)` | +| Release marker `v2.0.26` | `9b4ec5714d481559990db0a816d5dec19541a814` | — | Version-bump commit whose parent is the base. It is not on `upstream/v2` and is not a merge target | +| `upstream/v2` at capture | `d2905b8ddae8b0353cd2166d68d59795397921db` | 2026-10-09 | `feat(ai): add Vertex AI Interactions route (#54175)`. 31 commits ahead of the base | +| Integration branch `kilo-v2-clean` | `5e73d5cb` | — | Equal to the base | +| Phase branch `kilo-v2-clean-bootstrap` (HEAD) | `a876c2912a5fd114066ce34e3a3fcc79f7f0a1b0` | 2026-10-11 | Base plus 2 documentation commits (`c4042b39a1`, `a876c2912a`). No source changes | +| V1 target `origin/main` | `fb7e96eda5409517a05948b9efaf01c53997bf6e` | 2026-10-09 | `Merge pull request #14893 …` | -`upstream/dev` moved after the pin was chosen. The pin is deliberately the -`upstream/v2` tip at pin time, not the newest `upstream/dev` commit. +The `packages/cli` version at the base is `2.0.25`, because the bump to 2.0.26 lives only in the tag commit. -## Merge bases and ancestry +## Ancestry -``` -$ git merge-base upstream/dev upstream/v2 -0e2dd4ad150d0182fc9e43d81424d8db11465977 -$ git merge-base origin/main upstream/v2 -0e2dd4ad150d0182fc9e43d81424d8db11465977 -$ git merge-base origin/main upstream/dev -dc67b7a3bed6334a9639fe1362bb90c56288bc7c -$ git merge-base --octopus origin/main upstream/dev upstream/v2 -0e2dd4ad150d0182fc9e43d81424d8db11465977 -``` - -`0e2dd4ad150d0182fc9e43d81424d8db11465977` (`chore: generate`, 2026-06-26) is -the dev/v2 divergence point and also the base shared with Kilo `origin/main`. +| Check | Result | +|---|---| +| `git merge-base --is-ancestor 5e73d5cb upstream/v2` | true | +| `git rev-list --count 5e73d5cb..HEAD` | 2 (documentation only) | +| `git merge-base 5e73d5cb fb7e96ed` | `0e2dd4ad` (`chore: generate`, 2026-06-26): the dev/v2 divergence point, also the last common commit with Kilo v1 | +| Commits from `0e2dd4ad` to the base | 4,343 | +| Commits from `0e2dd4ad` to V1 | 18,848 | -Commits ahead of that base, verified 2026-08-13: +## Toolchain (this host) -| Ref | Commits ahead of `0e2dd4ad` | -|---|---| -| `upstream/dev` | 899 | -| `upstream/v2` | 1470 | -| `origin/main` | 3391 | - -Ancestry checks at the time of writing: - -``` -$ git rev-parse HEAD -76dbaf20adbd43fd208a00ef3cda4a51e125a234 -$ git merge-base --is-ancestor 76dbaf20adbd43fd208a00ef3cda4a51e125a234 HEAD; echo $? -0 -$ git rev-list --count upstream/v2..HEAD -0 -$ git rev-list --count HEAD..upstream/v2 -0 -``` - -At baseline capture, `HEAD` was exactly the pinned SHA: the tree carried no Kilo -commits, so every result below is pristine upstream v2. The slice's own commit -sits on top of that; `git rev-list --count upstream/v2..HEAD` is expected to be -non-zero afterwards, while the first two checks must keep holding. - -`johnnyeric/kilo-opencode-v2` (the integration branch) also pointed at the -pinned SHA and is checked out in a different worktree; this slice did not touch -it. - -## Toolchain - -| Tool | This host | Upstream CI at the pin | +| Tool | This host | Upstream CI | |---|---|---| -| bun | 1.3.14 | 1.3.14 (`packageManager` in root `package.json`, via `.github/actions/setup-bun`) | -| node | v22.23.2 | 24 for install/unit; 26.4.0 for the Node build step; 24.15 for e2e | -| git | 2.50.1 (Apple Git-155) | 2.52.0 | -| OS | macOS 26.5.1, Darwin 25.5.0, arm64 | `blacksmith-4vcpu-ubuntu-2404` and `blacksmith-4vcpu-windows-2025` | -| ffmpeg | not installed | installed on Linux by the unit job | +| bun | 1.4.2 (equals `packageManager` `bun@1.4.2`) | from `package.json` via `.github/actions/setup-bun` | +| node | v24.13.0 | 24 for install and unit; 26.4.0 for the Node build step | +| git | 2.50.1 (Apple Git-155) | runner default | +| OS | macOS 26.3.1 (25D2128), arm64 | `blacksmith-4vcpu-ubuntu-2404`, `blacksmith-4vcpu-windows-2025` | +| `/bin/bash` | GNU bash 3.2.57 (macOS system bash) | bash 5.x on Linux runners | +| `rg` on PATH | absent | present on runners (UNVERIFIED; inferred from green upstream unit runs) | +| ffmpeg | absent | — | -`bun install` reported `5088 packages installed` and left `bun.lock` unchanged -(`git status --porcelain` empty afterwards). +The working tree also contains ignored leftover directories from an earlier v1 checkout (for example `packages/kilo-*` holding only `node_modules`). They are not tracked, are not workspaces of this tree, and do not affect the checks below. -## Authoritative v2 commands +## Validation commands defined by this tree -Derived from this tree only — root `package.json`, `turbo.json`, -`.github/workflows/typecheck.yml`, `.github/workflows/test.yml`, and per-package -`package.json` scripts. Kilo main's `packages/opencode` commands do not apply: -that package does not exist on v2. +Derived from the root `package.json`, `.github/workflows/check.yml`, `.github/workflows/test.yml` and per-package scripts at the base. -| Purpose | Command | Working dir | -|---|---|---| -| Install | `bun install` | repo root | -| Typecheck (all packages) | `bun typecheck` (= `bun turbo typecheck --concurrency=3`) | repo root | -| Unit tests (all packages) | `GITHUB_ACTIONS=false bun turbo test` | repo root | -| Generated client contract | `bun run check:generated` | `packages/client` | -| Generated docs/OpenAPI | `bun run check:generated` | `packages/www` | -| CLI binary build | `bun run script/build.ts --single --skip-install` | `packages/cli` | -| Service lifecycle smoke | `bun run script/service-smoke.ts` | `packages/cli` | -| Node CLI build + smoke | `bun run script/build-node.ts --single --skip-install --outdir=dist/node` then `bun run script/service-smoke.ts --node` | `packages/cli` | -| Client regeneration | `bun run generate` | `packages/client` | - -Constraints this tree imposes: - -- Tests cannot run from the repo root. `bunfig.toml` sets - `[test] root = "./do-not-run-tests-from-root"` and the root `test` script - exits 1. Use `bun turbo test` or run from a package directory. -- Typecheck must go through `bun typecheck` / `bun turbo typecheck`, never - `tsc` directly (root `AGENTS.md`). Packages use `tsgo`. -- `.husky/pre-push` enforces the bun version and runs `bun typecheck`. -- The `e2e` job in `.github/workflows/test.yml` is skipped on the v2 branch by - its own `if: github.ref_name != 'v2' && github.head_ref != 'v2'`. It is not - part of the v2 baseline gate. -- `bun run lint`, `bun run lint:effect-patterns`, and `bun run test:lint-rules` - exist but are not run by any workflow in this tree. - -## Results on pristine upstream v2 - -`GITHUB_ACTIONS=false bun turbo test` was run twice: once as CI runs it, then -once with `--continue` so a single failure would not mask the rest. +| Purpose | Command | Working dir | Run by upstream CI | +|---|---|---|---| +| Install | `bun install` | root | yes | +| Lint and typecheck (pre-push hook, `check.yml`) | `bun run check` (= `bun run lint && bun run typecheck`) | root | yes | +| Typecheck all packages | `bun typecheck` (= `bun turbo typecheck --concurrency=3`) | root | yes, inside `check` | +| Unit tests | `GITHUB_ACTIONS=false bun turbo test --continue` (CI adds `--affected --concurrency=3`) | root (turbo runs each package's own `test`) | yes | +| Effect simplification lint | `bun run lint:effect-simplifications`, `bun run test:effect-simplification-rules` | root | yes | +| Effect pattern lint | `bun run lint:effect-patterns` | root | no | +| Generated client | `bun run check:generated` | `packages/client` | yes (Linux) | +| Generated docs | `bun run check:generated` | `services/www` | yes (Linux) | +| Generated protocol OpenAPI | `bun run check:generated` | `packages/protocol` | no | +| CLI build | `bun run script/build.ts --single --skip-install` | `packages/cli` | yes | +| Service smoke | `bun run script/service-smoke.ts` (isolated `HOME`/XDG temp dir) | `packages/cli` | yes | +| Node build and smoke | `bun run script/build-node.ts --single --skip-install --outdir=dist/node`, then `service-smoke.ts --node` | `packages/cli` | yes (Node 26.4.0) | + +Constraints: + +- Tests cannot run from the root (`bunfig.toml` test root `do-not-run-tests-from-root`). Run them from a package directory or through turbo. +- Never run `tsc`. Packages use `tsgo` through `bun typecheck`. +- `.husky/pre-push` checks the bun version, then runs `bun run check`. +- `check.yml` runs on pushes to `dev`/`v2` and on pull requests whose base is `dev` or `v2`. It therefore does **not** run for pull requests that target `kilo-v2-clean`. `test.yml` runs on every `pull_request`. Whether Actions run on the Kilo fork for these branches is UNVERIFIED: no pull request targets `kilo-v2-clean` or `kilo-v2-clean-bootstrap` yet (`gh pr list`, 2026-10-11). +- `packages/www` no longer exists. Generated docs live in `services/www`, and `typecheck.yml` was replaced by `check.yml`. + +## Results on the phase branch (2026-10-11) + +HEAD `a876c291` carries no source changes, so these are the results for pristine upstream `5e73d5cb`. | Check | Result | Detail | |---|---|---| -| `bun install` | pass | 5088 packages, 13.2s, lockfile unchanged | -| `bun typecheck` | pass | 34 of 34 turbo tasks successful, 17.3s | -| `GITHUB_ACTIONS=false bun turbo test` | **fail** | 17/23 tasks successful, stopped at `@opencode-ai/app#test` | -| `GITHUB_ACTIONS=false bun turbo test --continue` | **fail** | 20/23 successful; failures: `@opencode-ai/app#test`, `@opencode-ai/tui#test`, `opencode-drive#test` | -| `packages/client` `bun run check:generated` | pass | no generated drift | -| `packages/www` `bun run check:generated` | pass | no generated drift | -| `packages/cli` build + `service-smoke.ts` | pass | `cli-darwin-arm64` built, smoke exits 0 | -| `bun run lint` | **fail** | 4232 warnings, 1 error (not CI-enforced) | -| `bun run lint:effect-patterns` | **fail** | 33 errors (not CI-enforced) | -| `packages/cli` Node build + `service-smoke --node` | not run | CI pins Node 26.4.0 for this step; this host has Node 22.23.2 | - -## Pre-existing failures - -None of these were introduced by this slice. Every one reproduced with `HEAD` -exactly at the pinned SHA and no Kilo files in the tree. - -### Also red on upstream CI at the pinned SHA - -Upstream's own `unit (linux)` and `unit (windows)` checks fail at -`76dbaf20`. Read-only confirmation: - -``` -$ gh api repos/anomalyco/opencode/commits/76dbaf20adbd43fd208a00ef3cda4a51e125a234/check-runs -unit (linux) completed failure -unit (windows) completed failure -typecheck completed success -build-cli completed success -generate completed success -e2e completed skipped -``` - -1. **`opencode-drive`**, in `test/instance/config.test.ts`: "instance - configuration > initializes the simulation provider with the current OpenCode - provider shape" fails with `SyntaxError: Failed to parse JSON`. Reproduced - locally and present verbatim in the upstream `unit (linux)` log. This is the - failure that turns upstream's job red. -2. **`@opencode-ai/tui`**, in `test/mini/footer.view.test.tsx`: "run entry - content preserves monochrome markdown grammar" expects the ASCII fallback - `Caf? -> ...` but receives `Café → …`. Reproduced locally and present in the - upstream `unit (linux)` log at the same SHA. - -### Host-environment failures, green on upstream CI - -3. **`@opencode-ai/app`**, in `src/i18n/desktop-native.test.ts`: "desktop native - locale detection > uses Unicode likely subtags for script-sensitive bundles" - — `detectDesktopNativeLocale(["pa-PK"])` returns `"en"`, expected `"pa"`. - Root cause is ICU data, not code. `packages/app/src/i18n/desktop-native.ts:158` - maps `pa` to `pa-Arab-PK` and compares maximized scripts, but this host - resolves the newer Nastaliq subtag: - - ``` - $ bun -e 'console.log(new Intl.Locale("pa-PK").maximize().toString())' - pa-Aran-PK - $ node -e 'console.log(new Intl.Locale("pa-PK").maximize().toString())' - pa-Arab-PK - ``` - - Upstream `unit (linux)` at the same SHA reports `676 pass / 0 fail` for this - package; this host reports `675 pass / 1 fail`. Local only. - -4. **`opencode-drive`** — three `test/driver/index.test.ts` cases fail with - `NotFound: ChildProcess.spawn (ffmpeg ...)`. The upstream unit job installs - ffmpeg on Linux; this host has none. Local only; install ffmpeg to clear - them, which still leaves failure 1. +| `bun install` | pass | "Checked 2477 installs across 2941 packages (no changes)". `bun.lock` unchanged | +| `bun typecheck` | pass | 36/36 turbo tasks. The first run was a full turbo cache replay, so it was re-run with `--force`: 36/36 pass, 0 cached, 22.3 s | +| `bun run lint` | pass | oxlint: 1,509 warnings, 0 errors. ast-grep gui-extensions scan and sdk-docs: 0 problems | +| `bun run check` (lint + typecheck) | pass | Follows from the two rows above | +| `bun run lint:effect-simplifications` | pass | | +| `bun run lint:effect-patterns` | **fail** | 27 errors: 20 `no-star-import`, 4 `no-effect-die-string`, 2 `no-json-parse-cast`, 1 `no-import-alias`. Not run by CI | +| `GITHUB_ACTIONS=false bun turbo test --continue` | **fail** | 26/27 tasks pass. Only `@opencode/core#test` fails: 6,535 pass, 30 fail (see below) | +| `packages/client` `check:generated` | pass | No drift | +| `services/www` `check:generated` | pass | | +| `packages/protocol` `check:generated` | **fail** | "Generated OpenAPI document is stale". Regenerating gives `openapi.json` +951/−70 lines (for example, new `404 LocationNotFoundError` responses). Not run by CI. The file was restored after the probe; the tree is clean | +| `packages/cli` build + `service-smoke.ts` | pass | 50 s build, smoke exits 0 | +| Node build + `service-smoke --node` | not run | CI pins Node 26.4.0; this host has 24.13.0 | +| Upstream CI at the base | green | `gh api …/commits/5e73d5cb/check-runs`: `typecheck`, `unit (linux)`, `unit (windows)`, `e2e (linux)`, `e2e (windows)`, `build-cli` and the signing jobs all succeed | + +## Current reds + +Recorded as found, without fixes. None comes from a Kilo change: the tree has none. + +### Host-environment failures (green on upstream CI) + +1. **`@opencode/core` shell tests, 7 cases.** `ShellTool conditional process substitution` (4: legacy and native, `once` and `always`), `ShellTool compound syntax approval compatibility` (2) and `real-shell compound syntax > bash` (1). Output: `/bin/bash: wait: pid … is not a child of this shell`, exit 127. Probable cause: macOS system bash 3.2. Linux runners use bash 5. +2. **`@opencode/core` ripgrep-backed tests, 23 cases.** `Ripgrep > …` (11) and `search tools > …` (12). Cause: `Ripgrep.Error: ripgrep execution failed` → `HttpClientError: Transport error (GET https://github.com/BurntSushi/ripgrep/releases/download/15.1.0/…)`. No `rg` is on PATH, and the in-test download is refused even though the URL is reachable from a normal shell. Install `rg` to clear it (UNVERIFIED). + +The failures recorded for `76dbaf20` (`opencode-drive` JSON parse, the tui footer `Café`, `pa-PK` ICU, ffmpeg) do not reproduce at this base: the `app` and `tui` suites pass, and `opencode-drive` no longer exists in this tree. ### Not gated by CI -5. `bun run lint` (4232 warnings, 1 error) and `bun run lint:effect-patterns` - (33 errors) are red on pristine v2. No workflow in this tree runs them, so - they are not a regression signal on their own. Use them only as a delta - against this baseline. - -**Consequence for later slices:** a red `bun turbo test` does not by itself mean -a Kilo change broke something. Compare against this table, and re-run -`bun kilocode/script/v2-baseline.ts --checks` from a clean checkout of the -pinned SHA when a failure is ambiguous. - -## Why no fork CI workflow yet - -`.github/workflows/` is upstream-owned and this slice adds no workflow file, -deliberately: - -- This effort is local-only by explicit instruction. Nothing is pushed, so no - workflow added here could actually run; committing one would claim CI - coverage that does not exist. -- The baseline is already red upstream (failures 1 and 2). A fork workflow - running the same commands would be red on its first run, which is a worse - signal than this report plus the reproducer script. -- The integration branch `johnnyeric/kilo-opencode-v2` has no remote yet, so a - workflow has no meaningful trigger to target. `test.yml` and `typecheck.yml` - trigger on `push` to `dev`/`v2` and on `pull_request`; adapting them needs the - fork's branch and remote model to exist first. -- Editing shared upstream workflow files conflicts on every upstream merge and - is exactly what the fork conventions try to avoid. - -Revisit when the integration branch gets a remote. At that point the smallest -useful step is a Kilo-owned workflow that runs -`bun kilocode/script/v2-baseline.ts --checks` and compares against this report, -not a copy of upstream's `test.yml`. +3. `lint:effect-patterns`: 27 errors. +4. `packages/protocol` `check:generated`: stale `openapi.json`. + +Use these only as deltas against this table. + +## Out of scope for this baseline + +- **Kilo packages.** The base contains no `kilo-*` package (`git ls-tree HEAD packages/`). Kilo package checks (`kilo-vscode` typecheck, `bun run extension`, JetBrains Gradle build) start once each package lands. Each landing records its own baseline row here. +- **Fork CI.** No Kilo-owned workflow exists on this branch, and repo or Actions settings are an admin task (`migration-context.md`, Deferred admin tasks). ## Reproducer -`kilocode/script/v2-baseline.ts` is the automation for this report. - -``` -bun kilocode/script/v2-baseline.ts # offline ref and ancestry checks only -bun kilocode/script/v2-baseline.ts --checks # also replay the commands above -``` - -It asserts the pinned SHA is still an ancestor of `HEAD` and that local -`upstream/v2` still resolves to it, then runs the install, typecheck, test, -generated-artifact, and CLI build/smoke commands, writing per-check output to -`kilocode/baseline/logs/` (gitignored by the root `logs/` rule). It exits -non-zero if any check fails; it never suppresses a known-red check, because -suppression would hide a real regression behind a pre-existing one. - -`kilocode/script/` is intentionally not a workspace package, so the script is -not covered by `bun typecheck`: every typecheck project in this repo lives -inside a workspace, and `@types/bun` is only installed in packages that declare -it. Making `kilocode/` a workspace would mean editing upstream's root -`package.json` for one script. It is validated by running it in both modes -instead. Revisit if `kilocode/script/` grows beyond a couple of files. +`migration-tracking/technical-notes/script/v2-baseline.ts` **does not run on this base** as written. It hard-codes `PINNED_V2 = 76dbaf20`, refuses to run unless `upstream/v2` equals that pin, and targets `packages/www`. It is classified rewrite-or-drop in `migration-tracking/discovery/doc-inventory.md`. Until it is rewritten, reproduce with the commands in the table above, run from the listed directories. This capture used a throwaway script outside the repo with the same sequence: install, typecheck (`--force` re-run), lint, turbo test `--continue`, the three `check:generated` commands, then CLI build and service smoke. From b16f4bd1d5e78bb97ece159bc0dfffc6e9464d78 Mon Sep 17 00:00:00 2001 From: Fabio Pliger Date: Sun, 11 Oct 2026 21:17:52 -0500 Subject: [PATCH 04/18] docs(migration): record deferred IDE and distribution questions with triggers --- .../discovery/v2-epic-taxonomy.md | 2 +- .../discovery/v2-feature-inventory.md | 4 +- .../discovery/v2-unit-register.md | 55 ++++++++++++++----- 3 files changed, 45 insertions(+), 16 deletions(-) diff --git a/migration-tracking/discovery/v2-epic-taxonomy.md b/migration-tracking/discovery/v2-epic-taxonomy.md index 3d91b0f6b814..521ca4949198 100644 --- a/migration-tracking/discovery/v2-epic-taxonomy.md +++ b/migration-tracking/discovery/v2-epic-taxonomy.md @@ -33,7 +33,7 @@ Every feature, chore and task must sit in exactly one epic and one surface, so t | **New E11 Post-cutover backlog** | Decision 7 lists non-gating scope (voice, image generation, team and incremental sharing, legacy TUI extras, default-off experimental features). Leaving these inside E4–E7 would make those epics' exit criteria ambiguous ("done except…"). Moving them gives gate epics crisp exits and a single place for ship/defer/drop decisions. KiloClaw is recorded here as not-needed (removed on V1, `3eaf47ba52`) so it has a home | | **E0 narrowed to one-time setup** | Recurring work (upstream merges, v1 delta sweeps, shared-patch ratchet, acceptance runs, docs) is continuous and has no exit. It moves to named lanes L1–L5, so E0 can actually finish | | **E5 keeps individual sharing as gating** | Individual share/unshare/fork-from-share and the public viewer for v2 messages are v1 CLI features. The old plan already made them G1 (`execution-plan.md:69`). Team ownership (#14399 ownership part) and incremental sync (#14400) are post-cutover (Decision 7) and go to E11. The fail-closed refusal of team upload stays in E5 | -| **FIM/next-edit producer in E6, consumed by E7** | One home per unit. VS Code is the first consumer. The producer is a gateway endpoint plus an editor-side engine (`kilo-gateway/src/{fim,edit,autocomplete}.ts`). E7 takes a dependency only if the G3 FIM gate stays: V1 JetBrains has no FIM producer or consumer, only migrated autocomplete settings (`packages/kilo-jetbrains/frontend/.../autocomplete/KiloAutocompleteSettingsService.kt`; see inventory Part 3). Escalated to the user as a proposed Decision 7 change | +| **FIM/next-edit producer in E6, consumed by E7** | One home per unit. VS Code is the first consumer. The producer is a gateway endpoint plus an editor-side engine (`kilo-gateway/src/{fim,edit,autocomplete}.ts`). E7 takes a dependency only if the G3 FIM gate stays: V1 JetBrains has no FIM producer or consumer, only migrated autocomplete settings (`packages/kilo-jetbrains/frontend/.../autocomplete/KiloAutocompleteSettingsService.kt`; see inventory Part 3). Deferred as Q-G3-01 in the register, to settle at U-E7-01 with the JetBrains owners | | **Presence split across E4 and E6** | #14395 has a host-registration part (CLI remote, G1) and a VS Code visibility-reporting part (G2). The old plan split it the same way; this taxonomy keeps it as two units in two epics | | **No separate "Editor services" epic** | #14021 was a subissue, but its consumers are the IDE tracks. Its runtime-side generation endpoints (enhance prompt, commit message, branch name) are E2; its editor consumers are E6/E7. That avoids a second epic with a split gate | | **Dropped: Phase 0 "create issues" as an epic** | Issue creation is user work (brief: "leave issue creation to the user"). It is a planner checklist item, not an epic | diff --git a/migration-tracking/discovery/v2-feature-inventory.md b/migration-tracking/discovery/v2-feature-inventory.md index 404b551edad2..4ec3713aec2c 100644 --- a/migration-tracking/discovery/v2-feature-inventory.md +++ b/migration-tracking/discovery/v2-feature-inventory.md @@ -377,7 +377,7 @@ Source: direct read of V1 (`fb7e96ed`), written by the analyst after the delegat | F-JB-010 | Telemetry | `jb/frontend/.../client/telemetry`, `jb/backend/.../telemetry` | Δ | needs-port | U-E7-03 | | F-JB-011 | Enhance prompt and branch name consumers (per `editor-services-inventory.md`) | UNVERIFIED paths | Δ | needs-port | U-E7-04 | -**FIM at V1 JetBrains.** No FIM or next-edit producer or consumer exists in `packages/kilo-jetbrains` at V1. The only `autocomplete` hits are a settings holder (`KiloAutocompleteSettingsService.kt`, persisting `enableAutoTrigger`, `enableSmartInlineTaskKeybinding`, `enableChatAutocomplete` migrated from the legacy plugin), the migration engine (`LegacyMigrationEngine.kt:84,349-353`) and a CHANGELOG line about the VS Code change #13655. This matches `technical-notes/baseline/editor-services-inventory.md`. Decision 7 makes FIM a hard G3 gate even though JetBrains v1 has no FIM. See the final report. +**FIM at V1 JetBrains.** No FIM or next-edit producer or consumer exists in `packages/kilo-jetbrains` at V1. The only `autocomplete` hits are a settings holder (`KiloAutocompleteSettingsService.kt`, persisting `enableAutoTrigger`, `enableSmartInlineTaskKeybinding`, `enableChatAutocomplete` migrated from the legacy plugin), the migration engine (`LegacyMigrationEngine.kt:84,349-353`) and a CHANGELOG line about the VS Code change #13655. This matches `technical-notes/baseline/editor-services-inventory.md`. Decision 7 makes FIM a hard G3 gate even though JetBrains v1 has no FIM. Deferred as Q-G3-01 in `v2-unit-register.md`. ## Part 4. Upstream v2 features without a Kilo v1 counterpart @@ -491,7 +491,7 @@ Checked on scope (S), errors (E), cancellation (C), persistence (P) and presenta | Flag on V1 | Default | Applied verdict | Note | |---|---|---|---| -| `KILO_EXPERIMENTAL_BACKGROUND_SUBAGENTS` | **on** (kill switch, `packages/opencode/src/effect/runtime-flags.ts:47-49`) | gating (U-E2-07) | Literal Decision 7 would defer a default-on feature; see final report | +| `KILO_EXPERIMENTAL_BACKGROUND_SUBAGENTS` | **on** (kill switch, `packages/opencode/src/effect/runtime-flags.ts:47-49`) | gating (U-E2-07) | Literal Decision 7 would defer a default-on feature; see Q-NOW-01 in the register | | `KILO_EXPERIMENTAL_SHARED_AGENT_BOARD` | **on** (kill switch, `runtime-flags.ts:61-62`) | gating (U-E2-24) | Same | | `experimental.openTelemetry` | on (opt-out) | gating (U-E2-21) | Same | | `KILO_EXPERIMENTAL_SCOUT`, `_CONTEXT_TOOLS`, `_CODE_MODE`, `_MCP_APPS`, `_SESSION_SWITCHER`, `_EVENT_SYSTEM`, `_CLAUDE_MIGRATION`, `_LSP_TOOL`, `KILO_ENABLE_EXA`, `experimental.native_notebook_tools`, `experimental.image_generation`, `experimental.speech_to_text_*` | off | post-cutover (E11) | `_EVENT_SYSTEM` debug view is not-needed | diff --git a/migration-tracking/discovery/v2-unit-register.md b/migration-tracking/discovery/v2-unit-register.md index 67ee9a047de6..73688d53494f 100644 --- a/migration-tracking/discovery/v2-unit-register.md +++ b/migration-tracking/discovery/v2-unit-register.md @@ -111,7 +111,7 @@ Donor paths are relative to the donor tag; `kc/` = `packages/kilo-cli/src/`, `kv | U-E3-09 | Publish `@kilocode/sdk` and `@kilocode/plugin` from BASE packages | packaging | G1 | F-CORE-126 | new | partial | SMALL (regen) | no | U-E3-07 | | U-E3-10 | v2 preview release train | packaging | G1 | — | new (old N17) | needs-port | n/a | no | U-E3-06, U-E3-07 | | U-E3-11 | Canary and cutover/rollback runbook, G1 child (G2/G3 children later) | packaging | G1 | — | #14414 | needs-port | n/a | no | U-E3-10 | -| U-E3-12 | Binary and channel naming; v1-IDE isolation from the v2 binary | packaging | G1 | F-V2-004 | new (user decision; see final report) | needs-port | n/a | no | U-E1-01 | +| U-E3-12 | Binary and channel naming; v1-IDE isolation from the v2 binary (answer Q-DIST-01 before U-E3-10 starts) | packaging | G1 | F-V2-004 | new (deferred user decision Q-DIST-01) | needs-port | n/a | no | U-E1-01 | ## E4 Remote sessions @@ -172,7 +172,7 @@ Shared facts for E7. The plugin is absent from BASE and from the donor (no reuse | U-E7-01 | Inventory plugin contracts (routes used by the generated Kotlin client, events, terminal) and map each to a public v2 interface | JetBrains | G3 | — | #14420 | needs-port | MATERIAL | no | U-E0-02 | | U-E7-02 | Re-land the plugin on the clean base: Gradle build, CLI pinning to a v2 release, OpenAPI client generated from the assembled v2 contract, server startup, discovery, auth, version checks | JetBrains | G3 | F-JB-001, F-JB-002, F-JB-003 | #14421 | needs-port | MATERIAL | no | U-E7-01, U-E3-07 | | U-E7-03 | Sessions, streaming, permissions, history, config, board, subagents, prompt completion, settings, MCP OAuth, telemetry | JetBrains | G3 | F-JB-004, F-JB-005, F-JB-006, F-JB-010 | #14422 | needs-port | MATERIAL | no | U-E7-02 | -| U-E7-04 | Terminal, diff/VFS and shared editor-service consumers (enhance prompt, branch name; FIM only if the gate stays, see final report) | JetBrains | G3 | F-JB-007, F-JB-011 | #14423 | needs-port | MATERIAL | no | U-E7-02, U-E2-28 | +| U-E7-04 | Terminal, diff/VFS and shared editor-service consumers (enhance prompt, branch name; FIM only if the gate stays, Q-G3-01) | JetBrains | G3 | F-JB-007, F-JB-011 | #14423 | needs-port | MATERIAL | no | U-E7-02, U-E2-28 | | U-E7-05 | Agent Manager, worktrees, marketplace, legacy v5 migration | JetBrains | G3 | F-JB-008, F-JB-009 | new | needs-port | MATERIAL | no | U-E2-27, U-E7-03 | | U-E7-06 | Validate in supported IDEs: reconnect, restart, version mismatch | JetBrains | G3 | — | #14424 | needs-port | n/a | no | U-E7-02…05 | @@ -369,16 +369,45 @@ Every issue in the #13750 tree maps to a unit or is closed out as a tracker. | N15, N16 scenario sets | U-L4-01 | | N17 preview release train | U-E3-10 | -## Open items for the user +## Open questions -These items affect unit gates or scope. They need a user decision before the planner fixes Part B. +Questions are recorded where they become actionable, not answered up front. Each has a trigger: the unit whose start is the deadline for the answer. The coordinator raises a question with the named owners when its trigger unit is about to start. Nothing listed here blocks work that starts earlier. -| Item | Affected units | Evidence | -|---|---|---| -| FIM as a G3 hard gate, although V1 JetBrains has no FIM | U-E6-08, U-E7-04 | Inventory Part 3, "FIM at V1 JetBrains" | -| Decision 7 and default-on experimental flags (background subagents, shared agent board): treated as gating here | U-E2-07, U-E2-24 | `packages/opencode/src/effect/runtime-flags.ts:47-49,61-62` at V1 | -| Branch-flip timing: v1 VS Code builds its bundled CLI from `packages/opencode` in the same tree | U-E8-01 | `packages/kilo-vscode/script/local-bin.ts` at V1 | -| Binary and channel naming (`kilo2` binary and npm `next` per cloud owners, 2026-10-07) | U-E3-12, U-E10-01, U-E10-03 | `technical-notes/baseline/cloud-agent-contracts.md:40-47` | -| Gate attachment for cloud agents (independent pin `@kilocode/cli@7.8.1`) and Anaconda Desktop | U-E10-01, U-E10-02 | `cloud-agent-consumers.md:48-63`; #14429 | -| Product disposition of upstream desktop/web app and pairing | U-E9-14, U-E9-15, U-E4-05 | Inventory Part 4 (F-V2-037, F-V2-049) | -| Snapshot SHA: record `d99662338` alongside `f7115470` in `migration-context.md` | U-L2-01 | `marker-audit/v1-kilo-marker-port-assessment.md:9` | +### Deferred to the VS Code track (G2) + +| ID | Question | Trigger | Owners to involve | Evidence | +|---|---|---|---|---| +| Q-G2-01 | When does `main` flip to v2? The v1 VS Code build compiles its bundled CLI from `packages/opencode` in the same tree. Options: flip only after G2 and G3, or build v1 IDE releases from the `v1` branch after the flip | Before any change to `main`; at the latest when E6 starts (U-E6-01) | VS Code owners, release owner | `packages/kilo-vscode/script/local-bin.ts` at V1 | +| Q-G2-02 | Capability handshake shape (what the extension reads to detect a v2 backend), shared with JetBrains | U-E6-02 (server side U-E1-08 can ship a minimal version first) | VS Code and JetBrains owners | `packages/kilo-vscode/src/features.ts` already probes `experimental.capabilities.get` | +| Q-G2-03 | First-launch import UX in the extension, and mapping of v1 extension `globalState` and secrets | U-E6-02 | VS Code owners | Old plan N9 (`kilo-opencode-v2-execution-plan.md:270-274`) | +| Q-G2-04 | Restricted Mode key conflict in the preview, and desktop verification of the Stop fix | U-E6-03 | VS Code owners | Old plan line 25; working log 2026-09-08 | +| Q-G2-05 | Message-deletion semantics on event-sourced v2 sessions (delete vs revert vs tombstone) | U-E2-05 | VS Code owners, core owner | #14380; `v2-v1-session-diffs.md` | +| Q-G2-06 | Agent Manager control-plane contract on v2 (worktree routing, ask/reply/reject, multi-project) | U-E2-27 | VS Code owners | #14391; V1 `kilocode/agent-manager/*` | +| Q-G2-07 | Confirm post-cutover for VS Code experimental settings: browser automation, conversation prompt history, Claude migration, native notebook tools | E6 kickoff | VS Code owners | Inventory Part 3, F-VSC-025…028 | +| Q-G2-08 | Name and channel of the v2 extension build that bundles the v2 binary | U-E6-19 | VS Code owners, release owner | Depends on Q-DIST-01 | + +### Deferred to the JetBrains track (G3) + +| ID | Question | Trigger | Owners to involve | Evidence | +|---|---|---|---|---| +| Q-G3-01 | Is FIM/next-edit a G3 hard gate (Decision 7), given that V1 JetBrains has no FIM producer or consumer, only migrated autocomplete settings? | U-E7-01 | JetBrains owners | Inventory Part 3, "FIM at V1 JetBrains" | +| Q-G3-02 | Kilo plugin RPC endpoints (`/api/rpc/:rpcID/:method`) may not appear in the assembled OpenAPI that the JetBrains Kotlin client is generated from. How does the plugin reach Kilo-specific operations? | U-E7-01 | JetBrains owners, core owner | `packages/kilo-jetbrains/backend/build.gradle.kts:11-21`; BASE `protocol/src/groups/rpc` (UNVERIFIED coverage in OpenAPI) | +| Q-G3-03 | Which v2 CLI release the plugin pins, and how the `jetbrains/v*` release line maps to it | U-E7-02 | JetBrains owners, release owner | `kilo.cli.pinned`, `packages/kilo-jetbrains/package.json` | +| Q-G3-04 | Supported IDE products and versions, and split-mode support, for validation | U-E7-06 | JetBrains owners | #14424 | +| Q-G3-05 | Is the legacy v5 plugin migration still needed on v2? | U-E7-05 | JetBrains owners | `backend/.../migration/*` at V1 | + +### Other deferred questions + +| ID | Question | Trigger | Owners to involve | Evidence | +|---|---|---|---|---| +| Q-DIST-01 | Binary name and npm channel for v2 (the cloud owners chose a `kilo2` binary on npm `next` on 2026-10-07), and how v1 IDEs are kept from resolving the v2 binary | Before the first distributed v2 build: U-E3-10 (preview train), which also gates U-E3-12 | Release owner, cloud owners | `technical-notes/baseline/cloud-agent-contracts.md:40-47` | +| Q-EXT-01 | Gate attachment for cloud agents (independent pin `@kilocode/cli@7.8.1`) | U-E10-01 | Cloud owners | `cloud-agent-consumers.md:48-63` | +| Q-EXT-02 | Anaconda Desktop integration mode and the surface it gates | U-E10-02 (#14429) | Anaconda Desktop owners | #14429–#14432 | +| Q-PROD-01 | Ship, hide or defer upstream's desktop/web app and pairing | U-E9-15 (U-E4-05 for pairing) | Product owner | Inventory Part 4 (F-V2-037, F-V2-049) | + +### Raised with the user now (affect G1 scope) + +| ID | Question | Affected units | Evidence | +|---|---|---|---| +| Q-NOW-01 | Decision 7 and features that are named experimental but on by default (background subagents, shared agent board). This register treats them as gating | U-E2-07, U-E2-24 | `packages/opencode/src/effect/runtime-flags.ts:47-49,61-62` at V1 | +| Q-NOW-02 | Record `d99662338` (assessment review point) alongside `f7115470` in `migration-context.md`. No unit verdict depends on it; it only sets where the first L2 sweep starts | U-L2-01 | `marker-audit/v1-kilo-marker-port-assessment.md:9`; register gap table | From 160ce879e08fbec87b21fec5dad2a103095334b0 Mon Sep 17 00:00:00 2001 From: Fabio Pliger Date: Sun, 11 Oct 2026 21:24:14 -0500 Subject: [PATCH 05/18] docs(migration): apply decisions on experimental flags, snapshot note and deferred channel naming --- .../discovery/v2-feature-inventory.md | 2 +- migration-tracking/discovery/v2-unit-register.md | 12 ++++++------ migration-tracking/plans/migration-context.md | 15 ++++++++++++--- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/migration-tracking/discovery/v2-feature-inventory.md b/migration-tracking/discovery/v2-feature-inventory.md index 4ec3713aec2c..bad580da4932 100644 --- a/migration-tracking/discovery/v2-feature-inventory.md +++ b/migration-tracking/discovery/v2-feature-inventory.md @@ -491,7 +491,7 @@ Checked on scope (S), errors (E), cancellation (C), persistence (P) and presenta | Flag on V1 | Default | Applied verdict | Note | |---|---|---|---| -| `KILO_EXPERIMENTAL_BACKGROUND_SUBAGENTS` | **on** (kill switch, `packages/opencode/src/effect/runtime-flags.ts:47-49`) | gating (U-E2-07) | Literal Decision 7 would defer a default-on feature; see Q-NOW-01 in the register | +| `KILO_EXPERIMENTAL_BACKGROUND_SUBAGENTS` | **on** (kill switch, `packages/opencode/src/effect/runtime-flags.ts:47-49`) | gating (U-E2-07) | Decision 7 applies only to default-off flags (user decision 2026-10-12) | | `KILO_EXPERIMENTAL_SHARED_AGENT_BOARD` | **on** (kill switch, `runtime-flags.ts:61-62`) | gating (U-E2-24) | Same | | `experimental.openTelemetry` | on (opt-out) | gating (U-E2-21) | Same | | `KILO_EXPERIMENTAL_SCOUT`, `_CONTEXT_TOOLS`, `_CODE_MODE`, `_MCP_APPS`, `_SESSION_SWITCHER`, `_EVENT_SYSTEM`, `_CLAUDE_MIGRATION`, `_LSP_TOOL`, `KILO_ENABLE_EXA`, `experimental.native_notebook_tools`, `experimental.image_generation`, `experimental.speech_to_text_*` | off | post-cutover (E11) | `_EVENT_SYSTEM` debug view is not-needed | diff --git a/migration-tracking/discovery/v2-unit-register.md b/migration-tracking/discovery/v2-unit-register.md index 73688d53494f..c1c1ae05d27c 100644 --- a/migration-tracking/discovery/v2-unit-register.md +++ b/migration-tracking/discovery/v2-unit-register.md @@ -400,14 +400,14 @@ Questions are recorded where they become actionable, not answered up front. Each | ID | Question | Trigger | Owners to involve | Evidence | |---|---|---|---|---| -| Q-DIST-01 | Binary name and npm channel for v2 (the cloud owners chose a `kilo2` binary on npm `next` on 2026-10-07), and how v1 IDEs are kept from resolving the v2 binary | Before the first distributed v2 build: U-E3-10 (preview train), which also gates U-E3-12 | Release owner, cloud owners | `technical-notes/baseline/cloud-agent-contracts.md:40-47` | +| Q-DIST-01 | Binary name and npm channel for v2 (the cloud owners proposed a `kilo2` binary on npm `next` on 2026-10-07), and how v1 IDEs are kept from resolving the v2 binary | **Must be decided before cutting any beta or preview release that is distributed** (first one: U-E3-10 preview train); also blocks U-E3-12 and U-E6-19. Recorded in `migration-context.md`, Deferred decisions | Release owner, cloud owners | `technical-notes/baseline/cloud-agent-contracts.md:40-47` | | Q-EXT-01 | Gate attachment for cloud agents (independent pin `@kilocode/cli@7.8.1`) | U-E10-01 | Cloud owners | `cloud-agent-consumers.md:48-63` | | Q-EXT-02 | Anaconda Desktop integration mode and the surface it gates | U-E10-02 (#14429) | Anaconda Desktop owners | #14429–#14432 | | Q-PROD-01 | Ship, hide or defer upstream's desktop/web app and pairing | U-E9-15 (U-E4-05 for pairing) | Product owner | Inventory Part 4 (F-V2-037, F-V2-049) | -### Raised with the user now (affect G1 scope) +### Resolved by the user (2026-10-12) -| ID | Question | Affected units | Evidence | -|---|---|---|---| -| Q-NOW-01 | Decision 7 and features that are named experimental but on by default (background subagents, shared agent board). This register treats them as gating | U-E2-07, U-E2-24 | `packages/opencode/src/effect/runtime-flags.ts:47-49,61-62` at V1 | -| Q-NOW-02 | Record `d99662338` (assessment review point) alongside `f7115470` in `migration-context.md`. No unit verdict depends on it; it only sets where the first L2 sweep starts | U-L2-01 | `marker-audit/v1-kilo-marker-port-assessment.md:9`; register gap table | +| ID | Resolution | Recorded in | +|---|---|---| +| Q-NOW-01 | Decision 7's experimental-flag rule applies only to features that are off by default on v1. Background subagents and the shared agent board are gating (U-E2-07, U-E2-24) | `migration-context.md`, Decision 7 | +| Q-NOW-02 | Note added: the assessment reviewed `d99662338e`; the first L2 sweep starts there | `migration-context.md`, pinned refs | diff --git a/migration-tracking/plans/migration-context.md b/migration-tracking/plans/migration-context.md index 39cdc6e608f4..9939e834a7a0 100644 --- a/migration-tracking/plans/migration-context.md +++ b/migration-tracking/plans/migration-context.md @@ -2,7 +2,7 @@ This file is the single source for the shared facts, decisions and rules of the Kilo v1 → v2 migration. Role prompts (discovery, planner, coordinator) and implementation session prompts point here instead of restating it. When a fact here changes, change it here first. -Status: bootstrap. Last updated 2026-10-11. +Status: bootstrap. Last updated 2026-10-12. ## Branches and pinned refs @@ -14,7 +14,7 @@ Status: bootstrap. Last updated 2026-10-11. | Upstream release tag | `v2.0.26` = `9b4ec5714d481559990db0a816d5dec19541a814` (reference marker only; not on `upstream/v2`, see note) | | Archived old port | tag `kilo-v2-checkpoints-2026-09` = `879b89e89034f0e9f4cfb7fd09a5323ef06f83c9` (former `kilo-v2` tip). Donor code only | | V1 target (behavioral reference) | `origin/main` pinned at `fb7e96eda5409517a05948b9efaf01c53997bf6e` (2026-10-09) | -| V1 assessment snapshot | `f7115470` (2026-08-12): where the existing 816-file source assessment stops | +| V1 assessment snapshot | `f7115470` (2026-08-12): where the existing 816-file source assessment stops. The assessment itself reviewed `d99662338e` (2026-08-10, 800 commits earlier); the first v1 delta sweep starts from `d99662338e` so the gap gets file-level dispositions. No unit verdict depends on the difference (discovery register, gap table) | Note on the base: upstream cuts each release as a separate version-bump commit (`release: vX`) on top of the `v2` tip, tags it, and does not merge it back into `v2`. The tag is therefore one commit off the branch. The base is the tag's parent: release-equivalent code, on `upstream/v2`, and free of the version-bump commit, so later merges of `upstream/v2` stay clean. @@ -36,11 +36,20 @@ Do not re-litigate these without new evidence. Proposed changes go to the user. 4. **Staleness gate.** For each feature, diff the v1 files it touches between the assessment snapshot and the v1 target. Empty delta: the donor is likely faithful. Non-empty: re-derive behavior from v1 and keep only the donor's v2 mapping. 5. **Staged cutover.** G1 CLI/TUI → G2 VS Code → G3 JetBrains, each with its own go/no-go. G2 and G3 are independent client-surface tracks that run in parallel with G1 once their prerequisites land. Until their gate, the IDEs stay on v1: VS Code bundles the CLI from the same `main` build (no version pin); JetBrains pins a CLI version. 6. **Storage.** A separate customer v2 data store. v1 files are never touched while any v1 surface ships. Import is opt-in and re-runnable. Path consolidation only after G3. -7. **Scope.** Post-cutover (non-gating): voice, image generation, team and incremental sharing, legacy TUI extras, and features behind experimental flags on v1 unless explicitly made a gate. KiloClaw is not needed. Hard gates: FIM/next-edit (G2, G3). Cloud agents and Anaconda Desktop gate whichever surface they consume. +7. **Scope.** Post-cutover (non-gating): voice, image generation, team and incremental sharing, legacy TUI extras, and features behind experimental flags on v1 that are off by default, unless explicitly made a gate. Features named experimental but on by default on v1 (for example background subagents and the shared agent board) are in scope like any shipped feature. KiloClaw is not needed. Hard gates: FIM/next-edit (G2, G3). Cloud agents and Anaconda Desktop gate whichever surface they consume. 8. **Gates override P-labels** for surface readiness. P-labels order work globally; gates define what a surface needs to ship. 9. **Plan format.** One file with two parts: Part A high level (epics, gates, critical path, risks), Part B low level (per-epic units, dependencies, acceptance), plus appendices (facts, glossary, links). 10. **Readability.** Separate why, what, when and how-to-verify. Progressive disclosure. No sub-numbered steps. Issue IDs only in Part B and the register. One canonical place per fact. +## Deferred decisions + +Decisions intentionally left open until the work that needs them. Each has a deadline; the coordinator raises it with the user and owners before that point. Detail and evidence: `migration-tracking/discovery/v2-unit-register.md`, "Open questions". + +| Decision | Deadline | +|---|---| +| v2 binary name and distribution channel (npm dist-tag), and how v1 IDEs are kept from resolving the v2 binary. Cloud owners proposed a `kilo2` binary on npm `next` (2026-10-07) | **Before the first beta or preview release that is distributed outside the team** | +| VS Code (G2) and JetBrains (G3) questions, including whether FIM/next-edit gates G3 when JetBrains v1 has no FIM | When the trigger unit listed in the register starts | + ## Working model | Rule | Detail | From 490703e46e7634128215c38901d7fdeca95990ff Mon Sep 17 00:00:00 2001 From: Fabio Pliger Date: Sun, 11 Oct 2026 21:26:06 -0500 Subject: [PATCH 06/18] docs(migration): inline staleness method and mark discovery done in context --- migration-tracking/discovery/v2-unit-register.md | 8 +++++++- migration-tracking/plans/migration-context.md | 5 +++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/migration-tracking/discovery/v2-unit-register.md b/migration-tracking/discovery/v2-unit-register.md index c1c1ae05d27c..ef330fa25be2 100644 --- a/migration-tracking/discovery/v2-unit-register.md +++ b/migration-tracking/discovery/v2-unit-register.md @@ -216,7 +216,13 @@ Shared facts for E7. The plugin is absent from BASE and from the donor (no reuse ## Staleness per area (SNAP `f7115470` → V1 `fb7e96ed`) -Path sets are explicit in the staleness scratch analysis: Kilo-owned paths (`packages/opencode/src/kilocode/**`, `src/kilo-sessions/**`, `packages/kilo-*/**`, `plugin-atomic-chat`) plus shared files that carry `kilocode_change` or were touched by a Kilo commit. Commits are non-merge; `sig` excludes release/chore/docs/test/style/ci. Every changed file in the range is Kilo-owned, marker-bearing or Kilo-touched (0 upstream-only files). +Method (reproducible with `git diff --numstat f7115470 fb7e96ed` and `git log --no-merges --full-history`; each changed file is assigned to exactly one area, first match wins): + +- **Counted paths.** Kilo-owned paths: `packages/opencode/src/kilocode/**`, `packages/opencode/{test,bin}/kilocode/**`, `packages/opencode/src/kilo-sessions/**` (A16), `packages/kilo-*/**`, `packages/plugin-atomic-chat/**`. Also shared files that carry `kilocode_change` at SNAP or V1, appear in the 816-file TSV, or were touched by a Kilo commit. Every changed file in the range falls in one of these (0 upstream-only files). +- **Kilo-owned paths by keyword on the sub-path,** in this order: A28 claw/image/speech → A27 atomic-chat → A26 anaconda → A07 session-export/import/portability/resume/cloud-session → A31 legacy-migration/roo/migrate → A20 cloud → A10 memory → A11 indexing/semantic-search/lancedb/embedding → A12 sandbox → A13 telemetry/feedback → A14 mcp/marketplace → A24 autocomplete/fim/edit/code-action/branch-name/commit-message/enhance-prompt/editor-context → A16 remote/presence/send-file → A17 share → A15 agent-manager/worktree/board/background-process/notebook/pr-link/git-refs → A03 auth/gateway/balance/profile/organization/provider-usage/console/caffeination → A04 provider/model-*/routed-model/system-prompt/catalog → A05 config/migrator/overlay → A06 session/snapshot/task-resume/wakeup/cron/compaction/goal/cost → A08 agent/plan/review/command/skill/rules/workflow/mode → A09 tool/permission/question/bash-hierarchy/suggestion/ts-check/websearch/shell/pty → A32 security/pii/encoding/ignore → A02 daemon/server/instance/parent-watchdog/event/effect/process/bootstrap/project/network/watcher → A01 paths/const/project-id/storage/database/log → A21 installation/bin → A18 cli/acp/help/docs/run → A19 tui/components/plugins/theme/logo → A30 plugin. +- **Package-level rules.** `kilo-vscode` → A28/A26/A31/A24/A15/A14/A13/A12/A11/A16 by feature directory, `webview-ui/**` → A23, build files → A21, else A22. `kilo-jetbrains` → A25. `kilo-docs`, `kilo-web-ui`, `kilo-console` → A29. `kilo-ui`, `kilo-i18n`, `packages/{ui,session-ui,storybook}` → A23. `.github/**`, `script/**`, `nix/**`, `patches/**`, `.changeset/**`, root manifests and `packages/opencode/{package.json,CHANGELOG.md,script/}` → A21. +- **Shared files by directory.** `share/` A17; `acp/`, `cli/` A18; `cli/cmd/tui`, `packages/tui` A19; `session/`, `snapshot/` A06; `provider/` A04; `config/` A05; `mcp/` A14; `tool/`, `permission/`, `question/`, `pty/` A09; `agent/`, `command/`, `skill/` A08; `worktree/` A15; `installation/` A21; `auth/` A03; `storage/`, `database/` A01; `plugin/`, `packages/{sdk,llm,plugin,client}` A30; `server/`, `project/`, `bus/`, `effect/`, events, lsp, filesystem A02. +- **Counting.** Commits are non-merge. `sig` excludes release, chore, docs, test, style and ci commits. Release bumps touching every `packages/kilo-*/package.json` are excluded from `sig`. | Area | Files (A/D/M) | +/− | Kilo commits (sig) | Verdict | What changed | |---|---|---|---|---|---| diff --git a/migration-tracking/plans/migration-context.md b/migration-tracking/plans/migration-context.md index 9939e834a7a0..688b432f8803 100644 --- a/migration-tracking/plans/migration-context.md +++ b/migration-tracking/plans/migration-context.md @@ -103,12 +103,13 @@ These need a repo admin (or account credentials) and do not block work. Track th | `kilo-v2-clean-bootstrap` pushed to origin | Done | | This file | Done | | Branch protection | Deferred (admin task) | -| Baseline health recorded (`bun install`, `bun run check`) | Recommended next | +| Baseline health recorded (`bun install`, `bun run check`) | Done 2026-10-11: `technical-notes/baseline/pinned-v2-baseline.md` | +| Discovery report | Done 2026-10-12: `migration-tracking/discovery/` | ## Reference artifacts - Plans: `migration-tracking/plans/` (previous execution plan, progress plan and working log; reference only until the new plan lands). -- Discovery outputs (to be created): `migration-tracking/discovery/`. +- Discovery outputs: `migration-tracking/discovery/` (feature inventory, epic taxonomy, unit register, doc inventory). - Source parity: `migration-tracking/marker-audit/` (the 816-file assessment and shared-change inventory). - API and session differences: `migration-tracking/technical-notes/baseline/v2-v1-schema-diffs.md`, `migration-tracking/technical-notes/baseline/v2-v1-session-diffs.md`. - Technical notes: `migration-tracking/technical-notes/` and `technical-notes/baseline/`. From c33ad813451aee9b43eda06b60345b27c35ad702 Mon Sep 17 00:00:00 2001 From: Fabio Pliger Date: Sun, 11 Oct 2026 22:34:41 -0500 Subject: [PATCH 07/18] docs(migration): add v2 migration plan and supersede old plans --- migration-tracking/README.md | 73 +- .../github-drafts/issue-13750/README.md | 3 + .../issue-13750/progress-comment.md | 24 - .../issue-13750/shared-source-footprint.md | 3 - .../issue-13750/tracking-policy.md | 21 - .../plans/kilo-opencode-v2-execution-plan.md | 3 + .../plans/kilo-opencode-v2-plan-progress.md | 3 + .../plans/kilo-opencode-v2-working-log.md | 3 + .../plans/kilo-v2-migration-plan.md | 1047 +++++++++++++++++ migration-tracking/plans/migration-context.md | 11 +- .../technical-notes/v2-fork-conventions.md | 3 + .../kilo-opencode-v2-test-plan-runtime.md | 3 + .../kilo-opencode-v2-test-plan-ui.md | 3 + .../test-plans/kilo-opencode-v2-test-plan.md | 3 + 14 files changed, 1100 insertions(+), 103 deletions(-) delete mode 100644 migration-tracking/github-drafts/issue-13750/progress-comment.md delete mode 100644 migration-tracking/github-drafts/issue-13750/shared-source-footprint.md delete mode 100644 migration-tracking/github-drafts/issue-13750/tracking-policy.md create mode 100644 migration-tracking/plans/kilo-v2-migration-plan.md diff --git a/migration-tracking/README.md b/migration-tracking/README.md index d7cc829a6ae8..b423afb02d09 100644 --- a/migration-tracking/README.md +++ b/migration-tracking/README.md @@ -1,60 +1,33 @@ # Kilo v2 migration tracking -Planning, progress, validation, and source-parity documentation for the migration of Kilo onto OpenCode v2. +Planning, evidence and validation material for moving Kilo onto upstream opencode v2 on the `kilo-v2-clean` branch. + +## Start here + +| Need | File | Class | +|---|---|---| +| Shared facts, pinned refs, decisions, working model, conventions | [plans/migration-context.md](plans/migration-context.md) | living | +| Epics, gates, critical path (Part A); units, dependencies, acceptance and status (Part B) | [plans/kilo-v2-migration-plan.md](plans/kilo-v2-migration-plan.md) | living | +| Baseline health of the pinned upstream base | [technical-notes/baseline/pinned-v2-baseline.md](technical-notes/baseline/pinned-v2-baseline.md) | living | +| v1 vs v2 API and session model differences | [v2-v1-schema-diffs.md](technical-notes/baseline/v2-v1-schema-diffs.md), [v2-v1-session-diffs.md](technical-notes/baseline/v2-v1-session-diffs.md) | living | ## Structure ```text migration-tracking/ -├── README.md -├── plans/ # Execution plan, public progress plan and milestone history -├── test-plans/ # Test strategy, runtime checks, and UI scenarios -├── marker-audit/ # Source-change inventory and v1-to-v2 assessment -└── technical-notes/ # Supporting design, parity, and validation material - ├── baseline/ - ├── script/ - └── v2-fork-conventions.md +├── plans/ # context brief and the migration plan (living); superseded plans and working log (archive/evidence) +├── discovery/ # feature inventory, epic taxonomy, unit register, doc inventory (frozen evidence, 2026-10-11) +├── marker-audit/ # 816-file v1 assessment and shared-change inventories (evidence) +├── technical-notes/ # parity notes, contracts, incidents (evidence unless marked living) +├── test-plans/ # scenario plans (stale; rewrite in U-L4-01) +├── github-drafts/ # publication drafts for the tracking issue (archive) +└── assets/ # screenshots of the archived port (archive) ``` -## Plans - -- [Execution plan](plans/kilo-opencode-v2-execution-plan.md): the index for finishing the migration: decisions, sequencing, gates (G1 CLI/TUI, G2 VS Code, G3 JetBrains), and the issue register. It changes only when a decision changes. Start here for how the remaining work is organized. -- [Plan and progress](plans/kilo-opencode-v2-plan-progress.md): the public-facing capability inventory, live status per row, and remaining acceptance requirements. Start here for an overview of current status. Its phase table and status line predate the execution plan until the N7 alignment lands. -- [Working log](plans/kilo-opencode-v2-working-log.md): the migration's milestone history. Under the execution plan, new entries are limited to milestones (upstream tag merges, delta sweeps, gate go/no-go decisions, decision changes, notable incidents) and are written by the coordinator only. Earlier entries hold the detailed implementation history; they describe their own checkpoints and may be superseded by later entries. - -The working log retains internal coordination references and machine-local paths. It needs editorial review before publication; it is not the public progress summary. - -## Test plans - -- [Main test plan](test-plans/kilo-opencode-v2-test-plan.md): overall validation plan. -- [Runtime test plan](test-plans/kilo-opencode-v2-test-plan-runtime.md): runtime and backend checks. -- [UI test plan](test-plans/kilo-opencode-v2-test-plan-ui.md): interface and interactive workflow checks. - -A listed test is a requirement, not proof that it has passed. Read the recorded execution status and its checkpoint before relying on a result. - -## Marker audit - -- [Marker inventory](marker-audit/kilo-override-marker-inventory.md): the historical inventory of Kilo change annotations, captured before marker cleanup. -- [Port assessment](marker-audit/v1-kilo-marker-port-assessment.md): the file-level assessment of v1 behavior, its v2 destination, and whether it is ported, native, partial, still needed, deferred, or unnecessary. -- [Assessment data](marker-audit/v1-kilo-marker-port-assessment.tsv): the corresponding tab-separated data for filtering and analysis. - -Source assessment and runtime acceptance are different measures. Fewer annotations can reflect relocation into Kilo-owned packages, obsolete changes, or behavior not yet ported; the count alone does not establish parity. - -## Technical notes - -This folder preserves the material previously collected under the repository's root `kilocode/` directory: - -- `baseline/`: focused parity assessments, design decisions, validation records, and remaining gaps. -- `script/`: supporting baseline tooling. -- `v2-fork-conventions.md`: ownership and upstream integration conventions. -- Other topic-specific notes, including session sharing. - -Use these notes to understand the reasoning and scope behind progress claims. They are checkpoint-specific supporting material, rather than a second capability checklist. - -## Organization and publication status - -This directory was assembled by copying existing files. The originals under root `plans/` and `kilocode/` remain intact. The originals are excluded from Git; this directory is the tracked migration collection. The copies are not automatically synchronized. Repository instructions point here, while historical links and tooling paths still need review. - -The public progress plan was prepared for external readers. The working log, technical notes, test plans, and audit documents retain their original content and need publication review for internal references, local paths, and outdated statements. Relative links and script paths inherited from the original locations may also need adjustment before the copies become the maintained versions. +## Rules for this collection -When updating this collection, keep decisions and sequencing in the execution plan, capability progress in the public plan, milestones in the working log, per-change evidence in the PR and its issue, test execution status in the test plans, and file-level mapping in the marker audit. Preserve the distinction between implemented behavior, source-reviewed equivalence, and verified end-to-end acceptance. +- One canonical place per fact. Decisions and refs live in `migration-context.md`; sequencing and unit status live in the plan; per-feature evidence lives in `discovery/` and `technical-notes/`. +- Evidence files are frozen: cite them, do not maintain them. Notes measured against the archived port's upstream base must have their seam claims re-checked at the current base before reuse. +- Superseded files carry a banner at the top. The class of every seeded file is recorded in [discovery/doc-inventory.md](discovery/doc-inventory.md). +- A listed test is a requirement, not proof that it passed. A local pass is never recorded as end to end. +- Keep markdown tables compact and do not run prettier on these files. diff --git a/migration-tracking/github-drafts/issue-13750/README.md b/migration-tracking/github-drafts/issue-13750/README.md index 83ba0572b43d..51e03a914ba0 100644 --- a/migration-tracking/github-drafts/issue-13750/README.md +++ b/migration-tracking/github-drafts/issue-13750/README.md @@ -1,5 +1,8 @@ # Issue #13750 publication drafts +> [!NOTE] +> Archive. `progress-comment.md` (unpublished, stale), `tracking-policy.md` (replaced by the tracking rules in `plans/kilo-v2-migration-plan.md` §B0) and `shared-source-footprint.md` (pointer stub) were removed on 2026-10-12; links to them below are historical. See git history for their content. + Local review package, prepared September 10, 2026. The issue body and all nine subissues have been published to GitHub; the progress comment remains unpublished. Existing plans are preserved. ## Review order diff --git a/migration-tracking/github-drafts/issue-13750/progress-comment.md b/migration-tracking/github-drafts/issue-13750/progress-comment.md deleted file mode 100644 index 0bf472b027d9..000000000000 --- a/migration-tracking/github-drafts/issue-13750/progress-comment.md +++ /dev/null @@ -1,24 +0,0 @@ -## Migration checkpoint — September 10, 2026 - -The migration now has an isolated Kilo v2 CLI/runtime and an in-progress port of the **original Kilo VS Code interface**. The original interface builds; session, account, model, settings, memory, generation and terminal adapters have local coverage. Full original-client parity is still incomplete. - -Recent work includes configuration refresh that preserves active sessions and terminal processes, ticket-based terminal connections, and a Plan handoff fix that opens a new Code tab while retaining the Plan tab. - -The source assessment covers all **816 v1 files / 6,128 change-marker occurrences** at the pinned v1 revision: - -| Assessment | Files | -|---|---:| -| Already ported | 65 | -| Native equivalent | 137 | -| Partial | 233 | -| Needs port | 163 | -| Deferred | 68 | -| Not needed | 150 | - -These are source-review dispositions, not test results. - -The next work is original-client contract completion and acceptance, remaining runtime/TUI differences, sharing and remote integration, and distribution/cutover. Sharing still has viewer, ownership and synchronization gates; signing, hosted updates, distribution and cross-platform acceptance remain open. - -The issue body now shows per-phase status and current inventory statuses. The expanded repository plan carries detailed coverage, with source mapping in `migration-tracking/marker-audit/v1-kilo-marker-port-assessment.md`. - -Status reflects recorded local verification, not a fresh full-suite run. The parent issue tracks remaining deliverables and their acceptance criteria. diff --git a/migration-tracking/github-drafts/issue-13750/shared-source-footprint.md b/migration-tracking/github-drafts/issue-13750/shared-source-footprint.md deleted file mode 100644 index f49ff1eb10d8..000000000000 --- a/migration-tracking/github-drafts/issue-13750/shared-source-footprint.md +++ /dev/null @@ -1,3 +0,0 @@ -# Shared source footprint - -The dated measurement is maintained in [v2-shared-source-footprint-2026-09-10.md](../../marker-audit/v2-shared-source-footprint-2026-09-10.md). diff --git a/migration-tracking/github-drafts/issue-13750/tracking-policy.md b/migration-tracking/github-drafts/issue-13750/tracking-policy.md deleted file mode 100644 index 3981d3db6fca..000000000000 --- a/migration-tracking/github-drafts/issue-13750/tracking-policy.md +++ /dev/null @@ -1,21 +0,0 @@ -# Maintaining the migration plan - -Keep the repository plan. It provides versioned detail that can change alongside implementation and tests; the GitHub issue remains the public entry point. - -| Document | Owns | Update when | -|---|---|---| -| `migration-tracking/plans/kilo-opencode-v2-plan-progress.md` | Detailed capability inventory, behavior, destination and acceptance boundaries | A change adds behavior, closes a gap, or changes scope | -| GitHub #13750 body | Architecture, original phases, compact capability status and links to remaining work | A milestone or material status/decision changes | -| Subissues | Independently actionable remaining work, owner, dependencies and acceptance checklist | Work progresses or acceptance changes | -| Issue comments | Dated results, decisions, blockers and links to PRs/test evidence | A meaningful checkpoint occurs | -| `migration-tracking/test-plans/` | Reproducible acceptance procedures and recorded limitations | Verification procedures or results change | -| `migration-tracking/marker-audit/` | Source-to-destination mapping and rationale for removing or retaining patches | A mapped behavior or placement changes | -| `migration-tracking/plans/kilo-opencode-v2-working-log.md` | Historical working record | Append only when useful; do not treat old checkpoints as current status | - -The detailed repository inventory is the reference for capability coverage. The issue summarizes it; do not maintain another expanded inventory in comments. A subissue completion must update affected repository rows and the parent summary, without implying adjacent capabilities are complete. - -For each accepted milestone, update the detailed plan in the implementation PR, include acceptance evidence and outstanding limitations, then summarize the changed rows on GitHub. While a branch is unpublished, identify local-only evidence explicitly. Link milestone evidence to a commit when stable provenance matters. - -Do not make a marker count into a completion score. “Ported”, “native equivalent” and “not needed” need source-backed rationale; behavioral acceptance remains separate. “Deferred” remains outstanding unless an explicit product decision removes it from scope. Refusing unsupported imports is safe import behavior, not parity for the refused feature. - -Retain the working log for traceability, with a clear historical role. Do not copy its internal coordination or obsolete percentages into the public issue. This draft package does not rename or overwrite either existing plan. diff --git a/migration-tracking/plans/kilo-opencode-v2-execution-plan.md b/migration-tracking/plans/kilo-opencode-v2-execution-plan.md index 904d5a083d84..01d772717767 100644 --- a/migration-tracking/plans/kilo-opencode-v2-execution-plan.md +++ b/migration-tracking/plans/kilo-opencode-v2-execution-plan.md @@ -1,5 +1,8 @@ # Kilo v1 → v2 migration plan +> [!WARNING] +> **Superseded by [`kilo-v2-migration-plan.md`](kilo-v2-migration-plan.md) (2026-10-12).** Archived reference only. This plan assumed the old `kilo-v2` branch and release-tag merges, which `migration-context.md` Decisions 1–2 replace. Its decision numbering differs from the current one. Do not act on it. + Finalized 2026-09-30. Base: opus-55. Merged in: upstream-resync rigor, capability handshake and validation rules from deepseek; VS Code blockers, sharing/remote steps, standing rules and metrics from kimi-k3. Status: not started. First actions: - Team sign-off on Decisions 3, 4 and 5. - Owners assigned for the upstream-sync lane (N1/N3) and the delta sweep (N4). diff --git a/migration-tracking/plans/kilo-opencode-v2-plan-progress.md b/migration-tracking/plans/kilo-opencode-v2-plan-progress.md index 5a9a4771f2c2..224366a3419a 100644 --- a/migration-tracking/plans/kilo-opencode-v2-plan-progress.md +++ b/migration-tracking/plans/kilo-opencode-v2-plan-progress.md @@ -1,5 +1,8 @@ # Phased Plan — Port Kilo onto OpenCode v2 +> [!WARNING] +> **Superseded by [`kilo-v2-migration-plan.md`](kilo-v2-migration-plan.md) (2026-10-12).** Archived reference only. Status and percentages below describe the archived `kilo-v2` port, not `kilo-v2-clean`. Live unit status is in Part B of the new plan. + Updated: September 9, 2026 Related issue: Kilo-Org/kilocode#13750 Status: Implementation paused; source parity assessment complete. diff --git a/migration-tracking/plans/kilo-opencode-v2-working-log.md b/migration-tracking/plans/kilo-opencode-v2-working-log.md index e52e10068374..b803fa5b1b98 100644 --- a/migration-tracking/plans/kilo-opencode-v2-working-log.md +++ b/migration-tracking/plans/kilo-opencode-v2-working-log.md @@ -1,5 +1,8 @@ # Phased Plan - Port Kilo onto OpenCode V2 +> [!WARNING] +> **Frozen evidence, superseded by [`kilo-v2-migration-plan.md`](kilo-v2-migration-plan.md) (2026-10-12).** Cite its dated facts; do not maintain it. Its "implementation is paused" instruction and its branch, store and `kilo2` rules describe the archived `kilo-v2` port and no longer apply. `migration-context.md` and the new plan govern. + > [!IMPORTANT] > Local working copy of > [Kilo-Org/kilocode#13750](https://github.com/Kilo-Org/kilocode/issues/13750). diff --git a/migration-tracking/plans/kilo-v2-migration-plan.md b/migration-tracking/plans/kilo-v2-migration-plan.md new file mode 100644 index 000000000000..7dba3ea4ce89 --- /dev/null +++ b/migration-tracking/plans/kilo-v2-migration-plan.md @@ -0,0 +1,1047 @@ +# Kilo v1 → v2 migration plan + +Status: active, not started. Created 2026-10-12 by the planning session on `kilo-v2-clean-bootstrap`. Single writer per change: the coordinator, through micro-PRs (`migration-context.md`, Working model). + +This file is the living index of the migration: epics, gates and sequencing (Part A), units, dependencies, acceptance and status (Part B). It replaces the previous execution plan and progress plan. Shared facts, refs, decisions and rules live in [`migration-context.md`](migration-context.md); per-feature evidence lives in the frozen discovery report under [`../discovery/`](../discovery/). This plan links to both and does not restate them. + +| Reader | Read | +|---|---| +| Leadership | Part A, then stop | +| Coordinator and implementers | Part A for context, Part B for the unit you own, the register row for its evidence | +| Anyone checking a fact (SHA, path, decision text) | Appendix A and the files it links | + +--- + +# Part A. High level + +## A1. Objective + +Ship Kilo on the upstream v2 runtime, surface by surface, without breaking a v1 user. The target feature set is upstream v2 at the pinned base plus every Kilo v1 behavior at the pinned v1 target, minus what Decision 7 marks post-cutover or not-needed: feature parity, not code parity. The terminal product (CLI/TUI) ships first at G1; VS Code (G2) and JetBrains (G3) follow on their own go/no-go, and until then they keep running on v1 against untouched v1 data. The work ends when `kilo-v2-clean` becomes `main` and v1 moves to a time-limited maintenance branch. + +## A2. Decisions in force + +The ten decisions in [`migration-context.md` §Decisions](migration-context.md#decisions-authoritative) are authoritative. The plan applies them as follows; it does not change any of them. + +| Decision | How this plan applies it | +|---|---| +| D1 New base | Every unit lands on `kilo-v2-clean` by PR. Upstream arrives only through the L1 lane, pinned by SHA | +| D2 Freeze | The archived port is read through its tag; nothing merges from it | +| D3 Donor rules | Each Part B row states donor reuse for mechanism only. Behavior acceptance is always written against v1 | +| D4 Staleness gate | Each Part B row carries a staleness verdict. Non-EMPTY means behavior is re-derived from v1 | +| D5 Staged cutover | Three gates, two parallel IDE tracks (A6) | +| D6 Storage | Store isolation is an E1 exit criterion; import is E3; consolidation is E8 after G3 | +| D7 Scope | Non-gating scope is isolated in E11; the hard FIM gate sits in E6 | +| D8 Gates over P-labels | Gate tags in Part B decide surface readiness; P-labels only order work inside a gate | +| D9 Plan format | This file | +| D10 Readability | Part A carries no issue IDs; facts are linked, not copied | + +Items that need user sign-off are listed in A8. None of them changes a decision. + +## A3. Approach + +**Clean base.** `kilo-v2-clean` starts as pristine upstream and contains no Kilo package yet. Kilo is re-landed onto it in small units, each one a session, a branch and a PR, merged by the user. Nothing from the old port branch is merged. + +**Re-land by unit.** Every Kilo v1 behavior and every upstream-only surface has exactly one unit (Part B). A unit is done only with code, tests, acceptance evidence recorded with a SHA, its Part B status updated, and docs. A local pass is never recorded as end to end. + +**Donor = mechanism, v1 = behavior.** The archived port shows how Kilo features attach to v2: host overrides, plugin and RPC registration, schema definitions, backend adapters. Those seams barely moved upstream, so that mechanism is reusable. Its behavior is a snapshot from August and its patches on TUI, theme, system-prompt and MCP files sit on code that upstream rewrote or deleted. Behavior therefore always comes from current v1. + +**Staleness gate.** Before a unit starts, its v1 files are diffed between the assessment snapshot and the v1 target. EMPTY: the donor is likely faithful. Anything else: re-derive behavior from v1 and keep only the donor's v2 mapping. Discovery has already measured this per area; 80 of 137 units are MATERIAL, so most work is re-derivation, not copying. + +**Lanes keep the base honest.** Five continuous lanes run beside the epics: upstream sync (L1), v1 delta sweep (L2), shared-patch ratchet (L3), acceptance (L4) and docs (L5). They stop the two moving targets (upstream v2 and v1 `main`) from silently invalidating finished units. + +**Sequencing, not dates.** No owner is assigned and no effort estimate exists. The plan orders work by dependency and external lead time. Dates attach when owners do. + +## A4. Program phase map + +```mermaid +flowchart LR + P0["Phase 0
    E0 Foundation"] --> P1["Phase 1
    E1 Core and identity"] + P1 --> P2["Phase 2
    G1 build-out
    E2 E9 E3 E4 E5 E10"] + P2 --> P3["Phase 3
    G1 hardening
    preview, acceptance, canary"] + P3 --> G1{{"G1 CLI/TUI"}} + P1 --> T2["VS Code track
    E6"] + T2 --> G2{{"G2 VS Code"}} + P1 --> T3["JetBrains track
    E7"] + T3 --> G3{{"G3 JetBrains"}} + G1 --> R["Phase 4
    E8 Retirement"] + G2 --> R + G3 --> R + R --> B["E11 Post-cutover backlog"] + L["Lanes L1-L5
    continuous"] -.-> P2 + L -.-> T2 + L -.-> T3 +``` + +| Phase or track | Epics | Starts when | Ends when | +|---|---|---|---| +| Phase 0 Foundation | E0; lanes L1–L5 start | Now | E0 exit criteria met | +| Phase 1 Core and identity | E1 | Host and RPC scaffolding exist (E0) | E1 exit criteria met | +| Phase 2 G1 build-out | E2, E9, E3, E4, E5, E10 (G1 units) | Each unit's dependencies land; many start during Phase 1 | Every G1 unit is done | +| Phase 3 G1 hardening | Preview train, G1 acceptance run, canary | Packaging and the distribution decision are ready | G1 go/no-go | +| VS Code track | E6 plus the G2 units of E2 | RPC scaffolding and the server extension surface exist; the FIM producer starts early | G2 go/no-go | +| JetBrains track | E7 | The contract inventory can start any time; the plugin needs the build pipeline | G3 go/no-go | +| Phase 4 Retirement | E8 | G3 go | Branch flip, store consolidation, v1 end of life | +| Backlog | E11 | Any time after its gate surface ships | Every item has a recorded decision | + +## A5. Epic map + +Epic IDs follow discovery so register rows stay traceable; they are presented in program order. Exit criteria below are canonical from now on. + +### E0 Foundation (pre-gate) + +- **Outcome:** a Kilo host, a Kilo plugin/RPC pattern, a Kilo CI signal and v1 reference fixtures exist on the clean base, and no upstream workflow can publish from the fork. +- **Why:** the base has no Kilo package at all. Every other epic needs these first. +- **Exit criteria:** the host starts an isolated server through public seams only (service smoke test); one sample Kilo RPC round-trips through the generated client; a Kilo-owned workflow runs `bun run check` and package tests on PRs to `kilo-v2-clean`, compared against the pinned baseline; every upstream publish or deploy workflow is disabled or guarded; Kilo `AGENTS.md` and fork conventions name the current base and branch rules; v1 reference fixtures for offline wait, overflow, slow snapshot and snapshot retention replay on the base. +- **Gate:** blocks everything. +- **Depends on:** the admin task for Actions permissions (CI lane only). +- **Top risks:** upstream's test workflow runs on every pull request, so it will run on fork PRs before the Kilo lane exists; branch protection is convention-only until an admin acts. + +### E1 Core and identity (G1; prerequisite for G2 and G3) + +- **Outcome:** v2 runs as Kilo: product identity, a separate customer store, Gateway login, the Kilo catalog, prompt selectors, usage accounting, the Kilo config layer and the Kilo server extension surface. +- **Why:** every surface needs identity, auth, models and config before any feature can be accepted. +- **Exit criteria:** v1 and v2 run side by side and a v2 boot never opens a v1 file (filesystem-access test on a seeded v1 store); device login, org switch and logout pass against the deployed Gateway; Kilo models are selectable and requests carry Kilo headers; all eight prompt selectors resolve on base seams; every Kilo config key folds or is refused with a diagnostic; every shared-file patch added here is counted by L3 with its missing seam. +- **Gate:** G1, and consumed by G2 and G3. +- **Depends on:** E0. +- **Top risks:** product identity is hard-coded in upstream shared files, so shared patches grow here first; the system-prompt seam the donor used was deleted upstream; Gateway acceptance needs the deployed service. + +### E2 Runtime behavior (G1; three units G2) + +- **Outcome:** v1 runtime policy runs on v2 seams: session policy, agents and plan, commands and skills, Kilo tools and permissions, the memory, indexing, sandbox and telemetry engines, MCP and marketplace, agent coordination, generation endpoints. +- **Why:** this is the behavior users notice, and the IDE tracks consume it through public contracts. +- **Exit criteria:** every G1 unit passes acceptance against the E0 fixtures where one exists; every native-equivalence claim has a test covering scope, errors, cancellation, persistence and presentation; the G2 units expose public contracts that the generated client consumes; no client package imports Core or Server internals. +- **Gate:** G1 (message deletion, Agent Manager control plane and generation endpoints are G2). +- **Depends on:** E0; parts of E1 (config, catalog, identity). +- **Top risks:** nearly every area is MATERIAL or NEW, so donor reuse is low; agent coordination alone grew by about 55k lines on v1; this is the largest epic (30 units). + +### E9 CLI/TUI surface (G1) + +- **Outcome:** the terminal product: daemon UX, the Kilo command set, headless run, ACP, the TUI shell, slash commands, home and sidebar plugins, picker, notifications, preferences, `kilo cloud`, and a recorded disposition for each upstream-only surface. +- **Why:** G1 ships the terminal product, which needs presentation and commands, not just runtime parity. +- **Exit criteria:** every G1 CLI/TUI scenario passes on an isolated real host, recorded with SHA; `kilo --help --all` lists the v1 command set or a removal note per missing command; each upstream-only surface (desktop, web app, GUI extensions, pairing) has a recorded user decision that is implemented. +- **Gate:** G1. +- **Depends on:** E1; E2 for the behavior each screen presents. +- **Top risks:** the upstream TUI churns heavily and the donor's TUI patches sit on files that were largely rewritten; several units touch the same TUI files and must run in series. + +### E3 Import and distribution (G1) + +- **Outcome:** a signed v2 binary that installs beside v1, updates and rolls back, and imports v1 data opt-in and re-runnably without touching v1 files; a preview train and a canary runbook. +- **Why:** customers need a safe install and their history (D6); the first v2 release must not be G1 itself. +- **Exit criteria:** importing a fixture store from each supported v1 release leaves the original byte-for-byte and a second run imports only new sessions; signed artifacts for every v1 platform install beside v1 and update and roll back on real artifacts; a v1 IDE build cannot resolve the v2 binary through PATH, global install or dist-tag; at least one preview release has shipped; the G1 canary runbook has been exercised. +- **Gate:** G1. +- **Depends on:** E0 (CI lane), E1 (identity, store, auth, config); the binary-and-channel decision before the first distributed preview. +- **Top risks:** copy-then-migrate import is not proven lossless; signing and notarization have external lead time and need account credentials; the binary-and-channel decision is still open. + +### E4 Remote sessions (G1) + +- **Outcome:** `kilo remote` over the Kilo relay: wire protocol, transcript and files, suggestions, detach and heartbeat, metadata and capabilities, presence host registration, PR linking. +- **Why:** remote is a shipped v1 CLI feature. +- **Exit criteria:** local contract tests pass for every relay message; deployed relay acceptance is recorded with SHA or explicitly waived by the user; unsupported capabilities are not advertised. +- **Gate:** G1. +- **Depends on:** E1 Gateway auth; the suggest tool from E2. +- **Top risks:** deployed acceptance depends on the cloud relay; upstream's pairing and tunnel model must coexist with the Kilo relay. + +### E5 Sharing (G1) + +- **Outcome:** individual share, unshare and fork-from-share, a public viewer that renders v2 messages, fail-closed team upload, and scrubbed background session ingest. +- **Why:** individual sharing and the viewer are shipped v1 CLI features; team and incremental sharing are post-cutover (E11). +- **Exit criteria:** share, unshare and fork-from-share round-trip against the deployed backend, recorded with SHA; the public viewer renders self-contained v2 messages; team upload is refused with a clear message; session ingest uploads scrubbed v2 sessions and opt-out works. +- **Gate:** G1. +- **Depends on:** E1 Gateway auth; a change in the cloud repository for the viewer. +- **Top risks:** the viewer lives in another repository with another owner; the export is not redacted, so public sharing needs explicit consent. + +### E10 External consumers (gate set per consumer) + +- **Outcome:** each product outside this repository that consumes the Kilo CLI has a disposition, an owner, a gate and a passing validation. +- **Why:** consumers break when the CLI changes package, binary or protocol, and Decision 7 attaches them to whichever surface they consume. +- **Exit criteria:** every consumer has a recorded disposition (adapt, repackage, no change), owner and gate; every gating consumer passes validation before its gate's go/no-go; no floating `@latest` consumer breaks at the dist-tag switch. +- **Gate:** cloud agents and other CLI consumers provisionally G1 (sign-off pending); Anaconda Desktop set by its discovery. +- **Depends on:** the binary-and-channel decision; import; the Gateway provider. +- **Top risks:** floating consumers silently pick up v2 at the dist-tag switch; Anaconda Desktop's integration mode is still unknown. + +### E6 VS Code track (G2) + +- **Outcome:** the original Kilo VS Code extension UI, re-copied from current v1, running on the v2 backend, including FIM and next-edit. +- **Why:** VS Code stays on v1 until G2. The user decided the original UI must be preserved (working log, 2026-09-08). +- **Exit criteria:** the extension typecheck is a real check with no skip shim; every G2 scenario passes on a real VS Code host, recorded with SHA; the extension never mixes v1 and v2 routes in one session and detects the backend through the capability handshake; FIM and next-edit work end to end; rollback to the previous extension works on untouched v1 files. +- **Gate:** G2. +- **Depends on:** E0 RPC scaffolding, E1 server surface, the G2 units of E2, E3 import and build pipeline. +- **Top risks:** size (the host and webview areas together moved about 1,070 files on v1 since the snapshot); FIM is the largest single IDE gate; the timing of the branch flip constrains how v1 VS Code releases keep building. + +### E7 JetBrains track (G3) + +- **Outcome:** the JetBrains plugin re-landed on the clean base, talking to v2 only through a client generated from the assembled contract. +- **Why:** JetBrains pins a v1 CLI until G3; the plugin is absent from both the base and the donor. +- **Exit criteria:** the plugin builds from `kilo-v2-clean` and uses only the generated client; every G3 scenario passes in each supported IDE, recorded with SHA; incompatible CLIs are refused with a clear message. +- **Gate:** G3. +- **Depends on:** E0 RPC scaffolding, E2 control-plane and generation contracts, E3 build pipeline. +- **Top risks:** no donor at all; Kilo plugin RPC endpoints may not appear in the OpenAPI the Kotlin client is generated from; whether FIM gates G3 is still open. + +### E8 Retirement (post-G3) + +- **Outcome:** `kilo-v2-clean` is `main`, v1 lives on a maintenance branch that still builds and releases, storage paths are consolidated once, and every deployment from `main` has an owner. +- **Why:** the end state in `migration-context.md`. +- **Exit criteria:** every workflow and deployment that builds from `main` has a v2-side owner or is retired before the flip; the v1 maintenance branch builds and releases; storage consolidation runs once, with a backup, and is never repeated; the v1 end-of-life policy is published. +- **Gate:** after G3. +- **Depends on:** the G3 go/no-go and the flip-timing decision. +- **Top risks:** v1 VS Code builds its bundled CLI from the same tree, so a premature flip breaks v1 IDE releases; consolidation has no reverse migration. + +### E11 Post-cutover backlog (non-gating) + +- **Outcome:** every Decision 7 non-gating item (voice, image generation, team and incremental sharing, legacy TUI extras, default-off experimental features, KiloClaw) has a recorded ship, drop or re-scope decision. +- **Why:** keeping these out of gate epics keeps gate exits crisp. +- **Exit criteria:** every item has a recorded user decision; nothing here blocks a gate. +- **Gate:** none. +- **Depends on:** the surface it extends. +- **Top risks:** scope creep back into gate epics. + +### L Cross-cutting lanes (continuous) + +| Lane | Steady state | +|---|---| +| L1 Upstream sync | Each upstream merge pinned by SHA, baseline re-run, upstream lag recorded; no sync window missed without a note | +| L2 v1 delta sweep | Zero unclassified Kilo-owned or marked v1 files at each gate; staleness re-run for touched units | +| L3 Shared-patch ratchet | Marked shared files, client→Core/Server imports and Kilo imports of upstream internals at or below baseline; each increase states its seam | +| L4 Acceptance | Gate-tagged scenarios with v1-derived expectations; one recorded run per gate | +| L5 Docs | kilo.ai/docs describes the shipped surface at each gate | + +## A6. Gate map and critical path + +```mermaid +flowchart LR + subgraph Spine["Runtime spine"] + H["E0 host and RPC"] --> AU["E1 Gateway auth"] --> CA["E1 catalog"] --> UI["E1 usage and E9 sidebar"] + H --> SE["E2 session core"] --> CO["E2 subagents, board, wakeups"] + FX["E0 v1 fixtures"] --> SP["E2 session policy"] + end + subgraph Ship["Distribution spine"] + CI["E0 CI lane"] --> BP["E3 build pipeline"] --> PT["E3 preview train"] --> CN["E3 G1 canary"] + DQ["Binary and channel decision"] --> PT + SG["E3 signing (external lead time)"] -.-> CN + end + subgraph Ext["External-gated"] + RL["E4 deployed relay"] + SH["E5 deployed share and viewer"] + end + UI --> ACC["L4 G1 acceptance run"] + CO --> ACC + SP --> ACC + RL --> ACC + SH --> ACC + ACC --> CN + CN --> G1{{"G1"}} + H --> FIM["E6 FIM producer (start early)"] --> V2["E6 extension on v2"] --> A2["L4 G2 run"] --> G2{{"G2"}} + BP --> JB["E7 plugin re-land"] --> A3["L4 G3 run"] --> G3{{"G3"}} +``` + +| Chain | Why it is critical | Mitigation | +|---|---|---| +| Host → Gateway auth → catalog → usage → sidebar → G1 run | Deepest dependency chain to G1; every screen needs auth and models | Start E1 auth and catalog as soon as RPC scaffolding lands; run E2 units that need only the host in parallel | +| CI lane → build pipeline → preview train → canary → G1 | Needs the binary-and-channel decision and external signing credentials | Raise the distribution decision now; start signing early | +| Deployed relay and share verification → G1 run | Depends on cloud services and another repository's owner | Book cloud-owner time at E4/E5 start; the user may waive with a recorded reason | +| FIM producer → VS Code integration → G2 | Largest single IDE gate | Start the producer during Phase 1, in parallel with G1 work | + +The critical path is structural (dependency depth plus external lead time). It is UNVERIFIED against durations because no estimates exist. + +What must be true at each gate: + +| Condition | G1 CLI/TUI | G2 VS Code | G3 JetBrains | +|---|---|---|---| +| Units | All G1 units done (82) | All G2 units done (26), plus the shared-runtime units they consume accepted | All G3 units done (9), plus the contracts they consume | +| Acceptance | L4 G1 run passed on an isolated real host | L4 G2 run passed on a real VS Code host | L4 G3 run passed in each supported IDE | +| External | Gateway, relay and share verified with SHA, or waived by the user | Gateway login from the extension verified | As G2, from the plugin | +| Consumers | Every E10 consumer attached to G1 validated | Consumers attached to G2 validated | Consumers attached to G3 validated | +| Drift | L2 sweep run, zero unclassified files; L3 at or below baseline; upstream lag recorded | Same, re-run | Same, re-run | +| Release | At least one preview release shipped; canary runbook exercised | VSIX with the v2 binary; G2 canary and rollback runbook exercised | Plugin pinned to a v2 CLI release; G3 runbook exercised | +| Decisions | Binary and channel decided | Flip timing and handshake shape decided | FIM gate question and plugin RPC access decided | +| Rollback | v1 CLI on untouched v1 files | Previous extension on untouched v1 files | Pinned v1 CLI on untouched v1 files | +| Docs | L5 describes the CLI/TUI surface | L5 describes VS Code | L5 describes JetBrains | + +G2 and G3 do not wait for the G1 go/no-go (D5). They do need the shared-runtime units they consume to be accepted. + +## A7. Program risks + +| Risk | Effect | Mitigation | Owner | +|---|---|---|---| +| Upstream moves about 1,000 commits every few weeks; it was already 31 commits past the base at capture | Finished units drift; large merges late in a gate | L1 merges on a cadence, pinned by SHA, with a baseline re-run each time | L1 | +| v1 `main` releases 4–5 times a week | Staleness verdicts age; behavior lands after its unit is done | L2 sweeps before each gate and on a cadence; touched units re-open | L2 | +| No owner is assigned to any epic, lane or issue | Nothing starts; no dates | Assign lane owners first (L1, L2), then E0 and E1 | User | +| Donor seam claims were measured against the old upstream base | A "reusable" seam may be gone | Every donor reuse re-checks its seam at the base inside its unit | Each unit | +| Split history: v2 CLI and v1 IDEs keep separate stores from G1 to G3 | User confusion; support load | Opt-in, re-runnable import; documented split; consolidation only after G3 | E3, L5 | +| A v1 IDE resolves the v2 binary | v1 users silently on v2 | Binary and channel decision; isolation test in E3 | E3 | +| First-release risk | G1 becomes the first real v2 release | Preview train before G1 | E3 | +| Shared-patch growth on upstream files | Expensive upstream merges | L3 ratchet; every patch states its missing seam | L3 | +| External services and repositories (Gateway, relay, share backend, viewer, cloud agents) | Gate acceptance blocked outside the team | Book owners at unit start; waivers only by the user, recorded | E4, E5, E10 | +| Import not proven lossless | Data loss on first import | Re-run the schema audit at the current v1 target and base; byte-identical originals test | E3 | + +## A8. Metrics rollup + +Counts at plan creation. Live values are tracked in Part B status cells. + +| Epic | Units | Pre-gate | G1 | G2 | G3 | Other | Need a new issue | +|---|---|---|---|---|---|---|---| +| E0 Foundation | 5 | 5 | | | | | 5 | +| E1 Core and identity | 8 | | 8 | | | | 8 | +| E2 Runtime behavior | 30 | | 27 | 3 | | | 23 | +| E9 CLI/TUI surface | 15 | | 15 | | | | 11 | +| E3 Import and distribution | 13 | | 13 | | | | 5 | +| E4 Remote sessions | 8 | | 8 | | | | 1 | +| E5 Sharing | 6 | | 6 | | | | 1 | +| E10 External consumers | 3 | | 2 | | | 1 TBD | 1 | +| E6 VS Code track | 21 | | | 21 | | | 6 | +| E7 JetBrains track | 7 | | | | 7 | | 2 | +| E8 Retirement | 4 | | | | | 4 post-G3 | 4 | +| E11 Post-cutover backlog | 6 | | | | | 6 non-gating | 1 | +| L Lanes | 11 | | 3 | 2 | 2 | 4 continuous | 10 | +| **Total** | **137** | **5** | **82** | **26** | **9** | **15** | **78** | + +| Staleness (D4) | Units | Donor reuse (D3) | Units | +|---|---|---|---| +| EMPTY | 8 | yes | 28 | +| SMALL | 12 | partial | 41 | +| MATERIAL (incl. MATERIAL + NEW) | 80 | no | 68 | +| NEW (post-snapshot only) | 9 | | | +| n/a (process, decision or packaging) | 28 | | | + +Metrics the coordinator reports at each gate review: + +| Metric | Definition | Source | +|---|---|---| +| Gate readiness | Done units / units tagged for the gate | Part B status | +| Open work per gate | Units not done, by epic | Part B status | +| Upstream lag | Commits on `upstream/v2` past the last merged pin | L1 | +| Unassessed v1 changes | Kilo-owned or marked v1 files changed past the assessment cutoff without a disposition | L2 | +| Shared-file patches | Marked shared files; client→Core/Server imports; Kilo imports of upstream internals | L3 | +| Units without an issue | Part B rows marked `new` | Part B | +| Canary health | Install, update, rollback and crash signals from the preview train and canary | E3 | + +## A9. Deferred decisions and sign-offs + +Deferred decisions and their deadlines are canonical in [`migration-context.md` §Deferred decisions](migration-context.md#deferred-decisions); the questions with triggers are in the [register's open questions](../discovery/v2-unit-register.md#open-questions). The coordinator raises each one before its trigger unit starts. + +| Decision | Deadline (trigger) | Blocks | +|---|---|---| +| Binary name, npm channel, v1-IDE isolation (Q-DIST-01) | Before the first preview distributed outside the team | Preview train, VSIX naming, floating consumers | +| Flip timing versus v1 IDE builds (Q-G2-01) | Before any change to `main`; at the latest when E6 starts | E6, E8 | +| Capability handshake shape (Q-G2-02) | VS Code connection unit | E6, E7 | +| FIM as a G3 gate (Q-G3-01) and plugin RPC access (Q-G3-02) | JetBrains contract inventory | E7 | +| Cloud-agent gate attachment (Q-EXT-01) | Cloud-agent unit | G1 scope | +| Anaconda Desktop integration mode and gate (Q-EXT-02) | Anaconda unit | Gate scope | +| Desktop, web app and pairing disposition (Q-PROD-01) | Upstream-surface disposition unit | E9 | +| Other G2 and G3 questions (Q-G2-03…08, Q-G3-03…05) | Their trigger units in the register | Their units | + +Added by this plan (canonical here): + +| ID | Question | Trigger | Owners | +|---|---|---|---| +| Q-HOST-01 | Host package shape on the base: a Kilo entry package that composes upstream packages through public seams (donor shape), or a thin wrapper over `@opencode/cli`. The donor chose its own package because upstream `packages/cli` exported no wrap surface; re-check at the base | U-E0-01 | Core owner | +| Q-TEL-01 | Telemetry consent on v2: v1 ships PostHog on by default; the donor chose an opt-in, allowlisted OTLP exporter as a privacy decision (`technical-notes/baseline/telemetry-v2-overlap.md`). Which default ships at G1? | U-E2-21 | Product owner, privacy | + +Sign-offs this plan asks for: the unit splits listed in Part B §B15 (no epic added or removed), the provisional G1 attachment of cloud agents and other CLI consumers, and Q-TEL-01. + +--- + +# Part B. Low level + +## B0. How to read Part B + +Each epic section has workstream tables, one row per unit, and one dependency diagram. The **Deps** column is the compact table that goes with each diagram. Feature IDs, donor paths and staleness evidence stay in the [unit register](../discovery/v2-unit-register.md) (frozen discovery evidence); this plan is the living source for gate, dependencies, acceptance and status. + +| Column | Meaning | +|---|---| +| Unit | `U--`. One unit = one session, one branch, one PR. Units added by this plan are numbered after discovery's | +| Issue | Existing GitHub issue, or `new` (the user creates it) | +| Gate | pre-gate, G1, G2, G3, post-G3, non-gating, continuous, TBD | +| Deps | Units (or decisions) that must be done first | +| Delta | needs-port, native, partial, decision, not-needed, post-cutover | +| Stale | D4 verdict, snapshot → v1 target: EMPTY, SMALL, MATERIAL (M), NEW, n/a. Non-EMPTY: re-derive behavior from v1 | +| Donor | Mechanism reuse from the archived tag: yes / partial / no, with confidence H/M/L. Re-check the seam at the base before reuse | +| Acceptance | What must be shown, against v1 behavior, with evidence recorded with SHA | +| Status | `todo`, `active`, `review`, `done`, `blocked`, `dropped` | + +**Tracking rules.** The GitHub issue is the public entry point for a unit; this file holds the versioned plan. The coordinator updates a unit's Status cell by micro-PR (`coord-status-`) with the PR link and acceptance evidence. Completing a unit changes only its own row; it never implies an adjacent row is done. Units copied from the donor apply the `@opencode/*` scope at copy time. Every unit follows the Definition of done in `migration-context.md`. + +## B1. E0 Foundation + +| Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | +|---|---|---|---|---|---|---|---|---|---| +| U-E0-01 | Kilo host and composition package (server overrides, Location service replacements, builtin plugin list) | new | pre-gate | Q-HOST-01 | needs-port | n/a | yes H | Host builds and starts an isolated server through public seams only; `service-smoke` passes with isolated HOME/XDG; Q-HOST-01 answer recorded | todo | +| U-E0-02 | Kilo RPC and plugin scaffolding (`packages/schema/src/kilocode`, plugin `define`, generated client) | new | pre-gate | U-E0-01 | native mechanism | n/a | yes H | One sample Kilo RPC registered as a plugin round-trips through the generated client; `bun run generate` leaves no drift | todo | +| U-E0-03 | Re-land Kilo `AGENTS.md`; rewrite `v2-fork-conventions.md` and `script/v2-baseline.ts` (README, banners and drops done by this plan, Appendix D) | new | pre-gate | — | needs-port | n/a | no | `AGENTS.md` names `kilo-v2-clean`, BASE and branch rules; conventions carry the base pin, no-prefix branches, `services/www`; `v2-baseline.ts` runs at BASE (pin-is-ancestor assertion, mirrors `check.yml`/`test.yml`) | todo | +| U-E0-04 | Kilo CI lane on `kilo-v2-clean`; neutralize upstream publish/deploy workflows, `sdks/vscode` publishing, hosted web properties, CI images | new | pre-gate | admin: Actions permissions | needs-port | n/a | no | Kilo workflow runs `bun run check` and package tests on PRs to `kilo-v2-clean`, compared with `pinned-v2-baseline.md`; each upstream publish/deploy workflow disabled or repository-guarded, verified by reading triggers | todo | +| U-E0-05 | v1 reference fixtures: offline wait, overflow, slow snapshot, snapshot retention (data at v1 target, harness on BASE) | new | pre-gate | U-E0-01 | needs-port | M | partial M | Fixtures generated through v1 writer methods at `fb7e96ed`, each isolating HOME/XDG itself; harness on BASE replays them | todo | + +```mermaid +flowchart LR + HQ["Q-HOST-01"]:::ext -.-> E0_01["U-E0-01 host"] + E0_01 --> E0_02["U-E0-02 RPC scaffolding"] + E0_01 --> E0_05["U-E0-05 v1 fixtures"] + E0_03["U-E0-03 conventions"] + ADM["admin: Actions permissions"]:::ext -.-> E0_04["U-E0-04 CI lane"] + classDef ext stroke-dasharray: 4 4 +``` + +## B2. E1 Core and identity + +| Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | +|---|---|---|---|---|---|---|---|---|---| +| U-E1-01 | Product identity and branding: app dir, binary, env prefix, UA, config filenames, default theme and logo | new | G1 | U-E0-01 | partial | SMALL | yes H paths / L theme | Binary, app dir, `KILO_*` env, UA and config filenames are Kilo; Kilo theme and logo render; each shared patch counted by L3 with its seam (donor theme files are deleted at BASE) | todo | +| U-E1-02 | Customer v2 store and paths (D6): distinct dirs, fail-closed on v1/opencode stores, DB preflight, SQLite errors, log correlation | new | G1 | U-E1-01 | partial | SMALL | yes H | Filesystem-access test: a v2 boot never opens a seeded v1 store; boot fails closed on v1/opencode stores; upstream `V1Migration` is a no-op on an empty store; v1 WAL-recovery and interrupted-first-access behavior reproduced | todo | +| U-E1-03 | Gateway auth, profile, orgs, teams, Pass, error codes, tRPC client on the integration/credential framework | new | G1 | U-E0-02 | partial | M | yes M | Device login, org switch, logout, Pass and balance pass against the deployed Gateway (SHA recorded); errors actionable; org default model and credential precedence match v1 | todo | +| U-E1-04 | Gateway provider, catalog merge/refresh/recovery, Kilo Auto and routed model, provider options, Codex/cloud auth | new | G1 | U-E1-03 | partial | M | yes L-M | Request-capture test shows Kilo headers; Kilo models selectable; catalog recovery and malformed-entry tolerance match v1; signed-out Gateway disables the provider when the catalog is empty | todo | +| U-E1-05 | System-prompt selectors (8), memory guidance, editor `` context on BASE seams | new | G1 | U-E1-04 | partial | M | partial L | Wire test: all eight selectors resolve per model family; memory guidance and editor context present (donor seam deleted at BASE, re-seat) | todo | +| U-E1-06 | Model usage, cost, throughput, provider usage and quotas (`/usage`) | new | G1 | U-E1-04 | needs-port | M | partial M | Session-family cost and throughput match v1 on fixture sessions incl. subagent propagation; Kilo, Codex and MiniMax provider usage shown | todo | +| U-E1-07 | Kilo config layer: keys, `kilo.json`/`.kilo` discovery, overlay/sources/writer, references, managed policy | new | G1 | U-E1-01 | partial | M | partial M | Per-key test from `config-mapping-v2-gap.md`: each key folds or is refused with a diagnostic; writer keeps 0600 and revision checks; v1-era new keys (`memory_model`, swarm, PTC, cleanup) handled | todo | +| U-E1-08 | Kilo server extension surface: route groups as plugin RPC, parent watchdog, reload, watcher policy, capability/version handshake (server side) | new | G1 | U-E0-02 | partial | M | yes M | Kilo groups served as plugin RPC; server exits when its parent dies (test); `/reload` reloads the project; minimal handshake reports Kilo version and capabilities (shape per Q-G2-02) | todo | + +```mermaid +flowchart LR + E0_01["U-E0-01"]:::ext --> E1_01["U-E1-01 identity"] + E1_01 --> E1_02["U-E1-02 v2 store"] + E1_01 --> E1_07["U-E1-07 config layer"] + E0_02["U-E0-02"]:::ext --> E1_03["U-E1-03 Gateway auth"] + E1_03 --> E1_04["U-E1-04 catalog"] + E1_04 --> E1_05["U-E1-05 prompt selectors"] + E1_04 --> E1_06["U-E1-06 usage"] + E0_02 --> E1_08["U-E1-08 server surface"] + classDef ext stroke-dasharray: 4 4 +``` + +## B3. E2 Runtime behavior + +Every E2 unit except U-E2-18, -21, -28 (SMALL) and U-E2-29 (EMPTY) re-derives behavior from v1. + +### Workstream: session policy + +| Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | +|---|---|---|---|---|---|---|---|---|---| +| U-E2-01 | Offline network wait and reconnect | #14371 | G1 | U-E0-05 | needs-port | M | no | Fixture: waits past the native ~84 s retry bound and resumes; connection-reset retry (v1 #14893) | todo | +| U-E2-02 | Overflow accounting, `threshold_percent`, chunked compaction, payload recovery | #14372 | G1 | U-E0-05 | partial | M | no | Fixture: compaction triggers at v1 thresholds; chunked compaction and payload recovery match v1 | todo | +| U-E2-03 | Slow-snapshot interaction policy and UI | #14373 | G1 | U-E0-05 | partial | M | no | Fixture: question after the v1 timeout; persisted disable honored | todo | +| U-E2-04 | Snapshot seed/materialize, locking, cleanup, retention, restore/diff | #14374 | G1 | U-E0-05 | partial | M | no | Fixture: seed, lock, cleanup and retention match v1; restore and diff correct | todo | +| U-E2-05 | Message deletion runtime operation, Kilo revert, stable ordering | #14380 | G2 | U-E0-02, Q-G2-05 | needs-port | M | no | Public delete operation (semantics per Q-G2-05) in the generated client; ordering stable after delete and revert | todo | +| U-E2-06 | Session core verification: drain/control, prompt queue and steer, title, processing hardening | new | G1 | U-E0-01 | partial | M + NEW | no | Native-equivalence tests (scope, errors, cancellation, persistence, presentation) for drain, stop/resume, queue dismissal, steer labels, bounded title, processing fixes | todo | +| U-E2-07 | Subagent resume, background subagents (default on), steering | new | G1 | U-E2-06 | partial | M | no | Background subagents on by default with kill switch, survive stop; resume hint; steering into subagents | todo | +| U-E2-08 | Wakeups and cron (persisted, re-armed) | new | G1 | U-E2-06 | needs-port | NEW | no | Wakeups and cron persist and re-arm after a server restart (test) | todo | +| U-E2-09 | Goal mode | new | G1 | U-E2-08 | needs-port | NEW | no | `/goal`, `goal` and `goal_report` behave as v1 | todo | +| U-E2-10 | Backend-owned session retention | new | G1 | U-E1-07 | needs-port | NEW | no | Retention prunes per config on the backend; clients see a consistent list | todo | +| U-E2-11 | Session transcript recall search | new | G1 | U-E0-02 | needs-port | M | partial M | Recall returns v1-equivalent results on fixture transcripts | todo | + +### Workstream: agents, commands and skills + +| Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | +|---|---|---|---|---|---|---|---|---|---| +| U-E2-12 | Kilo agents (code, ask, debug, orchestrator, plan), mode reminders, agent builder | new | G1 | U-E0-02 | partial | M | yes M | Agents and permissions as v1 (`build` shown as Code, Ask read-only, user IDs win); mode reminders; agent builder | todo | +| U-E2-13 | Plan workflow and Plan-to-Code handoff | #14382 | G1 | U-E2-12 | needs-port | M | yes M | Plans saved in the Kilo location, not `~/.opencode/plan`; `plan_exit` requires a saved plan; follow-up offers new session, continue, refine; new-session handoff keeps Plan and opens Code | todo | +| U-E2-14 | Kilo review policy over native `/review` | new | G1 | U-E2-12 | partial | M | yes M | `/review` over working tree and worktree diff matches v1 | todo | +| U-E2-15 | Commands and workflows, skills (trust, builtin, remove), rules and primary-worktree discovery | new | G1 | U-E1-07 | partial | M | yes M | Command list/remove, reserved names, workflow variants, timeout; skill trust only for global/builtin; builtin `kilo-config`; rules from `.kilo`/`.kilocode` mirrored from the primary worktree | todo | + +### Workstream: tools and permissions + +| Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | +|---|---|---|---|---|---|---|---|---|---| +| U-E2-16 | Tool registry and client gating, question tool, `suggest`, todo transitions | new | G1 | U-E0-02 | partial | M + NEW | partial L-M | `KILO_CLIENT` gating per client; headless question handling; suggest lifecycle; todo transitions as v1 | todo | +| U-E2-30 | Content and code tools: TS check after edits, rich document read, repo overview, encoding-preserving I/O, model search/selection tools | new | G1 | U-E2-16 | partial | M + NEW | partial L-M | TS check runs after edits; docx/xlsx/ods/pdf read; repo overview; encodings and BOM preserved; task model selection as v1 | todo | +| U-E2-17 | Permission customizations and bash hierarchy | new | G1 | U-E2-16 | partial | M | partial M | `/approve-all`, headless policy, config-path protection, provenance; bash hierarchy, heredoc, PowerShell; Explore read-only bash counts (28 + 20) verified | todo | + +### Workstream: engines + +| Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | +|---|---|---|---|---|---|---|---|---|---| +| U-E2-18 | Memory engine, tools and RPC | new | G1 | U-E0-02 | needs-port | SMALL | yes H | Recall/save tools, `/memory`, `memory_model` fallback, save pulse; durable injection indicator or a recorded missing seam | todo | +| U-E2-19 | Codebase indexing engine, worker, `semantic_search` | new | G1 | U-E0-02 | needs-port | M | yes M-H | Worker isolated; embedders and stores as v1; consent off means no init; root/home safeguards; CLI and cloud paths covered | todo | +| U-E2-20 | Sandbox: shell/MCP/PTY confinement, network relay, git classifier, per-session lifecycle on the workspace/environment driver seam | new | G1 | U-E0-01 | needs-port | M | yes M | Confinement on macOS and Linux (bwrap) incl. MCP and PTY; prompt before sandboxed git writes; opt-in default off; v1 policy variants; release validation per platform | todo | +| U-E2-21 | Telemetry with consent and privacy policy, coexisting with OTLP | new | G1 | U-E1-01, Q-TEL-01 | needs-port | SMALL | partial M | Default per Q-TEL-01; events only with consent; OTLP observability unaffected | todo | + +### Workstream: MCP and marketplace + +| Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | +|---|---|---|---|---|---|---|---|---|---| +| U-E2-22 | MCP deltas (OAuth callback handoff, issuer discovery, auth-failure surfacing, SSE probe, jsonc writes) and `git:` plugin sources | new | G1 | U-E0-02 | partial | NEW | partial L | Each delta tested against BASE MCP (gap narrowed upstream; port only what is missing); `git:` sources install | todo | +| U-E2-23 | Marketplace (MCP servers, skills, modes, plugins) | new | G1 | U-E2-22 | needs-port | NEW | no | Marketplace lists and installs each item type through the CLI API | todo | + +### Workstream: agent coordination + +| Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | +|---|---|---|---|---|---|---|---|---|---| +| U-E2-24 | Shared agent board / swarm (default on) | new | G1 | U-E2-07 | needs-port | NEW | partial M | Board keyed by root session; recipients as v1; default on with kill switch | todo | +| U-E2-25 | Background processes tool, sidebar data, `/process`, on persistent PTY | new | G1 | U-E0-02 | needs-port | M | partial M | Tool and `/process` as v1; a stale `running` row shows no PID after restart | todo | +| U-E2-26 | Worktree runtime: family detection, teardown, Kilo layout, VCS | new | G1 | U-E0-01 | partial | M | no | Family detection and teardown as v1; `.kilo/worktrees` layout | todo | +| U-E2-27 | Agent Manager host contract and control plane (ask/reply/reject, protection) | #14391 | G2 | U-E2-26, Q-G2-06 | needs-port | M | partial L | Public control-plane contract (per Q-G2-06) in the generated client, incl. multi-project routing | todo | + +### Workstream: generation and providers + +| Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | +|---|---|---|---|---|---|---|---|---|---| +| U-E2-28 | Generation endpoints: enhance prompt, commit message, branch name | new | G2 | U-E0-02 | partial | SMALL | yes M | Endpoints in the generated client; output and errors match v1; editor-context prefix; equivalence with the BASE `generate` group decided | todo | +| U-E2-29 | Atomic Chat local provider plugin | new | G1 | U-E1-04 | needs-port | EMPTY | no | Provider discovered and usable | todo | + +```mermaid +flowchart TD + E0_01["U-E0-01"]:::ext + E0_02["U-E0-02"]:::ext + E0_05["U-E0-05"]:::ext + E1_01["U-E1-01"]:::ext + E1_04["U-E1-04"]:::ext + E1_07["U-E1-07"]:::ext + subgraph SP["Session policy"] + E2_01["01 offline wait"] + E2_02["02 overflow"] + E2_03["03 slow snapshot"] + E2_04["04 snapshots"] + E2_05["05 message deletion G2"]:::g2 + E2_06["06 session core"] + E2_07["07 subagents"] + E2_08["08 wakeups"] + E2_09["09 goals"] + E2_10["10 retention"] + E2_11["11 recall"] + end + subgraph AG["Agents and skills"] + E2_12["12 agents"] + E2_13["13 plan"] + E2_14["14 review"] + E2_15["15 commands, skills, rules"] + end + subgraph TL["Tools"] + E2_16["16 registry, question, suggest"] + E2_30["30 content tools"] + E2_17["17 permissions"] + end + subgraph EN["Engines"] + E2_18["18 memory"] + E2_19["19 indexing"] + E2_20["20 sandbox"] + E2_21["21 telemetry"] + end + subgraph MC["MCP"] + E2_22["22 MCP deltas"] + E2_23["23 marketplace"] + end + subgraph CO["Coordination"] + E2_24["24 board"] + E2_25["25 processes"] + E2_26["26 worktrees"] + E2_27["27 Agent Manager G2"]:::g2 + end + subgraph GP["Generation"] + E2_28["28 generation G2"]:::g2 + E2_29["29 Atomic Chat"] + end + E0_05 --> E2_01 & E2_02 & E2_03 & E2_04 + E0_01 --> E2_06 & E2_20 & E2_26 + E0_02 --> E2_05 & E2_11 & E2_12 & E2_16 & E2_18 & E2_19 & E2_22 & E2_25 & E2_28 + E2_06 --> E2_07 & E2_08 + E2_08 --> E2_09 + E1_07 --> E2_10 & E2_15 + E2_12 --> E2_13 & E2_14 + E2_16 --> E2_17 & E2_30 + E1_01 --> E2_21 + E2_22 --> E2_23 + E2_07 --> E2_24 + E2_26 --> E2_27 + E1_04 --> E2_29 + classDef ext stroke-dasharray: 4 4 + classDef g2 stroke-width:3px +``` + +## B4. E9 CLI/TUI surface + +U-E9-05, -09, -10, -11 and -12 touch the same TUI files: run them in series. + +| Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | +|---|---|---|---|---|---|---|---|---|---| +| U-E9-01 | Service/daemon UX: `kilo daemon` over the native service, startup performance, port warning | new | G1 | U-E1-08 | native | M | yes M | `kilo daemon status/stop/restart` map to the native service; port warning; startup time recorded against the baseline | todo | +| U-E9-02 | Kilo command set, help, generated CLI docs, roll-call, `kilo worktree`, inherited commands | new | G1 | U-E0-01 | partial | M | yes M | `kilo --help --all` lists the v1 set or a removal note per missing command; CLI docs generate; roll-call and `kilo worktree` as v1 | todo | +| U-E9-03 | `kilo run` and headless behavior (variant, auto, drain, stdin, questions) | new | G1 | U-E2-06 | partial | M | yes M | Each flag and headless question path matches v1; `run` waits for drain | todo | +| U-E9-04 | ACP host with Kilo permission mapping | new | G1 | U-E2-17 | partial | M | yes M | ACP permission mapping matches v1 | todo | +| U-E9-05 | TUI shell: title/activity, caffeinate, TUI config hot reload, worker transport check, inherited TUI | new | G1 | U-E1-01 | partial | M | partial L-M | OSC title and activity, `/caffeinate`, hot reload as v1; worker handshake defect reproduced or recorded not-needed | todo | +| U-E9-06 | Kilo slash-command registry and dialogs; Gateway notifications and news; provider/integration dialog guidance | new | G1 | U-E1-03 | partial | M | partial M | Registry in place, each command registered by its feature unit; news and notifications on home; provider dialogs show v1 guidance and failure detail | todo | +| U-E9-07 | Home and sidebar plugins; model picker, info panel, free/training disclosure; status/footer onboarding | new | G1 | U-E1-06 | partial | M | yes M | Sidebar order pr 50, footer 99, usage 150, indexing 225, processes 250, memory 1000; picker groups, `BYOK`/`May train` labels; model info panel rules as v1 | todo | +| U-E9-08 | Per-session soft maximum-cost alert | new | G1 | U-E1-06 | needs-port | M | no | Alert fires at the configured threshold as v1 | todo | +| U-E9-09 | Completion/abort/timeout notifications and attention policy | #14375 | G1 | U-E9-05 | partial | M | partial L | Notifications follow v1 attention policy on top of native notifications | todo | +| U-E9-10 | Session scope switching, list actions, clickable links, @session mentions, double-press guards, keybinds | #14377 | G1 | U-E9-05 | needs-port | M | no | Each interaction as v1 | todo | +| U-E9-11 | Location/session projection and foreign-project event filtering | #14379 | G1 | U-E9-05 | partial | M | no | Test proves foreign-project events are filtered; port only if BASE lacks it | todo | +| U-E9-12 | Multiline alerts and default sound; display preferences; privacy mode and PII redaction | #14378 (part) | G1 | U-E9-05 | partial | M | partial L | Alerts and sound as v1; tool/reasoning collapse; privacy redaction | todo | +| U-E9-13 | `kilo cloud` Cloud Agent CLI | new | G1 | U-E1-03 | needs-port | SMALL | yes H-M | `start/send/status/result` and stdin prompts against the deployed service (SHA recorded) | todo | +| U-E9-14 | Embedded web UI decision: `kilo-web-ui` vs upstream `packages/app` | new | G1 | U-E9-15 | decision | M | no | Decision recorded and implemented | todo | +| U-E9-15 | Disposition of upstream-only surfaces: desktop, web app, GUI extensions | new | G1 | Q-PROD-01 | decision | n/a | no | User decision per surface recorded and implemented (ship, hide or defer) | todo | + +```mermaid +flowchart LR + E1_08["U-E1-08"]:::ext --> E9_01["01 daemon"] + E0_01["U-E0-01"]:::ext --> E9_02["02 commands"] + E2_06["U-E2-06"]:::ext --> E9_03["03 kilo run"] + E2_17["U-E2-17"]:::ext --> E9_04["04 ACP"] + E1_01["U-E1-01"]:::ext --> E9_05["05 TUI shell"] + E1_03["U-E1-03"]:::ext --> E9_06["06 slash commands"] + E1_03 --> E9_13["13 kilo cloud"] + E1_06["U-E1-06"]:::ext --> E9_07["07 sidebar and picker"] + E1_06 --> E9_08["08 cost alert"] + E9_05 --> E9_09["09 notifications"] --> E9_10["10 scope switching"] --> E9_11["11 event filtering"] --> E9_12["12 alerts and privacy"] + PQ["Q-PROD-01"]:::ext -.-> E9_15["15 upstream surfaces"] --> E9_14["14 web UI"] + classDef ext stroke-dasharray: 4 4 +``` + +The chain 09 → 12 is the serialization rule for shared TUI files, not a functional dependency. + +## B5. E3 Import and distribution + +| Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | +|---|---|---|---|---|---|---|---|---|---| +| U-E3-01 | Opt-in, re-runnable v1 session/data import into the v2 store (read `kilo.db` read-only; upstream pure transforms; legacy JSON storage) | #14413 | G1 | U-E1-02 | needs-port | M | partial M | Fixture stores from each supported v1 release incl. `fb7e96ed`: originals byte-identical, v1 binary still opens them; second run imports only new sessions; schema audit re-run against v1 target and BASE | todo | +| U-E3-02 | Credential import (host-local only) | #14413 (part) | G1 | U-E1-03 | partial | M | yes M | Host-local import only; no credential writer over HTTP/RPC; OAuth account maps to the selected org | todo | +| U-E3-03 | Config key mapping, legacy Kilo Code migrators, TUI config migration | #14413 (part) | G1 | U-E1-07 | needs-port | M | partial M | Per-key mapping test; modes, rules, workflows, MCP and ignore migrators as v1 | todo | +| U-E3-04 | External transcript resume (Claude Code, Codex), migrate API, cloud session import and `--cloud-fork` | new | G1 | U-E3-01 | needs-port | M + NEW | partial L | Each import path as v1, incl. ambient discovery and the picker | todo | +| U-E3-05 | Fork, move and portability diff verification | new | G1 | U-E2-06 | native/partial | M | no | Native-equivalence tests; cumulative diff and restore on move as v1 | todo | +| U-E3-06 | Updater and installation detection under Kilo identity | #14410 | G1 | U-E1-01 | partial | M | yes M | Update and rollback on real artifacts; install method detected; overlap with BASE `services/update` decided | todo | +| U-E3-07 | Build and release pipeline: platforms, npm/GHCR/AUR/Homebrew/nix/install script, SBOM, PTY smoke, payload format, clean install | #14409, #14412 | G1 | U-E0-04 | partial | M | partial M | Artifacts for every v1 platform; each channel dry-runs; SBOM; PTY smoke; clean install beside v1 | todo | +| U-E3-13 | Signing and notarization (start early) | #14411 | G1 | U-E0-04, admin: signing credentials | partial | M | no | Signed and notarized macOS and Windows artifacts verify on clean hosts | todo | +| U-E3-08 | CI and repository guards (forbidden strings, generated artifacts, markers, md tables, CodeQL, sharding, bots) | #14408 | G1 | U-E0-04 | partial | M | no | Each v1 guard ported or recorded not-needed; guards run on PRs to `kilo-v2-clean` | todo | +| U-E3-09 | Publish `@kilocode/sdk` and `@kilocode/plugin` from BASE packages | new | G1 | U-E3-07 | partial | SMALL | no | Publish dry run passes `verify:package`; names and versions decided | todo | +| U-E3-10 | v2 preview release train | new | G1 | U-E3-06, U-E3-07, U-E3-08, U-E3-12 | needs-port | n/a | no | At least one preview shipped to the team under the Q-DIST-01 identity; findings filed against units | todo | +| U-E3-11 | Canary and cutover/rollback runbook, G1 | #14414 | G1 | U-E3-10, U-L4-05 | needs-port | n/a | no | Runbook written and exercised once on the G1 candidate; rollback to v1 CLI on untouched v1 files | todo | +| U-E3-12 | Binary and channel naming; v1-IDE isolation from the v2 binary | new | G1 | U-E1-01, Q-DIST-01 | needs-port | n/a | no | Name and channel implemented; test: a v1 IDE build cannot resolve the v2 binary via PATH, global install or dist-tag | todo | + +```mermaid +flowchart LR + E1_02["U-E1-02"]:::ext --> E3_01["01 session import"] --> E3_04["04 external resume"] + E1_03["U-E1-03"]:::ext --> E3_02["02 credentials"] + E1_07["U-E1-07"]:::ext --> E3_03["03 config migrators"] + E2_06["U-E2-06"]:::ext --> E3_05["05 fork and move"] + E1_01["U-E1-01"]:::ext --> E3_06["06 updater"] + E1_01 --> E3_12["12 binary and channel"] + DQ["Q-DIST-01"]:::ext -.-> E3_12 + E0_04["U-E0-04"]:::ext --> E3_07["07 build pipeline"] & E3_08["08 CI guards"] & E3_13["13 signing"] + SC["admin: signing credentials"]:::ext -.-> E3_13 + E3_07 --> E3_09["09 SDK publish"] + E3_06 & E3_07 & E3_08 & E3_12 --> E3_10["10 preview train"] + E3_10 --> E3_11["11 G1 canary"] + L4_05["U-L4-05"]:::ext --> E3_11 + classDef ext stroke-dasharray: 4 4 +``` + +## B6. E4 Remote sessions + +| Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | +|---|---|---|---|---|---|---|---|---|---| +| U-E4-01 | Relay wire protocol and command dispatcher | #14402 | G1 | U-E1-03 | needs-port | M | yes M | Local contract test for every `CLIOutboundMessageSchema` message | todo | +| U-E4-02 | Streamed transcript, attachments, `notify_user`/`send_file`, reconnect without duplication | #14403 | G1 | U-E4-01 | needs-port | M | yes M | Reconnect replays without duplicates; attachments and files delivered | todo | +| U-E4-03 | Remote suggestion lifecycle and subscribed-session ownership | #14404 | G1 | U-E4-01, U-E2-16 | needs-port | M | yes M | Suggestions accept/dismiss remotely; ownership as v1 | todo | +| U-E4-04 | Detach/exit, interruption, heartbeat fencing | #14405 | G1 | U-E4-01 | needs-port | M | yes M | Each lifecycle path as v1; stale heartbeats fenced | todo | +| U-E4-05 | Metadata, capability advertisement, instance identity, directory listing, headless link dialog, coexistence with upstream pairing/OpenTunnel | #14406 | G1 | U-E4-01, Q-PROD-01 | needs-port | M | partial M | Unsupported capabilities not advertised; identity fields as v1; pairing coexistence decision implemented | todo | +| U-E4-06 | Presence host registration | #14395 (part) | G1 | U-E4-01 | needs-port | M | yes M | Host registers and deregisters with the event service | todo | +| U-E4-07 | PR linking (GitHub, GitLab, Bitbucket), poller, sidebar row | new | G1 | U-E4-01 | needs-port | NEW | no | Each forge links; `gh` lookup chain as v1 | todo | +| U-E4-08 | Deployed relay and consumer verification (external-gated) | #14407 | G1 | U-E4-01…07 | needs-port | n/a | no | Deployed acceptance recorded with SHA, or a user waiver recorded | todo | + +```mermaid +flowchart LR + E1_03["U-E1-03"]:::ext --> E4_01["01 relay protocol"] + E4_01 --> E4_02["02 transcript"] & E4_03["03 suggestions"] & E4_04["04 lifecycle"] & E4_05["05 metadata"] & E4_06["06 presence"] & E4_07["07 PR linking"] + E2_16["U-E2-16"]:::ext --> E4_03 + E4_02 & E4_03 & E4_04 & E4_05 & E4_06 & E4_07 --> E4_08["08 deployed verification"] + CL["deployed relay"]:::ext -.-> E4_08 + classDef ext stroke-dasharray: 4 4 +``` + +## B7. E5 Sharing + +| Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | +|---|---|---|---|---|---|---|---|---|---| +| U-E5-01 | Share transport normalization; share/unshare/fork client operations | #14396 | G1 | U-E1-03 | needs-port | EMPTY | partial M-H | Operations target the JWT `share_token` contract (`session-sharing.md`) | todo | +| U-E5-02 | v2 share payload contract (self-contained messages, metadata-first upload) | #14397 | G1 | U-E5-01 | needs-port | EMPTY | partial M | Payload of self-contained v2 messages; explicit consent before a public share | todo | +| U-E5-03 | Public viewer for v2 messages (cloud repository) | #14398 | G1 | U-E5-02 | needs-port | EMPTY | no | Viewer renders v2 messages; change merged by the cloud owner | todo | +| U-E5-04 | Team upload stays refused (fail-closed) | #14399 (part) | G1 | U-E5-01 | needs-port | EMPTY | partial M | Team upload refused with a clear message | todo | +| U-E5-05 | Deployed share contract verification (external-gated) | #14401 | G1 | U-E5-01…04 | needs-port | n/a | no | Share, unshare, fork-from-share round-trip against the deployed backend, SHA recorded | todo | +| U-E5-06 | Background session export/ingest to Kilo cloud (scrubbed), opt-out | new | G1 | U-E1-03 | needs-port | EMPTY (feature) | no | Scrubbed upload of v2 sessions; opt-out honored | todo | + +```mermaid +flowchart LR + E1_03["U-E1-03"]:::ext --> E5_01["01 transport"] --> E5_02["02 payload"] --> E5_03["03 viewer"] + E5_01 --> E5_04["04 team refusal"] + E5_02 & E5_03 & E5_04 --> E5_05["05 deployed verification"] + E1_03 --> E5_06["06 ingest"] + CR["cloud repository owner"]:::ext -.-> E5_03 + classDef ext stroke-dasharray: 4 4 +``` + +## B8. E10 External consumers + +| Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | +|---|---|---|---|---|---|---|---|---|---| +| U-E10-01 | Cloud agent consumers: adapt confirmed gaps through public interfaces and validate | #14427, #14428 | G1 (provisional, Q-EXT-01) | U-E3-12, U-E3-01 | needs-port | n/a | no | Disposition, owner and gate recorded; each contract gap (renamed CA-1…CA-11 to avoid the gate-name collision) adapted or waived; validation with cloud owners | todo | +| U-E10-02 | Anaconda Desktop: integration mode, compatibility, adaptation, validation; v1 `anaconda-desktop` provider | #14429, #14430, #14431, #14432 | TBD (Q-EXT-02) | U-E1-04 | needs-port | EMPTY | no | Integration mode and gate recorded; provider ported; validation passes on the attached gate | todo | +| U-E10-03 | Other CLI consumers: Zed extension, GitHub Action, floating `@latest` users | new | G1 | U-E3-12 | needs-port | n/a | no | Each consumer pinned or adapted before the dist-tag switch | todo | + +```mermaid +flowchart LR + E3_12["U-E3-12"]:::ext --> E10_01["01 cloud agents"] & E10_03["03 other consumers"] + E3_01["U-E3-01"]:::ext --> E10_01 + E1_04["U-E1-04"]:::ext --> E10_02["02 Anaconda Desktop"] + classDef ext stroke-dasharray: 4 4 +``` + +## B9. E6 VS Code track (G2) + +Every unit is MATERIAL except U-E6-07, -08, -20 (SMALL), U-E6-18 (NEW) and U-E6-21 (n/a). Re-copy the extension and webview from v1 `fb7e96ed`; keep only the donor's backend-adapter pattern (`kv/backend/*`, `connection.ts`, `local-server.ts`). The `virtua` patch caveat in the old fork conventions applies once the webview lands. Start U-E6-08 during Phase 1. + +| Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | +|---|---|---|---|---|---|---|---|---|---| +| U-E6-01 | Re-land v1 extension host, webview, `kilo-ui`, `kilo-i18n` behind v2 adapters; real typecheck | #14383 | G2 | U-E0-02, Q-G2-01 | needs-port | M | partial L | `typecheck` is real with no skip shim; `bun run extension` builds | todo | +| U-E6-02 | Connection: server manager for the v2 binary, handshake (client), v2 store, opt-in first-launch import | #14387 | G2 | U-E6-01, U-E1-08, U-E3-01, Q-G2-02, Q-G2-03 | needs-port | M | partial M | Bundled v2 binary starts; handshake detects v2; never mixes v1 and v2 routes; opt-in import; `globalState` and secrets mapped | todo | +| U-E6-03 | Sidebar chat lifecycle: streaming, Stop, reconnect, Restricted Mode conflict, message-deletion caller | #14384 | G2 | U-E6-02, U-E2-05, Q-G2-04 | needs-port | M | partial L | Stop verified on desktop; Restricted Mode no longer needed; delete message works | todo | +| U-E6-04 | Editor tabs: streaming, restore, independent routing | #14385 | G2 | U-E6-03 | needs-port | M | partial L | Tabs stream, restore after reload and route independently | todo | +| U-E6-05 | Settings webview controls and persistence (max cost, display, language, CA certs) | #14386 | G2 | U-E6-01, U-E1-07 | needs-port | M | partial M | Each v1 setting persists through the config overlay | todo | +| U-E6-06 | Models, accounts, auth and provider OAuth variants, org switch, provider usage | #14388 | G2 | U-E6-02, U-E1-03 | needs-port | M | partial M | Each auth path and org switch as v1 | todo | +| U-E6-07 | Generation consumers (SCM commit message, enhance prompt, branch naming); code and terminal actions | #14389, #14417 | G2 | U-E2-28 | needs-port | SMALL | partial M | Each action works in the real UI as v1 | todo | +| U-E6-08 | FIM and next-edit producer: Gateway endpoints and service contract (shared runtime) | #14416 (part) | G2 | U-E1-03 | needs-port | SMALL | no | `/kilo/fim` and `/kilo/edit` reachable through a public contract from the generated client; shared FIM model selection as v1 | todo | +| U-E6-20 | FIM and next-edit in VS Code: inline completion, next-edit, chat autocomplete, `autocomplete.*` settings | #14416 (part) | G2 | U-E6-08, U-E6-01 | needs-port | SMALL | no | Inline completion and next-edit work end to end on a real host (hard gate) | todo | +| U-E6-09 | Terminal integration and ticket lifecycle | #14390 | G2 | U-E6-02 | needs-port | M | partial M | Terminals and PTY tickets as v1 | todo | +| U-E6-10 | Agent Manager UI on the v2 control plane | #14391 (consumer) | G2 | U-E2-27, U-E6-09 | needs-port | M | partial L | Worktrees, sessions, tabs, terminals, PR, diff, previews as v1 | todo | +| U-E6-11 | VS Code-only tools and panels: browser tab/open, chart, image preview, document viewer, sub-agent viewer, diff provider | new | G2 | U-E6-01, U-E2-16 | needs-port | M + NEW | partial L | Each tool and panel as v1 | todo | +| U-E6-12 | Notifications and suggestions lifecycle | #14392 | G2 | U-E2-16, U-E9-06 | needs-port | M | no | List, accept, dismiss as v1 | todo | +| U-E6-13 | Marketplace panel; agent, skill and MCP removal; MCP OAuth sign-in | #14393 | G2 | U-E2-15, U-E2-22, U-E2-23 | needs-port | M + NEW | partial L | Each action as v1 | todo | +| U-E6-14 | Task timeline, diff viewer projection, token throughput | #14394 | G2 | U-E6-03 | needs-port | M | partial L | Timeline and diff render v2 messages as v1 | todo | +| U-E6-15 | Remote status and presence visibility reporting | #14395 (part) | G2 | U-E4-06 | needs-port | M | partial M | Status and visibility as v1 | todo | +| U-E6-16 | Engine and policy controls: memory, indexing consent and write controls, sandbox, network ask, caffeination, attention, telemetry proxy | new | G2 | U-E2-18, U-E2-19, U-E2-20, U-E2-21 | needs-port | M | partial M | Each control as v1 | todo | +| U-E6-17 | Kilo-classic session migration and Roo import | new | G2 | U-E3-01 | needs-port | M | partial L | Sessions migrate (scope reduced by v1's server-side move) | todo | +| U-E6-18 | Session extras: board, background jobs, wakeups, retention, model usage, snapshots, move, cloud import | new | G2 | U-E2-08, U-E2-10, U-E2-24, U-E3-04 | needs-port | NEW | no | Each extra as v1 | todo | +| U-E6-19 | VSIX packaging and publishing with the v2 binary | new | G2 | U-E6-02, U-E3-07, Q-G2-08 | needs-port | M | partial M | VSIX builds with `vsce package --no-dependencies`; name and channel per Q-G2-08 | todo | +| U-E6-21 | G2 canary and rollback runbook | new (child of #14414) | G2 | U-E6-19, U-L4-06 | needs-port | n/a | no | Runbook exercised; previous extension works on untouched v1 files | todo | + +```mermaid +flowchart TD + E0_02["U-E0-02"]:::ext --> E6_01["01 re-land extension"] + E1_08["U-E1-08"]:::ext --> E6_02["02 connection"] + E3_01["U-E3-01"]:::ext --> E6_02 & E6_17["17 classic migration"] + E6_01 --> E6_02 + E6_02 --> E6_03["03 chat lifecycle"] + E2_05["U-E2-05"]:::ext --> E6_03 + E6_03 --> E6_04["04 tabs"] & E6_14["14 timeline"] + E6_01 --> E6_05["05 settings"] + E6_02 --> E6_06["06 accounts"] & E6_09["09 terminals"] + E2_28["U-E2-28"]:::ext --> E6_07["07 generation"] + E1_03["U-E1-03"]:::ext --> E6_08["08 FIM producer"] --> E6_20["20 FIM in VS Code"] + E6_01 --> E6_20 + E2_27["U-E2-27"]:::ext --> E6_10["10 Agent Manager"] + E6_09 --> E6_10 + E6_01 --> E6_11["11 VS Code tools"] + E2_16["U-E2-16"]:::ext --> E6_11 & E6_12["12 notifications"] + E2_23["U-E2-23"]:::ext --> E6_13["13 marketplace"] + E4_06["U-E4-06"]:::ext --> E6_15["15 presence"] + EN["U-E2-18..21"]:::ext --> E6_16["16 engine controls"] + E2X["U-E2-08, 10, 24; U-E3-04"]:::ext --> E6_18["18 session extras"] + E6_02 --> E6_19["19 VSIX"] + E3_07["U-E3-07"]:::ext --> E6_19 + E6_19 --> E6_21["21 G2 canary"] + L4_06["U-L4-06"]:::ext --> E6_21 + classDef ext stroke-dasharray: 4 4 +``` + +Secondary dependencies not drawn: U-E6-05 on U-E1-07, U-E6-06 on U-E1-03, U-E6-12 on U-E9-06, U-E6-13 on U-E2-15 and U-E2-22 (see the table). + +## B10. E7 JetBrains track (G3) + +No donor exists; behavior comes entirely from v1 (`packages/kilo-jetbrains` at `fb7e96ed`). + +| Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | +|---|---|---|---|---|---|---|---|---|---| +| U-E7-01 | Inventory plugin contracts and map each to a public v2 interface | #14420 | G3 | U-E0-02 | needs-port | M | no | Every route, event and terminal contract mapped; Q-G3-01 and Q-G3-02 answered | todo | +| U-E7-02 | Re-land the plugin: Gradle build, CLI pin to a v2 release, client generated from the assembled contract, startup, discovery, auth, version checks | #14421 | G3 | U-E7-01, U-E3-07, Q-G3-03 | needs-port | M | no | Builds from `kilo-v2-clean`; only the generated client is used; incompatible CLIs refused with a clear message | todo | +| U-E7-03 | Sessions, streaming, permissions, history, config, board, subagents, prompt completion, settings, MCP OAuth, telemetry | #14422 | G3 | U-E7-02 | needs-port | M | no | Each area as v1 | todo | +| U-E7-04 | Terminal, diff/VFS, editor-service consumers (enhance prompt, branch name; FIM only if Q-G3-01 keeps the gate) | #14423 | G3 | U-E7-02, U-E2-28 (U-E6-08 if FIM gates) | needs-port | M | no | Each consumer as v1 | todo | +| U-E7-05 | Agent Manager, worktrees, marketplace, legacy v5 migration | new | G3 | U-E2-27, U-E7-03, Q-G3-05 | needs-port | M | no | Each area as v1; v5 migration per Q-G3-05 | todo | +| U-E7-06 | Validate in supported IDEs: reconnect, restart, version mismatch | #14424 | G3 | U-E7-02…05, Q-G3-04 | needs-port | n/a | no | Validation recorded per supported IDE | todo | +| U-E7-07 | G3 canary and rollback runbook | new (child of #14414) | G3 | U-E7-06, U-L4-07 | needs-port | n/a | no | Runbook exercised; pinned v1 CLI works on untouched v1 files | todo | + +```mermaid +flowchart LR + E0_02["U-E0-02"]:::ext --> E7_01["01 contract inventory"] --> E7_02["02 plugin re-land"] + E3_07["U-E3-07"]:::ext --> E7_02 + E7_02 --> E7_03["03 sessions"] & E7_04["04 editor services"] + E2_28["U-E2-28"]:::ext --> E7_04 + E6_08["U-E6-08 if Q-G3-01"]:::ext -.-> E7_04 + E2_27["U-E2-27"]:::ext --> E7_05["05 Agent Manager"] + E7_03 --> E7_05 + E7_03 & E7_04 & E7_05 --> E7_06["06 IDE validation"] + E7_06 --> E7_07["07 G3 canary"] + L4_07["U-L4-07"]:::ext --> E7_07 + classDef ext stroke-dasharray: 4 4 +``` + +## B11. E8 Retirement (post-G3) + +| Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | +|---|---|---|---|---|---|---|---|---|---| +| U-E8-01 | Branch flip (`main`→`v1`, `kilo-v2-clean`→`main`), timed against v1 IDE builds | new | post-G3 | G3 go, Q-G2-01 | needs-port | n/a | no | Flip done; the `v1` branch builds and releases CLI and IDEs | todo | +| U-E8-02 | Storage path consolidation (D6), once, with backup | new | post-G3 | U-E8-01 | needs-port | n/a | no | Runs once with backup; a second run is a no-op | todo | +| U-E8-03 | Packages that deploy from `main`: keep docs site, retire `kilo-console`, re-home workflows | new | post-G3 | U-L5-01 | partial / not-needed | M | no | Every workflow and deployment from `main` has a v2 owner or is retired before the flip | todo | +| U-E8-04 | v1 end-of-life policy and maintenance window | new | post-G3 | U-E8-01 | needs-port | n/a | no | Policy published with a date | todo | + +```mermaid +flowchart LR + G3{{"G3 go"}} --> E8_01["01 branch flip"] + FQ["Q-G2-01"]:::ext -.-> E8_01 + E8_01 --> E8_02["02 consolidation"] & E8_04["04 v1 end of life"] + L5_01["U-L5-01"]:::ext --> E8_03["03 deploy owners"] + classDef ext stroke-dasharray: 4 4 +``` + +## B12. E11 Post-cutover backlog (non-gating) + +Acceptance for every row is a recorded user decision (ship, drop or re-scope); shipping becomes a new unit. + +| Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | +|---|---|---|---|---|---|---|---|---|---| +| U-E11-01 | Speech-to-text (Gateway endpoints and VS Code capture) | #14418 | non-gating | — | post-cutover | SMALL | no | Decision recorded | todo | +| U-E11-02 | Image generation (tool and VS Code consumer) | #14419 (image part) | non-gating | — | post-cutover | SMALL | no | Decision recorded | todo | +| U-E11-03 | Team sharing ownership; incremental sharing with deletion/revert semantics | #14399 (ownership), #14400 | non-gating | U-E5-02 | post-cutover | EMPTY | no | Decision recorded | todo | +| U-E11-04 | Legacy TUI extras: kilo-v1/colorblind themes, legacy bindings, fixed-width tables, vim mode | #14376, #14378 (part) | non-gating | U-E9-05 | post-cutover | M | partial L | Decision recorded | todo | +| U-E11-05 | Default-off experimental features (Exa, context tools, code mode/PTC, MCP Apps, session switcher, notebook tools, Claude migration, scout, LSP tool, VS Code browser automation and prompt history) | new | non-gating | Q-G2-07 | post-cutover | NEW | no | Decision recorded per feature | todo | +| U-E11-06 | KiloClaw: not needed (removed on v1); no donor remnants copied | #14419 (Claw part) | non-gating | — | not-needed | n/a | no | Grep shows no Claw paths on `kilo-v2-clean` | todo | + +```mermaid +flowchart LR + E5_02["U-E5-02"]:::ext --> E11_03["03 team and incremental sharing"] + E9_05["U-E9-05"]:::ext --> E11_04["04 legacy TUI extras"] + E11_01["01 speech"] + E11_02["02 image generation"] + E11_05["05 experimental"] + E11_06["06 KiloClaw"] + classDef ext stroke-dasharray: 4 4 +``` + +## B13. L Cross-cutting lanes + +| Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | +|---|---|---|---|---|---|---|---|---|---| +| U-L1-01 | Upstream sync: merge `upstream/v2` pinned by SHA; dry-run workflow; merge tooling (one child per merge) | new | continuous | U-E0-04 | needs-port | M | no | Each merge pinned, baseline re-run, failures diffed against the baseline, lag recorded | todo | +| U-L2-01 | v1 delta sweep: advance the cutoff, refresh the experimental-flag list, re-run staleness (one child per sweep) | new | continuous | — | needs-port | n/a | no | First sweep starts at `d99662338`; zero unclassified Kilo-owned or marked files in range; touched units re-opened | todo | +| U-L3-01 | Shared-patch ratchet and marker disposition sweep | #14381 | continuous | U-E0-01 | needs-port | n/a | no | Script counts the three L3 metrics; CI fails an increase without a stated seam | todo | +| U-L4-01 | Rewrite test plans with gate tags and v1-derived expectations (G1 scenario set) | new | G1 | — | needs-port | n/a | no | Each scenario tagged G1/G2/G3; expectations quoted from v1; RT and UI IDs kept | todo | +| U-L4-02 | Acceptance harness: simulation/http-recorder, isolated real-host runner | new | G1 | U-E0-01 | needs-port | n/a | no | Harness runs scenarios on BASE; every fixture isolates HOME/XDG itself | todo | +| U-L4-03 | G2 scenario set (VS Code) | new | G2 | — | needs-port | n/a | no | Scenarios cover sidebar, tabs, Agent Manager, settings, terminals, reconnect, import, handshake, rollback | todo | +| U-L4-04 | G3 scenario set (JetBrains) | new | G3 | — | needs-port | n/a | no | Scenarios cover startup, auth, sessions, permissions, history, terminal, editor services, reconnect, restart, version mismatch | todo | +| U-L4-05 | G1 acceptance run | new | G1 | All G1 units except U-E3-11; U-L4-01, U-L4-02 | needs-port | n/a | no | Every G1 scenario run on an isolated real host, recorded with SHA | todo | +| U-L4-06 | G2 acceptance run | new | G2 | All G2 units except U-E6-21; U-L4-03 | needs-port | n/a | no | Every G2 scenario run on a real VS Code host, recorded with SHA | todo | +| U-L4-07 | G3 acceptance run | new | G3 | All G3 units except U-E7-07; U-L4-04 | needs-port | n/a | no | Every G3 scenario run in each supported IDE, recorded with SHA | todo | +| U-L5-01 | Docs: kilo.ai/docs content for v2 at each gate | new | continuous | — | partial | M | no | Docs describe the shipped surface at each gate | todo | + +```mermaid +flowchart LR + E0_04["U-E0-04"]:::ext --> L1_01["L1 upstream sync"] + E0_01["U-E0-01"]:::ext --> L3_01["L3 ratchet"] & L4_02["L4-02 harness"] + L2_01["L2 delta sweep"] + L4_01["L4-01 G1 scenarios"] & L4_02 --> L4_05["L4-05 G1 run"] + G1U["all G1 units"]:::ext --> L4_05 + L4_03["L4-03 G2 scenarios"] --> L4_06["L4-06 G2 run"] + G2U["all G2 units"]:::ext --> L4_06 + L4_04["L4-04 G3 scenarios"] --> L4_07["L4-07 G3 run"] + G3U["all G3 units"]:::ext --> L4_07 + L5_01["L5 docs"] + classDef ext stroke-dasharray: 4 4 +``` + +## B14. Reconciliation mapping + +### Issues in the #13750 tree + +| Issue | New home | Note | +|---|---|---| +| #13750 | Plan root | Published body still points at `kilo-v2`; needs a user update to link this plan | +| #14016 | Tracker → E6 | | +| #14017 | Tracker → E2, E9 | | +| #14018 | Tracker → E5 (+ E11) | Team ownership and incremental sync are now non-gating | +| #14019 | Tracker → E4 | | +| #14020 | Tracker → E3 | | +| #14021 | Tracker → U-E2-28, U-E6-07, U-E6-08, U-E6-20 | Speech, image and Claw items move to E11 | +| #14022 | Tracker → E7 | | +| #14023 | Tracker → U-E10-01 | | +| #14024 | Tracker → U-E10-02 | | +| #14371 | U-E2-01 | | +| #14372 | U-E2-02 | | +| #14373 | U-E2-03 | | +| #14374 | U-E2-04 | | +| #14375 | U-E9-09 | | +| #14376 | U-E11-04 | | +| #14377 | U-E9-10 | | +| #14378 | U-E9-12 (alerts, sound) + U-E11-04 (tables, bindings) | Split | +| #14379 | U-E9-11 | | +| #14380 | U-E2-05 (+ caller in U-E6-03) | | +| #14381 | U-L3-01 | | +| #14382 | U-E2-13 | | +| #14383 | U-E6-01 | | +| #14384 | U-E6-03 | | +| #14385 | U-E6-04 | | +| #14386 | U-E6-05 | | +| #14387 | U-E6-02 | | +| #14388 | U-E6-06 | | +| #14389 | U-E6-07 | | +| #14390 | U-E6-09 | | +| #14391 | U-E2-27 (runtime) + U-E6-10 (consumer) | Split | +| #14392 | U-E6-12 | | +| #14393 | U-E6-13 | | +| #14394 | U-E6-14 | | +| #14395 | U-E4-06 (host) + U-E6-15 (VS Code) | Split | +| #14396 | U-E5-01 | | +| #14397 | U-E5-02 | | +| #14398 | U-E5-03 | | +| #14399 | U-E5-04 (fail-closed) + U-E11-03 (ownership) | Split | +| #14400 | U-E11-03 | | +| #14401 | U-E5-05 | | +| #14402 | U-E4-01 | | +| #14403 | U-E4-02 | | +| #14404 | U-E4-03 | | +| #14405 | U-E4-04 | | +| #14406 | U-E4-05 | | +| #14407 | U-E4-08 | | +| #14408 | U-E3-08 | The fork-safety part of its scope is U-E0-04 | +| #14409 | U-E3-07 | | +| #14410 | U-E3-06 | | +| #14411 | U-E3-13 | Split out of the build pipeline to start early | +| #14412 | U-E3-07 | | +| #14413 | U-E3-01 + U-E3-02 + U-E3-03 | Split | +| #14414 | U-E3-11 (G1) + U-E6-21 (G2 child, new) + U-E7-07 (G3 child, new) | | +| #14415 | Evidence for U-E6-08 (`editor-services-inventory.md`) | Done; close candidate for the user | +| #14416 | U-E6-08 (producer) + U-E6-20 (VS Code) (+ U-E7-04 if Q-G3-01 keeps the gate) | Split | +| #14417 | U-E6-07 | | +| #14418 | U-E11-01 | | +| #14419 | U-E11-02 (image) + U-E11-06 (Claw) | Split | +| #14420 | U-E7-01 | | +| #14421 | U-E7-02 | | +| #14422 | U-E7-03 | | +| #14423 | U-E7-04 | | +| #14424 | U-E7-06 | | +| #14425 | Evidence for U-E10-01 (`cloud-agent-consumers.md`) | Done; close candidate | +| #14426 | Evidence for U-E10-01 (`cloud-agent-contracts.md`) | Done; close candidate | +| #14427 | U-E10-01 | | +| #14428 | U-E10-01 | | +| #14429 | U-E10-02 (discovery first) | | +| #14430 | U-E10-02 | | +| #14431 | U-E10-02 | | +| #14432 | U-E10-02 | | + +Issues that map to more than one unit (#14378, #14391, #14395, #14399, #14413, #14414, #14416, #14419) either become umbrellas with one child per unit, or are narrowed; that choice is the user's. + +### Old-plan units + +| Old unit | New home | +|---|---| +| Phase 0 P1–P4 (create and link issues) | User checklist: the `new` rows below | +| N1 consolidation merge and scope rename | Not needed: the base is a clean upstream commit. Scope rename happens at copy time in each unit (B0) | +| N2 ratchet | U-L3-01 | +| N3 sync automation and e2e lane | U-L1-01 + U-E0-04 | +| N4 delta sweep | U-L2-01 | +| N5 v1 reference fixtures | U-E0-05 | +| N6a package assessment | U-E9-14 (`kilo-web-ui`), U-E8-03 (`kilo-console` retired, docs deploy), U-L5-01 (docs content) | +| N6b package imports onto the old branch | Not needed as a unit: `kilo-jetbrains` lands in U-E7-02, `kilo-docs` in U-E8-03 | +| N7 progress-plan and issue alignment | Not needed: this plan replaces the progress plan; issue-body updates are a user follow-up | +| N8 customer v2 store identity | U-E1-02 (+ v1-IDE isolation in U-E3-12) | +| N9 handshake and extension switch | U-E1-08 (server) + U-E6-02 (client) | +| N10 v1 retirement | U-E8-01, U-E8-02, U-E8-04 (backlog → E11) | +| N11 gate acceptance runs | U-L4-05, U-L4-06, U-L4-07 | +| N12 TUI picker, dialogs, status/footer (progress-plan rows without issues) | U-E9-06 (dialogs, provider guidance), U-E9-07 (picker, status/footer) | +| N13 runtime integration remainder (progress-plan rows without issues) | U-E2-18 (memory indicators), U-E2-19 (indexing CLI/cloud paths), U-E2-20 (sandbox validation and policy variants) | +| N14 original-extension consumers (progress-plan rows without issues) | U-E6-16 | +| N15 VS Code scenario set | U-L4-03 | +| N16 JetBrains scenario set | U-L4-04 | +| N17 preview release train | U-E3-10 | + +### Units that need a new issue (78) + +| Epic | Units | +|---|---| +| E0 | U-E0-01, -02, -03, -04, -05 | +| E1 | U-E1-01 … -08 (all eight) | +| E2 | U-E2-06 … -12, -14, -15, -16, -17 … -26, -28, -29, -30 (all except -01 … -05, -13, -27) | +| E9 | U-E9-01 … -08, -13, -14, -15 | +| E3 | U-E3-04, -05, -09, -10, -12 | +| E4 | U-E4-07 | +| E5 | U-E5-06 | +| E10 | U-E10-03 | +| E6 | U-E6-11, -16, -17, -18, -19, -21 (child of #14414) | +| E7 | U-E7-05, -07 (child of #14414) | +| E8 | U-E8-01 … -04 | +| E11 | U-E11-05 | +| L | U-L1-01, U-L2-01, U-L4-01 … -07, U-L5-01 | + +## B15. Divergences from discovery + +| Change | Reason | +|---|---| +| Epic IDs kept (E9, E10, E11 not renumbered); presented in program order | Keeps register rows traceable; renumbering would add a mapping table and no information | +| U-E2-16 split into U-E2-16 (registry, question, suggest, todo) and U-E2-30 (content and code tools) | MATERIAL + NEW (+2,205 lines in `tool/`) is too large for one PR; U-E4-03 needs only the suggest tool | +| U-E3-07 split; signing becomes U-E3-13 | External lead time and account credentials (old plan line 194; admin task); it should start before the pipeline is complete | +| U-E6-08 split into the producer (U-E6-08, shared runtime) and the VS Code integration (U-E6-20) | The producer is the largest IDE gate and also the possible JetBrains dependency; it can start in Phase 1 independently of the extension re-land | +| New U-E6-21 and U-E7-07 (G2 and G3 canary children) | Discovery left them "later"; the gate map needs them as units | +| U-L4-01 split into U-L4-01 … -07 | Scenario sets for G2/G3 can start now and belong to the IDE tracks; one run per gate keeps one unit = one PR and makes gate conditions countable | +| U-E3-10 gains explicit deps on U-E3-08 and U-E3-12; U-E3-11 on U-L4-05 | The old plan required CI guards before the preview; the distribution decision must precede it; the canary is exercised on the accepted candidate | +| U-E0-03 narrowed | README rewrite, superseded banners and the doc drops were done in this planning commit (Appendix D) | +| Q-HOST-01 and Q-TEL-01 added | Open question from the old issue body (host package shape) and an undecided privacy default (telemetry) | +| Status column added; the register is frozen | D9 one file: Part B is the living unit index | +| Serialization chain U-E9-09 → -12 made explicit | Same-file TUI work (old plan line 112) | + +No discovery epic or unit was dropped. Every discovery unit keeps its ID. + +--- + +# Appendices + +## Appendix A. Pinned refs and current-state facts + +| Fact | Canonical place | +|---|---| +| Integration branch, upstream base, release marker, archived donor tag, v1 target, assessment snapshot and the `d99662338` note | [`migration-context.md` §Branches and pinned refs](migration-context.md#branches-and-pinned-refs) | +| Ancestry, toolchain, validation commands, current reds | [`pinned-v2-baseline.md`](../technical-notes/baseline/pinned-v2-baseline.md) | +| v1 line facts (version, cadence, channels) and upstream v2 facts | [`v2-feature-inventory.md`](../discovery/v2-feature-inventory.md) | +| Donor facts and staleness per area | [`v2-unit-register.md`](../discovery/v2-unit-register.md) | +| Facts preserved from the old plans | [`doc-inventory.md` §7](../discovery/doc-inventory.md) | +| Working model, conventions, Definition of done, admin tasks | [`migration-context.md`](migration-context.md) | + +## Appendix B. Glossary + +| Term | Meaning | +|---|---| +| v1 | Kilo on `main`, built on opencode v1. The behavioral reference at the pinned v1 target | +| v2 | Kilo on upstream opencode `v2`, built on `kilo-v2-clean` | +| BASE | The pinned upstream commit `kilo-v2-clean` started from | +| Donor | The archived old port (tag), used for mechanism only | +| Snapshot / cutoff | The v1 commit up to which every Kilo change has a disposition. L2 advances it | +| Staleness | v1 change to a unit's files between snapshot and v1 target (D4) | +| Unit | One session, one branch, one PR, one Part B row | +| Gate | A surface go/no-go: G1 CLI/TUI, G2 VS Code, G3 JetBrains | +| Surface | Where a unit ships: shared runtime, gateway/cloud, CLI/TUI, packaging, import, VS Code, JetBrains, external consumer, repo | +| Lane | Continuous work with a steady state instead of an exit (L1–L5) | +| External-gated | Acceptance that needs a deployed service or another team | + +## Appendix C. Technical notes + +| Epic | Notes under `technical-notes/baseline/` unless stated | +|---|---| +| E0 | `pinned-v2-baseline.md`, `v1-fixture-generation.md`, `cli-fixture-storage-incident.md`, `../v2-fork-conventions.md`, `../script/v2-baseline.ts` | +| E1 | `model-prompt-policy-v2-parity.md`, `model-prompt-v2-audit.md`, `settings-v2-parity.md`, `startup-loading-v2.md`, `reliability-checkpoint-2026-09-07.md` | +| E2 | `agent-policy-v2-parity.md`, `explore-policy-v2-parity.md`, `plan-v2-parity.md`, `skills-v2-parity.md`, `skill-shell-parity.md`, `memory-v2-parity.md`, `memory-activity-next-slice.md`, `indexing-v2-parity.md`, `sandbox-linux-validation.md`, `sandbox-spawn-next-slice.md`, `telemetry-v2-overlap.md`, `swarm-v2-parity.md`, `v2-v1-session-diffs.md` | +| E9 | `cli-tui-closure-verification.md`, `model-loading-v2-parity.md`, `model-info-bench-seam.md`, `model-sidebar-v2-parity.md`, `sidebar-*-v2-parity.md`, `memory-sidebar-v2-parity.md`, `memory-ui-v2-parity.md`, `cloud-cli-v2-parity.md` | +| E3 | `v1-session-schema-audit.md`, `credential-import-v2-parity.md`, `config-mapping-v2-gap.md`, `external-resume-parity.md`, `updater-local-parity.md`, `packaging-correction-plan.md`, `auto-packaging-v2-probe.md` | +| E4 | `remote-v2-parity.md`, `remote-share-next-contracts.md` | +| E5 | `../session-sharing.md`, `remote-share-next-contracts.md` | +| E10 | `cloud-agent-consumers.md`, `cloud-agent-contracts.md` | +| E6 | `existing-vscode-port.md`, `extension-surface-inventory.md`, `editor-services-inventory.md`, `vscode-packaging.md`, `v2-v1-schema-diffs.md` | +| E7 | `editor-services-inventory.md` (JetBrains consumers) | +| L | `../../marker-audit/` (assessment, shared-change inventory) | + +Notes measured against the donor's upstream base are evidence: re-check their seam claims at BASE before reuse. + +## Appendix D. Document moves + +Applied by this plan: + +| File | Action | +|---|---| +| `plans/kilo-opencode-v2-execution-plan.md`, `plans/kilo-opencode-v2-plan-progress.md` | Superseded banner | +| `plans/kilo-opencode-v2-working-log.md` | Banner: frozen evidence; its pause instruction no longer applies | +| `README.md` | Rewritten as the living index | +| `github-drafts/issue-13750/progress-comment.md`, `shared-source-footprint.md`, `tracking-policy.md` | Dropped (tracking rules now in B0) | +| `test-plans/*.md`, `technical-notes/v2-fork-conventions.md` | Stale banner pending rewrite | + +Recorded as units: Kilo `AGENTS.md`, `v2-fork-conventions.md` and `v2-baseline.ts` rewrites (U-E0-03); test-plan rewrite (U-L4-01). `.DS_Store` files are untracked and already ignored; nothing to do. diff --git a/migration-tracking/plans/migration-context.md b/migration-tracking/plans/migration-context.md index 688b432f8803..7cb03e7372a5 100644 --- a/migration-tracking/plans/migration-context.md +++ b/migration-tracking/plans/migration-context.md @@ -2,7 +2,7 @@ This file is the single source for the shared facts, decisions and rules of the Kilo v1 → v2 migration. Role prompts (discovery, planner, coordinator) and implementation session prompts point here instead of restating it. When a fact here changes, change it here first. -Status: bootstrap. Last updated 2026-10-12. +Status: planned (bootstrap docs complete; no unit started). Last updated 2026-10-12. Plan: [`kilo-v2-migration-plan.md`](kilo-v2-migration-plan.md). ## Branches and pinned refs @@ -43,7 +43,7 @@ Do not re-litigate these without new evidence. Proposed changes go to the user. ## Deferred decisions -Decisions intentionally left open until the work that needs them. Each has a deadline; the coordinator raises it with the user and owners before that point. Detail and evidence: `migration-tracking/discovery/v2-unit-register.md`, "Open questions". +Decisions intentionally left open until the work that needs them. Each has a deadline; the coordinator raises it with the user and owners before that point. Detail and evidence: `migration-tracking/discovery/v2-unit-register.md`, "Open questions"; questions added by the planner (Q-HOST-01, Q-TEL-01): `kilo-v2-migration-plan.md` §A9. | Decision | Deadline | |---|---| @@ -80,7 +80,7 @@ Decisions intentionally left open until the work that needs them. Each has a dea ## Definition of done per unit -Code, tests, acceptance evidence, an updated progress row, and docs. A local pass is never recorded as end-to-end. +Code, tests, acceptance evidence, an updated unit row (Status cell in Part B of the plan), and docs. A local pass is never recorded as end-to-end. ## Deferred admin tasks @@ -105,11 +105,12 @@ These need a repo admin (or account credentials) and do not block work. Track th | Branch protection | Deferred (admin task) | | Baseline health recorded (`bun install`, `bun run check`) | Done 2026-10-11: `technical-notes/baseline/pinned-v2-baseline.md` | | Discovery report | Done 2026-10-12: `migration-tracking/discovery/` | +| Migration plan | Done 2026-10-12: `migration-tracking/plans/kilo-v2-migration-plan.md` | ## Reference artifacts -- Plans: `migration-tracking/plans/` (previous execution plan, progress plan and working log; reference only until the new plan lands). -- Discovery outputs: `migration-tracking/discovery/` (feature inventory, epic taxonomy, unit register, doc inventory). +- Plan (living): `migration-tracking/plans/kilo-v2-migration-plan.md`. The previous execution plan, progress plan and working log in `plans/` are superseded (banners at the top) and kept as reference only. +- Discovery outputs (frozen evidence once absorbed by the plan): `migration-tracking/discovery/` (feature inventory, epic taxonomy, unit register, doc inventory). - Source parity: `migration-tracking/marker-audit/` (the 816-file assessment and shared-change inventory). - API and session differences: `migration-tracking/technical-notes/baseline/v2-v1-schema-diffs.md`, `migration-tracking/technical-notes/baseline/v2-v1-session-diffs.md`. - Technical notes: `migration-tracking/technical-notes/` and `technical-notes/baseline/`. diff --git a/migration-tracking/technical-notes/v2-fork-conventions.md b/migration-tracking/technical-notes/v2-fork-conventions.md index 8e96b7e88bbe..4dc3f2de4f74 100644 --- a/migration-tracking/technical-notes/v2-fork-conventions.md +++ b/migration-tracking/technical-notes/v2-fork-conventions.md @@ -1,5 +1,8 @@ # Kilo OpenCode v2 fork conventions +> [!WARNING] +> **Partly stale; rewrite pending in U-E0-03 ([plan](../plans/kilo-v2-migration-plan.md)).** The pin `76dbaf20`, the `johnnyeric/` branch model, `packages/www` paths and companion links are wrong for `kilo-v2-clean`. Where this file conflicts with `plans/migration-context.md` or `AGENTS.md`, those win. The substantive rules (merge forward, triage vocabulary, Kilo-owned placement, narrow hooks, markers, compact tables) still apply. Evidence: `discovery/doc-inventory.md` §4. + Rules for working on the Kilo product line built on `upstream/v2`. Companion to `migration-tracking/plans/kilo-opencode-v2-plan-progress.md` (scope and phases) and `migration-tracking/technical-notes/baseline/pinned-v2-baseline.md` (refs, commands, known-red checks). diff --git a/migration-tracking/test-plans/kilo-opencode-v2-test-plan-runtime.md b/migration-tracking/test-plans/kilo-opencode-v2-test-plan-runtime.md index ac56ba7b08e3..3cc19a0657bb 100644 --- a/migration-tracking/test-plans/kilo-opencode-v2-test-plan-runtime.md +++ b/migration-tracking/test-plans/kilo-opencode-v2-test-plan-runtime.md @@ -1,5 +1,8 @@ # Kilo OpenCode v2 runtime test plan +> [!WARNING] +> **Stale; rewrite pending in U-L4-01 ([plan](../plans/kilo-v2-migration-plan.md)).** Expected behavior was quoted from the archived port, not v1 (Decision 3). Keep the RT IDs; do not use the expectations as acceptance. + Owned by the runtime test-plan delegate for thread `thr_qr9z7p3cqc` (Bootstrap OpenCode v2 baseline). Companion to the main index `plans/kilo-opencode-v2-test-plan.md` (not edited here) and the separately diff --git a/migration-tracking/test-plans/kilo-opencode-v2-test-plan-ui.md b/migration-tracking/test-plans/kilo-opencode-v2-test-plan-ui.md index 75a0c8784a3a..aa041825292f 100644 --- a/migration-tracking/test-plans/kilo-opencode-v2-test-plan-ui.md +++ b/migration-tracking/test-plans/kilo-opencode-v2-test-plan-ui.md @@ -1,5 +1,8 @@ # Kilo v2 CLI/TUI Scenario Test Plan +> [!WARNING] +> **Stale; rewrite pending in U-L4-01 ([plan](../plans/kilo-v2-migration-plan.md)).** Expected behavior was quoted from the archived port, not v1 (Decision 3). Keep the UI IDs; do not use the expectations as acceptance. + > **Scope:** Interactive CLI/TUI scenarios, including startup, branding, theme/dimensions, BB terminal lifecycle, slash commands, model picker, agent selection and policies, native Plan mode, Kilo settings, memory UI and modal, and status/sidebar truthfulness. > > **Status:** **ALL MANUAL TESTS NOT RUN.** diff --git a/migration-tracking/test-plans/kilo-opencode-v2-test-plan.md b/migration-tracking/test-plans/kilo-opencode-v2-test-plan.md index 169cd9971234..5c95ff758637 100644 --- a/migration-tracking/test-plans/kilo-opencode-v2-test-plan.md +++ b/migration-tracking/test-plans/kilo-opencode-v2-test-plan.md @@ -1,5 +1,8 @@ # Kilo v2 scenario test plan +> [!WARNING] +> **Stale; rewrite pending in U-L4-01 ([plan](../plans/kilo-v2-migration-plan.md)).** Expected behavior was quoted from the archived port, not v1 (Decision 3), and scenarios carry no gate tags. Keep the scenario IDs; do not use the expectations as acceptance. + Separate from the [implementation plan](kilo-opencode-v2-issue-13750.md). This is a test checklist, not a claim that scenarios have passed. From 3bdf2fd5bd7d76109d7e08730b0f99ffaa44092b Mon Sep 17 00:00:00 2001 From: Fabio Pliger Date: Sun, 11 Oct 2026 23:08:26 -0500 Subject: [PATCH 08/18] docs(migration): explain why the new plan replaces the previous one --- .../plans/kilo-v2-migration-plan.md | 41 ++++++++++++++----- migration-tracking/plans/migration-context.md | 2 +- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/migration-tracking/plans/kilo-v2-migration-plan.md b/migration-tracking/plans/kilo-v2-migration-plan.md index 7dba3ea4ce89..3574aa04d45e 100644 --- a/migration-tracking/plans/kilo-v2-migration-plan.md +++ b/migration-tracking/plans/kilo-v2-migration-plan.md @@ -18,7 +18,28 @@ This file is the living index of the migration: epics, gates and sequencing (Par Ship Kilo on the upstream v2 runtime, surface by surface, without breaking a v1 user. The target feature set is upstream v2 at the pinned base plus every Kilo v1 behavior at the pinned v1 target, minus what Decision 7 marks post-cutover or not-needed: feature parity, not code parity. The terminal product (CLI/TUI) ships first at G1; VS Code (G2) and JetBrains (G3) follow on their own go/no-go, and until then they keep running on v1 against untouched v1 data. The work ends when `kilo-v2-clean` becomes `main` and v1 moves to a time-limited maintenance branch. -## A2. Decisions in force +## A2. Why this plan replaces the previous one + +The previous plan (finalized 2026-09-30, never started) was a sound plan for the situation it assumed: finish the existing `kilo-v2` port. That assumption no longer holds. The port is now frozen as donor code (Decisions 1–2), and the facts measured during discovery change what the remaining work actually is. The previous plan's facts and decisions are preserved; its structure is not. + +| What the previous plan assumed or lacked | What we found | How this plan handles it | +|---|---|---| +| Work continues on the `kilo-v2` port; the first step is one large consolidation merge of upstream | The port never merged upstream and sits 1,454 upstream commits behind the new base. Upstream moves about 1,000 commits every few weeks | Start from a clean upstream base; upstream arrives through small SHA-pinned merges in a dedicated lane (L1) | +| The port's behavior is close enough to v1 to finish from | Port behavior is an August snapshot. Since then v1 shipped 3,107 commits touching 4,104 files. Staleness was never measured per feature | Measure staleness per unit (D4). 80 of 137 units are MATERIAL: behavior is re-derived from current v1, and the port supplies only the v2 wiring | +| Identity, Gateway, catalog, config, engines and most of the TUI were "implemented, full end-to-end pending", so they had no issues | On a clean base none of that code exists | Those areas are now explicit units (E0, E1, most of E2 and E9). This is the largest block of work and the main source of new issues | +| Its own 17 new units (consolidation, ratchet, sync, fixtures, store, handshake, scenario sets, preview train) would be created as issues first | None was created | Every one is mapped to a unit in Part B; the obsolete ones (consolidation merge, old-branch imports, progress-plan alignment) are dropped with a reason | +| Live status was split across the plan, a progress plan and a working log | The three disagreed; the progress plan said "paused", and the working log still carried a live "do not resume implementation" instruction | One file holds sequencing and unit status (D9); the old documents carry superseded banners | +| Acceptance was the existing test plans | Their expected results were quoted from the port's source, not from v1, and there were no IDE scenarios at all | Acceptance is written against v1 for every unit; G2 and G3 get their own scenario sets and acceptance runs (L4) | +| Some sharing and editor-service items were completion criteria | Team and incremental sharing, speech, image generation and KiloClaw are non-gating under Decision 7 | Non-gating scope lives in one backlog epic (E11), so gate exits are unambiguous | +| The v2 binary would ship under the customer `kilo` name; the preview identity was not for customers | The cloud owners have since proposed a `kilo2` binary on a `next` dist-tag, and v1 VS Code resolves the CLI without a version pin | The binary-and-channel decision is a named deadline before the first distributed preview, and v1-IDE isolation is a tested unit | + +**What this plan does not do yet.** +- No owner is assigned, so the critical path in A7 is structural and has no dates. +- Acceptance lines were written by the planner from discovery evidence; epic owners should challenge them. +- Donor seam claims were measured against the old upstream base and are re-checked inside each unit, not up front. +- The plan is long by design: only Part A is meant to be read end to end. + +## A3. Decisions in force The ten decisions in [`migration-context.md` §Decisions](migration-context.md#decisions-authoritative) are authoritative. The plan applies them as follows; it does not change any of them. @@ -28,16 +49,16 @@ The ten decisions in [`migration-context.md` §Decisions](migration-context.md#d | D2 Freeze | The archived port is read through its tag; nothing merges from it | | D3 Donor rules | Each Part B row states donor reuse for mechanism only. Behavior acceptance is always written against v1 | | D4 Staleness gate | Each Part B row carries a staleness verdict. Non-EMPTY means behavior is re-derived from v1 | -| D5 Staged cutover | Three gates, two parallel IDE tracks (A6) | +| D5 Staged cutover | Three gates, two parallel IDE tracks (A7) | | D6 Storage | Store isolation is an E1 exit criterion; import is E3; consolidation is E8 after G3 | | D7 Scope | Non-gating scope is isolated in E11; the hard FIM gate sits in E6 | | D8 Gates over P-labels | Gate tags in Part B decide surface readiness; P-labels only order work inside a gate | | D9 Plan format | This file | | D10 Readability | Part A carries no issue IDs; facts are linked, not copied | -Items that need user sign-off are listed in A8. None of them changes a decision. +Items that need user sign-off are listed in A10. None of them changes a decision. -## A3. Approach +## A4. Approach **Clean base.** `kilo-v2-clean` starts as pristine upstream and contains no Kilo package yet. Kilo is re-landed onto it in small units, each one a session, a branch and a PR, merged by the user. Nothing from the old port branch is merged. @@ -51,7 +72,7 @@ Items that need user sign-off are listed in A8. None of them changes a decision. **Sequencing, not dates.** No owner is assigned and no effort estimate exists. The plan orders work by dependency and external lead time. Dates attach when owners do. -## A4. Program phase map +## A5. Program phase map ```mermaid flowchart LR @@ -83,7 +104,7 @@ flowchart LR | Phase 4 Retirement | E8 | G3 go | Branch flip, store consolidation, v1 end of life | | Backlog | E11 | Any time after its gate surface ships | Every item has a recorded decision | -## A5. Epic map +## A6. Epic map Epic IDs follow discovery so register rows stay traceable; they are presented in program order. Exit criteria below are canonical from now on. @@ -205,7 +226,7 @@ Epic IDs follow discovery so register rows stay traceable; they are presented in | L4 Acceptance | Gate-tagged scenarios with v1-derived expectations; one recorded run per gate | | L5 Docs | kilo.ai/docs describes the shipped surface at each gate | -## A6. Gate map and critical path +## A7. Gate map and critical path ```mermaid flowchart LR @@ -259,7 +280,7 @@ What must be true at each gate: G2 and G3 do not wait for the G1 go/no-go (D5). They do need the shared-runtime units they consume to be accepted. -## A7. Program risks +## A8. Program risks | Risk | Effect | Mitigation | Owner | |---|---|---|---| @@ -274,7 +295,7 @@ G2 and G3 do not wait for the G1 go/no-go (D5). They do need the shared-runtime | External services and repositories (Gateway, relay, share backend, viewer, cloud agents) | Gate acceptance blocked outside the team | Book owners at unit start; waivers only by the user, recorded | E4, E5, E10 | | Import not proven lossless | Data loss on first import | Re-run the schema audit at the current v1 target and base; byte-identical originals test | E3 | -## A8. Metrics rollup +## A9. Metrics rollup Counts at plan creation. Live values are tracked in Part B status cells. @@ -315,7 +336,7 @@ Metrics the coordinator reports at each gate review: | Units without an issue | Part B rows marked `new` | Part B | | Canary health | Install, update, rollback and crash signals from the preview train and canary | E3 | -## A9. Deferred decisions and sign-offs +## A10. Deferred decisions and sign-offs Deferred decisions and their deadlines are canonical in [`migration-context.md` §Deferred decisions](migration-context.md#deferred-decisions); the questions with triggers are in the [register's open questions](../discovery/v2-unit-register.md#open-questions). The coordinator raises each one before its trigger unit starts. diff --git a/migration-tracking/plans/migration-context.md b/migration-tracking/plans/migration-context.md index 7cb03e7372a5..553c641cd031 100644 --- a/migration-tracking/plans/migration-context.md +++ b/migration-tracking/plans/migration-context.md @@ -43,7 +43,7 @@ Do not re-litigate these without new evidence. Proposed changes go to the user. ## Deferred decisions -Decisions intentionally left open until the work that needs them. Each has a deadline; the coordinator raises it with the user and owners before that point. Detail and evidence: `migration-tracking/discovery/v2-unit-register.md`, "Open questions"; questions added by the planner (Q-HOST-01, Q-TEL-01): `kilo-v2-migration-plan.md` §A9. +Decisions intentionally left open until the work that needs them. Each has a deadline; the coordinator raises it with the user and owners before that point. Detail and evidence: `migration-tracking/discovery/v2-unit-register.md`, "Open questions"; questions added by the planner (Q-HOST-01, Q-TEL-01): `kilo-v2-migration-plan.md` §A10. | Decision | Deadline | |---|---| From 9d6b6776b8ad713442b6f52615500a4ca4d13696 Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Mon, 12 Oct 2026 12:15:23 +0200 Subject: [PATCH 09/18] chore(dev): run bun dev in an isolated v2 profile --- .gitignore | 1 + package.json | 2 +- script/dev-isolated.ts | 68 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 script/dev-isolated.ts diff --git a/.gitignore b/.gitignore index 477fdab9d85f..9015f838b52e 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ target # Local dev files opencode-dev +.kilo-v2-dev/ UPCOMING_CHANGELOG.md RELEASE_REVIEW.md logs/ diff --git a/package.json b/package.json index c0c9573f3994..aa96fdc685f2 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "type": "module", "packageManager": "bun@1.4.2", "scripts": { - "dev": "bun run --cwd packages/cli src/index.ts", + "dev": "bun script/dev-isolated.ts", "dev:live": "sh -c 'OPENCODE_TUI_CHANNEL=dev OPENCODE_PASSWORD=\"$(opencode service get password)\" exec bun run dev \"$@\" --server \"$(opencode service status)\"' --", "dev:vite": "bun run --cwd packages/cli --conditions=browser dev/vite.ts", "dev:vite:live": "sh -c 'OPENCODE_TUI_CHANNEL=dev OPENCODE_PASSWORD=\"$(opencode service get password)\" exec bun run dev:vite \"$@\" --server \"$(opencode service status)\"' --", diff --git a/script/dev-isolated.ts b/script/dev-isolated.ts new file mode 100644 index 000000000000..6522ebe10e80 --- /dev/null +++ b/script/dev-isolated.ts @@ -0,0 +1,68 @@ +// `bun dev` on the v2 branch: runs the CLI from this checkout with a profile that is separate from +// installed Kilo and OpenCode. Data, config, state, cache, home and tmp all live under one directory, +// so no global store, managed service or database outside it is read or changed. +import os from "node:os" +import path from "node:path" +import { mkdir } from "node:fs/promises" + +const root = path.resolve(import.meta.dir, "..") +const profile = path.resolve(process.env.KILO_V2_PROFILE ?? path.join(root, ".kilo-v2-dev")) +const dirs = ["data", "config", "state", "cache", "home", "tmp"].map((name) => path.join(profile, name)) +await Promise.all(dirs.map((dir) => mkdir(dir, { recursive: true }))) +const login = await kiloLogin() +console.error(`kilo v2 dev profile: ${profile}${login.KILO_API_KEY ? " (Kilo login from v1 Kilo)" : ""}`) + +const child = Bun.spawn( + [ + process.execPath, + "--preload", + Bun.resolveSync("@opentui/solid/preload", path.join(root, "packages/cli")), + path.join(root, "packages/cli/src/index.ts"), + ...process.argv.slice(2), + ], + { + // The CLI uses the working directory as the project directory. + cwd: process.env.INIT_CWD ?? process.cwd(), + env: { + ...process.env, + ...login, + XDG_DATA_HOME: dirs[0], + XDG_CONFIG_HOME: dirs[1], + XDG_STATE_HOME: dirs[2], + XDG_CACHE_HOME: dirs[3], + OPENCODE_TEST_HOME: dirs[4], + TMPDIR: dirs[5], + OPENCODE_DISABLE_AUTOUPDATE: "1", + }, + stdin: "inherit", + stdout: "inherit", + stderr: "inherit", + }, +) +process.exit(await child.exited) + +// Development only: reuse the v1 Kilo login so the Kilo Gateway works without a separate sign-in, +// billed to the selected team like v1. The v1 auth file is only read. KILO_API_KEY in the +// environment wins. Product login (device auth, team switching) replaces this later. +async function kiloLogin(): Promise> { + const config = (headers: Record) => + process.env.OPENCODE_CONFIG_CONTENT + ? {} + : { + OPENCODE_CONFIG_CONTENT: JSON.stringify({ + model: "kilo/kilo-auto/free", + provider: { kilo: { options: { headers } } }, + }), + } + if (process.env.KILO_API_KEY) return config({}) + const file = Bun.file( + path.join(process.env.XDG_DATA_HOME ?? path.join(os.homedir(), ".local", "share"), "kilo", "auth.json"), + ) + if (!(await file.exists())) return config({}) + const auth = (await file.json()).kilo + if (auth?.type !== "oauth" || typeof auth.access !== "string") return config({}) + return { + KILO_API_KEY: auth.access, + ...config(typeof auth.accountId === "string" ? { "X-KILOCODE-ORGANIZATIONID": auth.accountId } : {}), + } +} From a542093b5c4fec2104bce2c9e1aa9a8c83366de4 Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Mon, 12 Oct 2026 13:41:56 +0200 Subject: [PATCH 10/18] docs: add v2 migration skill for agents --- .kilo/skills/v2-migration/SKILL.md | 80 ++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 .kilo/skills/v2-migration/SKILL.md diff --git a/.kilo/skills/v2-migration/SKILL.md b/.kilo/skills/v2-migration/SKILL.md new file mode 100644 index 000000000000..7c2ee84303ae --- /dev/null +++ b/.kilo/skills/v2-migration/SKILL.md @@ -0,0 +1,80 @@ +--- +name: v2-migration +description: Use for any work on the Kilo v2 migration (branches kilo-v2-clean*, v2-*). Covers strategy, branches and PRs, the isolated test profile, how to self-test the v2 CLI yourself, and the test policy. +--- + +# Kilo v2 migration + +Kilo moves onto upstream OpenCode v2 from a clean base, one feature at a time. Verify your own work. The user should not need to test it for you. + +## Sources of truth + +- Behavior: v1 Kilo on `main`. Mechanism only: the old port, tag `kilo-v2-checkpoints-2026-09` (former `kilo-v2`). Never merge or copy it as a whole. +- Context and decisions: `migration-tracking/plans/migration-context.md`. Plan: `migration-tracking/plans/kilo-v2-migration-plan.md` (PR #14987). +- Epic #13750. Gateway, login and enterprise support: #14991. +- Known upstream test failures: `migration-tracking/technical-notes/baseline/pinned-v2-baseline.md`. +- Fork conventions: `migration-tracking/technical-notes/v2-fork-conventions.md`. + +## Strategy + +- Order: CLI/TUI first (G1), then VS Code (G2), then JetBrains (G3). Cloud consumers follow a versioned release. +- Kilo code lives in Kilo-owned paths (`packages/kilo-*`, `src/kilocode/`) and uses public upstream seams (`@opencode/plugin`). Keep changes to shared upstream files minimal and mark them `kilocode_change`. +- Kilo logic packages (for example `kilo-gateway-core`) import nothing from `@opencode/*`. Only thin adapters do. +- The Kilo Gateway is the only source of Kilo models (`models.set`, no models.dev fallback). Requests identify as Kilo (token, team header, `X-KILOCODE-*`). See #14991. + +## Branches and PRs + +- Integration branch: `kilo-v2-clean`. Use the working base named in `migration-context.md`. Never target `main`. +- Stack on an open v2 PR when you depend on it, and target that PR's branch. +- One plan unit per PR. Never push to `kilo-v2-clean*`. A human merges. +- Short hyphenated branch names, no slashes. Titles `type(scope): summary`. + +## Isolation: never touch the v1 setup + +- Run the CLI only through `bun dev` (`script/dev-isolated.ts`). It keeps all data in one profile directory. Never run `bun run --cwd packages/cli`, `dev:live` or `opencode` directly. They use the installed OpenCode data and service. +- Use a fresh profile per test: `KILO_V2_PROFILE=/tmp/kilo-v2-`. Add `--standalone` so parallel agents do not share the service port. +- `bun dev` reuses the v1 Kilo login (read only). For a signed-out test, start it with `XDG_DATA_HOME=/tmp/kilo-v2-empty`. +- Do not edit the main checkout, `~/.config/kilo`, `~/.local/share/kilo` or OpenCode directories. `bun run extension` does not exist on v2. +- Models: `kilo/kilo-auto/free` by default. Use `kilo/deepseek/deepseek-v4.1-flash` only when a paid model is needed. + +## Self-test the CLI + +Self-testing means you use the real CLI like a user and judge the result. Unit tests and typecheck do not count as self-testing. + +**Quick headless check:** `KILO_V2_PROFILE=/tmp/kilo-v2- bun dev run --standalone ""`. + +**Visual TUI check.** Load `vscode-self-test`. Its harness hosts the TUI in the VS Code terminal. The v1 extension only provides the window, so no v2 extension is needed. + +```bash +export KILO_SELF_TEST_REPO= # prefer a separate v1 worktree +st() { node ~/.config/kilo/scripts/vscode-self-test/cli.mjs "$@" --session ; } +st start +st launch-vscode --mode dev --build false --auth false --workspace +st run-command --command "Terminal: Create New Terminal" +st run-command --command "View: Toggle Maximized Panel" +st type --submit --value "KILO_V2_PROFILE=/tmp/kilo-v2- bun /script/dev-isolated.ts --standalone" +st screenshot # then read the PNG +``` + +- Interact with `st type --value ""` (prompts, slash commands such as `/connect`, `@file` mentions) and `st press --keys ` (`Enter`, `Escape`, `Shift+Tab` agents, `Control+p` commands, `Control+x` then `m` models). +- Take and read a screenshot after every step. Dialogs chain: picking a model opens a variant dialog, and the next typed text lands in its search field. +- Terminal text is not in the DOM. Judge from screenshots. +- Never type real credentials. They would appear in screenshots. Stop auth flows before the secret input, or use a test account. +- Clean up: `st press --keys Control+c` twice, `st stop-vscode --cleanup true`, `st stop`, then delete the profile and fixture directories. +- Parallel agents: each uses its own session selector, profile directory and fixture repo. + +The VS Code extension (G2) uses the same harness later, with `KILO_SELF_TEST_REPO` pointing at the v2 worktree. + +## Test policy + +Grow the test surface with each PR. + +- Run `bun typecheck` and focused `bun test` from each changed package directory (never from the repo root). +- New Kilo code comes with tests for its behavior. Prefer real integration with the Kilo Gateway over mocks. Use small unit tests only for paths a live server cannot trigger (outage, malformed data). +- Upstream failures listed in `pinned-v2-baseline.md` are known. Do not fix unrelated ones. A new failure in a package you touched blocks the PR. +- Never delete or skip a test silently. List every skip in the PR with its reason. +- Self-test each user-visible behavior you add, and record the scenario so the next PR can repeat it. + +## Evidence + +In the PR and the final response, list the commands you ran, the self-test scenarios with pass, fail or blocked, and the limits (for example, a flow that needs credentials). From 4a82d4617b1cee999b4dd3d0729d83e0d988c9b5 Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Mon, 12 Oct 2026 13:54:35 +0200 Subject: [PATCH 11/18] docs: add quiet TUI self-test recipe to v2 migration skill --- .kilo/skills/v2-migration/SKILL.md | 106 ++++++++++++++++++++++++----- 1 file changed, 90 insertions(+), 16 deletions(-) diff --git a/.kilo/skills/v2-migration/SKILL.md b/.kilo/skills/v2-migration/SKILL.md index 7c2ee84303ae..6bf299eec00d 100644 --- a/.kilo/skills/v2-migration/SKILL.md +++ b/.kilo/skills/v2-migration/SKILL.md @@ -41,27 +41,101 @@ Kilo moves onto upstream OpenCode v2 from a clean base, one feature at a time. V Self-testing means you use the real CLI like a user and judge the result. Unit tests and typecheck do not count as self-testing. -**Quick headless check:** `KILO_V2_PROFILE=/tmp/kilo-v2- bun dev run --standalone ""`. +**Quick check without UI:** `env KILO_V2_PROFILE=$(mktemp -d) bun dev run --standalone ""`. -**Visual TUI check.** Load `vscode-self-test`. Its harness hosts the TUI in the VS Code terminal. The v1 extension only provides the window, so no v2 extension is needed. +**TUI check.** The `vscode-self-test` harness runs the TUI in a quiet VS Code terminal and reads the terminal text. The v1 extension only hosts the window. No v2 extension is needed. + +### 1. Setup (one shell call) + +Set the first line, then run the whole block from the v2 worktree. `V1` must be a v1 checkout with a built `packages/kilo-vscode/dist` (prefer a separate v1 worktree, never build in the user's main checkout). Use a new `ID` for each test. ```bash -export KILO_SELF_TEST_REPO= # prefer a separate v1 worktree -st() { node ~/.config/kilo/scripts/vscode-self-test/cli.mjs "$@" --session ; } -st start -st launch-vscode --mode dev --build false --auth false --workspace -st run-command --command "Terminal: Create New Terminal" -st run-command --command "View: Toggle Maximized Panel" -st type --submit --value "KILO_V2_PROFILE=/tmp/kilo-v2- bun /script/dev-isolated.ts --standalone" -st screenshot # then read the PNG +ID=my-test V1=/path/to/v1/checkout HEADLESS=true V2=$(git rev-parse --show-toplevel) +T=/tmp/kilo-v2-st-$ID; rm -rf $T; mkdir -p $T/user/User $T/repo +cat > $T/user/User/settings.json <<'EOF' +{ + "terminal.integrated.gpuAcceleration": "off", + "terminal.integrated.tabs.enabled": false, + "terminal.integrated.suggest.enabled": false, + "terminal.integrated.shellIntegration.enabled": false, + "terminal.integrated.enablePersistentSessions": false, + "workbench.startupEditor": "none", + "workbench.activityBar.location": "hidden", + "workbench.statusBar.visible": false, + "workbench.panel.opensMaximized": "always", + "workbench.tips.enabled": false, + "editor.accessibilitySupport": "off", + "security.workspace.trust.enabled": false, + "chat.disableAIFeatures": true, + "telemetry.telemetryLevel": "off" +} +EOF +printf 'export const VERSION = "1.2.3"\n' > $T/repo/math.ts && git -C $T/repo init -q && git -C $T/repo add -A && git -C $T/repo -c user.name=t -c user.email=t@t commit -qm init +cat > $T/env.sh < div")].map(r => r.textContent.trim()).filter(t => /[^\s┃╹▀]/.test(t)).join("\n")' | jq -r '.structuredContent.result | fromjson? // .'; } +idle() { sleep 1; st wait --text "esc interrupt" --state hidden >/dev/null; } +tclick() { P=\$(st evaluate --script "const el = [...document.querySelectorAll('.xterm-rows span')].find(s => s.textContent.includes(\$(jq -Rn --arg s "\$1" '\$s'))); if (!el) return ''; const r = el.getBoundingClientRect(); return Math.round(r.x + Math.min(r.width / 2, 20)) + ' ' + Math.round(r.y + r.height / 2)" | jq -r '.structuredContent.result | fromjson? // .'); [ -n "\$P" ] || { echo "not found: \$1"; return 1; }; st click --x \${P% *} --y \${P#* } >/dev/null; } +logs() { grep -h "\${1:-level=ERROR}" \$T/profile/data/opencode/log/*.log | tail -\${2:-20}; } +EOF +source $T/env.sh +st start >/dev/null +st launch-vscode --mode dev --build false --auth false --headless $HEADLESS --user-dir $T/user --workspace $T/repo >/dev/null +st run-command --command "View: Close Primary Side Bar" >/dev/null +st run-command --command "View: Close Secondary Side Bar" >/dev/null +st run-command --command "Terminal: Focus Terminal" >/dev/null +st type --submit --value "env KILO_V2_PROFILE=$T/profile bun $V2/script/dev-isolated.ts --standalone" >/dev/null +st wait --text "ctrl+p commands" >/dev/null && screen ``` -- Interact with `st type --value ""` (prompts, slash commands such as `/connect`, `@file` mentions) and `st press --keys ` (`Enter`, `Escape`, `Shift+Tab` agents, `Control+p` commands, `Control+x` then `m` models). -- Take and read a screenshot after every step. Dialogs chain: picking a model opens a variant dialog, and the next typed text lands in its search field. -- Terminal text is not in the DOM. Judge from screenshots. -- Never type real credentials. They would appear in screenshots. Stop auth flows before the secret input, or use a test account. -- Clean up: `st press --keys Control+c` twice, `st stop-vscode --cleanup true`, `st stop`, then delete the profile and fixture directories. -- Parallel agents: each uses its own session selector, profile directory and fixture repo. +The TUI is ready when the last command prints the home screen (about 15 seconds). To test signed out, start the CLI with `env XDG_DATA_HOME=$T/empty KILO_V2_PROFILE=...` instead. + +### 2. Test + +Start every later shell call with `source /tmp/kilo-v2-st-/env.sh`. Then act, read `screen`, and check for the expected text. + +| Command | Use | +|---|---| +| `st type --value "" >/dev/null` | Type a prompt, a slash command (`/connect`) or a mention (`@math`) | +| `st press --keys >/dev/null` | `Enter`, `Escape`, `Shift+Tab` (agent), `Control+p` (commands). Chords are two calls: `Control+x`, then `n` (new tab) or `m` (model) | +| `idle` | Wait until the answer is complete. Use after sending a prompt | +| `screen` | Print the terminal text | +| `tclick ""` | Click visible TUI text, for example a tab title. Use a short unique part | +| `st wait --text ""` | Wait until text appears | +| `logs` | Print CLI log errors. `logs "" ` for other lines | +| `st screenshot` | Only with `HEADLESS=false`. Read the PNG | + +Example: check that an `@` mention attaches a file and the agent reads it. + +```bash +source /tmp/kilo-v2-st-my-test/env.sh +st type --value "@math" >/dev/null; screen | grep math.ts # suggestion is shown +st press --keys Enter >/dev/null +st type --value "What is VERSION? Reply with only the value." >/dev/null; st press --keys Enter >/dev/null; idle +screen | grep 1.2.3 # answer comes from the file +logs # no output means no errors +``` + +### 3. Clean up + +```bash +source /tmp/kilo-v2-st-my-test/env.sh +st press --keys Control+c >/dev/null; st press --keys Control+c >/dev/null +st stop-vscode --cleanup true >/dev/null; st stop >/dev/null; rm -rf $T +``` + +### Pitfalls + +- Read `screen` after every step. Dialogs chain: choosing a model opens a variant dialog, and the next typed text lands in its search field. +- Do not wait for text you typed. It is already on the screen. Use `idle`. +- `st click --text` does not reach the terminal. Use `tclick`. +- Headless runs cannot take screenshots. Set `HEADLESS=false` only for visual review, because the window then opens on the user's screen. +- New tabs get their title a few seconds after the first answer. The active tab title scrolls, so match a short part. +- Never type real credentials. Stop auth flows before the secret input, or use a test account. +- CLI data of a test: log in `$T/profile/data/opencode/log/`, database `$T/profile/data/opencode/opencode-local.db`. +- Parallel agents use different `ID` values. The VS Code extension (G2) uses the same harness later, with `KILO_SELF_TEST_REPO` pointing at the v2 worktree. From f7c446c6da9322630e4c5179fe9a618952131068 Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Mon, 12 Oct 2026 14:52:03 +0200 Subject: [PATCH 12/18] chore(ci): remove upstream workflows and guard the workflow list Upstream OpenCode workflows that publish, deploy, post, label or close things do not belong in Kilo CI. Remove them, and remove the pure check workflows that never trigger on Kilo branches. Keep test.yml as the test gate and run check.yml on every pull request, so v2 PRs get `bun run check`. Add script/check-workflows.ts, modeled on the v1 guard, and run it from the new kilo-v2.yml so an upstream merge cannot bring a workflow back without review. --- .github/workflows/check.yml | 2 +- .github/workflows/close-issues.yml | 24 - .github/workflows/close-prs.yml | 50 -- .github/workflows/compliance-close.yml | 133 ---- .github/workflows/containers.yml | 45 -- .github/workflows/deploy-files.yml | 34 - .github/workflows/deploy-posts.yml | 37 -- .github/workflows/deploy-www.yml | 36 -- .github/workflows/deploy.yml | 50 -- .github/workflows/docs-locale-sync.yml | 100 --- .github/workflows/docs-update.yml | 72 --- .github/workflows/duplicate-issues.yml | 221 ------- .github/workflows/kilo-v2.yml | 35 + .github/workflows/nix-eval.yml | 95 --- .github/workflows/nix-hashes.yml | 161 ----- .github/workflows/notify-discord.yml | 14 - .github/workflows/opencode.yml | 34 - .github/workflows/pr-management.yml | 95 --- .github/workflows/pr-standards.yml | 360 ----------- .github/workflows/publish-github-action.yml | 30 - .github/workflows/publish-vscode.yml | 37 -- .github/workflows/publish.yml | 676 -------------------- .github/workflows/release-github-action.yml | 29 - .github/workflows/review.yml | 83 --- .github/workflows/stats.yml | 35 - .github/workflows/storybook.yml | 40 -- .github/workflows/triage.yml | 56 -- script/check-workflows.ts | 56 ++ 28 files changed, 92 insertions(+), 2548 deletions(-) delete mode 100644 .github/workflows/close-issues.yml delete mode 100644 .github/workflows/close-prs.yml delete mode 100644 .github/workflows/compliance-close.yml delete mode 100644 .github/workflows/containers.yml delete mode 100644 .github/workflows/deploy-files.yml delete mode 100644 .github/workflows/deploy-posts.yml delete mode 100644 .github/workflows/deploy-www.yml delete mode 100644 .github/workflows/deploy.yml delete mode 100644 .github/workflows/docs-locale-sync.yml delete mode 100644 .github/workflows/docs-update.yml delete mode 100644 .github/workflows/duplicate-issues.yml create mode 100644 .github/workflows/kilo-v2.yml delete mode 100644 .github/workflows/nix-eval.yml delete mode 100644 .github/workflows/nix-hashes.yml delete mode 100644 .github/workflows/notify-discord.yml delete mode 100644 .github/workflows/opencode.yml delete mode 100644 .github/workflows/pr-management.yml delete mode 100644 .github/workflows/pr-standards.yml delete mode 100644 .github/workflows/publish-github-action.yml delete mode 100644 .github/workflows/publish-vscode.yml delete mode 100644 .github/workflows/publish.yml delete mode 100644 .github/workflows/release-github-action.yml delete mode 100644 .github/workflows/review.yml delete mode 100644 .github/workflows/stats.yml delete mode 100644 .github/workflows/storybook.yml delete mode 100644 .github/workflows/triage.yml create mode 100644 script/check-workflows.ts diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index a9415182e5f2..03e7322892e1 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -3,8 +3,8 @@ name: check on: push: branches: [dev, v2] + # kilocode_change - run for every pull request, so Kilo v2 PRs (which target kilo-v2-clean* or a stacked PR branch) are checked pull_request: - branches: [dev, v2] workflow_dispatch: concurrency: diff --git a/.github/workflows/close-issues.yml b/.github/workflows/close-issues.yml deleted file mode 100644 index b8a2e3f575d8..000000000000 --- a/.github/workflows/close-issues.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: close-issues - -on: - schedule: - - cron: "0 2 * * *" # Daily at 2:00 AM - workflow_dispatch: - -jobs: - close: - runs-on: ubuntu-latest - permissions: - contents: read - issues: write - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 - with: - bun-version: latest - - - name: Close stale issues - env: - GITHUB_TOKEN: ${{ github.token }} - run: bun script/github/close-issues.ts diff --git a/.github/workflows/close-prs.yml b/.github/workflows/close-prs.yml deleted file mode 100644 index a1e603a88104..000000000000 --- a/.github/workflows/close-prs.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: close-prs - -on: - schedule: - - cron: "0 22 * * *" # Daily at 10:00 PM UTC - workflow_dispatch: - inputs: - dry-run: - description: "Log matching PRs without closing them" - type: boolean - default: true - max-close: - description: "Maximum matching PRs to close" - type: string - required: false - default: "50" - -jobs: - close: - runs-on: ubuntu-latest - timeout-minutes: 240 - permissions: - contents: read - issues: write - pull-requests: write - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 - with: - bun-version: latest - - - name: Close old PRs without enough positive reactions - env: - GITHUB_TOKEN: ${{ github.token }} - run: | - max_close="${{ inputs['max-close'] }}" - if [ -z "$max_close" ]; then - max_close="50" - fi - - args=("--threshold" "2" "--age-months" "1" "--sleep-ms" "20000" "--max-close" "$max_close") - - if [ "${{ github.event_name }}" = "schedule" ]; then - args+=("--execute") - elif [ "${{ inputs['dry-run'] }}" = "false" ]; then - args+=("--execute") - fi - - bun script/github/close-prs.ts "${args[@]}" diff --git a/.github/workflows/compliance-close.yml b/.github/workflows/compliance-close.yml deleted file mode 100644 index cc09925c5b73..000000000000 --- a/.github/workflows/compliance-close.yml +++ /dev/null @@ -1,133 +0,0 @@ -name: compliance-close - -on: - schedule: - # Run every 30 minutes to check for expired compliance windows - - cron: "*/30 * * * *" - workflow_dispatch: - -permissions: - contents: read - issues: write - pull-requests: write - -jobs: - close-non-compliant: - runs-on: ubuntu-latest - steps: - - name: Close non-compliant issues and PRs after 72 hours - uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 - with: - script: | - const { data: items } = await github.rest.issues.listForRepo({ - owner: context.repo.owner, - repo: context.repo.repo, - labels: 'needs:compliance', - state: 'open', - per_page: 100, - }); - - if (items.length === 0) { - core.info('No open issues/PRs with needs:compliance label'); - return; - } - - const now = Date.now(); - const seventyTwoHours = 72 * 60 * 60 * 1000; - const orgMemberAssociations = new Set(['OWNER', 'MEMBER']); - const agentLogin = 'opencode-agent[bot]'; - const { data: file } = await github.rest.repos.getContent({ - owner: context.repo.owner, - repo: context.repo.repo, - path: '.github/TEAM_MEMBERS', - ref: 'dev', - }); - const teamMembers = new Set( - Buffer.from(file.content, 'base64') - .toString() - .split('\n') - .map((line) => line.trim().toLowerCase()) - .filter(Boolean) - ); - - function isExempt(item) { - const login = item.user?.login?.toLowerCase(); - return ( - login === agentLogin || - orgMemberAssociations.has(item.author_association) || - (login && teamMembers.has(login)) - ); - } - - for (const item of items) { - const isPR = !!item.pull_request; - const kind = isPR ? 'PR' : 'issue'; - const login = item.user?.login; - - if (isExempt(item)) { - core.info(`Skipping ${kind} #${item.number}; author ${login || 'unknown'} is exempt`); - try { - await github.rest.issues.removeLabel({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: item.number, - name: 'needs:compliance', - }); - } catch (e) {} - continue; - } - - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: item.number, - }); - - const complianceComment = comments.find(c => c.body.includes('')); - if (!complianceComment) continue; - - const commentAge = now - new Date(complianceComment.created_at).getTime(); - if (commentAge < seventyTwoHours) { - core.info(`${kind} #${item.number} still within 72-hour window (${Math.round(commentAge / 60000)}m elapsed)`); - continue; - } - - const closeMessage = isPR - ? 'This pull request has been automatically closed because it was not updated to meet our [contributing guidelines](../blob/dev/CONTRIBUTING.md) within the 72-hour window.\n\nFeel free to open a new pull request that follows our guidelines.' - : 'This issue has been automatically closed because it was not updated to meet our [contributing guidelines](../blob/dev/CONTRIBUTING.md) within the 72-hour window.\n\nFeel free to open a new issue that follows our issue templates.'; - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: item.number, - body: closeMessage, - }); - - try { - await github.rest.issues.removeLabel({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: item.number, - name: 'needs:compliance', - }); - } catch (e) {} - - if (isPR) { - await github.rest.pulls.update({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: item.number, - state: 'closed', - }); - } else { - await github.rest.issues.update({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: item.number, - state: 'closed', - state_reason: 'not_planned', - }); - } - - core.info(`Closed non-compliant ${kind} #${item.number} after 72-hour window`); - } diff --git a/.github/workflows/containers.yml b/.github/workflows/containers.yml deleted file mode 100644 index 15bf0783160e..000000000000 --- a/.github/workflows/containers.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: containers - -on: - push: - branches: - - dev - paths: - - packages/containers/** - - .github/workflows/containers.yml - - package.json - workflow_dispatch: - -permissions: - contents: read - packages: write - -jobs: - build: - runs-on: blacksmith-4vcpu-ubuntu-2404 - env: - REGISTRY: ghcr.io/${{ github.repository_owner }} - TAG: "24.04" - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - - uses: ./.github/actions/setup-bun - - - name: Set up QEMU - uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 - - - name: Login to GHCR - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push containers - run: bun ./packages/containers/script/build.ts --push - env: - REGISTRY: ${{ env.REGISTRY }} - TAG: ${{ env.TAG }} diff --git a/.github/workflows/deploy-files.yml b/.github/workflows/deploy-files.yml deleted file mode 100644 index 0d34779d5d2c..000000000000 --- a/.github/workflows/deploy-files.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: deploy-files - -on: - push: - branches: - - dev - - v2 - workflow_dispatch: - -concurrency: - group: deploy-files-${{ github.ref_name }} - cancel-in-progress: false - -permissions: - contents: read - -jobs: - deploy: - if: github.repository == 'anomalyco/opencode' && (github.ref_name == 'dev' || github.ref_name == 'v2') - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - - uses: ./.github/actions/setup-bun - - - name: Typecheck - working-directory: services/files - run: bun typecheck - - - name: Deploy - working-directory: services/files - run: bun run deploy --env ${{ github.ref_name == 'v2' && 'production' || 'dev' }} - env: - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} diff --git a/.github/workflows/deploy-posts.yml b/.github/workflows/deploy-posts.yml deleted file mode 100644 index 943ed49a85da..000000000000 --- a/.github/workflows/deploy-posts.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: deploy-posts - -on: - push: - branches: - - v2 - paths: - - packages/posts/** - - bun.lock - - .github/workflows/deploy-posts.yml - workflow_dispatch: - -concurrency: - group: deploy-posts-${{ github.ref_name }} - cancel-in-progress: false - -permissions: - contents: read - -jobs: - deploy: - if: github.repository == 'anomalyco/opencode' && github.ref_name == 'v2' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - - uses: ./.github/actions/setup-bun - - - name: Build - working-directory: packages/posts - run: bun run build - - - name: Deploy - working-directory: packages/posts - run: bun run deploy - env: - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} diff --git a/.github/workflows/deploy-www.yml b/.github/workflows/deploy-www.yml deleted file mode 100644 index 1d602c936e58..000000000000 --- a/.github/workflows/deploy-www.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: deploy-www - -on: - push: - branches: - - dev - - v2 - workflow_dispatch: - -concurrency: - group: deploy-www-${{ github.ref_name }} - cancel-in-progress: false - -permissions: - contents: read - -jobs: - deploy: - if: github.repository == 'anomalyco/opencode' && (github.ref_name == 'dev' || github.ref_name == 'v2') - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - - uses: ./.github/actions/setup-bun - - - name: Build - working-directory: services/www - run: bun run build - env: - CLOUDFLARE_ENV: ${{ github.ref_name == 'v2' && 'production' || 'dev' }} - - - name: Deploy - working-directory: services/www - run: bun run deploy - env: - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index b9c4630a3db0..000000000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: deploy - -on: - push: - branches: - - dev - - production - - beta - workflow_dispatch: - -concurrency: ${{ github.workflow }}-${{ github.ref }} - -permissions: - contents: read - id-token: write - -jobs: - deploy: - if: github.repository == 'anomalyco/opencode' && (github.ref_name == 'dev' || github.ref_name == 'production' || github.ref_name == 'beta') - runs-on: ubuntu-latest - environment: ${{ github.ref_name }} - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - - uses: ./.github/actions/setup-bun - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: "24" - - - uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1 - if: github.ref_name != 'beta' - with: - role-to-assume: ${{ vars.AWS_DEPLOY_ROLE_ARN }} - role-session-name: opencode-${{ github.run_id }} - aws-region: us-east-1 - - - run: bun sst deploy --stage=${{ github.ref_name }} - env: - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - PLANETSCALE_SERVICE_TOKEN_NAME: ${{ secrets.PLANETSCALE_SERVICE_TOKEN_NAME }} - PLANETSCALE_SERVICE_TOKEN: ${{ secrets.PLANETSCALE_SERVICE_TOKEN }} - STRIPE_SECRET_KEY: ${{ github.ref_name == 'production' && secrets.STRIPE_SECRET_KEY_PROD || secrets.STRIPE_SECRET_KEY_DEV }} - HONEYCOMB_API_KEY: ${{ secrets.HONEYCOMB_API_KEY }} - SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - SENTRY_ORG: ${{ vars.SENTRY_ORG }} - SENTRY_PROJECT: ${{ vars.WEB_SENTRY_PROJECT }} - SENTRY_RELEASE: web@${{ github.sha }} - VITE_SENTRY_DSN: ${{ vars.WEB_SENTRY_DSN }} - VITE_SENTRY_RELEASE: web@${{ github.sha }} diff --git a/.github/workflows/docs-locale-sync.yml b/.github/workflows/docs-locale-sync.yml deleted file mode 100644 index 5f921e8bb717..000000000000 --- a/.github/workflows/docs-locale-sync.yml +++ /dev/null @@ -1,100 +0,0 @@ -name: docs-locale-sync - -on: - push: - branches: - - dev - paths: - - packages/web/src/content/docs/*.mdx - -jobs: - sync-locales: - if: false - #if: github.actor != 'opencode-agent[bot]' - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - contents: write - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - persist-credentials: false - fetch-depth: 0 - ref: ${{ github.ref_name }} - - - name: Setup Bun - uses: ./.github/actions/setup-bun - - - name: Setup git committer - id: committer - uses: ./.github/actions/setup-git-committer - with: - opencode-app-id: ${{ vars.OPENCODE_APP_ID }} - opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} - - - name: Compute changed English docs - id: changes - run: | - FILES=$(git diff --name-only "${{ github.event.before }}" "${{ github.sha }}" -- ':(glob)packages/web/src/content/docs/*.mdx' || true) - if [ -z "$FILES" ]; then - echo "has_changes=false" >> "$GITHUB_OUTPUT" - echo "No English docs changed in push range" - exit 0 - fi - echo "has_changes=true" >> "$GITHUB_OUTPUT" - { - echo "files<> "$GITHUB_OUTPUT" - - - name: Install OpenCode - if: steps.changes.outputs.has_changes == 'true' - run: curl -fsSL https://opencode.ai/install | bash - - - name: Sync locale docs with OpenCode - if: steps.changes.outputs.has_changes == 'true' - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - OPENCODE_CONFIG_CONTENT: | - { - "permission": { - "*": "deny", - "read": "allow", - "edit": "allow", - "glob": "allow", - "task": "allow" - } - } - run: | - opencode run --agent docs --model opencode/gpt-5.3-codex <<'EOF' - Update localized docs to match the latest English docs changes. - - Changed English doc files: - - ${{ steps.changes.outputs.files }} - - - Requirements: - 1. Update all relevant locale docs under packages/web/src/content/docs// so they reflect these English page changes. - 2. You MUST use the Task tool for translation work and launch subagents with subagent_type `translator` (defined in .opencode/agent/translator.md). - 3. Do not translate directly in the primary agent. Use translator subagent output as the source for locale text updates. - 4. Run translator subagent Task calls in parallel whenever file/locale translation work is independent. - 5. Use only the minimum tools needed for this task (read/glob, file edits, and translator Task). Do not use shell, web, search, or GitHub tools for translation work. - 6. Preserve frontmatter keys, internal links, code blocks, and existing locale-specific metadata unless the English change requires an update. - 7. Keep locale docs structure aligned with their corresponding English pages. - 8. Do not modify English source docs in packages/web/src/content/docs/*.mdx. - 9. If no locale updates are needed, make no changes. - EOF - - - name: Commit and push locale docs updates - if: steps.changes.outputs.has_changes == 'true' - run: | - if [ -z "$(git status --porcelain)" ]; then - echo "No locale docs changes to commit" - exit 0 - fi - git add -A - git commit -m "docs(i18n): sync locale docs from english changes" - git pull --rebase --autostash origin "$GITHUB_REF_NAME" - git push origin HEAD:"$GITHUB_REF_NAME" diff --git a/.github/workflows/docs-update.yml b/.github/workflows/docs-update.yml deleted file mode 100644 index 4767dec53999..000000000000 --- a/.github/workflows/docs-update.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: docs-update - -on: - schedule: - - cron: "0 */12 * * *" - workflow_dispatch: - -env: - LOOKBACK_HOURS: 4 - -jobs: - update-docs: - if: github.repository == 'sst/opencode' - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - id-token: write - contents: write - pull-requests: write - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - fetch-depth: 0 # Fetch full history to access commits - - - name: Setup Bun - uses: ./.github/actions/setup-bun - - - name: Get recent commits - id: commits - run: | - COMMITS=$(git log --since="${{ env.LOOKBACK_HOURS }} hours ago" --pretty=format:"- %h %s" 2>/dev/null || echo "") - if [ -z "$COMMITS" ]; then - echo "No commits in the last ${{ env.LOOKBACK_HOURS }} hours" - echo "has_commits=false" >> $GITHUB_OUTPUT - else - echo "has_commits=true" >> $GITHUB_OUTPUT - { - echo "list<> $GITHUB_OUTPUT - fi - - - name: Run opencode - if: steps.commits.outputs.has_commits == 'true' - uses: sst/opencode/github@2c14fc5586fe0b88e5c04732d2e846769cc35671 # latest - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - with: - model: opencode/gpt-5.2 - agent: docs - prompt: | - Review the following commits from the last ${{ env.LOOKBACK_HOURS }} hours and identify any new features that may need documentation. - - - ${{ steps.commits.outputs.list }} - - - Steps: - 1. For each commit that looks like a new feature or significant change: - - Read the changed files to understand what was added - - Check if the feature is already documented in packages/web/src/content/docs/* - 2. If you find undocumented features: - - Update the relevant documentation files in packages/web/src/content/docs/* - - Follow the existing documentation style and structure - - Make sure to document the feature clearly with examples where appropriate - 3. If all new features are already documented, report that no updates are needed - 4. If you are creating a new documentation file be sure to update packages/web/astro.config.mjs too. - - Focus on user-facing features and API changes. Skip internal refactors, bug fixes, and test updates unless they affect user-facing behavior. - Don't feel the need to document every little thing. It is perfectly okay to make 0 changes at all. - Try to keep documentation only for large features or changes that already have a good spot to be documented. diff --git a/.github/workflows/duplicate-issues.yml b/.github/workflows/duplicate-issues.yml deleted file mode 100644 index 3c007e03d730..000000000000 --- a/.github/workflows/duplicate-issues.yml +++ /dev/null @@ -1,221 +0,0 @@ -name: duplicate-issues - -on: - issues: - types: [opened, edited] - -jobs: - check-duplicates: - if: github.event.action == 'opened' - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - contents: read - issues: write - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - fetch-depth: 1 - - - name: Check exempt issue author - id: author - run: | - LOGIN="${{ github.event.issue.user.login }}" - ASSOCIATION="${{ github.event.issue.author_association }}" - - if [ "$LOGIN" = "opencode-agent[bot]" ] || - [ "$ASSOCIATION" = "OWNER" ] || - [ "$ASSOCIATION" = "MEMBER" ] || - grep -qxiF "$LOGIN" .github/TEAM_MEMBERS; then - echo "skip=true" >> "$GITHUB_OUTPUT" - echo "Skipping issue automation for exempt author: $LOGIN ($ASSOCIATION)" - else - echo "skip=false" >> "$GITHUB_OUTPUT" - fi - - - uses: ./.github/actions/setup-bun - if: steps.author.outputs.skip != 'true' - - - name: Install opencode - if: steps.author.outputs.skip != 'true' - run: curl -fsSL https://opencode.ai/install | bash - - - name: Check duplicates and compliance - if: steps.author.outputs.skip != 'true' - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OPENCODE_PERMISSION: | - { - "bash": { - "*": "deny", - "gh issue*": "allow" - }, - "webfetch": "deny" - } - run: | - opencode run -m opencode/claude-sonnet-4-6 "A new issue has been created: - - Issue number: ${{ github.event.issue.number }} - Issue author association: ${{ github.event.issue.author_association }} - - Lookup this issue with gh issue view ${{ github.event.issue.number }}. - - You have TWO tasks. Perform both, then post a SINGLE comment (if needed). - - --- - - TASK 1: CONTRIBUTING GUIDELINES COMPLIANCE CHECK - - Check whether the issue follows our contributing guidelines and issue templates. - - If the issue author association is OWNER or MEMBER, skip this compliance check. Do not add the needs:compliance label for organization-owned issues. - - This project has three issue templates that every issue MUST use one of: - - 1. Bug Report - requires a Description field with real content - 2. Feature Request - requires a verification checkbox and description, title should start with [FEATURE]: - 3. Question - requires the Question field with real content - - Additionally check: - - No AI-generated walls of text (long, AI-generated descriptions are not acceptable) - - The issue has real content, not just template placeholder text left unchanged - - Bug reports should include some context about how to reproduce - - Feature requests should explain the problem or need - - We want to push for having the user provide system description & information - - Do NOT be nitpicky about optional fields. Only flag real problems like: no template used, required fields empty or placeholder text only, obviously AI-generated walls of text, or completely empty/nonsensical content. - - --- - - TASK 2: DUPLICATE CHECK - - Search through existing issues (excluding #${{ github.event.issue.number }}) to find potential duplicates. - Consider: - 1. Similar titles or descriptions - 2. Same error messages or symptoms - 3. Related functionality or components - 4. Similar feature requests - - Additionally, if the issue mentions keybinds, keyboard shortcuts, or key bindings, note the pinned keybinds issue #4997. - - --- - - POSTING YOUR COMMENT: - - Based on your findings, post a SINGLE comment on issue #${{ github.event.issue.number }}. Build the comment as follows: - - If the issue is NOT compliant and the author association is not OWNER or MEMBER, start the comment with: - - Then explain what needs to be fixed and that they have 72 hours to edit the issue before it is automatically closed. Also add the label needs:compliance to the issue using: gh issue edit ${{ github.event.issue.number }} --add-label needs:compliance - - If duplicates were found, include a section about potential duplicates with links. - - If the issue mentions keybinds/keyboard shortcuts, include a note about #4997. - - If the issue IS compliant AND no duplicates were found AND no keybind reference, do NOT comment at all. - - Use this format for the comment: - - [If not compliant:] - - This issue doesn't fully meet our [contributing guidelines](../blob/dev/CONTRIBUTING.md). - - **What needs to be fixed:** - - [specific reasons] - - Please edit this issue to address the above within **72 hours**, or it will be automatically closed. - - [If duplicates found, add:] - --- - This issue might be a duplicate of existing issues. Please check: - - #[issue_number]: [brief description of similarity] - - [If keybind-related, add:] - For keybind-related issues, please also check our pinned keybinds documentation: #4997 - - [End with if not compliant:] - If you believe this was flagged incorrectly, please let a maintainer know. - - Remember: post at most ONE comment combining all findings. If everything is fine, post nothing." - - recheck-compliance: - if: github.event.action == 'edited' && contains(github.event.issue.labels.*.name, 'needs:compliance') - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - contents: read - issues: write - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - fetch-depth: 1 - - - name: Check exempt issue author - id: author - run: | - LOGIN="${{ github.event.issue.user.login }}" - ASSOCIATION="${{ github.event.issue.author_association }}" - - if [ "$LOGIN" = "opencode-agent[bot]" ] || - [ "$ASSOCIATION" = "OWNER" ] || - [ "$ASSOCIATION" = "MEMBER" ] || - grep -qxiF "$LOGIN" .github/TEAM_MEMBERS; then - echo "skip=true" >> "$GITHUB_OUTPUT" - echo "Skipping issue automation for exempt author: $LOGIN ($ASSOCIATION)" - else - echo "skip=false" >> "$GITHUB_OUTPUT" - fi - - - uses: ./.github/actions/setup-bun - if: steps.author.outputs.skip != 'true' - - - name: Install opencode - if: steps.author.outputs.skip != 'true' - run: curl -fsSL https://opencode.ai/install | bash - - - name: Recheck compliance - if: steps.author.outputs.skip != 'true' - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OPENCODE_PERMISSION: | - { - "bash": { - "*": "deny", - "gh issue*": "allow" - }, - "webfetch": "deny" - } - run: | - opencode run -m opencode/claude-sonnet-4-6 "Issue #${{ github.event.issue.number }} was previously flagged as non-compliant and has been edited. - Issue author association: ${{ github.event.issue.author_association }} - - Lookup this issue with gh issue view ${{ github.event.issue.number }}. - - If the issue author association is OWNER or MEMBER, remove the needs:compliance label if present, delete the previous compliance comment if present, and do not post a new comment. - - Re-check whether the issue now follows our contributing guidelines and issue templates. - - This project has three issue templates that every issue MUST use one of: - - 1. Bug Report - requires a Description field with real content - 2. Feature Request - requires a verification checkbox and description, title should start with [FEATURE]: - 3. Question - requires the Question field with real content - - Additionally check: - - No AI-generated walls of text (long, AI-generated descriptions are not acceptable) - - The issue has real content, not just template placeholder text left unchanged - - Bug reports should include some context about how to reproduce - - Feature requests should explain the problem or need - - We want to push for having the user provide system description & information - - Do NOT be nitpicky about optional fields. Only flag real problems like: no template used, required fields empty or placeholder text only, obviously AI-generated walls of text, or completely empty/nonsensical content. - - If the issue is NOW compliant: - 1. Remove the needs:compliance label: gh issue edit ${{ github.event.issue.number }} --remove-label needs:compliance - 2. Find and delete the previous compliance comment (the one containing ) using: gh api repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/comments --jq '.[] | select(.body | contains(\"\")) | .id' then delete it with: gh api -X DELETE repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/comments/{id} - 3. Post a short comment thanking them for updating the issue. - - If the issue is STILL not compliant: - Post a comment explaining what still needs to be fixed. Keep the needs:compliance label." diff --git a/.github/workflows/kilo-v2.yml b/.github/workflows/kilo-v2.yml new file mode 100644 index 000000000000..cece71418fb7 --- /dev/null +++ b/.github/workflows/kilo-v2.yml @@ -0,0 +1,35 @@ +name: kilo-v2 + +on: + push: + branches: + - kilo-v2-clean + - kilo-v2-clean-* + # Every pull request, so stacked PRs that target another v2 PR branch also run. + # This file exists only on the v2 line, so PRs into main do not run it. + pull_request: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +permissions: + contents: read + +jobs: + workflows: + name: workflow allowlist + runs-on: ubuntu-24.04 + timeout-minutes: 5 + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup Bun + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 + with: + bun-version-file: package.json + + - name: Check workflow allowlist + run: bun script/check-workflows.ts diff --git a/.github/workflows/nix-eval.yml b/.github/workflows/nix-eval.yml deleted file mode 100644 index ba2929b1aa7e..000000000000 --- a/.github/workflows/nix-eval.yml +++ /dev/null @@ -1,95 +0,0 @@ -name: nix-eval - -on: - push: - branches: [dev, v2] - pull_request: - branches: [dev, v2] - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: read - -jobs: - nix-eval: - runs-on: blacksmith-4vcpu-ubuntu-2404 - timeout-minutes: 15 - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Setup Nix - uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34 - - - name: Evaluate flake outputs (all systems) - run: | - set -euo pipefail - nix --version - - echo "=== Flake metadata ===" - nix flake metadata - - echo "" - echo "=== Flake structure ===" - nix flake show --all-systems - - SYSTEMS="x86_64-linux aarch64-linux aarch64-darwin" - PACKAGES="opencode" - # TODO: move 'desktop' to PACKAGES when #11755 is fixed - OPTIONAL_PACKAGES="desktop" - - echo "" - echo "=== Evaluating packages for all systems ===" - for system in $SYSTEMS; do - echo "" - echo "--- $system ---" - for pkg in $PACKAGES; do - printf " %s: " "$pkg" - if output=$(nix eval ".#packages.$system.$pkg.drvPath" --raw 2>&1); then - echo "✓" - else - echo "✗" - echo "::error::Evaluation failed for packages.$system.$pkg" - echo "$output" - exit 1 - fi - done - done - - echo "" - echo "=== Evaluating optional packages ===" - for system in $SYSTEMS; do - echo "" - echo "--- $system ---" - for pkg in $OPTIONAL_PACKAGES; do - printf " %s: " "$pkg" - if output=$(nix eval ".#packages.$system.$pkg.drvPath" --raw 2>&1); then - echo "✓" - else - echo "✗" - echo "::warning::Evaluation failed for packages.$system.$pkg" - echo "$output" - fi - done - done - - echo "" - echo "=== Evaluating devShells for all systems ===" - for system in $SYSTEMS; do - printf "%s: " "$system" - if output=$(nix eval ".#devShells.$system.default.drvPath" --raw 2>&1); then - echo "✓" - else - echo "✗" - echo "::error::Evaluation failed for devShells.$system.default" - echo "$output" - exit 1 - fi - done - - echo "" - echo "=== All evaluations passed ===" diff --git a/.github/workflows/nix-hashes.yml b/.github/workflows/nix-hashes.yml deleted file mode 100644 index 202ab8dd457b..000000000000 --- a/.github/workflows/nix-hashes.yml +++ /dev/null @@ -1,161 +0,0 @@ -name: nix-hashes - -permissions: - contents: write - -on: - workflow_dispatch: - push: - branches: [dev, beta, v2] - paths: - - "bun.lock" - - "package.json" - - "packages/*/package.json" - - "services/*/package.json" - - "flake.lock" - - "nix/node_modules.nix" - - "nix/scripts/**" - - "patches/**" - - ".github/workflows/nix-hashes.yml" - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - # Native runners required: bun install cross-compilation flags (--os/--cpu) - # do not produce byte-identical node_modules as native installs. - compute-hash: - strategy: - fail-fast: false - matrix: - include: - - system: x86_64-linux - runner: blacksmith-4vcpu-ubuntu-2404 - - system: aarch64-linux - runner: blacksmith-4vcpu-ubuntu-2404-arm - - system: aarch64-darwin - runner: macos-latest - runs-on: ${{ matrix.runner }} - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Setup Nix - uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34 - - - name: Compute node_modules hash - id: hash - env: - SYSTEM: ${{ matrix.system }} - run: | - set -euo pipefail - - BUILD_LOG=$(mktemp) - trap 'rm -f "$BUILD_LOG"' EXIT - - HASH="" - MAX_ATTEMPTS=3 - for ((ATTEMPT = 1; ATTEMPT <= MAX_ATTEMPTS; ATTEMPT++)); do - # Build with fakeHash to trigger hash mismatch and reveal correct hash - nix build ".#packages.${SYSTEM}.node_modules_updater" --no-link 2>&1 | tee "$BUILD_LOG" || true - - HASH="$(nix run --inputs-from . nixpkgs#gnugrep -- -oP 'got:\s*\Ksha256-[A-Za-z0-9+/=]+' "$BUILD_LOG" | tail -n1 || true)" - - [ -n "$HASH" ] && break - - if [ "$ATTEMPT" -lt "$MAX_ATTEMPTS" ]; then - echo "::warning::Attempt ${ATTEMPT}/${MAX_ATTEMPTS} produced no hash for ${SYSTEM}; retrying in $((ATTEMPT * 10))s" - sleep $((ATTEMPT * 10)) - fi - done - - if [ -z "$HASH" ]; then - echo "::error::Failed to compute hash for ${SYSTEM} after ${MAX_ATTEMPTS} attempts" - cat "$BUILD_LOG" - exit 1 - fi - - echo "$HASH" > hash.txt - echo "Computed hash for ${SYSTEM}: $HASH" - - - name: Upload hash - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: hash-${{ matrix.system }} - path: hash.txt - retention-days: 1 - - update-hashes: - needs: compute-hash - if: github.event_name != 'pull_request' - runs-on: blacksmith-4vcpu-ubuntu-2404 - - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - persist-credentials: false - fetch-depth: 0 - ref: ${{ github.ref_name }} - - - name: Setup git committer - uses: ./.github/actions/setup-git-committer - with: - opencode-app-id: ${{ vars.OPENCODE_APP_ID }} - opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} - - - name: Pull latest changes - run: | - git pull --rebase --autostash origin "$GITHUB_REF_NAME" - - - name: Download hash artifacts - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - with: - path: hashes - pattern: hash-* - - - name: Update hashes.json - run: | - set -euo pipefail - - HASH_FILE="nix/hashes.json" - - [ -f "$HASH_FILE" ] || echo '{"nodeModules":{}}' > "$HASH_FILE" - - for SYSTEM in x86_64-linux aarch64-linux aarch64-darwin; do - FILE="hashes/hash-${SYSTEM}/hash.txt" - if [ -f "$FILE" ]; then - HASH="$(tr -d '[:space:]' < "$FILE")" - echo "${SYSTEM}: ${HASH}" - jq --arg sys "$SYSTEM" --arg h "$HASH" '.nodeModules[$sys] = $h' "$HASH_FILE" > tmp.json - mv tmp.json "$HASH_FILE" - else - echo "::warning::Missing hash for ${SYSTEM}" - fi - done - - cat "$HASH_FILE" - - - name: Commit changes - run: | - set -euo pipefail - - HASH_FILE="nix/hashes.json" - - if [ -z "$(git status --short -- "$HASH_FILE")" ]; then - echo "No changes to commit" - echo "### Nix hashes" >> "$GITHUB_STEP_SUMMARY" - echo "Status: no changes" >> "$GITHUB_STEP_SUMMARY" - exit 0 - fi - - git add "$HASH_FILE" - git commit -m "chore: update nix node_modules hashes" - - git pull --rebase --autostash origin "$GITHUB_REF_NAME" - git push origin HEAD:"$GITHUB_REF_NAME" - - echo "### Nix hashes" >> "$GITHUB_STEP_SUMMARY" - echo "Status: committed $(git rev-parse --short HEAD)" >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/notify-discord.yml b/.github/workflows/notify-discord.yml deleted file mode 100644 index 0b2b1cde051b..000000000000 --- a/.github/workflows/notify-discord.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: notify-discord - -on: - release: - types: [released] # fires when a draft release is published - -jobs: - notify: - runs-on: blacksmith-4vcpu-ubuntu-2404 - steps: - - name: Send nicely-formatted embed to Discord - uses: SethCohen/github-releases-to-discord@24d166886aee4646d448c8a389ff9e1ebcab3682 # v1.20.0 - with: - webhook_url: ${{ secrets.DISCORD_WEBHOOK }} diff --git a/.github/workflows/opencode.yml b/.github/workflows/opencode.yml deleted file mode 100644 index 3469c21917f8..000000000000 --- a/.github/workflows/opencode.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: opencode - -on: - issue_comment: - types: [created] - pull_request_review_comment: - types: [created] - -jobs: - opencode: - if: | - contains(github.event.comment.body, ' /oc') || - startsWith(github.event.comment.body, '/oc') || - contains(github.event.comment.body, ' /opencode') || - startsWith(github.event.comment.body, '/opencode') - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - id-token: write - contents: read - pull-requests: read - issues: read - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - - uses: ./.github/actions/setup-bun - - - name: Run opencode - uses: anomalyco/opencode/github@2c14fc5586fe0b88e5c04732d2e846769cc35671 # latest - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - OPENCODE_PERMISSION: '{"bash": "deny"}' - with: - model: opencode/claude-opus-4-5 diff --git a/.github/workflows/pr-management.yml b/.github/workflows/pr-management.yml deleted file mode 100644 index b6aa4e589d89..000000000000 --- a/.github/workflows/pr-management.yml +++ /dev/null @@ -1,95 +0,0 @@ -name: pr-management - -on: - pull_request_target: - types: [opened] - -jobs: - check-duplicates: - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - contents: read - pull-requests: write - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - fetch-depth: 1 - - - name: Check team membership - id: team-check - run: | - LOGIN="${{ github.event.pull_request.user.login }}" - if [ "$LOGIN" = "opencode-agent[bot]" ] || grep -qxF "$LOGIN" .github/TEAM_MEMBERS; then - echo "is_team=true" >> "$GITHUB_OUTPUT" - echo "Skipping: $LOGIN is a team member or bot" - else - echo "is_team=false" >> "$GITHUB_OUTPUT" - fi - - - name: Setup Bun - if: steps.team-check.outputs.is_team != 'true' - uses: ./.github/actions/setup-bun - - - name: Install dependencies - if: steps.team-check.outputs.is_team != 'true' - run: bun install - - - name: Install opencode - if: steps.team-check.outputs.is_team != 'true' - run: curl -fsSL https://opencode.ai/install | bash - - - name: Build prompt - if: steps.team-check.outputs.is_team != 'true' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_NUMBER: ${{ github.event.pull_request.number }} - run: | - { - echo "Check for duplicate PRs related to this new PR:" - echo "" - echo "CURRENT_PR_NUMBER: $PR_NUMBER" - echo "" - echo "Title: $(gh pr view "$PR_NUMBER" --json title --jq .title)" - echo "" - echo "Description:" - gh pr view "$PR_NUMBER" --json body --jq .body - } > pr_info.txt - - - name: Check for duplicate PRs - if: steps.team-check.outputs.is_team != 'true' - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_NUMBER: ${{ github.event.pull_request.number }} - run: | - COMMENT=$(bun script/duplicate-pr.ts -f pr_info.txt "Check the attached file for PR details and search for duplicates") - - if [ "$COMMENT" != "No duplicate PRs found" ]; then - gh pr comment "$PR_NUMBER" --body "_The following comment was made by an LLM, it may be inaccurate:_ - - $COMMENT" - fi - - add-contributor-label: - runs-on: ubuntu-latest - permissions: - pull-requests: write - issues: write - steps: - - name: Add Contributor Label - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - const isPR = !!context.payload.pull_request; - const issueNumber = isPR ? context.payload.pull_request.number : context.payload.issue.number; - const authorAssociation = isPR ? context.payload.pull_request.author_association : context.payload.issue.author_association; - - if (authorAssociation === 'CONTRIBUTOR') { - await github.rest.issues.addLabels({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issueNumber, - labels: ['contributor'] - }); - } diff --git a/.github/workflows/pr-standards.yml b/.github/workflows/pr-standards.yml deleted file mode 100644 index 4f9a59320434..000000000000 --- a/.github/workflows/pr-standards.yml +++ /dev/null @@ -1,360 +0,0 @@ -name: pr-standards - -on: - pull_request_target: - types: [opened, edited, synchronize] - -jobs: - check-standards: - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write - steps: - - name: Check PR standards - uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 - with: - script: | - const pr = context.payload.pull_request; - const login = pr.user.login; - - // Skip PRs older than Feb 18, 2026 at 6PM EST (Feb 19, 2026 00:00 UTC) - const cutoff = new Date('2026-02-19T00:00:00Z'); - const prCreated = new Date(pr.created_at); - if (prCreated < cutoff) { - console.log(`Skipping: PR #${pr.number} was created before cutoff (${prCreated.toISOString()})`); - return; - } - - // Check if author is a team member or bot - if (login === 'opencode-agent[bot]') return; - const { data: file } = await github.rest.repos.getContent({ - owner: context.repo.owner, - repo: context.repo.repo, - path: '.github/TEAM_MEMBERS', - ref: 'dev' - }); - const members = Buffer.from(file.content, 'base64').toString().split('\n').map(l => l.trim()).filter(Boolean); - if (members.includes(login)) { - console.log(`Skipping: ${login} is a team member`); - return; - } - - const title = pr.title; - - async function addLabel(label) { - await github.rest.issues.addLabels({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - labels: [label] - }); - } - - async function removeLabel(label) { - try { - await github.rest.issues.removeLabel({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - name: label - }); - } catch (e) { - // Label wasn't present, ignore - } - } - - async function comment(marker, body) { - const markerText = ``; - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number - }); - - const existing = comments.find(c => c.body.includes(markerText)); - if (existing) return; - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - body: markerText + '\n' + body - }); - } - - // Step 1: Check title format - // Matches: feat:, feat(scope):, feat (scope):, etc. - const titlePattern = /^(feat|fix|docs|chore|refactor|test)\s*(\([a-zA-Z0-9-]+\))?\s*:/; - const hasValidTitle = titlePattern.test(title); - - if (!hasValidTitle) { - await addLabel('needs:title'); - await comment('title', `Hey! Your PR title \`${title}\` doesn't follow conventional commit format. - - Please update it to start with one of: - - \`feat:\` or \`feat(scope):\` new feature - - \`fix:\` or \`fix(scope):\` bug fix - - \`docs:\` or \`docs(scope):\` documentation changes - - \`chore:\` or \`chore(scope):\` maintenance tasks - - \`refactor:\` or \`refactor(scope):\` code refactoring - - \`test:\` or \`test(scope):\` adding or updating tests - - Where \`scope\` is the package name (e.g., \`app\`, \`desktop\`, \`opencode\`). - - See [CONTRIBUTING.md](../blob/dev/CONTRIBUTING.md#pr-titles) for details.`); - return; - } - - await removeLabel('needs:title'); - - // Step 2: Check for linked issue (skip for docs/refactor/feat PRs) - const skipIssueCheck = /^(docs|refactor|feat)\s*(\([a-zA-Z0-9-]+\))?\s*:/.test(title); - if (skipIssueCheck) { - await removeLabel('needs:issue'); - console.log('Skipping issue check for docs/refactor/feat PR'); - return; - } - const query = ` - query($owner: String!, $repo: String!, $number: Int!) { - repository(owner: $owner, name: $repo) { - pullRequest(number: $number) { - closingIssuesReferences(first: 1) { - totalCount - } - } - } - } - `; - - const result = await github.graphql(query, { - owner: context.repo.owner, - repo: context.repo.repo, - number: pr.number - }); - - const linkedIssues = result.repository.pullRequest.closingIssuesReferences.totalCount; - - // GitHub only populates closingIssuesReferences when a PR targets the repository's - // default branch (dev). PRs targeting other branches like v2 always return totalCount 0. - // Fall back to checking the PR description for closing keywords (e.g. Closes #123). - const body = pr.body || ''; - const issueMatch = body.match(/### Issue for this PR\s*\n([\s\S]*?)(?=###|$)/); - const issueContent = issueMatch ? issueMatch[1].trim() : body; - const hasBodyIssueRef = /(closes|fixes|resolves)\s+#\d+/i.test(issueContent) || /#\d+/.test(issueContent); - const hasLinkedIssue = linkedIssues > 0 || hasBodyIssueRef; - - if (!hasLinkedIssue) { - await addLabel('needs:issue'); - await comment('issue', `Thanks for your contribution! - - This PR doesn't have a linked issue. All PRs must reference an existing issue. - - Please: - 1. Open an issue describing the bug/feature (if one doesn't exist) - 2. Add \`Fixes #\` or \`Closes #\` to this PR description - - See [CONTRIBUTING.md](../blob/dev/CONTRIBUTING.md#issue-first-policy) for details.`); - return; - } - - await removeLabel('needs:issue'); - console.log('PR meets all standards'); - - check-compliance: - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write - steps: - - name: Check PR template compliance - uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 - with: - script: | - const pr = context.payload.pull_request; - const login = pr.user.login; - - // Skip PRs older than Feb 18, 2026 at 6PM EST (Feb 19, 2026 00:00 UTC) - const cutoff = new Date('2026-02-19T00:00:00Z'); - const prCreated = new Date(pr.created_at); - if (prCreated < cutoff) { - console.log(`Skipping: PR #${pr.number} was created before cutoff (${prCreated.toISOString()})`); - return; - } - - // Check if author is a team member or bot - if (login === 'opencode-agent[bot]') return; - const { data: file } = await github.rest.repos.getContent({ - owner: context.repo.owner, - repo: context.repo.repo, - path: '.github/TEAM_MEMBERS', - ref: 'dev' - }); - const members = Buffer.from(file.content, 'base64').toString().split('\n').map(l => l.trim()).filter(Boolean); - if (members.includes(login)) { - console.log(`Skipping: ${login} is a team member`); - return; - } - - const body = pr.body || ''; - const title = pr.title; - const isDocsRefactorOrFeat = /^(docs|refactor|feat)\s*(\([a-zA-Z0-9-]+\))?\s*:/.test(title); - - const issues = []; - - // Check: template sections exist - const hasWhatSection = /### What does this PR do\?/.test(body); - const hasTypeSection = /### Type of change/.test(body); - const hasVerifySection = /### How did you verify your code works\?/.test(body); - const hasChecklistSection = /### Checklist/.test(body); - const hasIssueSection = /### Issue for this PR/.test(body); - - if (!hasWhatSection || !hasTypeSection || !hasVerifySection || !hasChecklistSection || !hasIssueSection) { - issues.push('PR description is missing required template sections. Please use the [PR template](../blob/dev/.github/pull_request_template.md).'); - } - - // Check: "What does this PR do?" has real content (not just placeholder text) - if (hasWhatSection) { - const whatMatch = body.match(/### What does this PR do\?\s*\n([\s\S]*?)(?=###|$)/); - const whatContent = whatMatch ? whatMatch[1].trim() : ''; - const placeholder = 'Please provide a description of the issue'; - const onlyPlaceholder = whatContent.includes(placeholder) && whatContent.replace(placeholder, '').replace(/[*\s]/g, '').length < 20; - if (!whatContent || onlyPlaceholder) { - issues.push('"What does this PR do?" section is empty or only contains placeholder text. Please describe your changes.'); - } - } - - // Check: at least one "Type of change" checkbox is checked - if (hasTypeSection) { - const typeMatch = body.match(/### Type of change\s*\n([\s\S]*?)(?=###|$)/); - const typeContent = typeMatch ? typeMatch[1] : ''; - const hasCheckedBox = /- \[x\]/i.test(typeContent); - if (!hasCheckedBox) { - issues.push('No "Type of change" checkbox is checked. Please select at least one.'); - } - } - - // Check: issue reference (skip for docs/refactor/feat) - if (!isDocsRefactorOrFeat && hasIssueSection) { - const issueMatch = body.match(/### Issue for this PR\s*\n([\s\S]*?)(?=###|$)/); - const issueContent = issueMatch ? issueMatch[1].trim() : ''; - const hasIssueRef = /(closes|fixes|resolves)\s+#\d+/i.test(issueContent) || /#\d+/.test(issueContent); - if (!hasIssueRef) { - issues.push('No issue referenced. Please add `Closes #` linking to the relevant issue.'); - } - } - - // Check: "How did you verify" has content - if (hasVerifySection) { - const verifyMatch = body.match(/### How did you verify your code works\?\s*\n([\s\S]*?)(?=###|$)/); - const verifyContent = verifyMatch ? verifyMatch[1].trim() : ''; - if (!verifyContent) { - issues.push('"How did you verify your code works?" section is empty. Please explain how you tested.'); - } - } - - // Check: checklist boxes are checked - if (hasChecklistSection) { - const checklistMatch = body.match(/### Checklist\s*\n([\s\S]*?)(?=###|$)/); - const checklistContent = checklistMatch ? checklistMatch[1] : ''; - const unchecked = (checklistContent.match(/- \[ \]/g) || []).length; - const checked = (checklistContent.match(/- \[x\]/gi) || []).length; - if (checked < 2) { - issues.push('Not all checklist items are checked. Please confirm you have tested locally and have not included unrelated changes.'); - } - } - - // Helper functions - async function addLabel(label) { - await github.rest.issues.addLabels({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - labels: [label] - }); - } - - async function removeLabel(label) { - try { - await github.rest.issues.removeLabel({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - name: label - }); - } catch (e) {} - } - - const hasComplianceLabel = pr.labels.some(l => l.name === 'needs:compliance'); - - if (issues.length > 0) { - // Non-compliant - if (!hasComplianceLabel) { - await addLabel('needs:compliance'); - } - - const marker = ''; - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number - }); - const existing = comments.find(c => c.body.includes(marker)); - - const body_text = `${marker} - This PR doesn't fully meet our [contributing guidelines](../blob/dev/CONTRIBUTING.md) and [PR template](../blob/dev/.github/pull_request_template.md). - - **What needs to be fixed:** - ${issues.map(i => `- ${i}`).join('\n')} - - Please edit this PR description to address the above within **72 hours**, or it will be automatically closed. - - If you believe this was flagged incorrectly, please let a maintainer know.`; - - if (existing) { - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existing.id, - body: body_text - }); - } else { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - body: body_text - }); - } - - console.log(`PR #${pr.number} is non-compliant: ${issues.join(', ')}`); - } else if (hasComplianceLabel) { - // Was non-compliant, now fixed - await removeLabel('needs:compliance'); - - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number - }); - const marker = ''; - const existing = comments.find(c => c.body.includes(marker)); - if (existing) { - await github.rest.issues.deleteComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existing.id - }); - } - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - body: 'Thanks for updating your PR! It now meets our contributing guidelines. :+1:' - }); - - console.log(`PR #${pr.number} is now compliant, label removed`); - } else { - console.log(`PR #${pr.number} is compliant`); - } diff --git a/.github/workflows/publish-github-action.yml b/.github/workflows/publish-github-action.yml deleted file mode 100644 index e5ca91b5618a..000000000000 --- a/.github/workflows/publish-github-action.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: publish-github-action - -on: - workflow_dispatch: - push: - tags: - - "github-v*.*.*" - - "!github-v1" - -concurrency: ${{ github.workflow }}-${{ github.ref }} - -permissions: - contents: write - -jobs: - publish: - runs-on: blacksmith-4vcpu-ubuntu-2404 - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - with: - fetch-depth: 0 - - - run: git fetch --force --tags - - - name: Publish - run: | - git config --global user.email "opencode@sst.dev" - git config --global user.name "opencode" - ./script/publish - working-directory: ./github diff --git a/.github/workflows/publish-vscode.yml b/.github/workflows/publish-vscode.yml deleted file mode 100644 index 00c7e260482e..000000000000 --- a/.github/workflows/publish-vscode.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: publish-vscode - -on: - workflow_dispatch: - push: - tags: - - "vscode-v*.*.*" - -concurrency: ${{ github.workflow }}-${{ github.ref }} - -permissions: - contents: write - -jobs: - publish: - runs-on: blacksmith-4vcpu-ubuntu-2404 - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - with: - fetch-depth: 0 - - - uses: ./.github/actions/setup-bun - - - run: git fetch --force --tags - - run: bun install -g @vscode/vsce - - - name: Install extension dependencies - run: bun install - working-directory: ./sdks/vscode - - - name: Publish - run: | - ./script/publish - working-directory: ./sdks/vscode - env: - VSCE_PAT: ${{ secrets.VSCE_PAT }} - OPENVSX_TOKEN: ${{ secrets.OPENVSX_TOKEN }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index fd0f5b64c1e9..000000000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,676 +0,0 @@ -name: publish -run-name: "${{ format('release {0}', inputs.bump) }}" - -on: - push: - branches: - - ci - - dev - - beta - - v2 - - fix/npm-native-binary-install - - snapshot-* - workflow_dispatch: - inputs: - bump: - description: "Bump major, minor, or patch" - required: false - type: choice - options: - - major - - minor - - patch - version: - description: "Override version (optional)" - required: false - type: string - release_notes: - description: "Reviewed V2 release notes for CHANGELOG.md and the Discord announcement (optional)" - required: false - type: string - -concurrency: ${{ github.workflow }}-${{ github.ref }}-${{ (github.ref_name == 'v2' && (inputs.version || inputs.bump) && 'release') || inputs.version || inputs.bump }} - -permissions: - id-token: write - contents: write - packages: write - -env: - OPENCODE_CHANNEL: ${{ (github.ref_name == 'v2' && !inputs.bump && !inputs.version && 'dev') || '' }} - -jobs: - version: - runs-on: blacksmith-4vcpu-ubuntu-2404 - if: github.repository == 'anomalyco/opencode' - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - with: - fetch-depth: 0 - - - uses: ./.github/actions/setup-bun - - - name: Deploy update service - if: github.ref_name == 'v2' - working-directory: services/update - run: bun run deploy - env: - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - - - name: Setup git committer - id: committer - uses: ./.github/actions/setup-git-committer - with: - opencode-app-id: ${{ vars.OPENCODE_APP_ID }} - opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} - - - name: Install OpenCode - if: inputs.bump || inputs.version - run: bun i -g opencode-ai - - - id: version - run: | - ./script/version.ts - env: - GH_TOKEN: ${{ steps.committer.outputs.token }} - OPENCODE_BUMP: ${{ inputs.bump }} - OPENCODE_VERSION: ${{ inputs.version }} - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - GH_REPO: ${{ (github.ref_name == 'beta' && 'anomalyco/opencode-beta') || github.repository }} - outputs: - version: ${{ steps.version.outputs.version }} - release: ${{ steps.version.outputs.release }} - tag: ${{ steps.version.outputs.tag }} - repo: ${{ steps.version.outputs.repo }} - - build-cli: - needs: version - runs-on: blacksmith-4vcpu-ubuntu-2404 - timeout-minutes: 30 - if: github.repository == 'anomalyco/opencode' - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - with: - fetch-tags: true - - - uses: ./.github/actions/setup-bun - with: - bun-version: 1.4.2 - - - name: Setup git committer - id: committer - uses: ./.github/actions/setup-git-committer - with: - opencode-app-id: ${{ vars.OPENCODE_APP_ID }} - opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} - - - name: Build legacy CLI - if: github.ref_name != 'v2' && github.ref_name != 'beta' - run: ./packages/opencode/script/build.ts ${{ (github.ref_name == 'beta' && '--sourcemaps') || '' }} - env: - OPENCODE_VERSION: ${{ needs.version.outputs.version }} - OPENCODE_RELEASE: ${{ needs.version.outputs.release }} - GH_REPO: ${{ needs.version.outputs.repo }} - GH_TOKEN: ${{ steps.committer.outputs.token }} - - - name: Build preview CLI - id: build - run: ./packages/cli/script/build.ts ${{ (github.ref_name == 'beta' && '--sourcemaps') || '' }} - env: - BUN_COMPILE_RELEASE: bun-v1.4.2 - OPENCODE_VERSION: ${{ needs.version.outputs.version }} - OPENCODE_RELEASE: ${{ needs.version.outputs.release }} - GH_REPO: ${{ needs.version.outputs.repo }} - GH_TOKEN: ${{ steps.committer.outputs.token }} - - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - if: github.ref_name != 'v2' && github.ref_name != 'beta' - with: - name: opencode-cli - path: | - packages/opencode/dist/opencode-darwin* - packages/opencode/dist/opencode-linux* - - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - if: github.ref_name != 'v2' && github.ref_name != 'beta' - with: - name: opencode-cli-windows - path: packages/opencode/dist/opencode-windows* - - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: opencode-preview-cli-unsigned - path: packages/cli/dist/cli-* - - outputs: - version: ${{ needs.version.outputs.version }} - - sign-cli-macos: - needs: build-cli - runs-on: macos-26 - if: github.repository == 'anomalyco/opencode' - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - - uses: apple-actions/import-codesign-certs@8f3fb608891dd2244cdab3d69cd68c0d37a7fe93 # v2.0.0 - with: - keychain: build - p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }} - p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - - - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - with: - name: opencode-preview-cli-unsigned - path: packages/cli/dist - - - name: Sign macOS CLI binaries - run: | - identity=$(security find-identity -v -p codesigning build.keychain | sed -n 's/.*"\(Developer ID Application:.*\)"/\1/p' | head -n 1) - if [ -z "$identity" ]; then - echo "Developer ID Application identity not found" - exit 1 - fi - - found=0 - for file in packages/cli/dist/cli-darwin-*/bin/opencode; do - if [ ! -f "$file" ]; then - continue - fi - found=1 - codesign \ - --force \ - --timestamp \ - --options runtime \ - --entitlements packages/cli/script/entitlements.plist \ - --sign "$identity" \ - "$file" - codesign --verify --deep --strict --verbose=4 "$file" - codesign --display --requirements - "$file" - done - - if [ "$found" -eq 0 ]; then - echo "No macOS CLI binaries found" - exit 1 - fi - - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: opencode-preview-cli-macos - path: packages/cli/dist/cli-* - if-no-files-found: error - - build-node-app-archive: - needs: version - runs-on: blacksmith-4vcpu-ubuntu-2404 - timeout-minutes: 30 - if: github.repository == 'anomalyco/opencode' && !(github.ref_name == 'v2' && (inputs.bump || inputs.version)) - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - - uses: ./.github/actions/setup-bun - - - name: Build app archive - run: bun packages/cli/script/build-node.ts --app-archive-only --app-archive=.cache/app-archive.bin --skip-install - env: - OPENCODE_VERSION: ${{ needs.version.outputs.version }} - OPENCODE_RELEASE: ${{ needs.version.outputs.release }} - - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: opencode-node-app-archive - path: packages/cli/.cache/app-archive.bin - if-no-files-found: error - - build-node-cli: - needs: - - version - - build-node-app-archive - if: github.repository == 'anomalyco/opencode' && !(github.ref_name == 'v2' && (inputs.bump || inputs.version)) - strategy: - fail-fast: false - matrix: - settings: - - target: linux-arm64 - host: blacksmith-4vcpu-ubuntu-2404-arm - - target: linux-x64 - host: blacksmith-4vcpu-ubuntu-2404 - - target: darwin-arm64 - host: macos-26 - - target: windows-arm64 - host: blacksmith-4vcpu-windows-2025 - bun_install_flags: --cpu=* - - target: windows-x64 - host: blacksmith-4vcpu-windows-2025 - runs-on: ${{ matrix.settings.host }} - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - - uses: ./.github/actions/setup-bun - with: - install-flags: ${{ matrix.settings.bun_install_flags }} - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: "26.4.0" - - - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - with: - name: opencode-node-app-archive - path: packages/cli/.cache - - - name: Build - run: bun packages/cli/script/build-node.ts --target=${{ matrix.settings.target }} --skip-install --outdir=dist/node --app-archive=.cache/app-archive.bin - env: - OPENCODE_VERSION: ${{ needs.version.outputs.version }} - OPENCODE_RELEASE: ${{ needs.version.outputs.release }} - - - name: Verify service lifecycle - if: matrix.settings.target != 'windows-arm64' - working-directory: packages/cli - run: bun run script/service-smoke.ts --node - - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: opencode-node-cli-${{ matrix.settings.target }} - path: packages/cli/dist/node/cli-node-* - if-no-files-found: error - - sign-cli-windows: - needs: - - sign-cli-macos - runs-on: blacksmith-4vcpu-windows-2025 - if: github.repository == 'anomalyco/opencode' && (github.ref_name == 'v2' || github.ref_name == 'beta') - env: - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} - AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE }} - AZURE_TRUSTED_SIGNING_ENDPOINT: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }} - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - with: - name: opencode-preview-cli-macos - path: packages/cli/dist - - - name: Azure login - uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v2.3.0 - with: - client-id: ${{ env.AZURE_CLIENT_ID }} - tenant-id: ${{ env.AZURE_TENANT_ID }} - subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }} - - - uses: azure/artifact-signing-action@b443cf8ea4124818d2ea9f043cba29fc3ec47b16 # v1.2.0 - with: - endpoint: ${{ env.AZURE_TRUSTED_SIGNING_ENDPOINT }} - signing-account-name: ${{ env.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} - certificate-profile-name: ${{ env.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE }} - files: | - ${{ github.workspace }}\packages\cli\dist\cli-windows-arm64\bin\opencode.exe - ${{ github.workspace }}\packages\cli\dist\cli-windows-x64\bin\opencode.exe - ${{ github.workspace }}\packages\cli\dist\cli-windows-x64-baseline\bin\opencode.exe - exclude-environment-credential: true - exclude-workload-identity-credential: true - exclude-managed-identity-credential: true - exclude-shared-token-cache-credential: true - exclude-visual-studio-credential: true - exclude-visual-studio-code-credential: true - exclude-azure-cli-credential: false - exclude-azure-powershell-credential: true - exclude-azure-developer-cli-credential: true - exclude-interactive-browser-credential: true - - - name: Verify Windows CLI signatures - shell: pwsh - run: | - $files = @( - "${{ github.workspace }}\packages\cli\dist\cli-windows-arm64\bin\opencode.exe", - "${{ github.workspace }}\packages\cli\dist\cli-windows-x64\bin\opencode.exe", - "${{ github.workspace }}\packages\cli\dist\cli-windows-x64-baseline\bin\opencode.exe" - ) - - foreach ($file in $files) { - $sig = Get-AuthenticodeSignature $file - if ($sig.Status -ne "Valid") { - throw "Invalid signature for ${file}: $($sig.Status)" - } - } - - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: opencode-preview-cli - path: packages/cli/dist/cli-* - if-no-files-found: error - - build-electron: - needs: - - version - - sign-cli-windows - if: github.repository == 'anomalyco/opencode' && (github.ref_name != 'v2' || needs.version.outputs.release != '') - continue-on-error: false - env: - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} - AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE }} - AZURE_TRUSTED_SIGNING_ENDPOINT: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }} - strategy: - fail-fast: false - matrix: - settings: - - host: macos-26-intel - target: x86_64-apple-darwin - platform_flag: --mac --x64 - bun_install_flags: --os=darwin --cpu=x64 - - host: macos-26 - target: aarch64-apple-darwin - platform_flag: --mac --arm64 - bun_install_flags: --os=darwin --cpu=arm64 - # github-hosted: blacksmith lacks ARM64 MSVC cross-compilation toolchain - - host: "windows-2025" - target: aarch64-pc-windows-msvc - platform_flag: --win --arm64 - - host: "blacksmith-4vcpu-windows-2025" - target: x86_64-pc-windows-msvc - platform_flag: --win - - host: "blacksmith-4vcpu-ubuntu-2404" - target: x86_64-unknown-linux-gnu - platform_flag: --linux - - host: "blacksmith-4vcpu-ubuntu-2404-arm" - target: aarch64-unknown-linux-gnu - platform_flag: --linux --arm64 - runs-on: ${{ matrix.settings.host }} - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - with: - name: opencode-preview-cli - path: packages/cli/dist - - - uses: apple-actions/import-codesign-certs@8f3fb608891dd2244cdab3d69cd68c0d37a7fe93 # v2.0.0 - if: runner.os == 'macOS' - with: - keychain: build - p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }} - p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - - - name: Setup Apple API Key - if: runner.os == 'macOS' - run: echo "${{ secrets.APPLE_API_KEY_PATH }}" > $RUNNER_TEMP/apple-api-key.p8 - - - uses: ./.github/actions/setup-bun - with: - install-flags: ${{ matrix.settings.bun_install_flags }} - - - name: Azure login - if: runner.os == 'Windows' - uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v2.3.0 - with: - client-id: ${{ env.AZURE_CLIENT_ID }} - tenant-id: ${{ env.AZURE_TENANT_ID }} - subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }} - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: "24" - - - name: Cache apt packages - if: contains(matrix.settings.host, 'ubuntu') - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 - with: - path: ~/apt-cache - key: ${{ runner.os }}-${{ matrix.settings.target }}-apt-electron-${{ hashFiles('.github/workflows/publish.yml') }} - restore-keys: | - ${{ runner.os }}-${{ matrix.settings.target }}-apt-electron- - - - name: Install dependencies (ubuntu only) - if: contains(matrix.settings.host, 'ubuntu') - run: | - mkdir -p ~/apt-cache && chmod -R a+rw ~/apt-cache - sudo apt-get update - sudo apt-get install -y --no-install-recommends -o dir::cache::archives="$HOME/apt-cache" rpm - sudo chmod -R a+rw ~/apt-cache - - - name: Setup git committer - id: committer - uses: ./.github/actions/setup-git-committer - with: - opencode-app-id: ${{ vars.OPENCODE_APP_ID }} - opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} - - - name: Prepare - run: bun ./scripts/prepare.ts - working-directory: packages/desktop - env: - OPENCODE_VERSION: ${{ needs.version.outputs.version }} - OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }} - OPENCODE_CLI_TARGET: ${{ matrix.settings.target }} - OPENCODE_CLI_DIST: ${{ github.workspace }}/packages/cli/dist - - - name: Build - run: bun run build - working-directory: packages/desktop - env: - NODE_OPTIONS: --max-old-space-size=4096 - OPENCODE_VERSION: ${{ needs.version.outputs.version }} - OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }} - SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - SENTRY_ORG: ${{ vars.SENTRY_ORG }} - SENTRY_PROJECT: ${{ vars.WEB_SENTRY_PROJECT }} - SENTRY_RELEASE: desktop@${{ needs.version.outputs.version }} - VITE_SENTRY_DSN: ${{ vars.WEB_SENTRY_DSN }} - VITE_SENTRY_ENVIRONMENT: ${{ (github.ref_name == 'beta' && 'beta') || 'production' }} - VITE_SENTRY_RELEASE: desktop@${{ needs.version.outputs.version }} - OPENCODE_CLI_TARGET: ${{ matrix.settings.target }} - OPENCODE_CLI_DIST: ${{ github.workspace }}/packages/cli/dist - - - name: Package - if: needs.version.outputs.release - run: npx electron-builder ${{ matrix.settings.platform_flag }} --publish never --config electron-builder.config.ts - working-directory: packages/desktop - timeout-minutes: 60 - env: - OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }} - GH_TOKEN: ${{ steps.committer.outputs.token }} - CSC_KEYCHAIN: build.keychain - APPLE_API_KEY: ${{ runner.temp }}/apple-api-key.p8 - APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY }} - APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} - - - name: Package (no publish) - if: ${{ !needs.version.outputs.release }} - run: npx electron-builder ${{ matrix.settings.platform_flag }} --publish never --config electron-builder.config.ts - working-directory: packages/desktop - timeout-minutes: 60 - env: - OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }} - - - name: Create macOS .app.tar.gz - if: runner.os == 'macOS' && needs.version.outputs.release - working-directory: packages/desktop/dist - run: | - if [[ "${{ matrix.settings.target }}" == "x86_64-apple-darwin" ]]; then - APP_DIR="mac" - OUT_NAME="opencode-desktop-mac-x64.app.tar.gz" - elif [[ "${{ matrix.settings.target }}" == "aarch64-apple-darwin" ]]; then - APP_DIR="mac-arm64" - OUT_NAME="opencode-desktop-mac-arm64.app.tar.gz" - else - echo "Unknown macOS target: ${{ matrix.settings.target }}" - exit 1 - fi - APP_PATH=$(find "$APP_DIR" -maxdepth 1 -name "*.app" -type d | head -1) - if [ -z "$APP_PATH" ]; then - echo "No .app bundle found in $APP_DIR" - exit 1 - fi - tar -czf "$OUT_NAME" -C "$(dirname "$APP_PATH")" "$(basename "$APP_PATH")" - - - name: Verify signed Windows Electron artifacts - if: runner.os == 'Windows' - shell: pwsh - run: | - $files = @() - $files += Get-ChildItem "${{ github.workspace }}\packages\desktop\dist\*.exe" | Select-Object -ExpandProperty FullName - $files += Get-ChildItem "${{ github.workspace }}\packages\desktop\dist\*unpacked\*.exe" | Select-Object -ExpandProperty FullName - $files += Get-ChildItem "${{ github.workspace }}\packages\desktop\dist\*unpacked\resources\opencode-cli.exe" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName - - foreach ($file in $files | Select-Object -Unique) { - $sig = Get-AuthenticodeSignature $file - if ($sig.Status -ne "Valid") { - throw "Invalid signature for ${file}: $($sig.Status)" - } - } - - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: opencode-desktop-${{ matrix.settings.target }} - path: packages/desktop/dist/* - - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - if: needs.version.outputs.release - with: - name: latest-yml-${{ matrix.settings.target }} - path: packages/desktop/dist/latest*.yml - - publish: - needs: - - version - - build-cli - - sign-cli-macos - - build-node-app-archive - - build-node-cli - - sign-cli-windows - - build-electron - if: always() && !failure() && !cancelled() - runs-on: blacksmith-4vcpu-ubuntu-2404 - steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 - - - uses: ./.github/actions/setup-bun - - - name: Login to GitHub Container Registry - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up QEMU - uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: "24" - registry-url: "https://registry.npmjs.org" - - - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - if: github.ref_name != 'v2' && github.ref_name != 'beta' - with: - name: opencode-cli - path: packages/opencode/dist - - - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - if: github.ref_name != 'v2' && github.ref_name != 'beta' - with: - name: opencode-cli-windows - path: packages/opencode/dist - - - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - if: github.ref_name != 'v2' && github.ref_name != 'beta' - with: - name: opencode-cli-signed-windows - path: packages/opencode/dist - - - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - with: - name: opencode-preview-cli - path: packages/cli/dist - - - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - if: needs.build-node-cli.result == 'success' - with: - pattern: opencode-node-cli-* - path: packages/cli/dist/node - merge-multiple: true - - - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - if: needs.version.outputs.release - with: - pattern: latest-yml-* - path: /tmp/latest-yml - - - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - if: needs.version.outputs.release - with: - pattern: opencode-desktop-* - path: /tmp/desktop - merge-multiple: true - - - name: Setup git committer - id: committer - uses: ./.github/actions/setup-git-committer - with: - opencode-app-id: ${{ vars.OPENCODE_APP_ID }} - opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} - - - name: Cache apt packages (AUR) - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 - with: - path: /var/cache/apt/archives - key: ${{ runner.os }}-apt-aur-${{ hashFiles('.github/workflows/publish.yml') }} - restore-keys: | - ${{ runner.os }}-apt-aur- - - - name: Setup SSH for AUR - run: | - sudo apt-get update - sudo apt-get install -y pacman-package-manager - mkdir -p ~/.ssh - echo "${{ secrets.AUR_KEY }}" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - git config --global user.email "opencode@sst.dev" - git config --global user.name "opencode" - ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts || true - - - run: ./script/publish.ts - env: - OPENCODE_VERSION: ${{ needs.version.outputs.version }} - OPENCODE_RELEASE: ${{ needs.version.outputs.release }} - OPENCODE_RELEASE_NOTES: ${{ (github.ref_name == 'v2' && inputs.release_notes) || '' }} - AUR_KEY: ${{ secrets.AUR_KEY }} - GITHUB_TOKEN: ${{ steps.committer.outputs.token }} - GH_REPO: ${{ needs.version.outputs.repo }} - NPM_CONFIG_PROVENANCE: false - LATEST_YML_DIR: /tmp/latest-yml - TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} - TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} - OPENCODE_DESKTOP_DIST: /tmp/desktop - CLOUDFLARE_ACCOUNT_ID: 15d29c8639fd3733b1b5486a2acfd968 - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - - notify-discord-v2: - needs: - - version - - publish - if: ${{ !cancelled() && github.repository == 'anomalyco/opencode' && github.ref_name == 'v2' && needs.version.outputs.release && needs.publish.result == 'success' }} - runs-on: blacksmith-4vcpu-ubuntu-2404 - steps: - # Unlike dev, V2 publishes a tag rather than a GitHub Release event. - - name: Announce V2 release in Discord - uses: SethCohen/github-releases-to-discord@24d166886aee4646d448c8a389ff9e1ebcab3682 # v1.20.0 - with: - webhook_url: ${{ secrets.DISCORD_WEBHOOK }} - release_name: OpenCode V2 ${{ needs.version.outputs.tag }} - release_body: ${{ inputs.release_notes }} - release_html_url: https://github.com/${{ github.repository }}/tree/${{ needs.version.outputs.tag }} diff --git a/.github/workflows/release-github-action.yml b/.github/workflows/release-github-action.yml deleted file mode 100644 index 4a1d7218bb2c..000000000000 --- a/.github/workflows/release-github-action.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: release-github-action - -on: - push: - branches: - - dev - paths: - - "github/**" - -concurrency: ${{ github.workflow }}-${{ github.ref }} - -permissions: - contents: write - -jobs: - release: - runs-on: blacksmith-4vcpu-ubuntu-2404 - steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - fetch-depth: 0 - - - run: git fetch --force --tags - - - name: Release - run: | - git config --global user.email "opencode@sst.dev" - git config --global user.name "opencode" - ./github/script/release diff --git a/.github/workflows/review.yml b/.github/workflows/review.yml deleted file mode 100644 index 00a4fba8ca13..000000000000 --- a/.github/workflows/review.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: review - -on: - issue_comment: - types: [created] - -jobs: - check-guidelines: - if: | - github.event.issue.pull_request && - startsWith(github.event.comment.body, '/review') && - contains(fromJson('["OWNER","MEMBER"]'), github.event.comment.author_association) - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - contents: read - pull-requests: write - steps: - - name: Get PR number - id: pr-number - run: | - if [ "${{ github.event_name }}" = "pull_request_target" ]; then - echo "number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT - else - echo "number=${{ github.event.issue.number }}" >> $GITHUB_OUTPUT - fi - - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - fetch-depth: 1 - - - uses: ./.github/actions/setup-bun - - - name: Install opencode - run: curl -fsSL https://opencode.ai/install | bash - - - name: Get PR details - id: pr-details - run: | - gh api /repos/${{ github.repository }}/pulls/${{ steps.pr-number.outputs.number }} > pr_data.json - echo "title=$(jq -r .title pr_data.json)" >> $GITHUB_OUTPUT - echo "sha=$(jq -r .head.sha pr_data.json)" >> $GITHUB_OUTPUT - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Check PR guidelines compliance - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OPENCODE_PERMISSION: '{ "bash": { "*": "deny", "gh*": "allow", "gh pr review*": "deny" } }' - PR_TITLE: ${{ steps.pr-details.outputs.title }} - run: | - PR_BODY=$(jq -r .body pr_data.json) - opencode run -m opencode/gpt-5.5 --variant medium "A new pull request has been created: '${PR_TITLE}' - - - ${{ steps.pr-number.outputs.number }} - - - - $PR_BODY - - - Please check all the code changes in this pull request against the style guide, also look for any bugs if they exist. Diffs are important but make sure you read the entire file to get proper context. Make it clear the suggestions are merely suggestions and the human can decide what to do - - When critiquing code against the style guide, be sure that the code is ACTUALLY in violation, don't complain about else statements if they already use early returns there. You may complain about excessive nesting though, regardless of else statement usage. - When critiquing code style don't be a zealot, we don't like "let" statements but sometimes they are the simplest option, if someone does a bunch of nesting with let, they should consider using iife (see packages/opencode/src/util.iife.ts) - - Use the gh cli to create comments on the files for the violations. Try to leave the comment on the exact line number. If you have a suggested fix include it in a suggestion code block. - If you are writing suggested fixes, BE SURE THAT the change you are recommending is actually valid typescript, often I have seen missing closing "}" or other syntax errors. - Generally, write a comment instead of writing suggested change if you can help it. - - Command MUST be like this. - \`\`\` - gh api \ - --method POST \ - -H \"Accept: application/vnd.github+json\" \ - -H \"X-GitHub-Api-Version: 2022-11-28\" \ - /repos/${{ github.repository }}/pulls/${{ steps.pr-number.outputs.number }}/comments \ - -f 'body=[summary of issue]' -f 'commit_id=${{ steps.pr-details.outputs.sha }}' -f 'path=[path-to-file]' -F \"line=[line]\" -f 'side=RIGHT' - \`\`\` - - Only create comments for actual violations. If the code follows all guidelines, comment on the issue using gh cli: 'lgtm' AND NOTHING ELSE!!!!." diff --git a/.github/workflows/stats.yml b/.github/workflows/stats.yml deleted file mode 100644 index bc97cfcd7188..000000000000 --- a/.github/workflows/stats.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: stats - -on: - schedule: - - cron: "0 12 * * *" # Run daily at 12:00 UTC - workflow_dispatch: # Allow manual trigger - -concurrency: ${{ github.workflow }}-${{ github.ref }} - -jobs: - stats: - if: github.repository == 'anomalyco/opencode' - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - contents: write - - steps: - - name: Checkout - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - - name: Setup Bun - uses: ./.github/actions/setup-bun - - - name: Run stats script - run: bun script/stats.ts - - - name: Commit stats - run: | - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - git add STATS.md - git diff --staged --quiet || git commit -m "ignore: update download stats $(date -I)" - git push - env: - POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }} diff --git a/.github/workflows/storybook.yml b/.github/workflows/storybook.yml deleted file mode 100644 index be2e099d0ed9..000000000000 --- a/.github/workflows/storybook.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: storybook - -on: - push: - branches: [dev] - paths: - - ".github/workflows/storybook.yml" - - "package.json" - - "bun.lock" - - "packages/storybook/**" - - "packages/ui/**" - - "packages/session-ui/**" - pull_request: - branches: [dev] - paths: - - ".github/workflows/storybook.yml" - - "package.json" - - "bun.lock" - - "packages/storybook/**" - - "packages/ui/**" - - "packages/session-ui/**" - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - build: - name: storybook build - runs-on: blacksmith-4vcpu-ubuntu-2404 - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - - - name: Setup Bun - uses: ./.github/actions/setup-bun - - - name: Build Storybook - run: bun --cwd packages/storybook build diff --git a/.github/workflows/triage.yml b/.github/workflows/triage.yml deleted file mode 100644 index 0350e43877c4..000000000000 --- a/.github/workflows/triage.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: triage - -on: - issues: - types: [opened] - -jobs: - triage: - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - contents: read - issues: write - steps: - - name: Checkout repository - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - with: - fetch-depth: 1 - - - name: Check exempt issue author - id: author - run: | - LOGIN="${{ github.event.issue.user.login }}" - ASSOCIATION="${{ github.event.issue.author_association }}" - - if [ "$LOGIN" = "opencode-agent[bot]" ] || - [ "$ASSOCIATION" = "OWNER" ] || - [ "$ASSOCIATION" = "MEMBER" ] || - grep -qxiF "$LOGIN" .github/TEAM_MEMBERS; then - echo "skip=true" >> "$GITHUB_OUTPUT" - echo "Skipping issue automation for exempt author: $LOGIN ($ASSOCIATION)" - else - echo "skip=false" >> "$GITHUB_OUTPUT" - fi - - - name: Setup Bun - if: steps.author.outputs.skip != 'true' - uses: ./.github/actions/setup-bun - - - name: Install opencode - if: steps.author.outputs.skip != 'true' - run: curl -fsSL https://opencode.ai/install | bash - - - name: Triage issue - if: steps.author.outputs.skip != 'true' - env: - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ISSUE_NUMBER: ${{ github.event.issue.number }} - ISSUE_TITLE: ${{ github.event.issue.title }} - ISSUE_BODY: ${{ github.event.issue.body }} - run: | - opencode run --agent triage "The following issue was just opened, triage it: - - Title: $ISSUE_TITLE - - $ISSUE_BODY" diff --git a/script/check-workflows.ts b/script/check-workflows.ts new file mode 100644 index 000000000000..8c5aabf82eca --- /dev/null +++ b/script/check-workflows.ts @@ -0,0 +1,56 @@ +#!/usr/bin/env bun +// kilocode_change - new file + +/** + * Guards against accidentally inheriting workflows from upstream OpenCode. + * + * The Kilo v2 line merges `upstream/v2` regularly. When upstream adds a workflow under + * `.github/workflows/`, or a merge brings back one that Kilo removed, it silently starts to run in + * Kilo's CI. Upstream workflows can publish, deploy, post, label or close things. This check makes + * that decision explicit: the allowed workflows are listed below, and any drift (an added or a + * removed file) fails CI until the list is updated deliberately. + * + * To accept a new workflow: review what it does, then add its filename to `active`. + * To drop one: delete the file and remove its filename from `active`. + */ + +import { readdirSync } from "node:fs" +import path from "node:path" + +const DIR = path.resolve(import.meta.dir, "..", ".github", "workflows") + +// Workflows deliberately accepted into Kilo CI. Sort alphabetically. +const active = new Set([ + // Upstream: lint and typecheck (`bun run check`). + "check.yml", + // Kilo: v2 lane (this guard). + "kilo-v2.yml", + // Upstream: unit, e2e and compiled CLI smoke tests. + "test.yml", +]) + +// GitHub runs both .yml and .yaml files in .github/workflows/, so both count as drift. +const actual = new Set(readdirSync(DIR).filter((file) => file.endsWith(".yml") || file.endsWith(".yaml"))) + +const errors = [ + ...[...actual] + .filter((file) => !active.has(file)) + .map((file) => `unexpected workflow: ${file}. If it was added intentionally, add it to script/check-workflows.ts`), + ...[...active] + .filter((file) => !actual.has(file)) + .map( + (file) => + `expected workflow not found: ${file}. If it was removed intentionally, remove it from script/check-workflows.ts`, + ), +].sort() + +if (errors.length === 0) { + console.log(`check-workflows: ok (${actual.size} workflows)`) + process.exit(0) +} + +for (const error of errors) console.error(error) +console.error("") +console.error(`Found ${errors.length} workflow drift issue(s).`) +console.error("This guard keeps workflows from upstream merges from running in Kilo CI without review.") +process.exit(1) From e1af6dfc9d7670c454144dfd6b2613f68f3f30df Mon Sep 17 00:00:00 2001 From: marius-kilocode Date: Mon, 12 Oct 2026 15:03:29 +0200 Subject: [PATCH 13/18] chore(ci): remove dropped workflows again after upstream merges Port the v1 merge tooling `skipFiles` behavior for workflows: `bun script/check-workflows.ts --fix` runs `git rm -f` on the upstream workflows that Kilo removed, which also resolves their modify/delete conflicts. A new upstream workflow still fails the check for review. Add the step to the v2 upstream merge procedure in the fork conventions. --- .../technical-notes/v2-fork-conventions.md | 5 ++ script/check-workflows.ts | 71 ++++++++++++++++--- 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/migration-tracking/technical-notes/v2-fork-conventions.md b/migration-tracking/technical-notes/v2-fork-conventions.md index 8e96b7e88bbe..3be5cf6444d8 100644 --- a/migration-tracking/technical-notes/v2-fork-conventions.md +++ b/migration-tracking/technical-notes/v2-fork-conventions.md @@ -18,6 +18,11 @@ conventions. base in `migration-tracking/technical-notes/baseline/pinned-v2-baseline.md`, merge it into the integration branch, re-run the baseline checks, and diff the failure list against the previous baseline before declaring the bump clean. +- After each upstream merge, run `bun script/check-workflows.ts --fix`. It removes + the upstream workflows that Kilo dropped (it also resolves their modify/delete + conflicts), then fails on any new upstream workflow. Review each new one and add + it to `active` or `removed` in `script/check-workflows.ts`. CI runs the same + check without `--fix`. - Snapshot every upstream SHA the branch has consumed. If a recorded SHA stops being reachable from `upstream/v2`, upstream rewrote history: stop, assess a new integration base, and do not force-push the Kilo branch to match. diff --git a/script/check-workflows.ts b/script/check-workflows.ts index 8c5aabf82eca..c47984adda0a 100644 --- a/script/check-workflows.ts +++ b/script/check-workflows.ts @@ -2,22 +2,29 @@ // kilocode_change - new file /** - * Guards against accidentally inheriting workflows from upstream OpenCode. + * Keeps upstream OpenCode workflows out of Kilo CI. * - * The Kilo v2 line merges `upstream/v2` regularly. When upstream adds a workflow under - * `.github/workflows/`, or a merge brings back one that Kilo removed, it silently starts to run in - * Kilo's CI. Upstream workflows can publish, deploy, post, label or close things. This check makes - * that decision explicit: the allowed workflows are listed below, and any drift (an added or a - * removed file) fails CI until the list is updated deliberately. + * The Kilo v2 line merges `upstream/v2` regularly. Upstream workflows can publish, deploy, post, label or + * close things, and a merge can bring them back: a new upstream workflow arrives as a new file, and an + * upstream change to a workflow that Kilo removed arrives as a modify/delete conflict. * - * To accept a new workflow: review what it does, then add its filename to `active`. - * To drop one: delete the file and remove its filename from `active`. + * Check (CI): `bun script/check-workflows.ts` fails when `.github/workflows/` differs from `active`. + * + * Fix (after an upstream merge): `bun script/check-workflows.ts --fix` runs `git rm -f` on every workflow in + * `removed` that the merge brought back, which also resolves its modify/delete conflict. This is the v2 form + * of the v1 merge tooling `skipFiles` list (`script/upstream/utils/config.ts` on `main`). A workflow that is + * in neither list still fails the check, so a person reviews it. + * + * To accept a workflow: review what it does, then add its filename to `active`. + * To drop an upstream workflow: delete the file and move its filename to `removed`. */ +import { $ } from "bun" import { readdirSync } from "node:fs" import path from "node:path" -const DIR = path.resolve(import.meta.dir, "..", ".github", "workflows") +const ROOT = path.resolve(import.meta.dir, "..") +const DIR = path.join(ROOT, ".github", "workflows") // Workflows deliberately accepted into Kilo CI. Sort alphabetically. const active = new Set([ @@ -29,13 +36,55 @@ const active = new Set([ "test.yml", ]) +// Upstream workflows that Kilo removed: they act outside Kilo CI, or they never trigger on Kilo branches. +// Sort alphabetically. +const removed = new Set([ + "close-issues.yml", + "close-prs.yml", + "compliance-close.yml", + "containers.yml", + "deploy-files.yml", + "deploy-posts.yml", + "deploy-www.yml", + "deploy.yml", + "docs-locale-sync.yml", + "docs-update.yml", + "duplicate-issues.yml", + "nix-eval.yml", + "nix-hashes.yml", + "notify-discord.yml", + "opencode.yml", + "pr-management.yml", + "pr-standards.yml", + "publish-github-action.yml", + "publish-vscode.yml", + "publish.yml", + "release-github-action.yml", + "review.yml", + "stats.yml", + "storybook.yml", + "triage.yml", +]) + // GitHub runs both .yml and .yaml files in .github/workflows/, so both count as drift. -const actual = new Set(readdirSync(DIR).filter((file) => file.endsWith(".yml") || file.endsWith(".yaml"))) +const workflows = () => readdirSync(DIR).filter((file) => file.endsWith(".yml") || file.endsWith(".yaml")) +if (process.argv.includes("--fix")) { + for (const file of workflows().filter((file) => removed.has(file))) { + await $`git rm -f --quiet ${path.join(".github", "workflows", file)}`.cwd(ROOT) + console.log(`check-workflows: removed ${file}`) + } +} + +const actual = new Set(workflows()) const errors = [ ...[...actual] .filter((file) => !active.has(file)) - .map((file) => `unexpected workflow: ${file}. If it was added intentionally, add it to script/check-workflows.ts`), + .map((file) => + removed.has(file) + ? `removed upstream workflow is back: ${file}. Run \`bun script/check-workflows.ts --fix\`` + : `unexpected workflow: ${file}. Review it, then add it to \`active\` or \`removed\` in script/check-workflows.ts`, + ), ...[...active] .filter((file) => !actual.has(file)) .map( From a12f25be9a3b79fe687a7cce7134606cb9d44c24 Mon Sep 17 00:00:00 2001 From: Fabio Pliger Date: Mon, 12 Oct 2026 12:07:52 -0500 Subject: [PATCH 14/18] docs(migration): rename integration branch to v2 in migration docs --- migration-tracking/README.md | 2 +- migration-tracking/discovery/doc-inventory.md | 8 +++--- .../discovery/v2-epic-taxonomy.md | 10 +++---- .../discovery/v2-feature-inventory.md | 2 +- .../discovery/v2-unit-register.md | 4 +-- .../plans/kilo-opencode-v2-plan-progress.md | 2 +- .../plans/kilo-v2-migration-plan.md | 28 +++++++++---------- migration-tracking/plans/migration-context.md | 15 +++++----- .../baseline/pinned-v2-baseline.md | 4 +-- .../technical-notes/v2-fork-conventions.md | 2 +- 10 files changed, 39 insertions(+), 38 deletions(-) diff --git a/migration-tracking/README.md b/migration-tracking/README.md index b423afb02d09..6c779a2e34c5 100644 --- a/migration-tracking/README.md +++ b/migration-tracking/README.md @@ -1,6 +1,6 @@ # Kilo v2 migration tracking -Planning, evidence and validation material for moving Kilo onto upstream opencode v2 on the `kilo-v2-clean` branch. +Planning, evidence and validation material for moving Kilo onto upstream opencode v2 on the `v2` branch. ## Start here diff --git a/migration-tracking/discovery/doc-inventory.md b/migration-tracking/discovery/doc-inventory.md index 1c98fd2b3a40..c0641c45aec5 100644 --- a/migration-tracking/discovery/doc-inventory.md +++ b/migration-tracking/discovery/doc-inventory.md @@ -55,7 +55,7 @@ Columns: class · reason · pin/branch assumed · contradicts (decision) · fact | `technical-notes/.DS_Store` | rewrite-or-drop (drop) | macOS junk | — | — | — | | `assets/kilo-v2-cli-2026-09-10.png` | archive | Screenshot of the donor CLI, embedded in the published #13750 body via a raw `kilo-v2` URL | `kilo-v2` | — | Donor CLI UI on 2026-09-10 (Plan session, tabs, questions, usage sidebar) | | `assets/kilo-v2-vscode-2026-09-10.png` | archive | Same, for the donor VS Code UI | `kilo-v2` | — | Donor VS Code sidebar and Agent Manager on 2026-09-10 | -| `plans/migration-context.md` | **living** | Canonical brief (fixed rule) | BASE `5e73d5cb`, `kilo-v2-clean`, V1 `fb7e96ed`, SNAP `f7115470`, DONOR `879b89e8` | — | Needs the SNAP note from finding 1 and the `kilo2`/`next` channel question from finding 5 | +| `plans/migration-context.md` | **living** | Canonical brief (fixed rule) | BASE `5e73d5cb`, `v2`, V1 `fb7e96ed`, SNAP `f7115470`, DONOR `879b89e8` | — | Needs the SNAP note from finding 1 and the `kilo2`/`next` channel question from finding 5 | | `plans/kilo-opencode-v2-execution-plan.md` | archive | Old plan (fixed rule). Finalized 09-30, never started | `kilo-v2` on base `59b29de4`, merge release tags into `kilo-v2`, scope-rename codemod | D1 (tags as merge targets, work continues on `kilo-v2`), D2 | See §7. Its decisions 3/4/5/6/8 map to new D5/D6/D7/D7/D8 | | `plans/kilo-opencode-v2-plan-progress.md` | archive | Old progress plan (fixed rule). Status "Implementation paused", 09-09 | `kilo-v2`, phases 0-6 with a single cutover | D5 (single cutover, VS Code-first next steps), D7 (voice, image, Claw and JetBrains rows as needs-port or deferred, not post-cutover), D6 (preview store) | 88-row capability inventory (lines 77-198) with per-row remaining scope; see §7 | | `plans/kilo-opencode-v2-working-log.md` | evidence | Frozen dated record. It holds unique facts (user decisions, incidents, measurements, sharing gates, harness rationale) that exist nowhere else, so it is more than a superseded plan. Its headers and pause instruction are hazardous (finding 2) | `kilo-v2`, `76dbaf20` → `59b29de4`, V1 `ecccd1f`/`d99662338`, `kilo2` identity | D1/D2 (header and pause), D6 (`kilo2` and "take the kilo paths" rules, lines 558-596) | 09-08 user decision: preserve the original VS Code UI; the upstream-app wrapper was rejected (71-79). Store and rollback rules (558-596). Sharing gates (1786-1821). Self-sufficient CLI harness (1838-1889) | @@ -181,7 +181,7 @@ Unless stated otherwise, "Assumes" means donor `packages/kilo-cli` (or another ` | Stop if a recorded SHA becomes unreachable; never force-push (21-23) | Applies | | | `upstream/dev` is a sibling, diverged at `0e2dd4ad` (24-25) | Applies (fact) | | | `virtua@0.49.1` patch caveat (27-34) | **Stale / UNVERIFIED** | BASE root `package.json` has no `virtua`, and `patches/` has no virtua patch. The rule only matters once `kilo-vscode` or `kilo-ide-ui` are re-landed. Keep it as a note for G2 | -| Integration branch `johnnyeric/kilo-opencode-v2`, slices `johnnyeric/v2-*` (38-44) | **Wrong** | Integration branch is `kilo-v2-clean`. Branch names must be short and hyphenated with no slashes (AGENTS.md, migration-context Conventions) | +| Integration branch `johnnyeric/kilo-opencode-v2`, slices `johnnyeric/v2-*` (38-44) | **Wrong** | Integration branch is `v2`. Branch names must be short and hyphenated with no slashes (AGENTS.md, migration-context Conventions) | | Merge forward, never rebase the integration branch (46-48) | Applies | Matches D1 and the coordinator rules | | `johnnyeric/` prefix overrides upstream naming (49-53) | **Wrong** | Contradicts AGENTS.md and the brief. The `type(scope): summary` part still applies | | Dev-fix triage lane `not-applicable/already-fixed/port/defer` (55-67) | Applies (concept) | It should track the V1 delta under the staleness gate (D4) as well as `upstream/dev`. "Parity ledger dev-line lane" no longer exists, so name the new home | @@ -228,7 +228,7 @@ It was read, not run. Findings: 6. Pre-existing failures (140), with subsections: also red on upstream CI (145) (`opencode-drive` JSON parse; tui footer `Café`); host-environment failures (170) (`pa-PK` ICU; ffmpeg missing); not gated by CI (194). 7. Why no fork CI workflow yet (206). 8. Reproducer (229). -- **Refresh needs:** BASE `5e73d5cb` and the tag `v2.0.26` marker. Integration branch `kilo-v2-clean`. Current CI commands (`check.yml` `bun run check`; `test.yml` with `--affected --concurrency=3`, effect-simplifications, Node 26.4.0 build, `services/www`). Current `packageManager` bun version (UNVERIFIED; not read). New failure inventory. Package names under the `@opencode/*` scope. Bootstrap status in `migration-context.md` lists "Baseline health recorded" as recommended next. +- **Refresh needs:** BASE `5e73d5cb` and the tag `v2.0.26` marker. Integration branch `v2`. Current CI commands (`check.yml` `bun run check`; `test.yml` with `--affected --concurrency=3`, effect-simplifications, Node 26.4.0 build, `services/www`). Current `packageManager` bun version (UNVERIFIED; not read). New failure inventory. Package names under the `@opencode/*` scope. Bootstrap status in `migration-context.md` lists "Baseline health recorded" as recommended next. ## 7. Facts worth preserving from the old plans (for the new plan's facts appendix) @@ -311,7 +311,7 @@ It was read, not run. Findings: ## 8. Rewrite-or-drop list (11 remaining) 1. `migration-tracking/README.md`: rewrite as the living index (context brief, discovery, new plan, evidence folders). -2. `technical-notes/v2-fork-conventions.md`: rewrite (BASE pin, `kilo-v2-clean`, AGENTS branch names, path fixes); then living. +2. `technical-notes/v2-fork-conventions.md`: rewrite (BASE pin, `v2`, AGENTS branch names, path fixes); then living. 3. ~~`technical-notes/baseline/pinned-v2-baseline.md`~~: done in this phase. 4. `technical-notes/script/v2-baseline.ts`: fix the pin, the ancestry assertion, `services/www` and the CI mirror; then living. 5. `test-plans/kilo-opencode-v2-test-plan.md`: rewrite as a gate-tagged index. diff --git a/migration-tracking/discovery/v2-epic-taxonomy.md b/migration-tracking/discovery/v2-epic-taxonomy.md index 521ca4949198..4d894b81aba9 100644 --- a/migration-tracking/discovery/v2-epic-taxonomy.md +++ b/migration-tracking/discovery/v2-epic-taxonomy.md @@ -68,13 +68,13 @@ Each unit has exactly one surface. ### E0 Foundation - **Why:** the clean base has no Kilo package at all (`git ls-tree HEAD packages/` lists only upstream packages). Every other epic needs a host, a registration pattern and a working CI signal first. -- **What:** Kilo host/composition package; Kilo RPC and plugin scaffolding; re-landed Kilo `AGENTS.md`, rewritten fork conventions and migration-tracking index; Kilo CI lane on `kilo-v2-clean`; disable or neutralize upstream workflows and publish targets that must not run from the fork; v1 reference fixtures for the runtime-policy units. +- **What:** Kilo host/composition package; Kilo RPC and plugin scaffolding; re-landed Kilo `AGENTS.md`, rewritten fork conventions and migration-tracking index; Kilo CI lane on `v2`; disable or neutralize upstream workflows and publish targets that must not run from the fork; v1 reference fixtures for the runtime-policy units. - **Exit criteria:** - A Kilo host package builds and starts an isolated server through public seams only, verified by a service smoke test. - One sample Kilo RPC, registered as a plugin, passes a round-trip test through the generated client. - - A Kilo-owned workflow runs `bun run check` and package tests on PRs to `kilo-v2-clean`, with results compared to `pinned-v2-baseline.md`. + - A Kilo-owned workflow runs `bun run check` and package tests on PRs to `v2`, with results compared to `pinned-v2-baseline.md`. - No upstream publish or deploy workflow can publish from the fork, verified by reading each workflow's trigger and repository guard. - - `AGENTS.md` and `v2-fork-conventions.md` name `kilo-v2-clean`, BASE and the AGENTS branch rules. + - `AGENTS.md` and `v2-fork-conventions.md` name `v2`, BASE and the AGENTS branch rules. - v1 reference fixtures for offline wait, overflow, slow snapshot and snapshot retention are committed and replayable. ### E1 Core and identity @@ -152,7 +152,7 @@ Each unit has exactly one surface. - **Why:** JetBrains pins a v1 CLI version until G3 (D5). The plugin is absent from both BASE and the donor. - **What:** contract inventory; server startup, discovery, auth and version checks; sessions, streaming, permissions, history, config; terminal and editor-service dependencies; IDE validation; G3 scenario set. - **Exit criteria:** - - The plugin builds from `kilo-v2-clean` and talks to v2 only through the generated client from the assembled OpenAPI contract. + - The plugin builds from `v2` and talks to v2 only through the generated client from the assembled OpenAPI contract. - Every G3 scenario passes in each supported IDE, recorded with SHA. - Version-mismatch handling refuses incompatible CLIs with a clear message. @@ -167,7 +167,7 @@ Each unit has exactly one surface. ### E8 Retirement -- **Why:** the end state is `kilo-v2-clean` as `main` and v1 on a maintenance branch (`migration-context.md`). +- **Why:** the end state is `v2` as `main` and v1 on a maintenance branch (`migration-context.md`). - **What:** branch flip and its timing relative to IDE v1 builds; path consolidation after G3 (D6); re-landing or retiring packages that deploy from `main` (docs site, console); v1 end-of-life policy. - **Exit criteria:** - Every workflow and deployment that builds from `main` has a v2-side owner or is retired before the flip. diff --git a/migration-tracking/discovery/v2-feature-inventory.md b/migration-tracking/discovery/v2-feature-inventory.md index bad580da4932..8951d379ddd2 100644 --- a/migration-tracking/discovery/v2-feature-inventory.md +++ b/migration-tracking/discovery/v2-feature-inventory.md @@ -322,7 +322,7 @@ Source: direct read of V1 (`fb7e96ed`), written by the analyst after the delegat | VS Code | Bundles `bin/kilo`, built from `packages/opencode` **in the same checkout** (`script/local-bin.ts`, `prepare:cli-binary`). Also bundles bwrap, ffmpeg, tree-sitter and sandbox-worker resources | Generated `@kilocode/sdk/v2` client over HTTP/SSE, started by `kv/services/cli-backend/server-manager.ts`. Calls Kilo-specific routes (`kilo.*`, `kilocode.*`, `remote.*`, `network.*`, `suggestion.*`, `commitMessage.*`, `branchName.*`, `anacondaDesktop.*`), core routes (`session.*` incl. `session.deleteMessage`, `permission.*`, `question.*`, `mcp.*`, `pty.*`/`v2.pty.*`, `config.overlay*`, `provider.oauth.*`, `experimental.capabilities.get`, `experimental.controlPlane.moveSession`) | `kv/services/cli-backend/*`, `kv/features.ts` | | JetBrains | Pins the CLI release named in `jb/package.json` (`version` 7.8.8). `kilo.cli.pinned=false` is dev-only and refused for production builds (`jb/build.gradle.kts:89-98`). Plugin version 7.1.9 on its own `jetbrains/v*` tag line | Split-mode plugin (backend/frontend/shared modules). Kotlin client generated from the CLI's OpenAPI spec (`jb/backend/build.gradle.kts:11-21,64,129`, `generateOpenApiSpec`, `normalizeOpenApiSpec`) | `jb/gradle.properties`, `jb/build.gradle.kts` | -**Consequence for the branch flip (U-E8-01).** The VS Code v1 build compiles its bundled CLI from `packages/opencode` in the same tree. Once `kilo-v2-clean` becomes `main`, a v1 extension cannot be built from `main`. Either the flip waits until after G2 and G3, or v1 IDE releases build from the `v1` branch. +**Consequence for the branch flip (U-E8-01).** The VS Code v1 build compiles its bundled CLI from `packages/opencode` in the same tree. Once `v2` becomes `main`, a v1 extension cannot be built from `main`. Either the flip waits until after G2 and G3, or v1 IDE releases build from the `v1` branch. ### VS Code (A22, A23, A24 and runtime consumers) diff --git a/migration-tracking/discovery/v2-unit-register.md b/migration-tracking/discovery/v2-unit-register.md index ef330fa25be2..277066d99724 100644 --- a/migration-tracking/discovery/v2-unit-register.md +++ b/migration-tracking/discovery/v2-unit-register.md @@ -26,7 +26,7 @@ Donor paths are relative to the donor tag; `kc/` = `packages/kilo-cli/src/`, `kv | U-E0-01 | Kilo host and composition package (server, overrides, Location service replacements, builtin plugin list) | shared runtime | pre-gate | (host for all E1/E2) | new | needs-port | n/a | yes H: `kc/host.ts` (`ServerFetch.make(options,{overrides})`; `server/src/fetch.ts` overrides still present at BASE) | — | | U-E0-02 | Kilo RPC and plugin scaffolding (`packages/schema/src/kilocode`, plugin `define`, generated client consumption) | shared runtime | pre-gate | F-V2-050, Part 1 "Plugins and SDK" | new | native mechanism | n/a | yes H: `packages/schema/src/kilocode/*` (`schema/src/rpc.ts` unchanged since donor base) | U-E0-01 | | U-E0-03 | Re-land Kilo `AGENTS.md`; rewrite `v2-fork-conventions.md`, migration-tracking `README.md`, `v2-baseline.ts`; superseded banners on old plans (see `doc-inventory.md` §8) | repo | pre-gate | — | new | needs-port | n/a | no | — | -| U-E0-04 | Kilo CI lane on `kilo-v2-clean`; neutralize upstream publish/deploy workflows, `sdks/vscode` publishing, hosted web properties, CI images | repo | pre-gate | F-V2-055, F-V2-056, F-V2-057 | new (part of #14408 scope) | needs-port | n/a | no | admin task: Actions permissions | +| U-E0-04 | Kilo CI lane on `v2`; neutralize upstream publish/deploy workflows, `sdks/vscode` publishing, hosted web properties, CI images | repo | pre-gate | F-V2-055, F-V2-056, F-V2-057 | new (part of #14408 scope) | needs-port | n/a | no | admin task: Actions permissions | | U-E0-05 | v1 reference fixtures for offline wait, overflow, slow snapshot, snapshot retention (data at V1, harness on BASE) | repo | pre-gate | — | new (old N5) | needs-port | MATERIAL (A06) | partial M: `kc/test/fixtures/v1-session`, `v1-writer*` (built at V1 `d99662338`/`ecccd1f`; regenerate at V1 `fb7e96ed`) | U-E0-01 | ## E1 Core and identity @@ -188,7 +188,7 @@ Shared facts for E7. The plugin is absent from BASE and from the donor (no reuse | Unit | Title | Surface | Gate | Features | Issues | Delta | Stale | Donor | Depends | |---|---|---|---|---|---|---|---|---|---| -| U-E8-01 | Branch flip (`main`→`v1`, `kilo-v2-clean`→`main`) and its timing relative to IDE v1 builds | repo | post-G3 | — | new (old N10) | needs-port | n/a | no | G3 go/no-go | +| U-E8-01 | Branch flip (`main`→`v1`, `v2`→`main`) and its timing relative to IDE v1 builds | repo | post-G3 | — | new (old N10) | needs-port | n/a | no | G3 go/no-go | | U-E8-02 | Storage path consolidation after G3 (D6), one-time with backup | import | post-G3 | — | new (old N10) | needs-port | n/a | no | U-E8-01 | | U-E8-03 | Packages that deploy from `main`: keep (docs site), retire (`kilo-console`, F-CORE-123), re-home their workflows | repo | post-G3 | F-CORE-123 | new (old N6a/N6b in part) | not-needed (console) / needs-port (docs deploy) | MATERIAL (A29) | no | U-L5-01 | | U-E8-04 | v1 end-of-life policy and maintenance window | repo | post-G3 | — | new | needs-port | n/a | no | U-E8-01 | diff --git a/migration-tracking/plans/kilo-opencode-v2-plan-progress.md b/migration-tracking/plans/kilo-opencode-v2-plan-progress.md index 224366a3419a..244cbd44c1bb 100644 --- a/migration-tracking/plans/kilo-opencode-v2-plan-progress.md +++ b/migration-tracking/plans/kilo-opencode-v2-plan-progress.md @@ -1,7 +1,7 @@ # Phased Plan — Port Kilo onto OpenCode v2 > [!WARNING] -> **Superseded by [`kilo-v2-migration-plan.md`](kilo-v2-migration-plan.md) (2026-10-12).** Archived reference only. Status and percentages below describe the archived `kilo-v2` port, not `kilo-v2-clean`. Live unit status is in Part B of the new plan. +> **Superseded by [`kilo-v2-migration-plan.md`](kilo-v2-migration-plan.md) (2026-10-12).** Archived reference only. Status and percentages below describe the archived `kilo-v2` port, not `v2`. Live unit status is in Part B of the new plan. Updated: September 9, 2026 Related issue: Kilo-Org/kilocode#13750 diff --git a/migration-tracking/plans/kilo-v2-migration-plan.md b/migration-tracking/plans/kilo-v2-migration-plan.md index 3574aa04d45e..d74b5da3e6c9 100644 --- a/migration-tracking/plans/kilo-v2-migration-plan.md +++ b/migration-tracking/plans/kilo-v2-migration-plan.md @@ -16,7 +16,7 @@ This file is the living index of the migration: epics, gates and sequencing (Par ## A1. Objective -Ship Kilo on the upstream v2 runtime, surface by surface, without breaking a v1 user. The target feature set is upstream v2 at the pinned base plus every Kilo v1 behavior at the pinned v1 target, minus what Decision 7 marks post-cutover or not-needed: feature parity, not code parity. The terminal product (CLI/TUI) ships first at G1; VS Code (G2) and JetBrains (G3) follow on their own go/no-go, and until then they keep running on v1 against untouched v1 data. The work ends when `kilo-v2-clean` becomes `main` and v1 moves to a time-limited maintenance branch. +Ship Kilo on the upstream v2 runtime, surface by surface, without breaking a v1 user. The target feature set is upstream v2 at the pinned base plus every Kilo v1 behavior at the pinned v1 target, minus what Decision 7 marks post-cutover or not-needed: feature parity, not code parity. The terminal product (CLI/TUI) ships first at G1; VS Code (G2) and JetBrains (G3) follow on their own go/no-go, and until then they keep running on v1 against untouched v1 data. The work ends when `v2` becomes `main` and v1 moves to a time-limited maintenance branch. ## A2. Why this plan replaces the previous one @@ -45,7 +45,7 @@ The ten decisions in [`migration-context.md` §Decisions](migration-context.md#d | Decision | How this plan applies it | |---|---| -| D1 New base | Every unit lands on `kilo-v2-clean` by PR. Upstream arrives only through the L1 lane, pinned by SHA | +| D1 New base | Every unit lands on `v2` by PR. Upstream arrives only through the L1 lane, pinned by SHA | | D2 Freeze | The archived port is read through its tag; nothing merges from it | | D3 Donor rules | Each Part B row states donor reuse for mechanism only. Behavior acceptance is always written against v1 | | D4 Staleness gate | Each Part B row carries a staleness verdict. Non-EMPTY means behavior is re-derived from v1 | @@ -60,7 +60,7 @@ Items that need user sign-off are listed in A10. None of them changes a decision ## A4. Approach -**Clean base.** `kilo-v2-clean` starts as pristine upstream and contains no Kilo package yet. Kilo is re-landed onto it in small units, each one a session, a branch and a PR, merged by the user. Nothing from the old port branch is merged. +**Clean base.** `v2` starts as pristine upstream and contains no Kilo package yet. Kilo is re-landed onto it in small units, each one a session, a branch and a PR, merged by the user. Nothing from the old port branch is merged. **Re-land by unit.** Every Kilo v1 behavior and every upstream-only surface has exactly one unit (Part B). A unit is done only with code, tests, acceptance evidence recorded with a SHA, its Part B status updated, and docs. A local pass is never recorded as end to end. @@ -112,7 +112,7 @@ Epic IDs follow discovery so register rows stay traceable; they are presented in - **Outcome:** a Kilo host, a Kilo plugin/RPC pattern, a Kilo CI signal and v1 reference fixtures exist on the clean base, and no upstream workflow can publish from the fork. - **Why:** the base has no Kilo package at all. Every other epic needs these first. -- **Exit criteria:** the host starts an isolated server through public seams only (service smoke test); one sample Kilo RPC round-trips through the generated client; a Kilo-owned workflow runs `bun run check` and package tests on PRs to `kilo-v2-clean`, compared against the pinned baseline; every upstream publish or deploy workflow is disabled or guarded; Kilo `AGENTS.md` and fork conventions name the current base and branch rules; v1 reference fixtures for offline wait, overflow, slow snapshot and snapshot retention replay on the base. +- **Exit criteria:** the host starts an isolated server through public seams only (service smoke test); one sample Kilo RPC round-trips through the generated client; a Kilo-owned workflow runs `bun run check` and package tests on PRs to `v2`, compared against the pinned baseline; every upstream publish or deploy workflow is disabled or guarded; Kilo `AGENTS.md` and fork conventions name the current base and branch rules; v1 reference fixtures for offline wait, overflow, slow snapshot and snapshot retention replay on the base. - **Gate:** blocks everything. - **Depends on:** the admin task for Actions permissions (CI lane only). - **Top risks:** upstream's test workflow runs on every pull request, so it will run on fork PRs before the Kilo lane exists; branch protection is convention-only until an admin acts. @@ -193,14 +193,14 @@ Epic IDs follow discovery so register rows stay traceable; they are presented in - **Outcome:** the JetBrains plugin re-landed on the clean base, talking to v2 only through a client generated from the assembled contract. - **Why:** JetBrains pins a v1 CLI until G3; the plugin is absent from both the base and the donor. -- **Exit criteria:** the plugin builds from `kilo-v2-clean` and uses only the generated client; every G3 scenario passes in each supported IDE, recorded with SHA; incompatible CLIs are refused with a clear message. +- **Exit criteria:** the plugin builds from `v2` and uses only the generated client; every G3 scenario passes in each supported IDE, recorded with SHA; incompatible CLIs are refused with a clear message. - **Gate:** G3. - **Depends on:** E0 RPC scaffolding, E2 control-plane and generation contracts, E3 build pipeline. - **Top risks:** no donor at all; Kilo plugin RPC endpoints may not appear in the OpenAPI the Kotlin client is generated from; whether FIM gates G3 is still open. ### E8 Retirement (post-G3) -- **Outcome:** `kilo-v2-clean` is `main`, v1 lives on a maintenance branch that still builds and releases, storage paths are consolidated once, and every deployment from `main` has an owner. +- **Outcome:** `v2` is `main`, v1 lives on a maintenance branch that still builds and releases, storage paths are consolidated once, and every deployment from `main` has an owner. - **Why:** the end state in `migration-context.md`. - **Exit criteria:** every workflow and deployment that builds from `main` has a v2-side owner or is retired before the flip; the v1 maintenance branch builds and releases; storage consolidation runs once, with a backup, and is never repeated; the v1 end-of-life policy is published. - **Gate:** after G3. @@ -388,8 +388,8 @@ Each epic section has workstream tables, one row per unit, and one dependency di |---|---|---|---|---|---|---|---|---|---| | U-E0-01 | Kilo host and composition package (server overrides, Location service replacements, builtin plugin list) | new | pre-gate | Q-HOST-01 | needs-port | n/a | yes H | Host builds and starts an isolated server through public seams only; `service-smoke` passes with isolated HOME/XDG; Q-HOST-01 answer recorded | todo | | U-E0-02 | Kilo RPC and plugin scaffolding (`packages/schema/src/kilocode`, plugin `define`, generated client) | new | pre-gate | U-E0-01 | native mechanism | n/a | yes H | One sample Kilo RPC registered as a plugin round-trips through the generated client; `bun run generate` leaves no drift | todo | -| U-E0-03 | Re-land Kilo `AGENTS.md`; rewrite `v2-fork-conventions.md` and `script/v2-baseline.ts` (README, banners and drops done by this plan, Appendix D) | new | pre-gate | — | needs-port | n/a | no | `AGENTS.md` names `kilo-v2-clean`, BASE and branch rules; conventions carry the base pin, no-prefix branches, `services/www`; `v2-baseline.ts` runs at BASE (pin-is-ancestor assertion, mirrors `check.yml`/`test.yml`) | todo | -| U-E0-04 | Kilo CI lane on `kilo-v2-clean`; neutralize upstream publish/deploy workflows, `sdks/vscode` publishing, hosted web properties, CI images | new | pre-gate | admin: Actions permissions | needs-port | n/a | no | Kilo workflow runs `bun run check` and package tests on PRs to `kilo-v2-clean`, compared with `pinned-v2-baseline.md`; each upstream publish/deploy workflow disabled or repository-guarded, verified by reading triggers | todo | +| U-E0-03 | Re-land Kilo `AGENTS.md`; rewrite `v2-fork-conventions.md` and `script/v2-baseline.ts` (README, banners and drops done by this plan, Appendix D) | new | pre-gate | — | needs-port | n/a | no | `AGENTS.md` names `v2`, BASE and branch rules; conventions carry the base pin, no-prefix branches, `services/www`; `v2-baseline.ts` runs at BASE (pin-is-ancestor assertion, mirrors `check.yml`/`test.yml`) | todo | +| U-E0-04 | Kilo CI lane on `v2`; neutralize upstream publish/deploy workflows, `sdks/vscode` publishing, hosted web properties, CI images | new | pre-gate | admin: Actions permissions | needs-port | n/a | no | Kilo workflow runs `bun run check` and package tests on PRs to `v2`, compared with `pinned-v2-baseline.md`; each upstream publish/deploy workflow disabled or repository-guarded, verified by reading triggers | todo | | U-E0-05 | v1 reference fixtures: offline wait, overflow, slow snapshot, snapshot retention (data at v1 target, harness on BASE) | new | pre-gate | U-E0-01 | needs-port | M | partial M | Fixtures generated through v1 writer methods at `fb7e96ed`, each isolating HOME/XDG itself; harness on BASE replays them | todo | ```mermaid @@ -618,7 +618,7 @@ The chain 09 → 12 is the serialization rule for shared TUI files, not a functi | U-E3-06 | Updater and installation detection under Kilo identity | #14410 | G1 | U-E1-01 | partial | M | yes M | Update and rollback on real artifacts; install method detected; overlap with BASE `services/update` decided | todo | | U-E3-07 | Build and release pipeline: platforms, npm/GHCR/AUR/Homebrew/nix/install script, SBOM, PTY smoke, payload format, clean install | #14409, #14412 | G1 | U-E0-04 | partial | M | partial M | Artifacts for every v1 platform; each channel dry-runs; SBOM; PTY smoke; clean install beside v1 | todo | | U-E3-13 | Signing and notarization (start early) | #14411 | G1 | U-E0-04, admin: signing credentials | partial | M | no | Signed and notarized macOS and Windows artifacts verify on clean hosts | todo | -| U-E3-08 | CI and repository guards (forbidden strings, generated artifacts, markers, md tables, CodeQL, sharding, bots) | #14408 | G1 | U-E0-04 | partial | M | no | Each v1 guard ported or recorded not-needed; guards run on PRs to `kilo-v2-clean` | todo | +| U-E3-08 | CI and repository guards (forbidden strings, generated artifacts, markers, md tables, CodeQL, sharding, bots) | #14408 | G1 | U-E0-04 | partial | M | no | Each v1 guard ported or recorded not-needed; guards run on PRs to `v2` | todo | | U-E3-09 | Publish `@kilocode/sdk` and `@kilocode/plugin` from BASE packages | new | G1 | U-E3-07 | partial | SMALL | no | Publish dry run passes `verify:package`; names and versions decided | todo | | U-E3-10 | v2 preview release train | new | G1 | U-E3-06, U-E3-07, U-E3-08, U-E3-12 | needs-port | n/a | no | At least one preview shipped to the team under the Q-DIST-01 identity; findings filed against units | todo | | U-E3-11 | Canary and cutover/rollback runbook, G1 | #14414 | G1 | U-E3-10, U-L4-05 | needs-port | n/a | no | Runbook written and exercised once on the G1 candidate; rollback to v1 CLI on untouched v1 files | todo | @@ -768,7 +768,7 @@ No donor exists; behavior comes entirely from v1 (`packages/kilo-jetbrains` at ` | Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | |---|---|---|---|---|---|---|---|---|---| | U-E7-01 | Inventory plugin contracts and map each to a public v2 interface | #14420 | G3 | U-E0-02 | needs-port | M | no | Every route, event and terminal contract mapped; Q-G3-01 and Q-G3-02 answered | todo | -| U-E7-02 | Re-land the plugin: Gradle build, CLI pin to a v2 release, client generated from the assembled contract, startup, discovery, auth, version checks | #14421 | G3 | U-E7-01, U-E3-07, Q-G3-03 | needs-port | M | no | Builds from `kilo-v2-clean`; only the generated client is used; incompatible CLIs refused with a clear message | todo | +| U-E7-02 | Re-land the plugin: Gradle build, CLI pin to a v2 release, client generated from the assembled contract, startup, discovery, auth, version checks | #14421 | G3 | U-E7-01, U-E3-07, Q-G3-03 | needs-port | M | no | Builds from `v2`; only the generated client is used; incompatible CLIs refused with a clear message | todo | | U-E7-03 | Sessions, streaming, permissions, history, config, board, subagents, prompt completion, settings, MCP OAuth, telemetry | #14422 | G3 | U-E7-02 | needs-port | M | no | Each area as v1 | todo | | U-E7-04 | Terminal, diff/VFS, editor-service consumers (enhance prompt, branch name; FIM only if Q-G3-01 keeps the gate) | #14423 | G3 | U-E7-02, U-E2-28 (U-E6-08 if FIM gates) | needs-port | M | no | Each consumer as v1 | todo | | U-E7-05 | Agent Manager, worktrees, marketplace, legacy v5 migration | new | G3 | U-E2-27, U-E7-03, Q-G3-05 | needs-port | M | no | Each area as v1; v5 migration per Q-G3-05 | todo | @@ -794,7 +794,7 @@ flowchart LR | Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | |---|---|---|---|---|---|---|---|---|---| -| U-E8-01 | Branch flip (`main`→`v1`, `kilo-v2-clean`→`main`), timed against v1 IDE builds | new | post-G3 | G3 go, Q-G2-01 | needs-port | n/a | no | Flip done; the `v1` branch builds and releases CLI and IDEs | todo | +| U-E8-01 | Branch flip (`main`→`v1`, `v2`→`main`), timed against v1 IDE builds | new | post-G3 | G3 go, Q-G2-01 | needs-port | n/a | no | Flip done; the `v1` branch builds and releases CLI and IDEs | todo | | U-E8-02 | Storage path consolidation (D6), once, with backup | new | post-G3 | U-E8-01 | needs-port | n/a | no | Runs once with backup; a second run is a no-op | todo | | U-E8-03 | Packages that deploy from `main`: keep docs site, retire `kilo-console`, re-home workflows | new | post-G3 | U-L5-01 | partial / not-needed | M | no | Every workflow and deployment from `main` has a v2 owner or is retired before the flip | todo | | U-E8-04 | v1 end-of-life policy and maintenance window | new | post-G3 | U-E8-01 | needs-port | n/a | no | Policy published with a date | todo | @@ -819,7 +819,7 @@ Acceptance for every row is a recorded user decision (ship, drop or re-scope); s | U-E11-03 | Team sharing ownership; incremental sharing with deletion/revert semantics | #14399 (ownership), #14400 | non-gating | U-E5-02 | post-cutover | EMPTY | no | Decision recorded | todo | | U-E11-04 | Legacy TUI extras: kilo-v1/colorblind themes, legacy bindings, fixed-width tables, vim mode | #14376, #14378 (part) | non-gating | U-E9-05 | post-cutover | M | partial L | Decision recorded | todo | | U-E11-05 | Default-off experimental features (Exa, context tools, code mode/PTC, MCP Apps, session switcher, notebook tools, Claude migration, scout, LSP tool, VS Code browser automation and prompt history) | new | non-gating | Q-G2-07 | post-cutover | NEW | no | Decision recorded per feature | todo | -| U-E11-06 | KiloClaw: not needed (removed on v1); no donor remnants copied | #14419 (Claw part) | non-gating | — | not-needed | n/a | no | Grep shows no Claw paths on `kilo-v2-clean` | todo | +| U-E11-06 | KiloClaw: not needed (removed on v1); no donor remnants copied | #14419 (Claw part) | non-gating | — | not-needed | n/a | no | Grep shows no Claw paths on `v2` | todo | ```mermaid flowchart LR @@ -1024,8 +1024,8 @@ No discovery epic or unit was dropped. Every discovery unit keeps its ID. | Term | Meaning | |---|---| | v1 | Kilo on `main`, built on opencode v1. The behavioral reference at the pinned v1 target | -| v2 | Kilo on upstream opencode `v2`, built on `kilo-v2-clean` | -| BASE | The pinned upstream commit `kilo-v2-clean` started from | +| v2 | Kilo on upstream opencode `v2`, built on `v2` | +| BASE | The pinned upstream commit `v2` started from | | Donor | The archived old port (tag), used for mechanism only | | Snapshot / cutoff | The v1 commit up to which every Kilo change has a disposition. L2 advances it | | Staleness | v1 change to a unit's files between snapshot and v1 target (D4) | diff --git a/migration-tracking/plans/migration-context.md b/migration-tracking/plans/migration-context.md index 553c641cd031..19a6be340d82 100644 --- a/migration-tracking/plans/migration-context.md +++ b/migration-tracking/plans/migration-context.md @@ -8,8 +8,8 @@ Status: planned (bootstrap docs complete; no unit started). Last updated 2026-10 | Item | Value | |---|---| -| Integration branch | `kilo-v2-clean` (long-lived; becomes `main` at cutover) | -| Current working base | `kilo-v2-clean-bootstrap` (seeded docs on top of `kilo-v2-clean`). Discovery, planner and coordinator branch from this until its PR is merged into `kilo-v2-clean` | +| Integration branch | `v2` (long-lived; becomes `main` at cutover) | +| Current working base | `kilo-v2-clean-bootstrap` (seeded docs on top of `v2`). Discovery, planner and coordinator branch from this until its PR is merged into `v2` | | Upstream base commit | `5e73d5cb1f788637b1b3d5cf1298ea694cbad0c5`: the parent of upstream release tag `v2.0.26`, on `upstream/v2` (`feat(core): add Google Cloud credential setup for Vertex (#53798)`) | | Upstream release tag | `v2.0.26` = `9b4ec5714d481559990db0a816d5dec19541a814` (reference marker only; not on `upstream/v2`, see note) | | Archived old port | tag `kilo-v2-checkpoints-2026-09` = `879b89e89034f0e9f4cfb7fd09a5323ef06f83c9` (former `kilo-v2` tip). Donor code only | @@ -18,13 +18,14 @@ Status: planned (bootstrap docs complete; no unit started). Last updated 2026-10 Note on the base: upstream cuts each release as a separate version-bump commit (`release: vX`) on top of the `v2` tip, tags it, and does not merge it back into `v2`. The tag is therefore one commit off the branch. The base is the tag's parent: release-equivalent code, on `upstream/v2`, and free of the version-bump commit, so later merges of `upstream/v2` stay clean. -Eventual end state: `kilo-v2-clean` becomes `main`; today's `main` becomes `v1`, maintained for critical fixes only, for a limited period. +Eventual end state: `v2` becomes `main`; today's `main` becomes `v1`, maintained for critical fixes only, for a limited period. ## Repositories - Kilo repo: `Kilo-Org/kilocode` (remote `origin`). `main` is Kilo v1. - Upstream: `anomalyco/opencode` (remote `upstream`, fetch only), branch `v2`. Published on npm as `@opencode/cli`. Upstream renamed its workspace scope from `@opencode-ai/*` to `@opencode/*` on 2026-09-07 (`a5312e16`); a few packages kept the old scope. - The old port branch `kilo-v2` still exists but is frozen. Read it through the archive tag. +- **Name collision:** since 2026-10-12 the Kilo integration branch is `v2` on `origin` (Kilo-Org/kilocode), the same name as upstream's `v2` (anomalyco/opencode). Always qualify the remote: write `origin/v2` for the Kilo integration branch and `upstream/v2` for upstream, in docs, commands and merge targets. ## Decisions (authoritative) @@ -62,9 +63,9 @@ Decisions intentionally left open until the work that needs them. Each has a dea | Writers | One writer per branch and per file. Announce ownership before starting. | | Escalation | Epic or scope changes, gate assignment of consumers, and anything that changes a decision above go to the user. | -**Enforcement is by convention only, for now.** The account running the migration has write access but not repo admin, so branch protection on `kilo-v2-clean` cannot be set yet (see deferred admin tasks). Until an admin adds it: +**Enforcement is by convention only, for now.** The account running the migration has write access but not repo admin, so branch protection on `v2` cannot be set yet (see deferred admin tasks). Until an admin adds it: -- Direct pushes to `kilo-v2-clean` are forbidden by convention. Every change reaches it through a PR. +- Direct pushes to `v2` are forbidden by convention. Every change reaches it through a PR. - Repo settings, Actions settings, secrets and branch protection are escalations to a repo admin. No agent attempts them. ## Conventions @@ -88,7 +89,7 @@ These need a repo admin (or account credentials) and do not block work. Track th | Task | When | Owner | |---|---|---| -| Branch protection on `kilo-v2-clean`: require a PR before merging (0 approvals now, raise to 1 when reviewers are available), force-push and deletion disabled | Now | Repo admin | +| Branch protection on `v2`: require a PR before merging (0 approvals now, raise to 1 when reviewers are available), force-push and deletion disabled | Now | Repo admin | | Actions permissions and secret access for the upstream sync workflow and the e2e lane | When those workflows land | Repo admin | | Signing and notarization credentials | Before the G1 preview release train | Account owner | | Protect the branch that becomes `main` | At cutover | Repo admin | @@ -97,7 +98,7 @@ These need a repo admin (or account credentials) and do not block work. Track th | Item | State | |---|---| -| `kilo-v2-clean` created from the upstream base | Done | +| `v2` created from the upstream base | Done | | Old port archived as a tag | Done | | Migration docs seeded on `kilo-v2-clean-bootstrap` | Done | | `kilo-v2-clean-bootstrap` pushed to origin | Done | diff --git a/migration-tracking/technical-notes/baseline/pinned-v2-baseline.md b/migration-tracking/technical-notes/baseline/pinned-v2-baseline.md index 2d33a52a25cb..61ba2b364677 100644 --- a/migration-tracking/technical-notes/baseline/pinned-v2-baseline.md +++ b/migration-tracking/technical-notes/baseline/pinned-v2-baseline.md @@ -11,7 +11,7 @@ This file records the upstream commit the Kilo v2 line is built on, the validati | Upstream base (pinned) | `5e73d5cb1f788637b1b3d5cf1298ea694cbad0c5` | 2026-10-08 | `feat(core): add Google Cloud credential setup for Vertex (#53798)` | | Release marker `v2.0.26` | `9b4ec5714d481559990db0a816d5dec19541a814` | — | Version-bump commit whose parent is the base. It is not on `upstream/v2` and is not a merge target | | `upstream/v2` at capture | `d2905b8ddae8b0353cd2166d68d59795397921db` | 2026-10-09 | `feat(ai): add Vertex AI Interactions route (#54175)`. 31 commits ahead of the base | -| Integration branch `kilo-v2-clean` | `5e73d5cb` | — | Equal to the base | +| Integration branch `v2` | `5e73d5cb` | — | Equal to the base | | Phase branch `kilo-v2-clean-bootstrap` (HEAD) | `a876c2912a5fd114066ce34e3a3fcc79f7f0a1b0` | 2026-10-11 | Base plus 2 documentation commits (`c4042b39a1`, `a876c2912a`). No source changes | | V1 target `origin/main` | `fb7e96eda5409517a05948b9efaf01c53997bf6e` | 2026-10-09 | `Merge pull request #14893 …` | @@ -65,7 +65,7 @@ Constraints: - Tests cannot run from the root (`bunfig.toml` test root `do-not-run-tests-from-root`). Run them from a package directory or through turbo. - Never run `tsc`. Packages use `tsgo` through `bun typecheck`. - `.husky/pre-push` checks the bun version, then runs `bun run check`. -- `check.yml` runs on pushes to `dev`/`v2` and on pull requests whose base is `dev` or `v2`. It therefore does **not** run for pull requests that target `kilo-v2-clean`. `test.yml` runs on every `pull_request`. Whether Actions run on the Kilo fork for these branches is UNVERIFIED: no pull request targets `kilo-v2-clean` or `kilo-v2-clean-bootstrap` yet (`gh pr list`, 2026-10-11). +- `check.yml` runs on pushes to `dev`/`v2` and on pull requests whose base is `dev` or `v2`. `test.yml` runs on every `pull_request`. **Changed 2026-10-12:** the Kilo integration branch was renamed to `v2`, so upstream's `check.yml` triggers now match pushes to `origin/v2` and pull requests that target it. Whether it runs on the fork, passes, or fails for lack of admin-configured secrets or Actions permissions is UNVERIFIED. CI/admin follow-up: observe the first pull request into `origin/v2` and record the result here (relates to U-E0-04 and the Actions-permissions admin task). The 2026-10-11 capture found no pull request targeting the integration branch or `kilo-v2-clean-bootstrap` (`gh pr list`). - `packages/www` no longer exists. Generated docs live in `services/www`, and `typecheck.yml` was replaced by `check.yml`. ## Results on the phase branch (2026-10-11) diff --git a/migration-tracking/technical-notes/v2-fork-conventions.md b/migration-tracking/technical-notes/v2-fork-conventions.md index 4dc3f2de4f74..11041c367186 100644 --- a/migration-tracking/technical-notes/v2-fork-conventions.md +++ b/migration-tracking/technical-notes/v2-fork-conventions.md @@ -1,7 +1,7 @@ # Kilo OpenCode v2 fork conventions > [!WARNING] -> **Partly stale; rewrite pending in U-E0-03 ([plan](../plans/kilo-v2-migration-plan.md)).** The pin `76dbaf20`, the `johnnyeric/` branch model, `packages/www` paths and companion links are wrong for `kilo-v2-clean`. Where this file conflicts with `plans/migration-context.md` or `AGENTS.md`, those win. The substantive rules (merge forward, triage vocabulary, Kilo-owned placement, narrow hooks, markers, compact tables) still apply. Evidence: `discovery/doc-inventory.md` §4. +> **Partly stale; rewrite pending in U-E0-03 ([plan](../plans/kilo-v2-migration-plan.md)).** The pin `76dbaf20`, the `johnnyeric/` branch model, `packages/www` paths and companion links are wrong for `v2`. Where this file conflicts with `plans/migration-context.md` or `AGENTS.md`, those win. The substantive rules (merge forward, triage vocabulary, Kilo-owned placement, narrow hooks, markers, compact tables) still apply. Evidence: `discovery/doc-inventory.md` §4. Rules for working on the Kilo product line built on `upstream/v2`. Companion to `migration-tracking/plans/kilo-opencode-v2-plan-progress.md` (scope and phases) and From 7be5a1eb3683b9b9d5b473bad4a41ea76ad74e8b Mon Sep 17 00:00:00 2001 From: Fabio Pliger Date: Mon, 12 Oct 2026 12:15:51 -0500 Subject: [PATCH 15/18] docs(migration): disambiguate origin/v2 and upstream/v2 in migration docs --- migration-tracking/README.md | 2 +- migration-tracking/discovery/doc-inventory.md | 8 ++--- .../discovery/v2-epic-taxonomy.md | 10 +++---- .../discovery/v2-feature-inventory.md | 2 +- .../discovery/v2-unit-register.md | 4 +-- .../plans/kilo-opencode-v2-plan-progress.md | 2 +- .../plans/kilo-v2-migration-plan.md | 30 ++++++++++--------- migration-tracking/plans/migration-context.md | 16 +++++----- .../baseline/pinned-v2-baseline.md | 4 +-- .../technical-notes/v2-fork-conventions.md | 2 +- 10 files changed, 41 insertions(+), 39 deletions(-) diff --git a/migration-tracking/README.md b/migration-tracking/README.md index 6c779a2e34c5..ad0a68371c57 100644 --- a/migration-tracking/README.md +++ b/migration-tracking/README.md @@ -1,6 +1,6 @@ # Kilo v2 migration tracking -Planning, evidence and validation material for moving Kilo onto upstream opencode v2 on the `v2` branch. +Planning, evidence and validation material for moving Kilo onto the upstream opencode v2 runtime. The work lands on Kilo's integration branch, `origin/v2` (not to be confused with opencode's `upstream/v2`). ## Start here diff --git a/migration-tracking/discovery/doc-inventory.md b/migration-tracking/discovery/doc-inventory.md index c0641c45aec5..4e28ef058765 100644 --- a/migration-tracking/discovery/doc-inventory.md +++ b/migration-tracking/discovery/doc-inventory.md @@ -55,7 +55,7 @@ Columns: class · reason · pin/branch assumed · contradicts (decision) · fact | `technical-notes/.DS_Store` | rewrite-or-drop (drop) | macOS junk | — | — | — | | `assets/kilo-v2-cli-2026-09-10.png` | archive | Screenshot of the donor CLI, embedded in the published #13750 body via a raw `kilo-v2` URL | `kilo-v2` | — | Donor CLI UI on 2026-09-10 (Plan session, tabs, questions, usage sidebar) | | `assets/kilo-v2-vscode-2026-09-10.png` | archive | Same, for the donor VS Code UI | `kilo-v2` | — | Donor VS Code sidebar and Agent Manager on 2026-09-10 | -| `plans/migration-context.md` | **living** | Canonical brief (fixed rule) | BASE `5e73d5cb`, `v2`, V1 `fb7e96ed`, SNAP `f7115470`, DONOR `879b89e8` | — | Needs the SNAP note from finding 1 and the `kilo2`/`next` channel question from finding 5 | +| `plans/migration-context.md` | **living** | Canonical brief (fixed rule) | BASE `5e73d5cb`, Kilo integration branch `origin/v2`, V1 `fb7e96ed`, SNAP `f7115470`, DONOR `879b89e8` | — | Needs the SNAP note from finding 1 and the `kilo2`/`next` channel question from finding 5 | | `plans/kilo-opencode-v2-execution-plan.md` | archive | Old plan (fixed rule). Finalized 09-30, never started | `kilo-v2` on base `59b29de4`, merge release tags into `kilo-v2`, scope-rename codemod | D1 (tags as merge targets, work continues on `kilo-v2`), D2 | See §7. Its decisions 3/4/5/6/8 map to new D5/D6/D7/D7/D8 | | `plans/kilo-opencode-v2-plan-progress.md` | archive | Old progress plan (fixed rule). Status "Implementation paused", 09-09 | `kilo-v2`, phases 0-6 with a single cutover | D5 (single cutover, VS Code-first next steps), D7 (voice, image, Claw and JetBrains rows as needs-port or deferred, not post-cutover), D6 (preview store) | 88-row capability inventory (lines 77-198) with per-row remaining scope; see §7 | | `plans/kilo-opencode-v2-working-log.md` | evidence | Frozen dated record. It holds unique facts (user decisions, incidents, measurements, sharing gates, harness rationale) that exist nowhere else, so it is more than a superseded plan. Its headers and pause instruction are hazardous (finding 2) | `kilo-v2`, `76dbaf20` → `59b29de4`, V1 `ecccd1f`/`d99662338`, `kilo2` identity | D1/D2 (header and pause), D6 (`kilo2` and "take the kilo paths" rules, lines 558-596) | 09-08 user decision: preserve the original VS Code UI; the upstream-app wrapper was rejected (71-79). Store and rollback rules (558-596). Sharing gates (1786-1821). Self-sufficient CLI harness (1838-1889) | @@ -181,7 +181,7 @@ Unless stated otherwise, "Assumes" means donor `packages/kilo-cli` (or another ` | Stop if a recorded SHA becomes unreachable; never force-push (21-23) | Applies | | | `upstream/dev` is a sibling, diverged at `0e2dd4ad` (24-25) | Applies (fact) | | | `virtua@0.49.1` patch caveat (27-34) | **Stale / UNVERIFIED** | BASE root `package.json` has no `virtua`, and `patches/` has no virtua patch. The rule only matters once `kilo-vscode` or `kilo-ide-ui` are re-landed. Keep it as a note for G2 | -| Integration branch `johnnyeric/kilo-opencode-v2`, slices `johnnyeric/v2-*` (38-44) | **Wrong** | Integration branch is `v2`. Branch names must be short and hyphenated with no slashes (AGENTS.md, migration-context Conventions) | +| Integration branch `johnnyeric/kilo-opencode-v2`, slices `johnnyeric/v2-*` (38-44) | **Wrong** | Integration branch is Kilo's `origin/v2`. Branch names must be short and hyphenated with no slashes (AGENTS.md, migration-context Conventions) | | Merge forward, never rebase the integration branch (46-48) | Applies | Matches D1 and the coordinator rules | | `johnnyeric/` prefix overrides upstream naming (49-53) | **Wrong** | Contradicts AGENTS.md and the brief. The `type(scope): summary` part still applies | | Dev-fix triage lane `not-applicable/already-fixed/port/defer` (55-67) | Applies (concept) | It should track the V1 delta under the staleness gate (D4) as well as `upstream/dev`. "Parity ledger dev-line lane" no longer exists, so name the new home | @@ -228,7 +228,7 @@ It was read, not run. Findings: 6. Pre-existing failures (140), with subsections: also red on upstream CI (145) (`opencode-drive` JSON parse; tui footer `Café`); host-environment failures (170) (`pa-PK` ICU; ffmpeg missing); not gated by CI (194). 7. Why no fork CI workflow yet (206). 8. Reproducer (229). -- **Refresh needs:** BASE `5e73d5cb` and the tag `v2.0.26` marker. Integration branch `v2`. Current CI commands (`check.yml` `bun run check`; `test.yml` with `--affected --concurrency=3`, effect-simplifications, Node 26.4.0 build, `services/www`). Current `packageManager` bun version (UNVERIFIED; not read). New failure inventory. Package names under the `@opencode/*` scope. Bootstrap status in `migration-context.md` lists "Baseline health recorded" as recommended next. +- **Refresh needs:** BASE `5e73d5cb` and the tag `v2.0.26` marker. Kilo integration branch `origin/v2`. Current CI commands (`check.yml` `bun run check`; `test.yml` with `--affected --concurrency=3`, effect-simplifications, Node 26.4.0 build, `services/www`). Current `packageManager` bun version (UNVERIFIED; not read). New failure inventory. Package names under the `@opencode/*` scope. Bootstrap status in `migration-context.md` lists "Baseline health recorded" as recommended next. ## 7. Facts worth preserving from the old plans (for the new plan's facts appendix) @@ -311,7 +311,7 @@ It was read, not run. Findings: ## 8. Rewrite-or-drop list (11 remaining) 1. `migration-tracking/README.md`: rewrite as the living index (context brief, discovery, new plan, evidence folders). -2. `technical-notes/v2-fork-conventions.md`: rewrite (BASE pin, `v2`, AGENTS branch names, path fixes); then living. +2. `technical-notes/v2-fork-conventions.md`: rewrite (BASE pin, Kilo integration branch `origin/v2`, AGENTS branch names, path fixes); then living. 3. ~~`technical-notes/baseline/pinned-v2-baseline.md`~~: done in this phase. 4. `technical-notes/script/v2-baseline.ts`: fix the pin, the ancestry assertion, `services/www` and the CI mirror; then living. 5. `test-plans/kilo-opencode-v2-test-plan.md`: rewrite as a gate-tagged index. diff --git a/migration-tracking/discovery/v2-epic-taxonomy.md b/migration-tracking/discovery/v2-epic-taxonomy.md index 4d894b81aba9..fa061fe6e16f 100644 --- a/migration-tracking/discovery/v2-epic-taxonomy.md +++ b/migration-tracking/discovery/v2-epic-taxonomy.md @@ -68,13 +68,13 @@ Each unit has exactly one surface. ### E0 Foundation - **Why:** the clean base has no Kilo package at all (`git ls-tree HEAD packages/` lists only upstream packages). Every other epic needs a host, a registration pattern and a working CI signal first. -- **What:** Kilo host/composition package; Kilo RPC and plugin scaffolding; re-landed Kilo `AGENTS.md`, rewritten fork conventions and migration-tracking index; Kilo CI lane on `v2`; disable or neutralize upstream workflows and publish targets that must not run from the fork; v1 reference fixtures for the runtime-policy units. +- **What:** Kilo host/composition package; Kilo RPC and plugin scaffolding; re-landed Kilo `AGENTS.md`, rewritten fork conventions and migration-tracking index; Kilo CI lane on Kilo's `origin/v2`; disable or neutralize upstream workflows and publish targets that must not run from the fork; v1 reference fixtures for the runtime-policy units. - **Exit criteria:** - A Kilo host package builds and starts an isolated server through public seams only, verified by a service smoke test. - One sample Kilo RPC, registered as a plugin, passes a round-trip test through the generated client. - - A Kilo-owned workflow runs `bun run check` and package tests on PRs to `v2`, with results compared to `pinned-v2-baseline.md`. + - A Kilo-owned workflow runs `bun run check` and package tests on PRs to `origin/v2`, with results compared to `pinned-v2-baseline.md`. - No upstream publish or deploy workflow can publish from the fork, verified by reading each workflow's trigger and repository guard. - - `AGENTS.md` and `v2-fork-conventions.md` name `v2`, BASE and the AGENTS branch rules. + - `AGENTS.md` and `v2-fork-conventions.md` name the Kilo integration branch `origin/v2`, BASE and the AGENTS branch rules. - v1 reference fixtures for offline wait, overflow, slow snapshot and snapshot retention are committed and replayable. ### E1 Core and identity @@ -152,7 +152,7 @@ Each unit has exactly one surface. - **Why:** JetBrains pins a v1 CLI version until G3 (D5). The plugin is absent from both BASE and the donor. - **What:** contract inventory; server startup, discovery, auth and version checks; sessions, streaming, permissions, history, config; terminal and editor-service dependencies; IDE validation; G3 scenario set. - **Exit criteria:** - - The plugin builds from `v2` and talks to v2 only through the generated client from the assembled OpenAPI contract. + - The plugin builds from Kilo's `origin/v2` and talks to the v2 runtime only through the generated client from the assembled OpenAPI contract. - Every G3 scenario passes in each supported IDE, recorded with SHA. - Version-mismatch handling refuses incompatible CLIs with a clear message. @@ -167,7 +167,7 @@ Each unit has exactly one surface. ### E8 Retirement -- **Why:** the end state is `v2` as `main` and v1 on a maintenance branch (`migration-context.md`). +- **Why:** the end state is Kilo's `origin/v2` as `main` and v1 on a maintenance branch (`migration-context.md`). - **What:** branch flip and its timing relative to IDE v1 builds; path consolidation after G3 (D6); re-landing or retiring packages that deploy from `main` (docs site, console); v1 end-of-life policy. - **Exit criteria:** - Every workflow and deployment that builds from `main` has a v2-side owner or is retired before the flip. diff --git a/migration-tracking/discovery/v2-feature-inventory.md b/migration-tracking/discovery/v2-feature-inventory.md index 8951d379ddd2..d436ed600ad1 100644 --- a/migration-tracking/discovery/v2-feature-inventory.md +++ b/migration-tracking/discovery/v2-feature-inventory.md @@ -322,7 +322,7 @@ Source: direct read of V1 (`fb7e96ed`), written by the analyst after the delegat | VS Code | Bundles `bin/kilo`, built from `packages/opencode` **in the same checkout** (`script/local-bin.ts`, `prepare:cli-binary`). Also bundles bwrap, ffmpeg, tree-sitter and sandbox-worker resources | Generated `@kilocode/sdk/v2` client over HTTP/SSE, started by `kv/services/cli-backend/server-manager.ts`. Calls Kilo-specific routes (`kilo.*`, `kilocode.*`, `remote.*`, `network.*`, `suggestion.*`, `commitMessage.*`, `branchName.*`, `anacondaDesktop.*`), core routes (`session.*` incl. `session.deleteMessage`, `permission.*`, `question.*`, `mcp.*`, `pty.*`/`v2.pty.*`, `config.overlay*`, `provider.oauth.*`, `experimental.capabilities.get`, `experimental.controlPlane.moveSession`) | `kv/services/cli-backend/*`, `kv/features.ts` | | JetBrains | Pins the CLI release named in `jb/package.json` (`version` 7.8.8). `kilo.cli.pinned=false` is dev-only and refused for production builds (`jb/build.gradle.kts:89-98`). Plugin version 7.1.9 on its own `jetbrains/v*` tag line | Split-mode plugin (backend/frontend/shared modules). Kotlin client generated from the CLI's OpenAPI spec (`jb/backend/build.gradle.kts:11-21,64,129`, `generateOpenApiSpec`, `normalizeOpenApiSpec`) | `jb/gradle.properties`, `jb/build.gradle.kts` | -**Consequence for the branch flip (U-E8-01).** The VS Code v1 build compiles its bundled CLI from `packages/opencode` in the same tree. Once `v2` becomes `main`, a v1 extension cannot be built from `main`. Either the flip waits until after G2 and G3, or v1 IDE releases build from the `v1` branch. +**Consequence for the branch flip (U-E8-01).** The VS Code v1 build compiles its bundled CLI from `packages/opencode` in the same tree. Once Kilo's `origin/v2` becomes `main`, a v1 extension cannot be built from `main`. Either the flip waits until after G2 and G3, or v1 IDE releases build from the `v1` branch. ### VS Code (A22, A23, A24 and runtime consumers) diff --git a/migration-tracking/discovery/v2-unit-register.md b/migration-tracking/discovery/v2-unit-register.md index 277066d99724..57086071b83f 100644 --- a/migration-tracking/discovery/v2-unit-register.md +++ b/migration-tracking/discovery/v2-unit-register.md @@ -26,7 +26,7 @@ Donor paths are relative to the donor tag; `kc/` = `packages/kilo-cli/src/`, `kv | U-E0-01 | Kilo host and composition package (server, overrides, Location service replacements, builtin plugin list) | shared runtime | pre-gate | (host for all E1/E2) | new | needs-port | n/a | yes H: `kc/host.ts` (`ServerFetch.make(options,{overrides})`; `server/src/fetch.ts` overrides still present at BASE) | — | | U-E0-02 | Kilo RPC and plugin scaffolding (`packages/schema/src/kilocode`, plugin `define`, generated client consumption) | shared runtime | pre-gate | F-V2-050, Part 1 "Plugins and SDK" | new | native mechanism | n/a | yes H: `packages/schema/src/kilocode/*` (`schema/src/rpc.ts` unchanged since donor base) | U-E0-01 | | U-E0-03 | Re-land Kilo `AGENTS.md`; rewrite `v2-fork-conventions.md`, migration-tracking `README.md`, `v2-baseline.ts`; superseded banners on old plans (see `doc-inventory.md` §8) | repo | pre-gate | — | new | needs-port | n/a | no | — | -| U-E0-04 | Kilo CI lane on `v2`; neutralize upstream publish/deploy workflows, `sdks/vscode` publishing, hosted web properties, CI images | repo | pre-gate | F-V2-055, F-V2-056, F-V2-057 | new (part of #14408 scope) | needs-port | n/a | no | admin task: Actions permissions | +| U-E0-04 | Kilo CI lane on Kilo's `origin/v2`; neutralize upstream publish/deploy workflows, `sdks/vscode` publishing, hosted web properties, CI images | repo | pre-gate | F-V2-055, F-V2-056, F-V2-057 | new (part of #14408 scope) | needs-port | n/a | no | admin task: Actions permissions | | U-E0-05 | v1 reference fixtures for offline wait, overflow, slow snapshot, snapshot retention (data at V1, harness on BASE) | repo | pre-gate | — | new (old N5) | needs-port | MATERIAL (A06) | partial M: `kc/test/fixtures/v1-session`, `v1-writer*` (built at V1 `d99662338`/`ecccd1f`; regenerate at V1 `fb7e96ed`) | U-E0-01 | ## E1 Core and identity @@ -188,7 +188,7 @@ Shared facts for E7. The plugin is absent from BASE and from the donor (no reuse | Unit | Title | Surface | Gate | Features | Issues | Delta | Stale | Donor | Depends | |---|---|---|---|---|---|---|---|---|---| -| U-E8-01 | Branch flip (`main`→`v1`, `v2`→`main`) and its timing relative to IDE v1 builds | repo | post-G3 | — | new (old N10) | needs-port | n/a | no | G3 go/no-go | +| U-E8-01 | Branch flip (`main`→`v1`, Kilo's `origin/v2`→`main`) and its timing relative to IDE v1 builds | repo | post-G3 | — | new (old N10) | needs-port | n/a | no | G3 go/no-go | | U-E8-02 | Storage path consolidation after G3 (D6), one-time with backup | import | post-G3 | — | new (old N10) | needs-port | n/a | no | U-E8-01 | | U-E8-03 | Packages that deploy from `main`: keep (docs site), retire (`kilo-console`, F-CORE-123), re-home their workflows | repo | post-G3 | F-CORE-123 | new (old N6a/N6b in part) | not-needed (console) / needs-port (docs deploy) | MATERIAL (A29) | no | U-L5-01 | | U-E8-04 | v1 end-of-life policy and maintenance window | repo | post-G3 | — | new | needs-port | n/a | no | U-E8-01 | diff --git a/migration-tracking/plans/kilo-opencode-v2-plan-progress.md b/migration-tracking/plans/kilo-opencode-v2-plan-progress.md index 244cbd44c1bb..78a77027bc7c 100644 --- a/migration-tracking/plans/kilo-opencode-v2-plan-progress.md +++ b/migration-tracking/plans/kilo-opencode-v2-plan-progress.md @@ -1,7 +1,7 @@ # Phased Plan — Port Kilo onto OpenCode v2 > [!WARNING] -> **Superseded by [`kilo-v2-migration-plan.md`](kilo-v2-migration-plan.md) (2026-10-12).** Archived reference only. Status and percentages below describe the archived `kilo-v2` port, not `v2`. Live unit status is in Part B of the new plan. +> **Superseded by [`kilo-v2-migration-plan.md`](kilo-v2-migration-plan.md) (2026-10-12).** Archived reference only. Status and percentages below describe the archived `kilo-v2` port, not the new Kilo integration branch `origin/v2`. Live unit status is in Part B of the new plan. Updated: September 9, 2026 Related issue: Kilo-Org/kilocode#13750 diff --git a/migration-tracking/plans/kilo-v2-migration-plan.md b/migration-tracking/plans/kilo-v2-migration-plan.md index d74b5da3e6c9..ffe87e076084 100644 --- a/migration-tracking/plans/kilo-v2-migration-plan.md +++ b/migration-tracking/plans/kilo-v2-migration-plan.md @@ -16,7 +16,7 @@ This file is the living index of the migration: epics, gates and sequencing (Par ## A1. Objective -Ship Kilo on the upstream v2 runtime, surface by surface, without breaking a v1 user. The target feature set is upstream v2 at the pinned base plus every Kilo v1 behavior at the pinned v1 target, minus what Decision 7 marks post-cutover or not-needed: feature parity, not code parity. The terminal product (CLI/TUI) ships first at G1; VS Code (G2) and JetBrains (G3) follow on their own go/no-go, and until then they keep running on v1 against untouched v1 data. The work ends when `v2` becomes `main` and v1 moves to a time-limited maintenance branch. +Ship Kilo on the upstream v2 runtime, surface by surface, without breaking a v1 user. The target feature set is upstream v2 at the pinned base plus every Kilo v1 behavior at the pinned v1 target, minus what Decision 7 marks post-cutover or not-needed: feature parity, not code parity. The terminal product (CLI/TUI) ships first at G1; VS Code (G2) and JetBrains (G3) follow on their own go/no-go, and until then they keep running on v1 against untouched v1 data. The work ends when Kilo's integration branch, `origin/v2`, becomes `main` and v1 moves to a time-limited maintenance branch. ## A2. Why this plan replaces the previous one @@ -45,7 +45,7 @@ The ten decisions in [`migration-context.md` §Decisions](migration-context.md#d | Decision | How this plan applies it | |---|---| -| D1 New base | Every unit lands on `v2` by PR. Upstream arrives only through the L1 lane, pinned by SHA | +| D1 New base | Every unit lands on Kilo's `origin/v2` by PR. opencode's `upstream/v2` arrives only through the L1 lane, pinned by SHA | | D2 Freeze | The archived port is read through its tag; nothing merges from it | | D3 Donor rules | Each Part B row states donor reuse for mechanism only. Behavior acceptance is always written against v1 | | D4 Staleness gate | Each Part B row carries a staleness verdict. Non-EMPTY means behavior is re-derived from v1 | @@ -60,7 +60,7 @@ Items that need user sign-off are listed in A10. None of them changes a decision ## A4. Approach -**Clean base.** `v2` starts as pristine upstream and contains no Kilo package yet. Kilo is re-landed onto it in small units, each one a session, a branch and a PR, merged by the user. Nothing from the old port branch is merged. +**Clean base.** Kilo's integration branch, `origin/v2`, starts as pristine upstream and contains no Kilo package yet. Kilo is re-landed onto it in small units, each one a session, a branch and a PR, merged by the user. Nothing from the old port branch is merged. **Re-land by unit.** Every Kilo v1 behavior and every upstream-only surface has exactly one unit (Part B). A unit is done only with code, tests, acceptance evidence recorded with a SHA, its Part B status updated, and docs. A local pass is never recorded as end to end. @@ -112,7 +112,7 @@ Epic IDs follow discovery so register rows stay traceable; they are presented in - **Outcome:** a Kilo host, a Kilo plugin/RPC pattern, a Kilo CI signal and v1 reference fixtures exist on the clean base, and no upstream workflow can publish from the fork. - **Why:** the base has no Kilo package at all. Every other epic needs these first. -- **Exit criteria:** the host starts an isolated server through public seams only (service smoke test); one sample Kilo RPC round-trips through the generated client; a Kilo-owned workflow runs `bun run check` and package tests on PRs to `v2`, compared against the pinned baseline; every upstream publish or deploy workflow is disabled or guarded; Kilo `AGENTS.md` and fork conventions name the current base and branch rules; v1 reference fixtures for offline wait, overflow, slow snapshot and snapshot retention replay on the base. +- **Exit criteria:** the host starts an isolated server through public seams only (service smoke test); one sample Kilo RPC round-trips through the generated client; a Kilo-owned workflow runs `bun run check` and package tests on PRs to `origin/v2`, compared against the pinned baseline; every upstream publish or deploy workflow is disabled or guarded; Kilo `AGENTS.md` and fork conventions name the current base and branch rules; v1 reference fixtures for offline wait, overflow, slow snapshot and snapshot retention replay on the base. - **Gate:** blocks everything. - **Depends on:** the admin task for Actions permissions (CI lane only). - **Top risks:** upstream's test workflow runs on every pull request, so it will run on fork PRs before the Kilo lane exists; branch protection is convention-only until an admin acts. @@ -193,14 +193,14 @@ Epic IDs follow discovery so register rows stay traceable; they are presented in - **Outcome:** the JetBrains plugin re-landed on the clean base, talking to v2 only through a client generated from the assembled contract. - **Why:** JetBrains pins a v1 CLI until G3; the plugin is absent from both the base and the donor. -- **Exit criteria:** the plugin builds from `v2` and uses only the generated client; every G3 scenario passes in each supported IDE, recorded with SHA; incompatible CLIs are refused with a clear message. +- **Exit criteria:** the plugin builds from Kilo's `origin/v2` and uses only the generated client; every G3 scenario passes in each supported IDE, recorded with SHA; incompatible CLIs are refused with a clear message. - **Gate:** G3. - **Depends on:** E0 RPC scaffolding, E2 control-plane and generation contracts, E3 build pipeline. - **Top risks:** no donor at all; Kilo plugin RPC endpoints may not appear in the OpenAPI the Kotlin client is generated from; whether FIM gates G3 is still open. ### E8 Retirement (post-G3) -- **Outcome:** `v2` is `main`, v1 lives on a maintenance branch that still builds and releases, storage paths are consolidated once, and every deployment from `main` has an owner. +- **Outcome:** Kilo's `origin/v2` is `main`, v1 lives on a maintenance branch that still builds and releases, storage paths are consolidated once, and every deployment from `main` has an owner. - **Why:** the end state in `migration-context.md`. - **Exit criteria:** every workflow and deployment that builds from `main` has a v2-side owner or is retired before the flip; the v1 maintenance branch builds and releases; storage consolidation runs once, with a backup, and is never repeated; the v1 end-of-life policy is published. - **Gate:** after G3. @@ -388,8 +388,8 @@ Each epic section has workstream tables, one row per unit, and one dependency di |---|---|---|---|---|---|---|---|---|---| | U-E0-01 | Kilo host and composition package (server overrides, Location service replacements, builtin plugin list) | new | pre-gate | Q-HOST-01 | needs-port | n/a | yes H | Host builds and starts an isolated server through public seams only; `service-smoke` passes with isolated HOME/XDG; Q-HOST-01 answer recorded | todo | | U-E0-02 | Kilo RPC and plugin scaffolding (`packages/schema/src/kilocode`, plugin `define`, generated client) | new | pre-gate | U-E0-01 | native mechanism | n/a | yes H | One sample Kilo RPC registered as a plugin round-trips through the generated client; `bun run generate` leaves no drift | todo | -| U-E0-03 | Re-land Kilo `AGENTS.md`; rewrite `v2-fork-conventions.md` and `script/v2-baseline.ts` (README, banners and drops done by this plan, Appendix D) | new | pre-gate | — | needs-port | n/a | no | `AGENTS.md` names `v2`, BASE and branch rules; conventions carry the base pin, no-prefix branches, `services/www`; `v2-baseline.ts` runs at BASE (pin-is-ancestor assertion, mirrors `check.yml`/`test.yml`) | todo | -| U-E0-04 | Kilo CI lane on `v2`; neutralize upstream publish/deploy workflows, `sdks/vscode` publishing, hosted web properties, CI images | new | pre-gate | admin: Actions permissions | needs-port | n/a | no | Kilo workflow runs `bun run check` and package tests on PRs to `v2`, compared with `pinned-v2-baseline.md`; each upstream publish/deploy workflow disabled or repository-guarded, verified by reading triggers | todo | +| U-E0-03 | Re-land Kilo `AGENTS.md`; rewrite `v2-fork-conventions.md` and `script/v2-baseline.ts` (README, banners and drops done by this plan, Appendix D) | new | pre-gate | — | needs-port | n/a | no | `AGENTS.md` names the integration branch `origin/v2`, BASE and branch rules; conventions carry the base pin, no-prefix branches, `services/www`; `v2-baseline.ts` runs at BASE (pin-is-ancestor assertion, mirrors `check.yml`/`test.yml`) | todo | +| U-E0-04 | Kilo CI lane on Kilo's `origin/v2`; neutralize upstream publish/deploy workflows, `sdks/vscode` publishing, hosted web properties, CI images | new | pre-gate | admin: Actions permissions | needs-port | n/a | no | Kilo workflow runs `bun run check` and package tests on PRs to `origin/v2`, compared with `pinned-v2-baseline.md`; each upstream publish/deploy workflow disabled or repository-guarded, verified by reading triggers | todo | | U-E0-05 | v1 reference fixtures: offline wait, overflow, slow snapshot, snapshot retention (data at v1 target, harness on BASE) | new | pre-gate | U-E0-01 | needs-port | M | partial M | Fixtures generated through v1 writer methods at `fb7e96ed`, each isolating HOME/XDG itself; harness on BASE replays them | todo | ```mermaid @@ -618,7 +618,7 @@ The chain 09 → 12 is the serialization rule for shared TUI files, not a functi | U-E3-06 | Updater and installation detection under Kilo identity | #14410 | G1 | U-E1-01 | partial | M | yes M | Update and rollback on real artifacts; install method detected; overlap with BASE `services/update` decided | todo | | U-E3-07 | Build and release pipeline: platforms, npm/GHCR/AUR/Homebrew/nix/install script, SBOM, PTY smoke, payload format, clean install | #14409, #14412 | G1 | U-E0-04 | partial | M | partial M | Artifacts for every v1 platform; each channel dry-runs; SBOM; PTY smoke; clean install beside v1 | todo | | U-E3-13 | Signing and notarization (start early) | #14411 | G1 | U-E0-04, admin: signing credentials | partial | M | no | Signed and notarized macOS and Windows artifacts verify on clean hosts | todo | -| U-E3-08 | CI and repository guards (forbidden strings, generated artifacts, markers, md tables, CodeQL, sharding, bots) | #14408 | G1 | U-E0-04 | partial | M | no | Each v1 guard ported or recorded not-needed; guards run on PRs to `v2` | todo | +| U-E3-08 | CI and repository guards (forbidden strings, generated artifacts, markers, md tables, CodeQL, sharding, bots) | #14408 | G1 | U-E0-04 | partial | M | no | Each v1 guard ported or recorded not-needed; guards run on PRs to `origin/v2` | todo | | U-E3-09 | Publish `@kilocode/sdk` and `@kilocode/plugin` from BASE packages | new | G1 | U-E3-07 | partial | SMALL | no | Publish dry run passes `verify:package`; names and versions decided | todo | | U-E3-10 | v2 preview release train | new | G1 | U-E3-06, U-E3-07, U-E3-08, U-E3-12 | needs-port | n/a | no | At least one preview shipped to the team under the Q-DIST-01 identity; findings filed against units | todo | | U-E3-11 | Canary and cutover/rollback runbook, G1 | #14414 | G1 | U-E3-10, U-L4-05 | needs-port | n/a | no | Runbook written and exercised once on the G1 candidate; rollback to v1 CLI on untouched v1 files | todo | @@ -768,7 +768,7 @@ No donor exists; behavior comes entirely from v1 (`packages/kilo-jetbrains` at ` | Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | |---|---|---|---|---|---|---|---|---|---| | U-E7-01 | Inventory plugin contracts and map each to a public v2 interface | #14420 | G3 | U-E0-02 | needs-port | M | no | Every route, event and terminal contract mapped; Q-G3-01 and Q-G3-02 answered | todo | -| U-E7-02 | Re-land the plugin: Gradle build, CLI pin to a v2 release, client generated from the assembled contract, startup, discovery, auth, version checks | #14421 | G3 | U-E7-01, U-E3-07, Q-G3-03 | needs-port | M | no | Builds from `v2`; only the generated client is used; incompatible CLIs refused with a clear message | todo | +| U-E7-02 | Re-land the plugin: Gradle build, CLI pin to a v2 release, client generated from the assembled contract, startup, discovery, auth, version checks | #14421 | G3 | U-E7-01, U-E3-07, Q-G3-03 | needs-port | M | no | Builds from Kilo's `origin/v2`; only the generated client is used; incompatible CLIs refused with a clear message | todo | | U-E7-03 | Sessions, streaming, permissions, history, config, board, subagents, prompt completion, settings, MCP OAuth, telemetry | #14422 | G3 | U-E7-02 | needs-port | M | no | Each area as v1 | todo | | U-E7-04 | Terminal, diff/VFS, editor-service consumers (enhance prompt, branch name; FIM only if Q-G3-01 keeps the gate) | #14423 | G3 | U-E7-02, U-E2-28 (U-E6-08 if FIM gates) | needs-port | M | no | Each consumer as v1 | todo | | U-E7-05 | Agent Manager, worktrees, marketplace, legacy v5 migration | new | G3 | U-E2-27, U-E7-03, Q-G3-05 | needs-port | M | no | Each area as v1; v5 migration per Q-G3-05 | todo | @@ -794,7 +794,7 @@ flowchart LR | Unit | Title | Issue | Gate | Deps | Delta | Stale | Donor | Acceptance | Status | |---|---|---|---|---|---|---|---|---|---| -| U-E8-01 | Branch flip (`main`→`v1`, `v2`→`main`), timed against v1 IDE builds | new | post-G3 | G3 go, Q-G2-01 | needs-port | n/a | no | Flip done; the `v1` branch builds and releases CLI and IDEs | todo | +| U-E8-01 | Branch flip (`main`→`v1`, Kilo's `origin/v2`→`main`), timed against v1 IDE builds | new | post-G3 | G3 go, Q-G2-01 | needs-port | n/a | no | Flip done; the `v1` branch builds and releases CLI and IDEs | todo | | U-E8-02 | Storage path consolidation (D6), once, with backup | new | post-G3 | U-E8-01 | needs-port | n/a | no | Runs once with backup; a second run is a no-op | todo | | U-E8-03 | Packages that deploy from `main`: keep docs site, retire `kilo-console`, re-home workflows | new | post-G3 | U-L5-01 | partial / not-needed | M | no | Every workflow and deployment from `main` has a v2 owner or is retired before the flip | todo | | U-E8-04 | v1 end-of-life policy and maintenance window | new | post-G3 | U-E8-01 | needs-port | n/a | no | Policy published with a date | todo | @@ -819,7 +819,7 @@ Acceptance for every row is a recorded user decision (ship, drop or re-scope); s | U-E11-03 | Team sharing ownership; incremental sharing with deletion/revert semantics | #14399 (ownership), #14400 | non-gating | U-E5-02 | post-cutover | EMPTY | no | Decision recorded | todo | | U-E11-04 | Legacy TUI extras: kilo-v1/colorblind themes, legacy bindings, fixed-width tables, vim mode | #14376, #14378 (part) | non-gating | U-E9-05 | post-cutover | M | partial L | Decision recorded | todo | | U-E11-05 | Default-off experimental features (Exa, context tools, code mode/PTC, MCP Apps, session switcher, notebook tools, Claude migration, scout, LSP tool, VS Code browser automation and prompt history) | new | non-gating | Q-G2-07 | post-cutover | NEW | no | Decision recorded per feature | todo | -| U-E11-06 | KiloClaw: not needed (removed on v1); no donor remnants copied | #14419 (Claw part) | non-gating | — | not-needed | n/a | no | Grep shows no Claw paths on `v2` | todo | +| U-E11-06 | KiloClaw: not needed (removed on v1); no donor remnants copied | #14419 (Claw part) | non-gating | — | not-needed | n/a | no | Grep shows no Claw paths on Kilo's `origin/v2` | todo | ```mermaid flowchart LR @@ -1024,8 +1024,10 @@ No discovery epic or unit was dropped. Every discovery unit keeps its ID. | Term | Meaning | |---|---| | v1 | Kilo on `main`, built on opencode v1. The behavioral reference at the pinned v1 target | -| v2 | Kilo on upstream opencode `v2`, built on `v2` | -| BASE | The pinned upstream commit `v2` started from | +| v2 | Kilo running on the opencode v2 runtime (the product line this migration builds) | +| `origin/v2` | Kilo's integration branch on Kilo-Org/kilocode. Every unit merges here; it becomes `main` at cutover | +| `upstream/v2` | opencode's v2 branch on anomalyco/opencode. Kilo merges it in, pinned by SHA (L1) | +| BASE | The pinned `upstream/v2` commit that Kilo's `origin/v2` started from | | Donor | The archived old port (tag), used for mechanism only | | Snapshot / cutoff | The v1 commit up to which every Kilo change has a disposition. L2 advances it | | Staleness | v1 change to a unit's files between snapshot and v1 target (D4) | diff --git a/migration-tracking/plans/migration-context.md b/migration-tracking/plans/migration-context.md index 19a6be340d82..2b75a496816c 100644 --- a/migration-tracking/plans/migration-context.md +++ b/migration-tracking/plans/migration-context.md @@ -8,8 +8,8 @@ Status: planned (bootstrap docs complete; no unit started). Last updated 2026-10 | Item | Value | |---|---| -| Integration branch | `v2` (long-lived; becomes `main` at cutover) | -| Current working base | `kilo-v2-clean-bootstrap` (seeded docs on top of `v2`). Discovery, planner and coordinator branch from this until its PR is merged into `v2` | +| Integration branch | `origin/v2` on Kilo-Org/kilocode (long-lived; becomes `main` at cutover). Not to be confused with opencode's `upstream/v2` | +| Current working base | `kilo-v2-clean-bootstrap` (seeded docs on top of Kilo's `origin/v2`). Discovery, planner and coordinator branch from this until its PR is merged into `origin/v2` | | Upstream base commit | `5e73d5cb1f788637b1b3d5cf1298ea694cbad0c5`: the parent of upstream release tag `v2.0.26`, on `upstream/v2` (`feat(core): add Google Cloud credential setup for Vertex (#53798)`) | | Upstream release tag | `v2.0.26` = `9b4ec5714d481559990db0a816d5dec19541a814` (reference marker only; not on `upstream/v2`, see note) | | Archived old port | tag `kilo-v2-checkpoints-2026-09` = `879b89e89034f0e9f4cfb7fd09a5323ef06f83c9` (former `kilo-v2` tip). Donor code only | @@ -18,12 +18,12 @@ Status: planned (bootstrap docs complete; no unit started). Last updated 2026-10 Note on the base: upstream cuts each release as a separate version-bump commit (`release: vX`) on top of the `v2` tip, tags it, and does not merge it back into `v2`. The tag is therefore one commit off the branch. The base is the tag's parent: release-equivalent code, on `upstream/v2`, and free of the version-bump commit, so later merges of `upstream/v2` stay clean. -Eventual end state: `v2` becomes `main`; today's `main` becomes `v1`, maintained for critical fixes only, for a limited period. +Eventual end state: Kilo's `origin/v2` becomes `main`; today's `main` becomes `v1`, maintained for critical fixes only, for a limited period. ## Repositories - Kilo repo: `Kilo-Org/kilocode` (remote `origin`). `main` is Kilo v1. -- Upstream: `anomalyco/opencode` (remote `upstream`, fetch only), branch `v2`. Published on npm as `@opencode/cli`. Upstream renamed its workspace scope from `@opencode-ai/*` to `@opencode/*` on 2026-09-07 (`a5312e16`); a few packages kept the old scope. +- Upstream: `anomalyco/opencode` (remote `upstream`, fetch only), branch `v2`, always written `upstream/v2`. Published on npm as `@opencode/cli`. Upstream renamed its workspace scope from `@opencode-ai/*` to `@opencode/*` on 2026-09-07 (`a5312e16`); a few packages kept the old scope. - The old port branch `kilo-v2` still exists but is frozen. Read it through the archive tag. - **Name collision:** since 2026-10-12 the Kilo integration branch is `v2` on `origin` (Kilo-Org/kilocode), the same name as upstream's `v2` (anomalyco/opencode). Always qualify the remote: write `origin/v2` for the Kilo integration branch and `upstream/v2` for upstream, in docs, commands and merge targets. @@ -63,9 +63,9 @@ Decisions intentionally left open until the work that needs them. Each has a dea | Writers | One writer per branch and per file. Announce ownership before starting. | | Escalation | Epic or scope changes, gate assignment of consumers, and anything that changes a decision above go to the user. | -**Enforcement is by convention only, for now.** The account running the migration has write access but not repo admin, so branch protection on `v2` cannot be set yet (see deferred admin tasks). Until an admin adds it: +**Enforcement is by convention only, for now.** The account running the migration has write access but not repo admin, so branch protection on Kilo's `origin/v2` cannot be set yet (see deferred admin tasks). Until an admin adds it: -- Direct pushes to `v2` are forbidden by convention. Every change reaches it through a PR. +- Direct pushes to `origin/v2` are forbidden by convention. Every change reaches it through a PR. - Repo settings, Actions settings, secrets and branch protection are escalations to a repo admin. No agent attempts them. ## Conventions @@ -89,7 +89,7 @@ These need a repo admin (or account credentials) and do not block work. Track th | Task | When | Owner | |---|---|---| -| Branch protection on `v2`: require a PR before merging (0 approvals now, raise to 1 when reviewers are available), force-push and deletion disabled | Now | Repo admin | +| Branch protection on `origin/v2`: require a PR before merging (0 approvals now, raise to 1 when reviewers are available), force-push and deletion disabled | Now | Repo admin | | Actions permissions and secret access for the upstream sync workflow and the e2e lane | When those workflows land | Repo admin | | Signing and notarization credentials | Before the G1 preview release train | Account owner | | Protect the branch that becomes `main` | At cutover | Repo admin | @@ -98,7 +98,7 @@ These need a repo admin (or account credentials) and do not block work. Track th | Item | State | |---|---| -| `v2` created from the upstream base | Done | +| Kilo integration branch `origin/v2` created from the upstream base | Done | | Old port archived as a tag | Done | | Migration docs seeded on `kilo-v2-clean-bootstrap` | Done | | `kilo-v2-clean-bootstrap` pushed to origin | Done | diff --git a/migration-tracking/technical-notes/baseline/pinned-v2-baseline.md b/migration-tracking/technical-notes/baseline/pinned-v2-baseline.md index 61ba2b364677..5cba37f9b4ff 100644 --- a/migration-tracking/technical-notes/baseline/pinned-v2-baseline.md +++ b/migration-tracking/technical-notes/baseline/pinned-v2-baseline.md @@ -11,7 +11,7 @@ This file records the upstream commit the Kilo v2 line is built on, the validati | Upstream base (pinned) | `5e73d5cb1f788637b1b3d5cf1298ea694cbad0c5` | 2026-10-08 | `feat(core): add Google Cloud credential setup for Vertex (#53798)` | | Release marker `v2.0.26` | `9b4ec5714d481559990db0a816d5dec19541a814` | — | Version-bump commit whose parent is the base. It is not on `upstream/v2` and is not a merge target | | `upstream/v2` at capture | `d2905b8ddae8b0353cd2166d68d59795397921db` | 2026-10-09 | `feat(ai): add Vertex AI Interactions route (#54175)`. 31 commits ahead of the base | -| Integration branch `v2` | `5e73d5cb` | — | Equal to the base | +| Kilo integration branch `origin/v2` | `5e73d5cb` | — | Equal to the base | | Phase branch `kilo-v2-clean-bootstrap` (HEAD) | `a876c2912a5fd114066ce34e3a3fcc79f7f0a1b0` | 2026-10-11 | Base plus 2 documentation commits (`c4042b39a1`, `a876c2912a`). No source changes | | V1 target `origin/main` | `fb7e96eda5409517a05948b9efaf01c53997bf6e` | 2026-10-09 | `Merge pull request #14893 …` | @@ -65,7 +65,7 @@ Constraints: - Tests cannot run from the root (`bunfig.toml` test root `do-not-run-tests-from-root`). Run them from a package directory or through turbo. - Never run `tsc`. Packages use `tsgo` through `bun typecheck`. - `.husky/pre-push` checks the bun version, then runs `bun run check`. -- `check.yml` runs on pushes to `dev`/`v2` and on pull requests whose base is `dev` or `v2`. `test.yml` runs on every `pull_request`. **Changed 2026-10-12:** the Kilo integration branch was renamed to `v2`, so upstream's `check.yml` triggers now match pushes to `origin/v2` and pull requests that target it. Whether it runs on the fork, passes, or fails for lack of admin-configured secrets or Actions permissions is UNVERIFIED. CI/admin follow-up: observe the first pull request into `origin/v2` and record the result here (relates to U-E0-04 and the Actions-permissions admin task). The 2026-10-11 capture found no pull request targeting the integration branch or `kilo-v2-clean-bootstrap` (`gh pr list`). +- `check.yml` runs on pushes to any branch named `dev` or `v2`, and on pull requests whose base branch has one of those names. Upstream wrote it for `upstream/dev` and `upstream/v2`. `test.yml` runs on every `pull_request`. **Changed 2026-10-12:** the Kilo integration branch was renamed to `v2`, so upstream's `check.yml` triggers now match pushes to `origin/v2` and pull requests that target it. Whether it runs on the fork, passes, or fails for lack of admin-configured secrets or Actions permissions is UNVERIFIED. CI/admin follow-up: observe the first pull request into `origin/v2` and record the result here (relates to U-E0-04 and the Actions-permissions admin task). The 2026-10-11 capture found no pull request targeting the integration branch or `kilo-v2-clean-bootstrap` (`gh pr list`). - `packages/www` no longer exists. Generated docs live in `services/www`, and `typecheck.yml` was replaced by `check.yml`. ## Results on the phase branch (2026-10-11) diff --git a/migration-tracking/technical-notes/v2-fork-conventions.md b/migration-tracking/technical-notes/v2-fork-conventions.md index 11041c367186..94b431102290 100644 --- a/migration-tracking/technical-notes/v2-fork-conventions.md +++ b/migration-tracking/technical-notes/v2-fork-conventions.md @@ -1,7 +1,7 @@ # Kilo OpenCode v2 fork conventions > [!WARNING] -> **Partly stale; rewrite pending in U-E0-03 ([plan](../plans/kilo-v2-migration-plan.md)).** The pin `76dbaf20`, the `johnnyeric/` branch model, `packages/www` paths and companion links are wrong for `v2`. Where this file conflicts with `plans/migration-context.md` or `AGENTS.md`, those win. The substantive rules (merge forward, triage vocabulary, Kilo-owned placement, narrow hooks, markers, compact tables) still apply. Evidence: `discovery/doc-inventory.md` §4. +> **Partly stale; rewrite pending in U-E0-03 ([plan](../plans/kilo-v2-migration-plan.md)).** The pin `76dbaf20`, the `johnnyeric/` branch model, `packages/www` paths and companion links are wrong for Kilo's integration branch `origin/v2`. Where this file conflicts with `plans/migration-context.md` or `AGENTS.md`, those win. The substantive rules (merge forward, triage vocabulary, Kilo-owned placement, narrow hooks, markers, compact tables) still apply. Evidence: `discovery/doc-inventory.md` §4. Rules for working on the Kilo product line built on `upstream/v2`. Companion to `migration-tracking/plans/kilo-opencode-v2-plan-progress.md` (scope and phases) and From 0f2bc95bc318962c19802123473c0c6e576bf995 Mon Sep 17 00:00:00 2001 From: Fabio Pliger Date: Mon, 12 Oct 2026 22:58:16 -0500 Subject: [PATCH 16/18] docs(skill): point v2 migration skill at v2 branch --- .kilo/skills/v2-migration/SKILL.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.kilo/skills/v2-migration/SKILL.md b/.kilo/skills/v2-migration/SKILL.md index 6bf299eec00d..f15bcc551e1b 100644 --- a/.kilo/skills/v2-migration/SKILL.md +++ b/.kilo/skills/v2-migration/SKILL.md @@ -1,6 +1,6 @@ --- name: v2-migration -description: Use for any work on the Kilo v2 migration (branches kilo-v2-clean*, v2-*). Covers strategy, branches and PRs, the isolated test profile, how to self-test the v2 CLI yourself, and the test policy. +description: Use for any work on the Kilo v2 migration (branches v2, v2-*). Covers strategy, branches and PRs, the isolated test profile, how to self-test the v2 CLI yourself, and the test policy. --- # Kilo v2 migration @@ -24,9 +24,9 @@ Kilo moves onto upstream OpenCode v2 from a clean base, one feature at a time. V ## Branches and PRs -- Integration branch: `kilo-v2-clean`. Use the working base named in `migration-context.md`. Never target `main`. +- Integration branch: `v2`. Use the working base named in `migration-context.md`. Never target `main`. - Stack on an open v2 PR when you depend on it, and target that PR's branch. -- One plan unit per PR. Never push to `kilo-v2-clean*`. A human merges. +- One plan unit per PR. Never push directly to `v2`. A human merges. - Short hyphenated branch names, no slashes. Titles `type(scope): summary`. ## Isolation: never touch the v1 setup From e662693ee04f988ef187edc2eb44489fce1d9bd8 Mon Sep 17 00:00:00 2001 From: Fabio Pliger Date: Mon, 12 Oct 2026 23:21:59 -0500 Subject: [PATCH 17/18] chore(ci): target v2 instead of kilo-v2-clean --- .github/workflows/check.yml | 2 +- .github/workflows/kilo-v2.yml | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 03e7322892e1..89242e1e2a34 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -3,7 +3,7 @@ name: check on: push: branches: [dev, v2] - # kilocode_change - run for every pull request, so Kilo v2 PRs (which target kilo-v2-clean* or a stacked PR branch) are checked + # kilocode_change - run for every pull request, so Kilo v2 PRs (which target v2 or a stacked PR branch) are checked pull_request: workflow_dispatch: diff --git a/.github/workflows/kilo-v2.yml b/.github/workflows/kilo-v2.yml index cece71418fb7..317a181351c5 100644 --- a/.github/workflows/kilo-v2.yml +++ b/.github/workflows/kilo-v2.yml @@ -3,8 +3,7 @@ name: kilo-v2 on: push: branches: - - kilo-v2-clean - - kilo-v2-clean-* + - v2 # Every pull request, so stacked PRs that target another v2 PR branch also run. # This file exists only on the v2 line, so PRs into main do not run it. pull_request: From b84df54bc8783ec4b621e0a1e3b79f8fe281df4f Mon Sep 17 00:00:00 2001 From: Fabio Pliger Date: Tue, 13 Oct 2026 16:20:07 -0500 Subject: [PATCH 18/18] docs(migration): redact local paths and fix FIM gate wording --- .../marker-audit/kilo-override-marker-inventory.md | 2 +- migration-tracking/plans/kilo-opencode-v2-working-log.md | 2 +- migration-tracking/plans/migration-context.md | 2 +- .../baseline/cli-fixture-storage-incident.md | 2 +- .../technical-notes/baseline/cloud-agent-contracts.md | 4 ++-- .../baseline/extension-surface-inventory.md | 8 ++------ .../baseline/remote-share-next-contracts.md | 2 +- .../technical-notes/baseline/v1-fixture-generation.md | 4 ++-- 8 files changed, 11 insertions(+), 15 deletions(-) diff --git a/migration-tracking/marker-audit/kilo-override-marker-inventory.md b/migration-tracking/marker-audit/kilo-override-marker-inventory.md index 6bee828d7d5a..1295a7351a00 100644 --- a/migration-tracking/marker-audit/kilo-override-marker-inventory.md +++ b/migration-tracking/marker-audit/kilo-override-marker-inventory.md @@ -4,7 +4,7 @@ # Kilo override-marker inventory -Generated 2026-09-08 from the working tree at `/Users/johnnyamancio/Workspace/kilo_workspace/kilo-opencode-v2-baseline`. +Generated 2026-09-08 from the working tree at ``. ## Scope and method diff --git a/migration-tracking/plans/kilo-opencode-v2-working-log.md b/migration-tracking/plans/kilo-opencode-v2-working-log.md index b803fa5b1b98..7903d37110a4 100644 --- a/migration-tracking/plans/kilo-opencode-v2-working-log.md +++ b/migration-tracking/plans/kilo-opencode-v2-working-log.md @@ -723,7 +723,7 @@ all-wire credential regression. Opus follow-up review confirms the native delta closes the leak without a protocol/auth/reasoning regression. The stable snapshot passed the full CLI fresh-artifact wrapper: **443 pass / 0 fail, 2,741 assertions across 77 files** (359.21s, bundled Bun 1.4). Log: -`/Users/johnnyamancio/.bb/thread-storage/thr_qr9z7p3cqc/native-route-full-cli.log`. +a private thread-storage directory (not published). Post-snapshot cleanup shares the two native routes' parser/isolation helpers inside `packages/ai/src/kilocode/routed.ts`, preserves dialect option handling and explicit `extraBody`, and strengthens the real server-value sentinel. diff --git a/migration-tracking/plans/migration-context.md b/migration-tracking/plans/migration-context.md index 2b75a496816c..e0db9abcc2d0 100644 --- a/migration-tracking/plans/migration-context.md +++ b/migration-tracking/plans/migration-context.md @@ -37,7 +37,7 @@ Do not re-litigate these without new evidence. Proposed changes go to the user. 4. **Staleness gate.** For each feature, diff the v1 files it touches between the assessment snapshot and the v1 target. Empty delta: the donor is likely faithful. Non-empty: re-derive behavior from v1 and keep only the donor's v2 mapping. 5. **Staged cutover.** G1 CLI/TUI → G2 VS Code → G3 JetBrains, each with its own go/no-go. G2 and G3 are independent client-surface tracks that run in parallel with G1 once their prerequisites land. Until their gate, the IDEs stay on v1: VS Code bundles the CLI from the same `main` build (no version pin); JetBrains pins a CLI version. 6. **Storage.** A separate customer v2 data store. v1 files are never touched while any v1 surface ships. Import is opt-in and re-runnable. Path consolidation only after G3. -7. **Scope.** Post-cutover (non-gating): voice, image generation, team and incremental sharing, legacy TUI extras, and features behind experimental flags on v1 that are off by default, unless explicitly made a gate. Features named experimental but on by default on v1 (for example background subagents and the shared agent board) are in scope like any shipped feature. KiloClaw is not needed. Hard gates: FIM/next-edit (G2, G3). Cloud agents and Anaconda Desktop gate whichever surface they consume. +7. **Scope.** Post-cutover (non-gating): voice, image generation, team and incremental sharing, legacy TUI extras, and features behind experimental flags on v1 that are off by default, unless explicitly made a gate. Features named experimental but on by default on v1 (for example background subagents and the shared agent board) are in scope like any shipped feature. KiloClaw is not needed. Hard gates: FIM/next-edit for G2; whether it gates G3 is pending Q-G3-01. Cloud agents and Anaconda Desktop gate whichever surface they consume. 8. **Gates override P-labels** for surface readiness. P-labels order work globally; gates define what a surface needs to ship. 9. **Plan format.** One file with two parts: Part A high level (epics, gates, critical path, risks), Part B low level (per-epic units, dependencies, acceptance), plus appendices (facts, glossary, links). 10. **Readability.** Separate why, what, when and how-to-verify. Progressive disclosure. No sub-numbered steps. Issue IDs only in Part B and the register. One canonical place per fact. diff --git a/migration-tracking/technical-notes/baseline/cli-fixture-storage-incident.md b/migration-tracking/technical-notes/baseline/cli-fixture-storage-incident.md index eb6c0c597136..7be03b7d9c8e 100644 --- a/migration-tracking/technical-notes/baseline/cli-fixture-storage-incident.md +++ b/migration-tracking/technical-notes/baseline/cli-fixture-storage-incident.md @@ -24,7 +24,7 @@ After the user's explicit approval, a SQLite online backup was created in a mode-0700 private thread-storage directory, with the database file mode 0600. The backup passed `PRAGMA quick_check` and contained the expected 60 records. -Backup: `/Users/johnnyamancio/.bb/thread-storage/thr_qr9z7p3cqc/credential-recovery-20rbKX/before-cleanup.db`. +Backup: a private thread-storage directory (not published). It contains credentials: do not publish, attach, or commit it. One transaction deleted exactly 59 records, requiring integration `kilo`, key diff --git a/migration-tracking/technical-notes/baseline/cloud-agent-contracts.md b/migration-tracking/technical-notes/baseline/cloud-agent-contracts.md index 71318b3d2218..570f9657aa55 100644 --- a/migration-tracking/technical-notes/baseline/cloud-agent-contracts.md +++ b/migration-tracking/technical-notes/baseline/cloud-agent-contracts.md @@ -8,10 +8,10 @@ Discovery only: no source changes in any repository. | Item | Value | |---|---| | Date | 2026-10-07 (amended; first pass 2026-10-05) | -| Cloud repo | `Kilo-Org/cloud` at `3de933dd85070e9d15c41d8ff3af6c91ca856fd4` (read-only clone `/Users/fpliger/dev/kiloverse/cloud`) | +| Cloud repo | `Kilo-Org/cloud` at `3de933dd85070e9d15c41d8ff3af6c91ca856fd4` (read-only clone ``) | | This repo, `origin/main` (v1) | `62f674d4a91969ac077e4a7b6ceeb8a99de80e47` | | This repo, `kilo-v2` (v2) | `782263326df5be50081517ddedd937f1080a89cf` | -| Upstream opencode, `origin/v2` | `f20f5b68ee318721c453fb19d20e46d2b9225bc0` (clone `/Users/fpliger/dev/opencode`) | +| Upstream opencode, `origin/v2` | `f20f5b68ee318721c453fb19d20e46d2b9225bc0` (clone ``) | | #14425 inventory | `migration-tracking/technical-notes/baseline/cloud-agent-consumers.md` | #14425 assessed the cloud repo at the same SHA; its this-repo SHAs were older diff --git a/migration-tracking/technical-notes/baseline/extension-surface-inventory.md b/migration-tracking/technical-notes/baseline/extension-surface-inventory.md index 38bef397b5d9..672eb9603862 100644 --- a/migration-tracking/technical-notes/baseline/extension-surface-inventory.md +++ b/migration-tracking/technical-notes/baseline/extension-surface-inventory.md @@ -78,7 +78,7 @@ row; current coarse coverage is **24/43 (55.8%)**. ## Completed self-test observations (older runtime) -Framework: `/Users/johnnyamancio/.config/kilo/scripts/vscode-self-test/cli.mjs`; +Framework: ``; delegate `thr_7bdux6wma9`. VS Code `1.135.0` arm64. Existing extension artifact SHA-256 `713bac9cf684a46e9e0f143e9597dd3cf9f782245ce72df6e33506160faab7d2`; bundled CLI SHA-256 `5e8a5ac27339df6df19e156451e808baade23aebe2c9a2187a8619ab5ebab1fb`. @@ -104,11 +104,7 @@ All HOME/XDG/profile/workspace state was isolated. The delegate verified no Account Pooler access. Owned processes and disposable profile/workspace/socket artifacts were cleaned up; the existing user window/stores were untouched. -Sanitized evidence: [model groups](/Users/johnnyamancio/.bb/thread-storage/thr_7bdux6wma9/v1-selftest-model-picker.png), -[agent configuration](/Users/johnnyamancio/.bb/thread-storage/thr_7bdux6wma9/v1-selftest-agent-behaviour.png), -[runtime agent picker](/Users/johnnyamancio/.bb/thread-storage/thr_7bdux6wma9/v1-selftest-agent-picker.png), -[first-run sidebar](/Users/johnnyamancio/.bb/thread-storage/thr_7bdux6wma9/v1-selftest-sidebar-inner.png), -[Agent Manager](/Users/johnnyamancio/.bb/thread-storage/thr_7bdux6wma9/v1-selftest-agent-manager.png). +Sanitized evidence: screenshots not published. This was framework-driven navigation and observation, not model/tool workflow execution or live-contract verification. No completion credit is added. diff --git a/migration-tracking/technical-notes/baseline/remote-share-next-contracts.md b/migration-tracking/technical-notes/baseline/remote-share-next-contracts.md index 78352c2e97c4..ae80fd515060 100644 --- a/migration-tracking/technical-notes/baseline/remote-share-next-contracts.md +++ b/migration-tracking/technical-notes/baseline/remote-share-next-contracts.md @@ -5,7 +5,7 @@ accepted batch in this worktree is untouched; no task transitions, commits, installs, fetches, credentials, or live relay/share traffic. Sources, all read locally: this checkout (`kilo-v2` at `61a8707c03` plus the frozen dirty batch), Kilo v1 pinned `ecccd1f54b62f9bb16e53a2a58b32bb6d98d0fd7` (`git show`), and the -sibling cloud checkout `/Users/johnnyamancio/Workspace/kilo_workspace/cloud` +sibling cloud checkout `` (clean worktree at `c32be17d6`; its `origin/main` `08c4887fa68738f19089284101084f404eb6c9b8` read through local git objects only). Sharing and remote are kept distinct throughout. The accepted remote diff --git a/migration-tracking/technical-notes/baseline/v1-fixture-generation.md b/migration-tracking/technical-notes/baseline/v1-fixture-generation.md index ca4dbaed87d8..ec343c2a6840 100644 --- a/migration-tracking/technical-notes/baseline/v1-fixture-generation.md +++ b/migration-tracking/technical-notes/baseline/v1-fixture-generation.md @@ -8,7 +8,7 @@ and clean; that does not describe the cleanliness of the source checkout. ## Exact source and runtime -The source checkout is `/Users/johnnyamancio/Workspace/kilo_workspace/kilocode` +The source checkout is `` at `d99662338e3ddbd2613ab41fc0369837a6eb4be9` (merge pull request #13046, 2026-08-10). It was already dirty and was not changed by this work. Its porcelain status at audit time was: @@ -99,7 +99,7 @@ The live writer roundtrip is intentionally explicit because it loads the separate V1 dependency graph: ``` -KILOCODE_V1_CHECKOUT=/Users/johnnyamancio/Workspace/kilo_workspace/kilocode \ +KILOCODE_V1_CHECKOUT= \ dist/interactive/bun --no-env-file test --preload @opentui/solid/preload \ --timeout 180000 ./test/v1-writer-fixture-live.ts ```