Skip to content

Upstream sync 13M-1: merge DeusData 5689ea54c with CLI ports of #2198, #1561, #1430 - #33

Merged
ngallodev merged 108 commits into
mainfrom
sync/upstream-13m1-20261108
Nov 9, 2026
Merged

ngallodev merged 108 commits into
mainfrom
sync/upstream-13m1-20261108

Conversation

@ngallodev

Copy link
Copy Markdown
Owner

First of three stacked upstream-sync PRs (13M-1 → 13M-2 → 13N) that bring the fork up to DeusData/codebase-memory-mcp 612eee251. This one merges the first upstream checkpoint with real ancestry and ports the shared features that apply to the CLI.

The plan labels batches by upstream range: 13M-1 (this PR) and 13M-2 cover upstream up to e0c7a1da6, and 13N covers the rest up to 612eee251.

Ancestry

SHA
Fork base (main) cf63fe667579482c7e3b73238b73d4ad2d01db97
Upstream checkpoint 5689ea54ceac23d7e5bf59d54b9c718b59ae9ee0 (first-parent ancestor of upstream main; 104 commits, 11 first-parent PR merges)
Merge commit 3a4fdb9b975106489b764556823d9528681301c2 (--no-ff, parents cf63fe667 + 5689ea54c)
PR head c3c5271ab8f454287452f0df5eca9e38ffd250cf

git merge-base --is-ancestor 5689ea54c c3c5271ab holds. Relative to main, this branch is 108 ahead and 0 behind.

Commits on top of the merge

Excluded (MCP-only)

