.kilo/command/upstream-manual-merge.md
Resolve the manual part of an upstream merge.
Arguments: $ARGUMENTS
Use the first argument as the upstream version, for example v1.1.50 or
1.1.50. If no argument is provided, infer the version from the current branch
name, upstream-merge-report-<version>.md, or 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.
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.
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 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.