2472 Commits

Author SHA1 Message Date
Martin Vogel 010569fa6c Merge pull request #1678 from musichen/fix/pi-extension-tool-schemas
fix(pi): emit parameters and execute in the generated pi adapter
2026-08-21 10:44:22 +02:00
Alexander Musichen 104c9a7262 Merge branch 'main' into fix/pi-extension-tool-schemas 2026-08-21 04:26:56 +02:00
Alex Musichen 2ae84e7f73 test(pi): pin result wrap, error throw, and abort in generated adapter
Maintainer asked for in-tree coverage of {content, details} wrapping and
how tool errors are surfaced, plus a cheap abort-signal line. String-shape
only; no live Pi process.

Signed-off-by: Alex Musichen <alex.musichen@gmail.com>
2026-08-21 04:11:51 +02:00
Martin Vogel 65c7cf10f3 Merge pull request #1775 from DeusData/distill/install-plan-fixture-cleanup
test(cli): clean up before failing the install-plan receipt fixture
2026-08-21 02:58:52 +02:00
Martin Vogel 35ddda9f51 Merge pull request #1774 from DeusData/distill/nomic-blob-generator
fix(build): stop blob regeneration from reverting the ELF stack hardening
2026-08-21 02:15:24 +02:00
Martin Vogel 7b6363d0b6 Merge pull request #1772 from DeusData/fix/startup-coordination-lingering-lock
fix(cli): wait out a held startup transition instead of failing at 10s
2026-08-21 01:44:12 +02:00
Martin Vogel 8ec7e5c322 Merge pull request #1771 from DeusData/fix/java-enum-methods
test(java): pin enum constants surviving alongside enum methods
2026-08-21 00:58:57 +02:00
Martin Vogel f3b356afed test(cli): clean up before failing the install-plan receipt fixture
cli_install_plan_receipt_no_mutation_issue388 asserted inline, so any failure
returned before free(json) and test_rmdir_r(tmpdir). A red run therefore leaked
the receipt and left a stray /tmp/cli-plan-* directory behind -- the failure
report was accompanied by exactly the noise that makes the next debugging
session harder.

Record what went wrong, release everything, then fail. The message now names
the specific missing marker rather than reporting that some marker was absent,
which is the difference between reading a failure and bisecting one.

The env-isolation half of the original PR is not carried over: main's
tf_setup_cache_sentinel already unsets CODEX_HOME along with every other client
home override, so a per-test save/unset/restore would be redundant now.

Distilled from #1149 by Anand Aiyer, which predates that sentinel and could not
land as written once its companion PR closed unmerged.

Co-authored-by: Anand Aiyer <anand@aiyer.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-20 21:36:57 +02:00
Martin Vogel 978ac7d745 test: keep the step-0 contract sequence linear
scripts/test.sh labels its pre-build contracts as a running 0a..0u sequence.
The new blob-generator contract was slotted in as "0f2" to sit next to the
other build-tooling contract, which breaks the one convention the list has.
Append it as 0v instead; these steps all run before the build, so position
carries no meaning beyond the label.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-20 21:31:24 +02:00
Martin Vogel 18edfa0023 fix(build): stop blob regeneration from reverting the ELF stack hardening
vendored/nomic/code_vectors_blob.S is a GENERATED file that was hand-edited.
1f674c80 added the ELF .note.GNU-stack section to it after finding that every
Linux binary we had ever shipped requested an executable stack: an object
carrying no such note tells GNU ld nothing about its stack requirement, and ld
then assumes the worst for the entire link. That fix went into the artifact
only. scripts/extract_nomic_vectors.py still emitted the Mach-O branch alone,
so the next regeneration would have overwritten the tracked .S, dropped both
the ELF and COFF branches, and put GNU_STACK RWE back into every release --
silently, because nothing compared the generator against its own output.

write_blob_s() now emits the tracked wrapper verbatim, and the new contract
test fails if the two ever drift again. The template is compared rather than
executed so the test does not need the torch/transformers import the extraction
script pulls in. The write is pinned to UTF-8 because the WHY comment carries
an em dash and the default encoding is locale-dependent.

