.kilo/agent/upstream-merge.md
Resolve the manual part of an upstream merge.
The user will provide the upstream version (for example v1.1.50 or 1.1.50)
in their first message. If they don't, infer it from the current branch name,
from upstream-merge-report-<version>.md, or from the newest relevant report
file.
git status --shortgit diff --name-only --diff-filter=Uupstream-merge-report-<version>.md when present.worktrees/opencode-merge/auto-merge for the automated merge snapshot when presentUse script/upstream/find-conflict-markers.sh <file> to jump to each region,
then read enough surrounding lines to understand the code — not just the
conflict hunk. Specifically check:
kilocode_change marker in HEAD encode a bug fix, a feature, or a
defensive check?When HEAD includes a non-obvious Kilo-specific wrapper (e.g. a helper in
packages/opencode/src/kilocode/), find out why it exists before deciding to
keep or bypass it:
git log --all --oneline -S "<symbol>" -- packages/opencode/src/kilocode/
git log --all --oneline -- <kilo-file>
Look at the commit message and any PR reference. "We wrote our own because of PR #NNNN" is a real constraint; "we wrote our own because of a typo" is not.
When upstream narrows an externally-visible compatibility list (models, providers, routes, config keys, file formats), verify the intent from upstream PRs, issues, release notes, or current docs before dropping entries. Treat silent list shrinkage during a refactor as suspicious until proven intentional.
For every conflicted file (and any adjacent file the resolution forces you to touch — see §6) include:
hybrid, take-ours, take-theirs, regenerated,
removed, renamed, or otherlow, medium, or highGroup files by risk level. Ask the user which batch to start with. You can
resolve an entire low batch in one pass if the user approves the batch, but
resolve medium and high files one at a time.
Do not resolve a file until the user has approved that file's (or batch's) strategy.
The user needs to review intent, not just the raw change. For each file, in order:
kilocode_change comments when possible).Do not lead with the diff. A diff without reasoning forces the user to reverse-engineer the decision.
Reference worktrees when present:
.worktrees/opencode-merge/opencode — pristine upstream tree.worktrees/opencode-merge/kilo-main — Kilo base snapshot.worktrees/opencode-merge/auto-merge — automated merge snapshot (original
conflict reference)Apply in order:
kilocode_changekilocode_change markers around Kilo-specific code in shared opencode
fileskilocode_change, check whether the marker encodes a bug fix or a feature
delta. Bug fixes (missing await, defensive null-check, error capture)
usually need to be re-applied on top of the upstream refactor. Example from
v1.14.30: Workspace.isSyncing was missing an await — upstream's Effect
refactor reintroduced the same bug, so we had to port the fix into the new
Effect.gen block.take-theirs drops a line that was the target of a Kilo pre-filter,
the upstream line may be actively wrong for Kilo — e.g. an inner continue
filter whose condition collides with an outer filter Kilo added. Re-read the
surrounding 20 lines before committing to take-theirs.x-kilo-directory) by diffing against
pristine upstream.When removing code that existed in one side of a conflict, prefer
commenting it out with kilocode_change markers over deletion when the
surrounding structure (an if, a loop) still makes sense. That keeps the
intent visible to the next merger. Example:
} else if (input?.scope !== "project" && !Flag.KILO_EXPERIMENTAL_WORKSPACES) {
// kilocode_change start - directory filtering handled by KiloSession.filters above
// if (input?.directory) {
// conditions.push(eq(SessionTable.directory, input.directory))
// }
// kilocode_change end
}
Use TODO: not NOTE: for follow-ups. TODO is searchable and implies an
owner will act on it; NOTE reads as permanent commentary.
Upstream restructures sometimes split one file into several (e.g. permission.ts
→ groups/permission.ts + handlers/permission.ts). Only the renamed file
shows up in git diff --diff-filter=U; the new sibling may need a Kilo feature
ported in too. After resolving the flagged file, check:
../middleware/*, ./handlers/*)kilocode_change comments in the auto-merge snapshot that didn't end up in
the working tree because the hosting file was renamedAdd any such files to the plan as hybrid or take-ours with the same
approval flow.
Files not in --diff-filter=U merged without conflict markers but may still
be broken. Check every auto-merged file for:
script/upstream/find-conflict-markers.sh <file> prints nothinggrep the file and the
rest of the package before deleting — they may be called from non-conflicted
code elsewhere. Example: isTheme in theme.tsx looked unused at the
resolution site but was called twice further down.bun test file, or bun run typecheck in the touched package)medium / high; low batches can be staged together)git diff --name-only --diff-filter=U returns emptybun run typecheck from packages/opencode/ (targeted) and from repo root
(catches non-conflicted call-site breakage)bun run script/check-opencode-annotations.ts if packages/opencode/ shared
files changed. Note that this tool compares against the merge base via HEAD
and will be silent until the merge commit landskilo-vscode/,
check-kilocode-change, source-links, visual regression)Per script/upstream/README.md:
git commit -m "resolve merge conflicts"
The default git merge auto-message (Merge branch '…' into …) is also fine,
but resolve merge conflicts is the convention for these PRs.
Upstream often renames exported APIs. The rename itself auto-merges cleanly in
shared code, but the change cascades into Kilo-only files (kilocode tests,
kilo-specific source, plugins) that still reference the old symbol. Those
files don't appear in --diff-filter=U because their own content didn't
conflict.
Keep the behavioural merge commit focused on resolution decisions. Land the cascade in one or more follow-up commits:
Reviewers can then skim the behavioural commit without untangling mechanical rename noise from merge decisions.
Upstream sometimes adds tests that encode design contracts Kilo intentionally breaks. These auto-merge cleanly and then fail. Three resolution patterns:
kilocode_change marker explaining the divergence.kilocode_change and a rationale
explaining what would need to change for the test to run.Never silently delete; always leave a breadcrumb. A future reviewer should be able to understand why this one upstream test is treated differently.
Upstream stamps its own version into shared files — notably
packages/extensions/zed/extension.toml (version field + 5 Kilo-Org download
URLs), and any package.json that upstream bumped in the same release window.
After the merge this leaves parts of the tree pointing at upstream's version
(e.g. 1.14.30), whose release tag does not exist on Kilo's pipeline, so the
Zed download URLs silently 404.
Fix this in a dedicated commit after resolve merge conflicts:
bun run script/sync-versions.ts # uses root package.json version
# or, to target an explicit version:
bun run script/sync-versions.ts 7.2.41
git add -A
git commit -m "chore: resync versions after upstream merge"
The script rewrites every top-level "version" in package.json files
(excluding node_modules, hidden dirs, and packages/kilo-jetbrains/ which
tracks its own cadence), plus the Zed extension toml. It is idempotent — rerun
it any time to rebase the version back onto Kilo main (useful during
long-running upstream merges where main releases in the meantime).
Keeping this in its own commit makes reviewers' job easier: the merge commit only contains behavioural resolutions, and the version resync is a trivial diff they can skim in one glance.
Structure the description so reviewers can skim:
take-ours/take-theirs.
Focus on what Kilo behaviour survived and what upstream features were
adopted. Link to Kilo PRs when a kilocode_change encodes a specific fix.TODO: you left in code, as a bullet list with links.Every manual merge decision requires explicit user approval before applying and again after verification. Be especially cautious when a decision is destructive, changes auth, billing, data deletion, public API compatibility, config schema behaviour, migrations, provider routing, or security posture.
renamed should be used only when behaviour moves to a different file.kilocode_change had already fixed —
during big refactors check every kilocode_change the refactor touched.WebSocket.send, Headers, etc. — prefer narrowing the Kilo
type over adding any casts.kilocode_change
marker explaining the divergence.