Skip to content

Commit 86cc69b

Browse files
committed
Merge upstream DeusData/codebase-memory-mcp through ce0b4b2 (step 2 of upstream sync)
Brings upstream history from 13b7d06 (2026-09-04) through ce0b4b2 (2026-09-16) into the CLI fork. 52 files conflicted. Seven files the fork deleted were modified upstream: src/mcp/{mcp.c,mcp.h,mcp_internal.h}, tests/test_mcp.c, src/cli/client_adapter.c, tests/test_mcpb_bundle_contract.sh and tests/test_parent_watchdog.sh. They stay deleted, and the upstream behaviour in them is moved to the CLI's operation layer. Moved from upstream MCP handlers into src/operations (CLI commands): - index_status --diagnostics none|summary|full: coverage samples, indexed_at, omitted counts. - get_graph_schema: counts by default, with limit/offset paging and properties under diagnostics=full. - list_projects: limit up to 500, next_offset, detail=stats, branch shown. - index: named failure causes (over_memory_budget with a suggested budget, aborted_previous_preserved, persist_failed). - coverage: parse_unusable files, truncated range markers, path paging. - search_graph semantic hardening: fails closed on a bad vector scan, semantic paging, deterministic BM25 window ordering. - Non-UTF-8 identities encoded losslessly as @bytes:<hex> in every operation payload (cbm_operation_json_write). - search_code: * BusyBox-compatible grep and streaming reads with exact totals. * Fails closed on an incomplete scan. * Result and raw paging with byte-exact previews. - detect_changes: OID/merge-base resolution, -z git records, snapshot cursors for the changed, impact and module tables. - trace: test rows filtered before windowing, deterministic edge evidence, bounded BFS with saturation flags, strict cursors. - query_graph: snapshot cursors and offset paging (200 rows shown per page by default). - get_code_snippet: outline mode for large containers, start_line and max_lines. - manage_adr: outline by default, content required for update and store. - get_architecture: heap-built lists, so long identifiers are not cut. - Daemon: autoindex.skipped logs the normalized root (f6a2927). Not moved yet: the max_output_tokens output budget and compact re-encodings. They reduce output size and do not change correctness. Dropped as MCP-protocol-only: - JSON-RPC /rpc in the UI server. - The stdio parent watchdog. - MCP registration for OpenHands and the other installers. - The opencode/pi plugin adapters. - .mcpb bundle packaging. - MCP smoke-script sections. Other resolution notes: - main.c: kept the fork's CLI dispatcher. Ported --quiet/--verbose and the index-worker workspace scope fix (DeusData#2076), so a worker no longer inherits the daemon's ambient CBM_ALLOWED_ROOT. - application.c: worker memory slice is now budget / active jobs (upstream DeusData#1997). - cli.c: kept the fork's Codex instruction block and CLI naming. Ported the Antigravity legacy SessionStart cleanup and upstream's tool-help schema printing. Codex uninstall now also removes the block when hook preflight fails. - pipeline.c, pass_parallel.c, cbm.c: adopted upstream's spill/admission control and parse budget. Kept the fork's stage-lock code and the macro-line scan instrumentation. - extract_imports.c and test_extraction.c: upstream's generic embedded walker (Svelte, HTML, Astro), replacing the fork's Vue-only version. - graph-ui lock regenerated. The fork's Dependabot bumps are kept, and npm audit reports 0 vulnerabilities. - Hand-ported code that git kept twice was deduplicated: * pass_complexity.c comparators * compat_fs.h mkdir_p_ex declaration * main.c apply_ui_config * application_internal.h declaration * daemon and edge tests - Vendored swift/scanner.c is byte-identical to upstream, and all vendored checksums verify. Known gaps: - The Windows test that `daemon stop` refuses while a client is attached is gone. It needed an MCP stdio session. - The graph UI's /rpc calls (project list, schema, snippet) had no server route on the fork before this merge. That needs a native /api endpoint as a follow-up. The production build and the test-runner compile are clean with -Werror.
2 parents f4f9870 + ce0b4b2 commit 86cc69b

177 files changed

Lines changed: 18888 additions & 3139 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/_lint.yml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ jobs:
7171
- name: Install LLVM 21 (pinned analyzer toolchain)
7272
run: |
7373
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
74-
echo "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-22 main" | sudo tee /etc/apt/sources.list.d/llvm-22.list
74+
echo "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-21 main" | sudo tee /etc/apt/sources.list.d/llvm-21.list
7575
sudo apt-get update
76-
sudo apt-get install -y clang-tidy-22
76+
sudo apt-get install -y clang-tidy-21
7777
7878
- name: Memory-analyzer gate
79-
run: scripts/ci/lint-mem.sh clang-tidy-22
79+
run: scripts/ci/lint-mem.sh clang-tidy-21

‎.github/workflows/_test.yml‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,32 @@ jobs:
119119
if: startsWith(matrix.os, 'ubuntu')
120120
run: sudo apt-get update && sudo apt-get install -y zlib1g-dev ccache
121121

122+
# Ubuntu 23.10+ ships kernel.apparmor_restrict_unprivileged_userns=1, which
123+
# blocks unshare(CLONE_NEWUSER) for unprivileged users. Without this, the
124+
# #1830 single-uid-userns smoke test SKIPS on every 24.04 leg, and its only
125+
# real-namespace coverage lived on the two ubuntu-22.04 legs -- which are
126+
# broad-matrix only (never PR CI) and which GitHub retires on 2027-04-17.
127+
# A deliberate relaxation of a security check was therefore guarded by a
128+
# test that ran in no PR and was on a clock. Hosted runners have
129+
# passwordless sudo, so one sysctl puts it on the CORE matrix instead.
130+
# Best-effort: a runner that refuses simply returns to skipping.
131+
- name: Permit unprivileged user namespaces (Ubuntu)
132+
if: startsWith(matrix.os, 'ubuntu')
133+
run: |
134+
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || \
135+
echo "could not lift the userns restriction; namespace tests will skip"
136+
# Then SAY whether it worked. The runner prints per-shard aggregates
137+
# ("N passed, M skipped") and never individual test names, so a
138+
# namespace test that silently returns to skipping is invisible in a
139+
# green log -- the same false assurance that let #1830 ship with a
140+
# test that never executed. This line is the evidence. Probed exactly
141+
# as the test does it: unprivileged, no sudo.
142+
if unshare -U -r true 2>/dev/null; then
143+
echo "CBM_USERNS_AVAILABLE=yes"
144+
else
145+
echo "CBM_USERNS_AVAILABLE=no (namespace-dependent tests will skip)"
146+
fi
147+
122148
- name: Install ccache (macOS)
123149
if: startsWith(matrix.os, 'macos')
124150
run: command -v ccache >/dev/null 2>&1 || brew install ccache

‎.github/workflows/pages.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,4 @@ jobs:
4141
with:
4242
path: ./docs
4343
- id: deployment
44-
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
44+
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1

‎.github/workflows/release.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ jobs:
288288
git tag "$VERSION" "$GITHUB_SHA"
289289
git push origin "refs/tags/$VERSION"
290290
291-
- uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v2
291+
- uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
292292
with:
293293
tag_name: ${{ inputs.version }}
294294
draft: true

‎Makefile.cbm‎

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,11 @@ KOTLIN_DEDUP_TEST_DEFINE = -DCBM_KOTLIN_DEDUP_TEST_API=1
100100
CALL_REFERENCE_LOOKUP_TEST_DEFINE = -DCBM_CALL_REFERENCE_LOOKUP_TEST_API=1
101101
INCREMENTAL_TEST_DEFINE = -DCBM_INCREMENTAL_TEST_API=1
102102
CFLAGS_TEST_EXTRA ?=
103+
COVERAGE_MARKER_TEST_DEFINE = -DCBM_COVERAGE_MARKER_TEST_API=1
103104
CFLAGS_TEST = $(CFLAGS_COMMON) $(EDITOR_TEST_DEFINES) $(SANITIZED_DEFINE) \
104105
$(KOTLIN_DEDUP_TEST_DEFINE) $(CALL_REFERENCE_LOOKUP_TEST_DEFINE) \
105-
$(INCREMENTAL_TEST_DEFINE) -g -O1 $(SANITIZE) $(CFLAGS_TEST_EXTRA)
106+
$(INCREMENTAL_TEST_DEFINE) $(COVERAGE_MARKER_TEST_DEFINE) -g -O1 $(SANITIZE) \
107+
$(CFLAGS_TEST_EXTRA)
106108
CXXFLAGS_TEST = $(CXXFLAGS_COMMON) $(SANITIZED_DEFINE) -g -O1 $(SANITIZE) $(CXX_STDLIB_FLAGS)
107109

108110
# TSan (can't combine with ASan)
@@ -120,6 +122,7 @@ TSAN_SANITIZE = -fsanitize=thread -fno-omit-frame-pointer
120122
# macro of ours.
121123
CFLAGS_TSAN = $(CFLAGS_COMMON) $(EDITOR_TEST_DEFINES) $(KOTLIN_DEDUP_TEST_DEFINE) \
122124
$(CALL_REFERENCE_LOOKUP_TEST_DEFINE) $(INCREMENTAL_TEST_DEFINE) \
125+
$(COVERAGE_MARKER_TEST_DEFINE) \
123126
-DCBM_SANITIZED_BUILD=1 -g -O1 $(TSAN_SANITIZE)
124127
CXXFLAGS_TSAN = $(CXXFLAGS_COMMON) -DCBM_SANITIZED_BUILD=1 -g -O1 \
125128
$(TSAN_SANITIZE)
@@ -183,10 +186,35 @@ endif
183186
# ~259 MB of tree-sitter parse tables was mapped executable at runtime while the
184187
# amd64 build of the same source mapped them R only. That is a large ROP gadget
185188
# surface for data that is never executed, and it is invisible to section flags.
189+
#
190+
# -z relro + -z now finish the same job on the OTHER writable-but-shouldn't-be
191+
# region: the GOT. Both are free at runtime here (a static binary resolves
192+
# everything at link time, so there is no lazy binding left to pay for), and
193+
# they are added because the SHIPPED artifact measurably lacked the property --
194+
# not because the linker accepts the flags. Measured on this binary, built the
195+
# way .github/workflows/_build.yml builds the Linux release (STATIC=1, gcc 13.3
196+
# / GNU ld 2.42, Ubuntu 24.04 aarch64); PT_GNU_RELRO ends at 0x11DA0000 in both:
197+
#
198+
# without -z now: .got 0x11d9ec20+0x13c8, .got.plt 0x11d9ffe8+0x40
199+
# -> .got.plt ends at 0x11DA0028, i.e. 40 bytes PAST the
200+
# window: those GOT slots stayed writable for the whole
201+
# process lifetime, in every Linux binary we have shipped
202+
# with -z now: .got.plt folded into .got (0x11d9ec20+0x13c8, ends
203+
# 0x11D9FFE8) -> the whole GOT is inside the window and is
204+
# re-mapped read-only after startup
205+
#
206+
# -z relro is already this toolchain's default and changes nothing here; it is
207+
# named anyway so the property stops depending on one distro's spec file (the
208+
# musl/portable and glibc-floor images are different toolchains, and a default
209+
# is not a guarantee). A1c/A1d in scripts/ci/check-binary-composition.sh assert
210+
# the OUTCOME on the produced binary, because a flag the compiler accepts is
211+
# not evidence that the artifact gained anything -- the same reason A1 exists
212+
# next to the .note.GNU-stack annotation.
186213
ELF_HARDENING_FLAGS :=
187214
ifeq ($(IS_LINUX),yes)
188215
ifneq ($(IS_MINGW),yes)
189-
ELF_HARDENING_FLAGS := -Wl,-z,noexecstack -Wl,-z,separate-code
216+
ELF_HARDENING_FLAGS := -Wl,-z,noexecstack -Wl,-z,separate-code \
217+
-Wl,-z,relro -Wl,-z,now
190218
endif
191219
endif
192220

@@ -225,6 +253,7 @@ FOUNDATION_SRCS = \
225253
src/foundation/compat_fs.c \
226254
src/foundation/compat_regex.c \
227255
src/foundation/mem.c \
256+
src/foundation/mem_core.c \
228257
src/foundation/diagnostics.c \
229258
src/foundation/profile.c \
230259
src/foundation/dump_verify.c \
@@ -253,6 +282,8 @@ EXTRACTION_SRCS = \
253282
$(CBM_DIR)/extract_k8s.c \
254283
$(CBM_DIR)/extract_dbt.c \
255284
$(CBM_DIR)/helpers.c \
285+
$(CBM_DIR)/result_compact.c \
286+
$(CBM_DIR)/result_spill.c \
256287
$(CBM_DIR)/lang_specs.c \
257288
$(CBM_DIR)/macro_table.c \
258289
$(CBM_DIR)/iris_export_xml.c \
@@ -684,6 +715,7 @@ TEST_REPRO_SRCS = \
684715
tests/repro/repro_issue787.c \
685716
tests/repro/repro_issue842.c \
686717
tests/repro/repro_issue964.c \
718+
tests/repro/repro_issue1692.c \
687719
tests/repro/repro_invariant_calls.c \
688720
tests/repro/repro_invariant_graph.c \
689721
tests/repro/repro_invariant_breadth.c \
@@ -1182,8 +1214,17 @@ lint-no-suppress:
11821214
lint: lint-tidy lint-cppcheck lint-format lint-no-suppress
11831215
@echo "=== All linters passed ==="
11841216

1217+
# Memory-core linter: memory is allocated through src/foundation/mem_core.h,
1218+
# not through raw malloc/calloc/realloc/free/strdup. A checked-in baseline
1219+
# (scripts/memory-core-baseline.txt) records each file's raw-site count and the
1220+
# gate goes red the moment any file grows. Files only ever go down; lower the
1221+
# baseline line in the same change that migrates the file.
1222+
lint-memory-core:
1223+
@echo "=== memory-core linter ==="
1224+
@python3 scripts/lint-memory-core.py
1225+
11851226
# CI linters (no clang-tidy — platform-dependent, enforced locally via pre-commit)
1186-
lint-ci: lint-cppcheck lint-format lint-no-suppress
1227+
lint-ci: lint-cppcheck lint-format lint-no-suppress lint-memory-core
11871228
@echo "=== CI linters passed ==="
11881229

11891230
# ── Local memory-diagnostic lanes (not PR-CI gates by decision: the diag

‎README.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ Design rules:
221221

222222
- stdout is reserved for command results;
223223
- progress and diagnostics belong on stderr;
224+
- `cli --progress` forces lifecycle progress when stderr is redirected, `cli --quiet` disables automatic terminal progress and ordinary diagnostics while keeping errors (it cannot be combined with `--progress` or `cli --verbose`), and `cli --verbose` includes routine informational logs;
224225
- errors produce non-zero exit codes;
225226
- canonical JSON hides the historical transport envelope;
226227
- result ordering/pagination remain explicit where the underlying operation supports them.
@@ -279,7 +280,7 @@ Important environment variables include:
279280
| `CBM_ALLOWED_ROOT` | Constrain permissible indexing roots. |
280281
| `CBM_RUNTIME_DIR` | Override the secure local coordination rendezvous parent. |
281282
| `CBM_WORKERS` | Override indexing worker count. |
282-
| `CBM_LOG_LEVEL` | Control runtime logging. |
283+
| `CBM_LOG_LEVEL` | Control runtime logging. One-shot CLI and hook commands default to `warn`; the detached daemon and supervised index workers default to `info`. |
283284

284285
See [`docs/CONFIGURATION.md`](docs/CONFIGURATION.md) for the full reference.
285286

‎docs/CONFIGURATION.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ These environment variables affect runtime behavior:
133133
| `CBM_CACHE_DIR` | `~/.cache/codebase-memory-cli` | Override the cache directory used for indexes, `_config.db`, and UI `config.json`. |
134134
| `CBM_DIAGNOSTICS` | `false` | Enable periodic `snapshot.json` and retained `trajectory.ndjson` below a fresh owner-private directory in the system temp directory. The daemon records the randomized paths in the `diagnostics.start` discovery record (a single JSON line) in `${CBM_CACHE_DIR}/logs/cbm-daemon.log`; that one record is emitted even when `CBM_LOG_LEVEL` suppresses ordinary logging, so the paths always remain discoverable. |
135135
| `CBM_DOWNLOAD_URL` | GitHub releases | Override the update download URL. |
136-
| `CBM_LOG_LEVEL` | `info` | Set the log level to `debug`, `info`, `warn`, `error`, or `none` (or `0`-`4`). One-shot CLI messages use that command's stderr; retained detached-runtime events use `${CBM_CACHE_DIR}/logs/cbm-daemon.log`. |
136+
| `CBM_LOG_LEVEL` | role-aware | Set the log level to `debug`, `info`, `warn`, `error`, or `none` (or `0`-`4`). Thin CLI/hook frontends default to `warn`; the detached daemon and supervised index workers default to `info`. Physical workers retain INFO liveness records because their private logs drive the supervisor's no-progress timeout. Frontend messages use that command's stderr; detached daemon events use `${CBM_CACHE_DIR}/logs/cbm-daemon.log`. |
137137
| `CBM_RUNTIME_DIR` | `%LOCALAPPDATA%` (Windows), `/private/tmp` (macOS), `/tmp` (other) | Parent directory for the daemon/CLI rendezvous directory, which CBM creates inside it as `cbm-daemon-<uid>` (`cbm-daemon-<key>` on Windows). When an explicitly configured parent is missing, CBM creates it owner-private before use; existing parents are never modified and must pass the private-directory check. Set it when the default ancestry cannot pass that check — see below. `CBM_CACHE_DIR` does **not** move the rendezvous. |
138138
| `CBM_WORKERS` | auto-detected | Override the indexing worker count. |
139139

‎graph-ui/src/components/NodeDetailPanel.test.tsx‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ describe("NodeDetailPanel code preview + deep-link", () => {
6868
/* …but was NOT parsed into a real <script> element, and did not execute. */
6969
expect(container.querySelector("script")).toBeNull();
7070
expect((window as unknown as { __pwned?: boolean }).__pwned).toBeUndefined();
71+
expect(callToolMock).toHaveBeenCalledWith("get_code_snippet", {
72+
qualified_name: "app::render",
73+
project: "demo",
74+
format: "json",
75+
source_mode: "full",
76+
});
7177
});
7278

7379
it("builds an https GitHub deep-link with URL-encoded path segments", () => {

‎graph-ui/src/components/NodeDetailPanel.tsx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ export function NodeDetailPanel({
7676
const res = await callTool<SnippetResult>("get_code_snippet", {
7777
qualified_name: node.qualified_name,
7878
project,
79+
format: "json",
80+
source_mode: "full",
7981
});
8082
setCode(res.source ?? "(source not available)");
8183
} catch (e) {

‎graph-ui/src/components/StatsTab.test.tsx‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ function mockProjectsFetch(extra?: (url: string, init?: RequestInit) => Response
1212
if (overridden) return overridden;
1313
if (url === "/rpc") {
1414
return new Response(JSON.stringify({
15-
result: { content: [{ text: JSON.stringify({ projects: [] }) }] },
15+
result: { content: [{ text: JSON.stringify({ projects: [], has_more: false }) }] },
1616
}), { status: 200, headers: { "Content-Type": "application/json" } });
1717
}
1818
if (url.startsWith("/api/ui-config")) {
@@ -221,8 +221,11 @@ describe("StatsTab index modal", () => {
221221
root_path: "/repo",
222222
indexed_at: "2026-01-01T00:00:00Z",
223223
}],
224+
has_more: false,
224225
}
225-
: { node_labels: [], edge_types: [], total_nodes: 0, total_edges: 0 };
226+
: {
227+
node_labels: [], edge_types: [], total_nodes: 0, total_edges: 0, has_more: false,
228+
};
226229
return new Response(JSON.stringify({
227230
result: { content: [{ text: JSON.stringify(result) }] },
228231
}), { status: 200, headers: { "Content-Type": "application/json" } });

0 commit comments

Comments
 (0)