check-binary-composition.sh already fails a release whose binary has an
executable stack; this closes the same hole at edit time, where the diff is
still small enough to read.

Reported and originally fixed by Anand Aiyer in #1151, which also carried the
COFF branch. That PR sat unreviewed for a month while the .S half was
rediscovered and landed independently without credit.

Co-authored-by: Anand Aiyer <anand@aiyer.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-20 21:20:44 +02:00
Martin Vogel 31819c9c03 Merge pull request #1765 from DeusData/docs/npm-readme-counts
docs(npm): align README language and MCP tool counts with the root README
2026-08-20 19:46:09 +02:00
Martin Vogel e830293ef1 Merge pull request #1065 from tmonestudio/codex/fix-json-edge-properties
fix(pipeline): escape dynamic edge properties as JSON
2026-08-20 19:11:33 +02:00
Martin Vogel 33df13977d Merge pull request #1498 from DeusData/dependabot/github_actions/ossf/scorecard-action-2.4.4
build(deps): bump ossf/scorecard-action from 2.4.3 to 2.4.4
2026-08-20 19:02:14 +02:00
Martin Vogel bad0477235 Merge pull request #1496 from DeusData/dependabot/github_actions/github/codeql-action/upload-sarif-4.37.6
build(deps): bump github/codeql-action/upload-sarif from 4.37.1 to 4.37.6
2026-08-20 19:02:10 +02:00
Martin Vogel ee05ef39aa Merge pull request #1494 from DeusData/dependabot/github_actions/actions/stale-11.0.0
build(deps): bump actions/stale from 10.4.0 to 11.0.0
2026-08-20 19:02:05 +02:00
Martin Vogel 1acd87912f test(java): pin enum constants surviving alongside enum methods
Java enum methods already resolve on main: find_class_member_body
descends into enum_body_declarations (Java-gated) while find_class_body
keeps returning enum_body, which is what extract_enum_members needs to
reach the constants — they are siblings of enum_body_declarations, not
children.

That distinction was unguarded. java_enum_dedup_preserves_calls_issue1234
asserted the methods and the absence of duplicate Function defs, but
never asserted the constants, so collapsing the two lookups would have
passed the suite while silently dropping every enum constant from the
graph.

PR #984 proposed exactly that collapse — redirecting the shared
find_class_body — which is what surfaced the gap. Verified binding:
pointing extract_enum_members at find_class_member_body reddens the new
Variable assertions and nothing else.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Co-authored-by: sahil-mangla <manglasahil2017@gmail.com>
2026-08-20 18:42:26 +02:00
Martin Vogel 4a29f0da59 fix(cli): wait out a held startup transition instead of failing at 10s
A busy startup transition always resolves, by one of two routes: the live
peer holding it finishes its command, or the holder is already dead and
the operating system has not finished reclaiming the lock. Waiting was
capped at 10s regardless, so a clock decided a user-visible outcome and
commands were refused with "CLI startup coordination remained busy" while
nothing was actually wrong.

The second route is Windows-specific and is what makes this reachable in
practice. On POSIX the kernel drops flock the instant an owner dies. Windows
byte-range locks do not: Microsoft documents that after a process terminates
holding one, "the time it takes for the operating system to unlock these
locks depends upon available system resources", and that until then "access
to these files may be denied". A loaded CI runner is exactly where those
resources are scarce, and tests/windows/test_daemon_stability.py creates the
condition deliberately by hard-killing daemons with `taskkill /F`, including
a crash-recovery section -- so the next client meets a lock whose owner no
longer exists and is refused for a reason that no longer applies.

The cap becomes a backstop against a peer that never finishes rather than a
budget for healthy contention, and the message it prints now names both
explanations instead of just "busy", which sent reporters looking for a CBM
session that had already exited.

Clean exits already release via main_local_transition_close, so no new
release path is needed; this only affects what happens after an abrupt
termination or under genuine concurrency.

