Imported from tigercosmos/code-cortex-mcp (
.claude/skills/sync-upstream/SKILL.md). Install upstream withnpx skills add tigercosmos/code-cortex-mcp --skill sync-upstream. Copyright stays with the author.
Upstream sync (curated)
Port genuinely-new upstream work into this fork selectively. The upstream range between syncs is typically 100–400 commits; only a small fraction is worth porting. This skill is triage-first and gated on user approval — never start editing source before the user has confirmed the shortlist.
Background on the fork's structural divergences lives in UPSTREAM_SYNC.md
(read it — it is the source of truth for the last-synced marker and the sync
log). This skill is the procedure; that file is the state.
Scope filter
Port (in scope):
| Theme | Examples |
|---|---|
| C++ / Python / Go indexing correctness | LSP resolvers, extraction, FQN/module naming, import-alias resolution, header/source node identity, preprocessor handling |
| Bug fixes | crashes, hangs, SIGABRT, UAF/OOB, data loss, dropped edges, wrong query results |
| Performance / optimization | allocator, interning, parallel passes, streaming writers, caps and bounds, quantization, memoization |
| Security hardening | injection/validation, path escapes, overflow guards |
Skip (out of scope) — do not port, just list as skipped:
- UI (
src/ui,graph-ui), CI (.github/workflows), release/version bumps,pkg/*,server.json,Makefile.cbm, dependabot, docs/rebrand churn. - New languages the fork does not carry (cfml, cfscript, qml, ObjectScript, Perl, Mojo grammar) — see "Permanent fork deferrals" below.
- Large unrelated features: the shared-coordination daemon and its Windows/DACL /IPC follow-ups, Windows launcher/install-transaction work, MCP output-format migrations, coverage-signal/agent-integration features, test-infra/VM/sharding.
- Vendored-library churn unless it fixes something we actually hit.
When a commit is genuinely borderline, put it in the shortlist marked
? and let the user decide — do not silently drop it.
Procedure
1. Enumerate what's new
git fetch upstream main
.claude/skills/sync-upstream/list-new.sh # marker auto-read from UPSTREAM_SYNC.md
.claude/skills/sync-upstream/list-new.sh <marker> # or pass one explicitly
The helper dedups by commit subject, not SHA — SHAs diverge across the fork,
so a commit we already ported shows up in MARKER..upstream/main anyway.
Note the marker may be flagged partial in UPSTREAM_SYNC.md (it is as of
97ce23f). If so, also re-triage the areas the previous pass explicitly skipped
— read the "Skipped" cell of the most recent sync-log row and fold anything now
in scope back into this pass.
2. Triage — commit message first
Read subjects and bodies, not diffs, for the first pass:
git log --format='%h %s%n%b%n---' <marker>..upstream/main -- <path>
git show --stat <sha> # size/shape only, at this stage
Group by theme (crash/hang safety → correctness → perf → security). Use
--stat to see whether a commit touches first-party source at all; anything
confined to src/ui, graph-ui, .github, pkg, docs is out by definition.
Fan out to subagents for reading if the range is large, but keep the decision yourself — the point of this skill is curation, not bulk translation.
3. Check with the user — REQUIRED GATE
Present a compact table: theme, upstream SHA, one-line intent, why it matters here, rough size (files/LOC). Then a short "skipped" summary grouped by reason. Ask the user to confirm or amend the shortlist (AskUserQuestion or a plain question, whichever fits) and wait. Do not begin porting until they answer.
4. Port
For each approved commit:
- Read the commit message first, and the linked PR/issue when the message
is thin:
gh pr view <n> --repo DeusData/codebase-memory-mcp --json title,body(alsogh issue view). Upstream issue numbers appear as#NNNin subjects. - Small, mechanical diff → apply it:
git diff <sha>^ <sha> -- <path>and port hunk-by-hunk onto our.cpp. - Large or heavily-refactored diff → do NOT line-translate. Understand the intent from the message/PR body and implement it directly in this fork's own C++23 idioms. A faithful re-implementation beats a mechanical port that fights context drift.
- Map paths: upstream
foo/bar.c→ ourfoo/bar.cpp; headers stay.h. - Translate product-name strings. The fork renamed everything user-facing
to
code-cortex-mcp(v0.15.0, no backward compat): binary/asset names,~/.cache/code-cortex-mcp/, agent-config server key, skill names (code-cortex*),[code-cortex]hook prefix,.code-cortex/artifact dir,.code-cortex.json. Upstream diffs containingcodebase-memory/codebase-memory-mcpstrings must be mapped to those names (internalcbm_/CBM_*identifiers and.cbmignoreare unchanged). Full mapping inUPSTREAM_SYNC.md. - Keep commits themed — one fork commit per coherent group, crediting the
upstream SHA(s):
merge(upstream): <what> from DeusData/codebase-memory-mcp@<sha>
C11 → C++23 idiom rules
- Compound literals
(const CBMType*[]){a,b,NULL}are invalid C++ → usecbm_type_args(arena, a, b, NULL). Study an already-ported resolver (internal/cbm/lsp/cs_lsp.cpp,ts_lsp.cpp) and mirror it exactly. - Arena OOM must stay graceful (return NULL / degrade). Never introduce exceptions or let allocation terminate.
_Alignof→alignof,_Static_assert→static_assert,_Thread_local→thread_local, droprestrict, VLAs → fixed/arena buffers, implicitvoid*casts → explicit casts,_Generic→ drop/rewrite.- Headers consumed by the C-compiled test TUs need
extern "C"guards — copy the guard pattern from sibling headers. - Vendored code (sqlite3, mongoose, yyjson, lz4, zstd, tre, ts_runtime,
grammar_*.c) stays C. Don't C++-ify it. - Tests under
tests/compile as C11. Keep them C.
Fork-side pitfalls (all have bitten a previous sync)
- Parallel-path twins. This fork has fork-only twins of sequential pass
logic (e.g.
create_env_configuresinpass_parallel.cppvscreate_env_configures_for_file). Parallel is the default above 50 files, so an upstream fix applied only to the sequential path is inert in practice. After porting anysrc/pipeline/pass_*.cppchange, greppass_parallel.cppfor the twin and apply it there too. - Divergent internals can make an upstream fix a no-op. The fork's
authoritative per-file LSP index (
3eed547) silently swallowed several upstream strategies. Before declaring a ported fix "done", prove the new code path actually executes here — build the binary and index a fixture. - Deliberate absences are not omissions. If an upstream hunk's context contains cfml/cfscript/qml entries missing from our file, preserve the absence.
- A new
#includecan break Windows even when the code is fine. Upstream's portability macros (CBM_TLS, …) live in headers that pull<windows.h>on MinGW, which#definesfar,near,min,max,small,IN,OUT,DELETE,interface. Adding such an include to a TU that has a local namedfarbreaks a file you never edited — and macOS/Linux CI stays green, so it only surfaces in the Windows release job. Prefer the file's existing idiom over upstream's macro: this tree is C++23, so writethread_localdirectly rather than importingCBM_TLS. Before adding any foundation header to a TU, diff its#includeblock against the last released commit — if the set is unchanged, the exposure is unchanged. propertiesJSON is load-bearing, not decoration. Theedgestable declares generated columns overjson_extract(properties, …)(url_path_gen,local_name_gen), so ANY malformed blob from ANY producer makesjson_extractraise, which makesPRAGMA quick_checkerror out, which — sincea39fddf9put quick_check on the quarantine path — deletes the user's whole project database. Nothing downstream will catch it for you: SQLite itself REFUSES a write that breaks a generated column ("malformed JSON"), butinternal/cbm/sqlite_writer.cppis a hand-rolled writer that builds raw B-tree records and never evaluates the expression — so the dump path stores blobs the engine would have rejected, and the defect only surfaces later, on read. Treat every producer as a correctness surface: check both the sequential and parallel twins, check every conditional branch of a format string closes its quotes, and checkcbm_json_escapebuffers cannot truncate mid-escape. This is how the 2026-08-15 pass found51921d90.- Don't hand-edit
lsp_all.cpp/CMakeLists.txtfrom a fan-out — central build wiring is a single deliberate step. Same forscripts/test.sh: theCBM_TEST_SEAMSoption it passes is what compiles in fault-injection seams, so a guard that needs one is wired centrally, not by the agent that wrote it.
Permanent fork deferrals
No CBM_LANG_CFML / CBM_LANG_CFSCRIPT / CBM_LANG_QML; ObjectScript, Perl,
and the Mojo grammar are likewise not carried. Never re-introduce them, and strip
their rows/tests from anything ported.
5. Verify
scripts/build.sh # or a non-sanitizer CMake build
scripts/test.sh
- Measure the baseline FIRST, in a separate worktree
(
git worktree add <scratch> HEAD --detach), before you edit anything — especially when subagents are editing the tree in parallel. A baseline taken in the working tree is worthless the moment the first port lands, and you need it to be trustworthy: the 2026-08-15 pass had 6 failures that a subagent reported as "pre-existing" and the isolated baseline proved were a real fault the port had surfaced.git worktree remove --forcewhen done. scripts/test.shhardcodes-DCBM_SANITIZE=ON. ACBM_SANITIZE=OFFenvironment variable does NOT reach CMake through it (onlybuild.shsets OFF). ASan+UBSan runs fine on this Mac as of 2026-08-15 — the old "ASan hangs at init" note no longer holds. For a non-sanitizer tree, configure explicitly:cmake -S . -B build/nosan -DCBM_SANITIZE=OFF -DCBM_TEST_SEAMS=ON.- Run
test-runnerfrom the repo root — fixtures are cwd-relative, and running it out of the build dir produces ~6 spurious failures that look like real regressions. scripts/lint.shneeds bash 4+ (mapfile; macOS ships 3.2). Reproduce its three gates by hand:clang-format --dry-run --Werror(the binary is plainclang-format, version 20.1.8; there is noclang-format-20.1.8),cppcheckin C++ mode, andscripts/check-nolint-whitelist.sh. Copy cppcheck's flags verbatim fromrun_cppcheckand diff its output against HEAD rather than reading it — the only way to tell a finding you introduced from one that was already there. Note the lint set issrc/+internal/cbmonly;tests/is not covered by any gate. In zsh, an unquoted$FILESvariable does NOT word-split — pass globs directly or use${=FILES}.- Record the pass/fail counts and compare against the pre-sync baseline. Port the upstream regression tests for the fixes you took — a fix without its guard is half-ported.
- A green suite is not a green sync, and a red one is not automatically
someone else's fault. Chase every failure to a root cause before accepting
it; the 2026-08-15 pass found an unported upstream fix (
51921d90) that way. - For anything claiming a behavior change, functionally verify against the built binary on a purpose-built fixture (index it, query the graph, show the edge/node counts before and after).
6. Record
Update UPSTREAM_SYNC.md: bump the "Last synced" SHA + date, and add a sync-log
row with (a) what was ported and why, (b) fork-side adaptations and deviations,
(c) what was skipped and on what grounds — the skipped list is what makes
the next sync tractable. If the pass was curated (it will be), mark the marker
partial so the skipped areas get re-triaged rather than assumed absorbed.
Anti-patterns
- Starting to edit code before the user approves the shortlist.
git merge upstream/mainor blindgit cherry-pick— the merge base is ancient (a0e809d); a raw merge replays hundreds of already-ported commits.- Mechanically line-translating a large diff instead of implementing the intent.
- A giant undifferentiated agent fan-out over every upstream commit. Curate first; delegate only well-scoped chunks; hand-implement the big ones.
- Declaring a port done on "it compiles" — verify the path executes.