Changes to src/mcp/*, tests/test_mcp.c, the MCP frontend recovery in src/daemon/frontend.c (DeusData#1784; the file is deleted in the fork), and MCP registry/packaging steps in release.yml were not taken. The PR adds no automatic GitHub Actions triggers. The automatic-trigger workflows pr.yml and test-impact-artifact.yml are left out, and scripts/ci/check-github-actions-policy.py passes.

Verification

Migrations

None. No schema or index-format change. A new config key, ignore_worktrees, defaults to false.

Review

benrfairless and others added 30 commits August 18, 2026 16:22
In-process, type-aware call resolution for single Ruby files, mirroring
the perl_lsp.c / php_lsp.c shape and dispatched from cbm_extract_file:

- PASS 1 collects classes and modules with lexical nesting, superclasses,
  and include / prepend / extend mixins, plus method tables with instance
  and singleton methods classified from the AST. Singleton methods key on
  "<class_qn>.self", which cannot collide with a constant path.
- PASS 1.5 infers instance-variable types from `@x = Const.new`;
  conflicting assignments latch to no type rather than guessing.
- PASS 2 resolves constructors (`Const.new` targets the class node, the
  way Python's lsp_constructor does), implicit and explicit self
  dispatch, singleton methods, superclass chains, mixin lookup in Ruby's
  method-resolution order, super, and ivar / local / chained receivers.

The stdlib seed covers Ruby core plus a curated Rails surface, including
ActiveRecord query typing: `User.find(1)` is a User, and relation chains
keep the model type. Dynamic dispatch (send, method_missing,
define_method) and unresolved receivers emit no edge, and only
project-defined targets are emitted.

Cross-file resolution is a separate tier and is not part of this change;
the 18 rows here drive the resolver through cbm_extract_file exactly as
the extraction pipeline calls it.

PROVENANCE: this resolver was authored clean-room from the
tree-sitter-ruby grammar and Ruby language semantics. No Ruby language
server source was consulted. scripts/check-lsp-originality.sh gains
Shopify's ruby-lsp (MIT) as the Ruby reference so the boundary is
auditable alongside the generated data it guards;
`check-lsp-originality.sh --lang ruby` reports CLEAN — no verbatim
string or comment overlap and no structural clones.

Refs DeusData#1701

Assisted-by: OpenCode:anthropic/claude-opus-5
Signed-off-by: Ben Fairless <ben@oaf.org.au>
Signed-off-by: tmonestudio <tmonestudio@users.noreply.github.com>
Signed-off-by: lorenzozanee <wyz0707@proton.me>
Agents that use `git worktree add` to run parallel branches end up with one
indexed project per worktree. Each is a near-duplicate of the main checkout,
so the cache fills with redundant graphs and project pickers get noisy.

Add an opt-in `ignore_worktrees` config key. When enabled, a linked worktree
is left alone:

  - auto-index on connect skips it (logged as autoindex.skip)
  - the daemon's background auto-index skips it
  - explicit index_repository refuses with an actionable message naming both
    ways forward (index_worktree=true, or turn the key off)
  - the hook-augment "not indexed" guidance stops telling the agent to run
    index_repository in a directory where it would only be refused

Detection is git plumbing, no subprocess: <path>/.git must be a regular file
holding a "gitdir:" pointer AND that gitdir must contain a `commondir` file.
The commondir check is what separates a linked worktree from a submodule,
whose .git is also a gitlink file but whose gitdir has no commondir. The main
checkout of a repo that has worktrees is never affected.

Defaults to false, so behaviour is unchanged unless the key is set.

Signed-off-by: Nohj <vitalnohj@gmail.com>
pr-smoke (macos-14) was cancelled mid-run (operation was canceled) at
an unrelated UI-server check, with all 30+ other legs green; ci-ok
failed only as a consequence. Empty commit to get a fresh run.

Signed-off-by: Amir Fathi <amirfathi.me@gmail.com>
Agents that use `git worktree add` to run parallel branches end up with one
indexed project per worktree. Each is a near-duplicate of the main checkout,
so the cache fills with redundant graphs and project pickers get noisy.

Add an opt-in `ignore_worktrees` config key. When enabled, a linked worktree
is left alone:

  - auto-index on connect skips it (logged as autoindex.skip)
  - the daemon's background auto-index skips it
  - explicit index_repository refuses with an actionable message naming both
    ways forward (index_worktree=true, or turn the key off)
  - the hook-augment "not indexed" guidance stops telling the agent to run
    index_repository in a directory where it would only be refused

Detection is git plumbing, no subprocess: <path>/.git must be a regular file
holding a "gitdir:" pointer AND that gitdir must contain a `commondir` file.
The commondir check is what separates a linked worktree from a submodule,
whose .git is also a gitlink file but whose gitdir has no commondir. The main
checkout of a repo that has worktrees is never affected.

Defaults to false, so behaviour is unchanged unless the key is set.

Signed-off-by: Nohj <vitalnohj@gmail.com>
Upstream's Unicode path fix dropped stat() from this file because it follows
symlinks and rejects non-ASCII paths on Windows. Keep ignore_worktrees
detection on cbm_path_info_utf8 so the rebase does not restore that probe.

Signed-off-by: Nohj <vitalnohj@gmail.com>
A session queued while capacity was full could still be indexed after the
setting was turned on. Retry admission now applies the same linked-worktree
gate as the initial path.

Signed-off-by: Nohj <vitalnohj@gmail.com>
The production hook builds its server with cbm_mcp_server_new(NULL), so
ignore_worktrees stayed off and an unindexed linked worktree still told
the agent to run index_repository. Attach the runtime store for that
check, then drop it before return.

Signed-off-by: Nohj <vitalnohj@gmail.com>
ignore_worktrees only inspected <path>/.git, so a session or index path
inside a linked worktree still passed the gate. Walk ancestors until a
git anchor, and stop at a main checkout or a submodule.

Signed-off-by: Nohj <vitalnohj@gmail.com>
ignore_worktrees checked the setting only after project resolution, so a
linked worktree nested in an indexed checkout was reported as that parent
project. Drop an ancestor match and keep an index whose root is the
worktree itself.

Signed-off-by: Nohj <vitalnohj@gmail.com>
The parent-graph drop treated any linked worktree root as a match, so an
unindexed worktree nested inside an indexed one still inherited that
parent. Compare the gitdir of each path's own anchor and keep the project
only when both paths are that same linked worktree.

Signed-off-by: Nohj <vitalnohj@gmail.com>
A 64-level walk missed a deep session path, and a relative gitdir joined
onto a symlink spelling no longer matched the real project root. Walk to
the filesystem root and compare canonical gitdirs. Drop the unused project
resolver that failed the macOS -Werror build.

Signed-off-by: Nohj <vitalnohj@gmail.com>
A symlink into a linked worktree has no .git on its lexical path, so the
hook told the agent to run index_repository. That call canonicalizes first
and then refuses. Resolve the cwd before project lookup and the
ignore_worktrees checks.

Signed-off-by: Nohj <vitalnohj@gmail.com>
The ignore_linked initializer exceeded the formatter's assignment break.
Lint treats that as an error, so the line now matches .clang-format.

Signed-off-by: Nohj <vitalnohj@gmail.com>
The memory-core ratchet rejects another raw free() in the hook. Keep the
parent name until the existing free(project) and skip it when the match
is a different worktree.

Signed-off-by: Nohj <vitalnohj@gmail.com>
The structural extractor names a class by appending the declaration's
raw name text to the enclosing class QN, so `class Admin::User` yields
`<module_qn>.Admin::User` while `module Admin; class User` yields
`<module_qn>.Admin.User`. The resolver normalised both to the dotted
form, and because the pipeline joins resolved rows to defs by exact
caller QN, every row from or to a compact-declared class was silently
dropped — the most common Rails style.

All three passes now enter a class through one shared frame helper
that computes the extractor-shaped QN from the raw name and keeps the
dotted path only for constant lookup. The class table dedupes on QN.

Nesting is now one raw frame per declaration joined by '.', and the
constant probe strips one frame at a time: inside `class A::B` only
`A.B.x` and `x` are tried, never `A.x`, matching Ruby's lexical
scoping (`class A::B` does not open A).

Tests pin exact QN equality on both ends of the row for compact,
nested-compact, and the outer-scope-not-opened case.

Assisted by OpenCode using anthropic/claude-fable-5-1.

Signed-off-by: Ben Fairless <ben@oaf.org.au>
…ignment

A `def` body inherited the enclosing scope, so a typed local from the
surrounding body (or a preceding sibling method) typed a same-named
parameter or local inside it. Parameters and block parameters were
never bound, so they could not shadow an outer local either. And an
assignment whose right-hand side had no proven type was ignored, so
`a = Foo.new; a = x; a.bar` still resolved to Foo#bar.

Ruby scoping is now modeled explicitly: `def`, `class` and `module`
bodies start from a fresh, unparented scope; blocks and lambdas get a
chained child scope; method, block and lambda parameters (including
destructured groups and block-local names) bind to UNKNOWN; and any
write the resolver cannot type — plain reassignment, multiple
assignment, `||=` on an already-typed name, other compound operators,
or an assignment inside a block — rebinds the nearest existing binding
to UNKNOWN. `a ||= Foo.new` on a fresh name still types as Foo.

Assisted by OpenCode using anthropic/claude-fable-5-1.

Signed-off-by: Ben Fairless <ben@oaf.org.au>
PASS 1.5 only recorded `@x = Const.new` and ignored every other write,
so `@x = compute` (or a parameter, a compound operator, a multiple
assignment, or an unresolved constant's .new) left the ivar typed by
whichever constructor it saw elsewhere. Any such write now latches the
conflict, exactly as two different constructors already did; `nil` is
neutral. `@x ||= Const.new` — the memoised-constructor idiom — counts
as a constructor assignment.

Assisted by OpenCode using anthropic/claude-fable-5-1.

Signed-off-by: Ben Fairless <ben@oaf.org.au>
instance_eval/instance_exec, class_eval/class_exec, module_eval/
module_exec, define_method and Class.new/Module.new run their block
with a different `self`, but bare and `self.` calls inside them were
dispatched on the enclosing class. The eval call itself was already
typed unknown; the walk into its block was not.

The block of such a call is now walked with self_unknown set: bare
calls, `self.` calls and `super` emit nothing there, `self` has no
type, and PASS 1.5 does not attribute ivar writes inside it to the
enclosing class. Receiver and argument expressions of the same call
still resolve, and the suppression ends with the block. The *_exec
forms join the dynamic-dispatch list.

The existing negative test passed only because Meta had no `hidden`;
it now has one, and the calls are written with `()` because
tree-sitter-ruby parses a lone identifier as a variable reference,
which the extractor never emits as a call.

Assisted by OpenCode using anthropic/claude-fable-5-1.

Signed-off-by: Ben Fairless <ben@oaf.org.au>
The lookup walks skipped a mixin whose module did not resolve and
carried on to the class's own methods and superclass chain, and a bare
call whose class ancestry did not resolve fell through to a same-named
top-level def. An unseen ancestor may define or shadow the method, so
none of those answers is trustworthy.

Every lookup now carries a `blocked` flag set the moment the walk
meets an unresolved prepend, include, extend or superclass reference,
in Ruby's lookup order: an unresolved prepend blocks even an own-method
hit; an unresolved include (or superclass) stops the walk before the
superclass chain; an unresolved extend blocks the singleton walk. When
blocked, nothing is emitted — no Object typing fallback, no top-level
fallthrough, no `super` edge. Hits found before the unseen ancestor are
still returned (an own method ahead of an unresolved include; a resolved
include listed after an unresolved one).

Assisted by OpenCode using anthropic/claude-fable-5-1.

Signed-off-by: Ben Fairless <ben@oaf.org.au>
PASS 1 recursed through blocks and registered any `def` it met on the
enclosing class or module, so ActiveSupport's `class_methods do … end`
and `included do … end` bodies became instance methods of the concern
and were then found through `include Concern` — edges to defs the
structural extractor does not even emit. `include X` written inside
such a block was likewise recorded on the concern instead of the
includer.

PASS 1 now tracks the block depth since the nearest class/module body
and, inside a block, registers no methods and collects no mixins. In
PASS 2 a class-body block def is skipped outright (no def exists for it
to join), and a top-level block def (an RSpec group) is walked with
self unknown: typed-receiver calls in its body may still resolve, bare
and self. calls emit nothing, and other top-level code cannot reach it.

Assisted by OpenCode using anthropic/claude-fable-5-1.

Signed-off-by: Ben Fairless <ben@oaf.org.au>
The nested-modules row matched caller and callee by substring, which
would have let a wrongly-spelled class path pass; it now pins the full
extractor QNs on both ends with strcmp, as the compact-class rows
already do. Refresh the file headers to describe the scope gates,
fail-closed ancestry and self-rebinding rules the resolver now follows.

Assisted by OpenCode using anthropic/claude-fable-5-1.

Signed-off-by: Ben Fairless <ben@oaf.org.au>
Signed-off-by: Nohj <vitalnohj@gmail.com>
Signed-off-by: Nohj <vitalnohj@gmail.com>
Signed-off-by: Nohj <vitalnohj@gmail.com>
Signed-off-by: Nohj <vitalnohj@gmail.com>
DeusData and others added 22 commits November 2, 2026 18:07
…iority

perf(index): run supervised index workers at below-normal CPU priority
…e-drops-held-lock

fix(daemon): stop the lifetime-lock probe from dropping a held lock
…ons/actions/setup-python-7.1.0

build(deps): bump actions/setup-python from 7.0.0 to 7.1.0
…e-guard

fix(install): reject unsupported Android portable installs
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
index_coverage_meta is one of the tables the graph digest pins column
by column, so the new indexed_checkout_sha column made the digest
reject every live store with a schema error: the live-schema
compatibility test failed, and test-impact, which digests the graph it
builds for HEAD, fell back to running every test for every change.

Record the checkout in a table of its own instead: index_checkout
(project primary key referencing projects ON DELETE CASCADE,
indexed_checkout_sha NOT NULL). A checkout is source identity, which
the graph digest deliberately does not bind, and the store already
keeps other per-project data (doc_link_*, observed_*) outside the
digested tables. index_coverage_meta keeps exactly the shape the digest
expects, so the digest needs no change and no version bump, and a new
table needs no migration of existing databases.

The row is written and cleared in the same coverage transaction, so a
failed replace keeps the previous identity, and it is removed with its
project. The store API is unchanged: cbm_coverage_meta_t carries
indexed_checkout_sha as before. Read-only opens of databases without
the table report no identity, as before.

store_graph_digest_indexed_checkout_identity_is_not_bound writes a
store through the real coverage API with and without a checkout SHA
and asserts that it still digests and that the SHA leaves the digest
unchanged.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Second maintainer refresh of DeusData#1561 (truthful index_status freshness),
onto main 9c19b5d, after the fix-up that keeps the indexed checkout in
its own index_checkout table (a0d0521). The first refresh is b44a22c.

Five files conflicted; each is resolved as main plus the branch change:

- src/pipeline/pipeline.c and src/pipeline/pipeline_incremental.c (two
  sites): main added .unresolved_calls_complete to the coverage-meta
  initializers where the branch added .indexed_checkout_sha. Both fields
  are kept, main first.

- src/store/store.h cbm_coverage_meta_t: main added
  unresolved_calls_complete where the branch added indexed_checkout_sha.
  Both fields are kept, main first.

- src/store/store.c init_schema: main added the unresolved_calls_complete
  column where the branch closed index_coverage_meta and created
  index_checkout. index_coverage_meta keeps main DDL exactly, and the
  index_checkout table follows it.

- src/store/store.c coverage replace: main moved the coverage-meta write
  out of cbm_store_coverage_replace_ex into coverage_write_metadata,
  whose caller owns the transaction and rolls it back on failure. The
  branch wrote or cleared the index_checkout row right after that
  write, so its two calls move into the helper: the upsert path returns
  index_checkout_write(s, project, meta->indexed_checkout_sha), the
  delete path returns index_checkout_write(s, project, NULL).
  index_checkout_write stays where the branch defined it, ahead of the
  new helpers.

- tests/test_store_nodes.c: main added
  store_coverage_shadow_excludes_unresolved_calls and its helper where
  the branch added store_coverage_meta_indexed_checkout_sha_roundtrip_and_rollback.
  Both tests are kept and registered, main first.

src/pipeline/pipeline_internal.h merged without a conflict: main raised
CBM_SEMANTIC_INDEX_VERSION to 13 for Next.js App Router routes, in a
separate hunk from the branch's cbm_pipeline_indexed_checkout_sha
declaration.

No semantic conflicts:

- The branch bumps no version. index_checkout is created by init_schema
  with IF NOT EXISTS and read only when present, so main's semantic
  index version 13 applies unchanged.

- The branch changes no existing function signature, and main changes
  none that the branch calls.

- Every main caller of cbm_store_coverage_meta_get added since the
  merge base, including the new Next.js upgrade test in test_pipeline.c,
  releases the result with cbm_store_coverage_meta_clear exactly once,
  which frees the checkout string. That test passes a shallow copy of
  the result to cbm_store_coverage_replace_ex and never frees the copy
  on its own.

No test call site needed adapting.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
feat(config): add ignore_worktrees to skip linked git worktrees
…file

feat(lsp): add the Ruby per-file resolver
…uctor-calls-init

fix(python): resolve ClassName() calls to __init__
…ons/actions/upload-artifact-7.0.2

build(deps): bump actions/upload-artifact from 7.0.1 to 7.0.2
…e-isolation

fix(test): isolate benchmark harness daemon runtimes
…ul-index-freshness

feat(mcp): report truthful index freshness from checkout evidence
…o fork

Ancestry-preserving merge of upstream DeusData/codebase-memory-mcp through
first-parent merge 5689ea5 ("Merge pull request DeusData#1561 ... truthful index
freshness"). Fork base cf63fe6; previous merge base a5b248f; 104
upstream-only commits across 11 first-parent merges:
DeusData#2747 Next.js app-router routes, DeusData#2838 index worker priority, DeusData#2207 lifetime
lock probe, DeusData#2879 setup-python bump, DeusData#2245 Termux installer guard, DeusData#1430
ignore_worktrees, DeusData#1711 Ruby LSP per-file, DeusData#2424 Python constructor calls
__init__, DeusData#2781 upload-artifact bump, DeusData#2198 benchmark runtime isolation,
DeusData#1561 truthful index freshness.

Conflict resolution (CLI-first):
- src/mcp/mcp.c, src/mcp/mcp_internal.h, tests/test_mcp.c: kept deleted.
  MCP-side behaviour of DeusData#1430 (index_worktree override on explicit index) and
  DeusData#1561 (verbose index_status freshness block) is ported to the CLI in
  follow-up commits on this branch.
- .github/workflows/pr.yml, test-impact-artifact.yml: kept deleted (Jenkins
  owns automatic CI). _build.yml keeps the fork's `platforms != 'linux'`
  guards and takes upload-artifact v7.0.2. release.yml keeps the fork side
  (no MCP registry checksum artifact).
- Makefile.cbm: fork ALL_TEST_SRCS plus upstream TEST_RUBY_LSP_SRCS.
- scripts/test.sh: keeps the fork's Jenkins prepare phase and adds upstream's
  Android/Termux installer guard step.
- README.md, docs/CONFIGURATION.md, scripts/benchmark-*.sh: fork side kept;
  CLI-adapted docs and benchmark isolation follow separately.
- src/daemon/application.c: ignore_worktrees gate reads the daemon's own
  config (application_ignore_worktrees_enabled) instead of an MCP session
  server; includes git/git_context.h.
- src/cli/hook_augment.c / tests/test_cli.c: fork's CLI resolution path kept;
  worktree-ignored guidance text now names `codebase-memory-cli index`.
  Full ignore_worktrees hook resolution is ported in a follow-up.

Checks: strict GitHub Actions policy passes; scripts/build-dev.sh and
build/c/test-runner compile with -Werror; every fork-side hunk from
a5b248f..cf63fe6 still applies in reverse to the result for files both sides
touched (only the intentionally extended ALL_TEST_SRCS line differs); no
fork-defined function was dropped.
…usData#1696)

Finish the merge of the benchmark harness isolation work onto the CLI-first
fork. The auto-merged upstream hunks left both scripts as a mix of fork and
upstream invocations (undefined INDEX_RC, an unused INDEX_ERR, MCP
`cli index_repository` calls).

benchmark-index.sh: run `codebase-memory-cli index REPO --mode full --json`
with stderr captured to INDEX_ERR and the exit status kept in INDEX_RC, so a
refused index fails the run, prints "--- index stderr ---" and the cause.
Dropped the now-unneeded hand-built JSON path (argv carries the path).

benchmark-search-graph.sh: index through `index REPO --mode full --json`
instead of the MCP `cli index_repository` request; stop unwrapping the MCP
content envelope; fix usage comment (arg is a repo path).

docs/EVALUATION_PLAN.md: example invocations name codebase-memory-cli.

Contract test adaptations (assertions on isolation, stderr surfacing, exit
status, handoff and stop behaviour unchanged):
- Probe fixture detects `index` (argv 1) instead of `cli index_repository`.
- The fixture logs the repo path (argv 2) rather than a JSON body; the
  quoted/backslash-path check now asserts the path reaches the index command
  as one intact argument, since the fork's CLI has no JSON request.
- "MCP session" comment wording changed to "graph session".
Port upstream DeusData PR DeusData#1561 from the removed MCP server into the
operations layer. `status --verbose` now emits a `freshness` object next
to the unchanged `git` object:

- indexed_generation, indexed_checkout_sha (recorded in coverage meta at
  the staged-generation boundary), checkout_sha (live HEAD)
- status_available plus bounded tracked_changes / untracked_source
  samples (cap 16, truncated flag) from cbm_git_worktree_status
- verdict current/stale/unknown, dominant reason, reasons[] (max 8),
  recommended_action

The verdict is fail-closed: a missing recorded identity is unknown, a SHA
mismatch or tracked changes are stale, and untracked sources, an
unavailable git status or an unresolvable HEAD prevent "current". It is
report-only and never triggers indexing; the default (non-verbose) status
output is unchanged. The status tool schema description and
docs/MEASURING_SAVINGS.md describe the new block.

Integration tests (real git repos, real project DB in a private cache
dir) cover current, mismatch, tracked, untracked-only, mixed, non-git,
missing identity, sample-cap truncation and the verbose-only gate.
…d explicit index refusal

hook_augment: when ignore_worktrees is on in an existing _config.db (never
created by the hook), canonicalize cwd and resolve the project through
ha_resolve_project_ignoring_worktrees, dropping a parent graph that does not
belong to the cwd's own linked worktree and switching to the 'not indexed on
purpose' guidance. Key off (default) takes the previous path and the test
gate counter does not move.

index operation: an explicit index of a linked git worktree is refused when
ignore_worktrees is on, with an error naming --index-worktree (per-call
index_worktree arg, added to the tool schema) and
'config set ignore_worktrees false'. No silent skip.

docs/CONFIGURATION.md: settings row, config example, CLI wording for the
ignore_worktrees section, --index-worktree mention.

tests: refusal/override/off/main-checkout integration test with a real git
worktree, --index-worktree arg parsing test, and hook assertions updated for
the CLI guidance text (codebase-memory-cli index).
cbm_git_context_resolve returns 0 on success, but execute_status treated
its result as a boolean, so status --verbose only entered the git branch
when resolution failed (and then is_git was unset). The git object was
therefore never emitted. Compare against 0 like the other callers
(http_server.c, pipeline.c). Found while porting upstream DeusData#1561.
index_policy_mcp_rejects_forged_override_and_preserves_serving_index called
th_mktempdir twice and kept both returned pointers, but th_mktempdir returns
a shared static buffer. After the second call `repo` and `cache` were the
same string (the cache directory), so the first index_repository request
targeted the cache root and was refused by the workspace guard:

  /tmp/cbm_index_policy_mcp_cache_LjrHdR: path is a volume root or holds the
  codebase-memory cache; it cannot be indexed.

That guard is intentional (fork commit 19f5bdf, "fix workspace cache root
authorization"), which landed on main after the last green full Jenkins run
(DeusData#88 at 214b87b). The failure therefore pre-exists on main cf63fe6 and
surfaced in release-tooling build DeusData#89; it is not caused by the upstream
13M-1 merge. Copy each path into its own buffer so the fixture's repository
and cache are separate sibling directories. Product behaviour is unchanged.

Root cause found by a Codex gpt-6-luna agent; verified by rerunning the
index_policy and index_supervisor suites.
@coderabbitai

coderabbitai Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

Explain Change →

Caution

Review failed

The pull request is closed.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@ngallodev ngallodev left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed PR #33 at c3c5271. The index_policy failure is caused by the test retaining two pointers to th_mktempdir's shared static buffer; the second call replaced the repo path with the cache path, which the workspace guard correctly refuses. Copying each path into its own buffer fixes the fixture while preserving the intentional cache-root denial. Local validation passed: dev build, test-runner build, memory-core lint (3211 raw allocator sites across 102 files, none grew), index_policy 12/12, and index_supervisor 11/11. Jenkins DeusData#90 is reported successful on this exact head. Native Windows validation remains unrun. No blocking findings.

@ngallodev ngallodev left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review passed after an independent review of exact head c3c5271ab8f454287452f0df5eca9e38ffd250cf.

  • Verified 5689ea54c is an ancestor, the sync is 0 commits behind cf63fe667, and the checkpoint merge records both parents.
  • Checked the full 59-file diff, including the DeusData#2198, DeusData#1561 and DeusData#1430 CLI ports, their call sites, publication and rollback paths, and the MCP/automatic-trigger exclusions. No blocking correctness finding.
  • Local: scripts/test.sh --suites git_context,incremental passed 192 tests. tests/test_benchmark_runtime_isolation_contract.sh, tests/test_install_android_guard.sh, and git diff --check passed. The review used an unchanged local checkout; the working tree was clean at the files being reviewed.

Limits: native Windows was not exercised in this review. The added worktree and symlink fixtures are skipped on Windows, so Linux coverage does not establish Windows parity. The prior Jenkins DeusData#90 and draft-state checks are separate evidence.

@ngallodev
ngallodev marked this pull request as ready for review November 9, 2026 06:33
@ngallodev
ngallodev merged commit 929adfe into main Nov 9, 2026
1 check passed
@ngallodev
ngallodev deleted the sync/upstream-13m1-20261108 branch November 9, 2026 06:33
ngallodev added a commit that referenced this pull request Nov 9, 2026
…ification

Adds per-batch receipts (13m1.md, 13m2.md, 13n.md) and updates ledger.json,
PLAN.md and CONTINUE.md for the 2026-11-08 upstream closeout against
DeusData 612eee2.

Jenkins release-tooling full gate, exact heads:
- 13M-1 c3c5271: DeusData#90 SUCCESS; merged to main via PR #33 (929adfe).
  DeusData#89 on 3cbefe2 failed only on the index_policy fixture fixed there.
- 13M-2 0f1927f: DeusData#93 SUCCESS. DeusData#91 on 1c5f82a failed two upstream
  Hermes hook cycle tests, fixed in 0f1927f.
- 13N 98eab3c: DeusData#94 SUCCESS. DeusData#92 on cf24b6a was aborted because that
  head lacked the 13M-2 test fix.

Native Windows was not run for any batch, so the reserved-`nul` change
(DeusData#2903) is unverified on Windows. This commit only touches docs; the code
Jenkins qualified is 98eab3c.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.