VERIFICATION LIMIT, stated plainly: this cannot be reproduced or verified on
macOS -- the mechanism requires Windows lock-reclaim semantics, and a local
12-client storm passes even under the old cap. 458 daemon/ipc/runtime/cli/
watcher tests pass and lint is clean, but whether this removes the CI flake
can only be established on Windows CI.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-20 18:17:28 +02:00
Martin Vogel 1af49dfe58 fix(watcher): stop nested non-git dirs and monorepo siblings retriggering
Three related defects in how the watcher decides what counts as a change.

1. `git rev-parse --git-dir` walks UP the tree, so an ordinary folder that
   merely sits under an unrelated repository answered yes. The watcher then
   inherited that ancestor's dirty state -- permanently non-empty and about
   entirely different files -- and reindexed on every poll forever. A
   directory is now git-managed only if it carries its own .git (directory
   or gitlink file) or the ancestor actually tracks something inside it, so
   a real monorepo sub-package still qualifies while a scratch or ignored
   folder does not.

2. `git status` reports the whole repository regardless of -C, so every
   package in a monorepo reindexed whenever any sibling was edited. The
   status call is now scoped with a `-- .` pathspec.

3. Porcelain paths are REPOSITORY-relative but were stat'ed against
   root_path. For a project watched at the repository root the two coincide
   and the bug is invisible; for a subdirectory project every stat missed,
   silently degrading the dirty signature to text-only and losing the
   size/mtime component that makes an edit to an already-dirty file
   detectable. Paths now resolve through `rev-parse --show-cdup`.
   --show-cdup rather than --show-toplevel because MSYS/Cygwin git returns a
   translated absolute path from the latter that will not join onto the
   native path we hold; a relative hop composes on every platform.

Distilled from #1001 by bethzyy, whose diagnosis identified all three.
Main independently fixed that PR's two headline problems (the unbounded
non-git file count, and per-poll churn via a dirty-state signature) while
it waited, and the watcher was rewritten onto a supervised argv-spawn
harness in between, so this is a reimplementation against the new code
rather than a rebase of theirs.

Both new tests are verified binding: reverting the classification guard
reddens the nested-directory test, and removing the pathspec reddens the
monorepo one, each without disturbing the other.

Addresses the remaining parts of #713, #841 and #937.

Co-authored-by: bethzyy <bethzyy@users.noreply.github.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-20 16:43:38 +02:00
Martin Vogel 9eba027909 docs(npm): align README language and MCP tool counts with the root README
pkg/npm/README.md claimed 159 languages and 14 MCP tools while the root
README says 158 languages and 15 MCP tools. The tool count is verifiably
wrong: the TOOLS[] table in src/mcp/mcp.c has 15 entries. The language
count is aligned to the root README, which states 158 consistently in
five places.

Each stale figure appeared twice; both occurrences of each are fixed, so
the package README no longer contradicts itself or the root README.

Distilled from two PRs that each caught one half: #967 by ketpatil77
(language count) and #1268 by nhtkylc91 (tool count). Neither fixed all
four occurrences, and both touched the same file, so they are combined
here with credit to both.

Note for whoever refreshes these next: 159 grammar directories exist
under internal/cbm/vendored/grammars/, which does not reconcile trivially
with either figure (some languages share a grammar; some enums have no
vendored grammar). This change deliberately does not re-derive the
number, only stops the two READMEs disagreeing.

Co-authored-by: ketpatil77 <ket.patil77@gmail.com>
Co-authored-by: nhtkylc91 <nhtkylc@hotmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-20 15:33:44 +02:00
Martin Vogel dfe67cc771 Merge pull request #955 from EightDoor/fix/windows-process-enumeration
fix(ui): enumerate codebase-memory-mcp processes on Windows
2026-08-20 15:31:08 +02:00
Martin Vogel 5e9c63304e Merge pull request #1763 from DeusData/feat/swift-protocol-methods
fix(swift): index protocol method requirements as Method nodes
2026-08-20 15:19:53 +02:00
Martin Vogel edfc2de122 Merge pull request #1761 from DeusData/feat/nix-cross-dev-shell
feat(build): add a macOS cross-architecture Nix dev shell
2026-08-20 14:36:21 +02:00
Martin Vogel 5cabeb4b10 fix(swift): index protocol method requirements as Method nodes
A Swift protocol requirement — a bodyless `func generate() -> String`
inside a protocol — was absent from the graph entirely. Swift codebases
are heavily protocol-driven, so the requirement is very often the
declaration a reader is looking for, and "who declares generate()" had no
answer.

Three edits deliver it: protocol_body joins the class-body types so a
protocol's members are walked at all; protocol_function_declaration joins
swift_func_types, because extract_class_methods gates on that set and
would otherwise walk the requirement and discard it; and both name
resolvers accept the node, which has the same simple_identifier shape as
function_declaration.

Distilled from #613 by xbsjason. That PR also carried an enum/struct half
whose two assertions fail on CI: tree-sitter-swift has no
struct_declaration or enum_declaration node type (it models both as
class_declaration), so the corresponding swift_class_types entries are
inert and a bare enum is still labeled Class. That is a real pre-existing
modelling gap, left untouched here and now documented at the dead entries
rather than silently deleted.

Also dropped from the original: a find_first_descendant_by_kind rescue in
extract_class_methods. Removing it changes no test outcome once the
function-type entry is present, so it was carrying nothing.

Advances #43.

Co-authored-by: xbsjason <xbsjason@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-20 13:39:49 +02:00
Martin Vogel 34d18ae2ea Merge pull request #1756 from DeusData/feat/dbt-jinja-lineage
feat(dbt): dbt model lineage from Jinja-templated SQL
2026-08-20 13:20:43 +02:00
Martin Vogel 3308f36080 feat(build): add a macOS cross-architecture Nix dev shell
`nix develop .#cross` on macOS exposes the OTHER darwin architecture's
zlib, so `scripts/build.sh --arch <target>` can link an x86_64 binary on
Apple Silicon and vice versa. The native clang already cross-emits object
code via -arch; zlib is the product's single link-time dependency, and the
host-arch copy cannot satisfy a cross link, which is the whole gap.

Darwin-gated and opt-in: the attribute set is empty on Linux, the default
shell is untouched, flake.lock is unchanged and no new flake input is
introduced. The shell deliberately does not use inputsFrom, since that
would put the host-arch zlib back on the link path.

Distilled from #724 by Kris Williams. That version also wired libgit2 via
pkg-config, which is now dead weight: libgit2 was removed project-wide for
GPL-licensing reasons and appears nowhere in the tree, so both it and
pkg-config are dropped here — leaving zlib as the only library the cross
shell needs to place.

Validated by parsing the flake with nix-instantiate in a nixos/nix
container (no Nix toolchain on the maintainer host), which also confirms
the shellHook's ''${...} escaping resolves to literal shell expansions.
Evaluating or entering the shell needs a macOS host with Nix; no CI leg
exercises it, as noted on the original PR.

Addresses #705.

Co-authored-by: Kris Williams <115474+kriswill@users.noreply.github.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-20 13:08:21 +02:00
Martin Vogel 43ecc0988c feat(dbt): extract dbt model lineage from Jinja-templated SQL
A dbt model is an ordinary .sql file whose dependencies are written
{{ ref('other_model') }} or {{ source('group','table') }}, never as
literal table names. The SQL grammar cannot read those -- FROM {{ ref(
'x') }} is a parse error to it -- so the dependency structure of an
entire dbt project was invisible to the graph.

extract_dbt.c runs as a sub-extractor inside the normal indexing
pipeline. Per qualifying file it emits one Model definition (named by
the file stem, dbt's own model identity) plus one usage per ref()/
source() call; pass_usages resolves those into model -> relation
lineage edges like any other reference.

Model is a relation label alongside Table and View, which is what makes
the rest work without new plumbing: registry seeding, the central
relation veto, the incremental surface hash, search ranking and the
architecture queries all pick it up from cbm_label_is_relation. Sharing
one label class also means a model's source('raw','customers') resolves
onto a Table declared in a plain DDL migration in the same repository,
so dbt lineage and SQL lineage form one graph rather than two, while
the veto keeps model names out of every non-lineage consumer.

The pass is self-gating: SQL files only, and only those carrying a real
ref()/source() call. The dbt builtins are themselves the evidence, which
is cheaper than a dbt_project.yml lookup and more precise -- templated
SQL that is not dbt (an Airflow {{ ds }} parameter) produces no Model
node and no usages even inside a dbt repository.

{% macro %} definitions are deliberately excluded: the vendored
tree-sitter-jinja2 grammar has no node types for {% %} statements, so
they can only be recovered by a hand-written scanner that cannot see
comments or find {% endmacro %} for a correct span. Filed as follow-up
rather than approximated.

Tests cover the lineage, the last-string-argument semantics of both
builtins, the gate (against a plain-SQL control extraction), plain DDL
staying untouched, and an end-to-end pipeline case asserting model ->
model across files, model -> Table onto plain DDL, and cross-language
isolation. Disabling the pass reddens three of them; removing Model from
the relation set breaks lineage outright.

Implements the lineage half of #575.

Co-authored-by: alexisperinger-ux <alexis.peringer@iss-stoxx.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-20 10:23:48 +02:00
Alexander Musichen 495a79dc25 Merge branch 'main' into fix/pi-extension-tool-schemas 2026-08-20 04:14:09 +02:00
Martin Vogel 4bbc978c5c Merge pull request #1749 from DeusData/feat/sql-table-view-lineage
feat(sql): first-class Table/View nodes and FROM/JOIN lineage edges
2026-08-20 01:41:54 +02:00
Martin Vogel 7ba84e3754 feat(sql): first-class Table/View nodes with FROM/JOIN lineage
CREATE TABLE / CREATE VIEW / CREATE MATERIALIZED VIEW now produce Table
and View nodes (previously generic Variable), CREATE PROCEDURE produces
a Function, and schema-qualified DDL names (schema.table) are named by
the table identifier instead of the schema. A view's FROM/JOIN relations
are emitted as usages and resolve into view -> table USAGE lineage
edges.

Relations join the cross-file name registry so lineage can resolve, with
two structural safeguards:

- Registry membership is defined once by cbm_label_is_registry_symbol
  (helpers.c); the full, parallel and incremental seed sites all call
  it, ending the KEEP-IN-SYNC copies the old label lists required.
- The default cbm_registry_resolve vetoes relation-labeled results:
  common table names (users, orders, config) collide with code
  identifiers in every language, and no CALLS/USAGE/READS/WRITES/THROWS/
  handler/decorator consumer may bind them. The SQL lineage path opts in
  through the new cbm_registry_resolve_lineage.

Table/View also join the registry-only per-file LSP surface labels so a
table rename invalidates dependent SQL files on incremental (no stale
lineage edges), rank with the type tier in BM25 search, and appear in
the architecture boundary/package/cluster queries via the pinned
CBM_SQL_RELATION_LABELS fragment.

Tests: extraction trio (labels, lineage usages, schema-qualified names),
grammar golden + probe updates, relation-label contract pin, and two
pipeline tests: cross-language isolation (binding: fails without the
veto) and incremental table-rename stale-lineage.

Closes #574.

Co-authored-by: alexisperinger-ux <alexis.peringer@iss-stoxx.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-20 00:12:28 +02:00
Martin Vogel 46ae198fc1 Break-glass merge PR #1719 v0.10.8 2026-08-18 22:39:59 +02:00
Martin Vogel 98c1f8c3e1 test: gate-chain contract pins the explicit-result conditions
The contract required the bare '!cancelled() && !failure()' idiom on
build/smoke/soak — the exact form the v0.10.7 incident proved fail-open
(failure() does not cover a CANCELLED needed job). It now requires each
gate's explicit accepted results, the sanctioned skip_tests clause on
build, and the preflight version guard wired before lint.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-18 22:30:28 +02:00
Martin Vogel 9427dd075d fix(ci): release gates fail closed on cancelled jobs + refuse malformed version input
Three fixes from the v0.10.7 release incident (2026-08-18):

1. build (and smoke/soak) required 'not failed' instead of explicit success.
   failure() does not cover a needed job that TIMED OUT (conclusion
   'cancelled'), so lint hitting its 15-min timeout cascaded test into
   'skipped' and the pipeline published with the whole test matrix and
   asan-soak silently skipped. build now requires lint success plus either
   test success or the sanctioned skip_tests input; smoke/soak require build
   success explicitly.

2. The tag is inputs.version verbatim: dispatching a bare '0.10.7' published
   a release the installers can never resolve (they fetch
   releases/download/v<version>/...), and under immutable releases the
   mis-named tag cannot be retagged or its name reused. A preflight job now
   refuses any non-v-prefixed version before anything runs.

3. Lint's 15-min timeout was one slow-runner day away from cancelling a
   normally-5-min job; raised to 30 so only a genuine hang can hit it.

Release-path only (workflow_dispatch); adds one ~5s preflight job; no PR-CI
gating, cost, or trigger changes.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-18 22:12:19 +02:00
Alexander Musichen 0b22dfff7b Merge branch 'main' into fix/pi-extension-tool-schemas 2026-08-18 20:52:22 +02:00
Martin Vogel afebf0092b Merge pull request #1326 from Enferlain/fix/1287-persisted-coverage-summary
fix(index): preserve persisted coverage summaries
0.10.7 v0.10.7
2026-08-18 18:57:53 +02:00
Martin Vogel 39bd040505 Merge pull request #1699 from DeusData/fix/lf-endings-distill
fix(build): LF contract for extensionless git hooks + a line-ending guard
2026-08-18 16:36:50 +02:00
Martin Vogel 00beec9397 Merge pull request #1608 from ertankucukoglu/fix/search-code-cancellation
fix(mcp): bound and cancel Windows code search
2026-08-18 16:36:40 +02:00
Martin Vogel 2973ab763b Merge pull request #1698 from DeusData/fix/1196-aggregate-scan-truncation
fix(cypher): expansion materializes every matched row — the cap falsified aggregates (#1196)
2026-08-18 16:13:42 +02:00
Alexander Musichen 1f1832ab37 Merge branch 'main' into fix/pi-extension-tool-schemas 2026-08-18 13:37:23 +02:00
Martin Vogel fe4396f906 Merge branch 'main' into fix/1287-persisted-coverage-summary 2026-08-18 11:21:46 +02:00
Martin Vogel 32633bab9a fix(build): LF contract for extensionless git hooks + a line-ending guard
The *.sh eol=lf rule landed via #1314, but the git hooks
(scripts/git-hooks/commit-msg, scripts/hooks/pre-commit) are extensionless
and were still at the mercy of core.autocrlf. Add their explicit entries,
plus the contract test from #1272 wired as scripts/test.sh Step 0t: every
shell entrypoint (*.sh + both hook directories) must carry an eol=lf
attribute, with a matched-zero-files guard so a broken glob can never pass
vacuously.

Distilled from #1272: the diagnosis (CRLF checkouts breaking shebangs under
WSL/MSYS), the hook-file coverage, and the guard design are @xumian520's;
verified RED without the *.sh rule (105 uncovered entrypoints) and green
with it (107 files).

Co-Authored-By: xumian520 <126989134+xumian520@users.noreply.github.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-18 11:21:36 +02:00
Martin Vogel 58ef9f19ac fix(cypher): expansion materializes every matched row — the cap falsified aggregates
Second mechanism of #1196, exposed by the reporter's v0.10.6 retest: every
relationship-expansion site capped its output buffer at bind_cap*10, so edges
past the cap were silently dropped BEFORE WHERE and aggregation ever ran.
count() then reported the scanned prefix as if it were a fact — 9,360 of
13,691 DEFINES field-measured at --max-rows 1000 — and a LABEL on the source
did not protect you (the label workaround only ever fixed source
enumeration, which #1323 already made exact). max_rows is an output-row
limit per the public header; projection already enforces it.

All five capped sites now share one growable append (geometric growth,
size_t sizing): the per-hop expansion, its fixed/variable-length helpers and
process_edges, the bound-terminal driver, and the cross-join outer buffer.
Only allocation failure stops materialisation; match_count stays truthful
either way, so the #627 OPTIONAL contracts hold (a saturated buffer can no
longer exist, and the fallback rows share the same append). The #601
deadline still bounds pathological time, and hop caps keep bounding depth —
this removes only the silent row-dropping.

Regression test: 2 labeled sources x 30 edges with max_rows=2 — the old cap
returned count=20; ground truth 60 now holds, and the list form returns
exactly max_rows rows. Proven RED before and RED again on revert; cypher
181/181 and mcp suites green, including every OPTIONAL/#627 semantics test.

Fixes #1196 (together with #1323, which fixed the unlabeled source scan).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-18 11:19:32 +02:00
Martin Vogel 07cac7d6b3 Merge pull request #1371 from Joseph-MingEn/fix/py-aliased-from-import-calls
fix(python): resolve aliased from-import CALLS to real def
2026-08-18 11:19:19 +02:00
Martin Vogel 847da2fff2 fix: restore test closer lost in the merge resolution
The conflict's shared tail (#endif + closing brace) was emitted once for
two adjacent Windows tests; re-add the closer so both tests compile.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-18 11:18:43 +02:00
Martin Vogel e24ce80bdc Merge origin/main into fix/search-code-cancellation
Trivial insertion-point collision in tests/test_mcp.c: both this branch and
main (#1704's UTF-8 pin) add a Windows search_code test at the same spot.
Both tests kept.
2026-08-18 11:08:17 +02:00
Martin Vogel 9de6831d2c Merge pull request #1695 from astandrik/codex/fix-1691-test-runtime-isolation
fix(test): isolate harness daemon runtimes
2026-08-18 11:00:03 +02:00
Martin Vogel a6f73a6d38 Merge pull request #1704 from DeusData/fix/stdin-gate-tests-post-1181
fix: unbreak main — stdin-gate #1359 guards + Windows search_code UTF-8 pipe
2026-08-18 10:59:18 +02:00
Martin Vogel 4dd099679d fix(windows): pin the search_code PowerShell pipe to UTF-8
PowerShell 5.1 encodes stdout for a native-process pipe in the console
OEM codepage, so raw search content containing characters the inherited
CP cannot carry (Cyrillic under CP437/850, ...) reached
collect_grep_matches as '?' — and whether it degraded depended entirely
on which console the server happened to inherit. That surfaced as the
intermittent test_mcp raw-Русский mojibake on the windows CI leg and
means real Windows users in a default console get '?' for all
non-ASCII search_code raw content.

Every generated command now pins [Console]::OutputEncoding to UTF-8, so
the pipe is codepage-independent by construction. The read side needs
no pin: Select-String decodes BOM-less UTF-8 via .NET StreamReader
defaults. A Windows-side builder test asserts all five command variants
carry the prelude.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-18 09:31:59 +02:00
Martin Vogel 773bb037dc fix(test): the stdin gate's #1359 guards survive a registry with no zero-argument tool
#1181 gave list_projects pagination parameters, retiring the last
empty-properties schema — and the two #1359 regression tests leaned on
list_projects as their live zero-argument example, so main went red the
moment the merge train composed (the PR was green on its July base, which
predated these tests).

The gate's schema→decision core is split behind a CBM_CLI_ENABLE_TEST_API
seam, so the zero-argument branch stays pinned directly (empty properties,
absent properties, populated properties) regardless of what the registry
ships; list_projects now asserts its NEW truth (piped args accepted, TTY
still refused); and the schema↔gate parity sweep keeps running over every
tool without the impossible >=1 zero-argument floor. Production behavior
is unchanged — this is the tests catching up with an intended schema
change, plus a seam so they never again depend on a shipped example.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-08-18 07:15:09 +02:00
Alexander Musichen 4656a5f3e1 Merge branch 'main' into fix/pi-extension-tool-schemas 2026-08-18 04:20:37 +02:00
Alex Musichen 109299f262 fix(pi): match ToolDefinition.execute arity on 0.84.2
Pi 0.84.2 calls execute(toolCallId, params, signal, onUpdate, ctx).
The generated (args, ctx) shape bound the call id as the MCP arguments.

Forward params and signal, pin the @earendil-works/pi-coding-agent
contract in the generated header, and lock the 5-arg form in tests.

Signed-off-by: Alex Musichen <alex.musichen@gmail.com>
2026-08-18 04:11:49 +02:00