config_toml_codex_accepts_v0102_managed_windows_crlf asserted that re-upserting
over the v0.10.2 lifecycle-only block left the file byte-identical. That block
is now the previous generation: the upsert accepts it as owned and upgrades it
in place. The test now pins that upgrade (one PreToolUse table, BOM, CRLF and
the surrounding tables kept) and that a second upsert is byte-identical.
Refs #715
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Codex runs its hooks with the same contract as Claude Code: its shell tool is
named Bash, tool_input.command is a string, and PreToolUse hookSpecificOutput
additionalContext reaches the model (openai/codex hooks/src/events/
pre_tool_use.rs, core/src/hook_runtime.rs). Codex only had the SessionStart and
SubagentStart reminders (#330), so its grep and rg calls never got the graph
context Claude Code's searches get (#715).
- config.toml: the managed block adds [[hooks.PreToolUse]] matcher = "Bash"
running the same hook-augment command. The previous block (lifecycle pair
only, as installs up to v0.12 wrote it) stays recognized as owned, so an
upgrade replaces it in place and uninstall removes either generation.
- Inline forms (#1432): Codex's inline rewrite of our PreToolUse is replaced.
A user's own inline hooks.PreToolUse is kept and our block then leaves
PreToolUse out, because TOML cannot hold an inline array and array-of-tables
entries for the same key; install says so instead of failing.
- hooks.json: the Codex-only PreToolUse Bash entry is added and removed next to
the lifecycle pair, touching only the entry whose command is ours.
Codex asks users to re-trust changed hook definitions (/hooks).
Refs #715, #509
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Thank you for the Windows path fix and the careful pre-fix investigation. This uses the existing UTF-8 filesystem helpers so valid repository roots and cached graph databases are not mistaken for missing paths.
Contributor history and DCO are preserved.
The #2729 messaging refresh removed the language count from docs/index.html, docs/llms.txt, pkg/npm/README.md, scripts/package-release.sh and server.json, so Step 0w (tests/test_language_count_contract.sh) failed on main for every PR: each of those files was still registered as a surface but stated no count. They leave SURFACES; README.md and the Chocolatey nuspec still carry the count and stay gated. Section 4 still fails if any dropped file states a count again without being registered (checked by re-adding one).
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Bring CodeQL back to pull requests as jobs of PR CI itself (needed by
ci-ok, no polling), narrowed to first-party code, while push to main,
the weekly run, the dry run and the release keep the full scan.
- codeql.yml: one leg per language (c-cpp, javascript-typescript,
python, actions), scope in .github/codeql/codeql-config.yml. The full
scan traces the whole build, vendored C included. The PR scan
(light: true) analyses each selected language's whole first-party
scope; c-cpp compiles the third-party objects before tracing
(scripts/build.sh --vendored-only) and then traces the first-party
build plus the vendored files the PR changes (--reuse-vendored
--retrace-from, make -W). Diff-informed analysis (codeql-action
v4.38.1, forced on for PR calls) only restricts reported alerts.
The alert check moves into each leg and reads the analysed ref.
Push and PR runs cancel superseded runs per language; dispatched
runs never cancel. Compiler cache off so every compile is traced.
- pr.yml: a codeql job runs the legs of the selected codeql-* lanes;
ci-ok needs it.
- select-lanes.sh: codeql-cpp/-js/-py/-actions lanes, selected by a
change in their scope (any C/C++ incl. vendored; JS/TS, Python and
workflows inside the config's paths minus paths-ignore) and by every
full run (undeterminable diff, workflows, build, CodeQL machinery).
--codeql-scope LANGUAGE prints a list's in-scope paths.
- codeql-retrace-vendored.sh: the vendored C files a PR changes (empty,
never narrowing, when the diff cannot be determined).
- Makefile.cbm: cbm-third-party builds the vendored prod objects only.
- tests: the gate contract pins the new layout; the selector contract
pins that a C change always selects the C scan, an undeterminable
diff selects every language, and a vendored-only change retraces
exactly the changed vendored files. Selector fixtures, ci-ok's job
map, the replay's codeql-action failures (lane codeql-cpp) and the
interface probes follow.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
CodeQL ran on every pull_request push, and PR CI's security / codeql-gate
job polled up to 150 min for that run before ci-ok could pass. With ~30 PRs
pushed at once, the gate jobs held runners while the CodeQL runs they waited
for queued behind them, and PR CI starved for hours.
- codeql.yml: triggers are push to main, a weekly schedule, workflow_call
and workflow_dispatch; pull_request is gone. The open-alert check moves
here, unchanged and still fail-closed, as the `alerts` job (needs
analyze), so every main push and every weekly run fails while any
code-scanning alert is open. Push runs keep cancel-in-progress; called and
scheduled runs get a group of their own. The analysis key is pinned to the
push configuration so dry-run/release uploads do not open configurations
of their own on main.
- _security.yml: codeql-gate removed; callers grant contents: read only.
- dry-run.yml, release.yml: a `codeql` job calls codeql.yml on the
dispatched commit; release verify needs it and requires success.
- select-lanes.sh, require-all-green.sh: the codeql-gate lane is gone from
the selector universe and from ci-ok's job map; no other lane changes.
- tests: the fail-closed contract follows the check to codeql.yml and now
also pins the triggers, the derived callers' grants and release verify;
lane fixtures drop codeql-gate.
- SECURITY.md, MAINTAINERS.md: say where CodeQL runs now.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Maintainer refresh of #2457 (Haskell infix-form definition names,
#2440) onto main a2227157. The branch point 0f52d30c was 1061 commits
behind main.
No textual conflicts. No semantic conflicts:
- resolve_haskell_func_name is unchanged on main since the branch
point, so the new infix-head branch still sits in front of the
first-child fallback it replaces.
- cbm_func_name_node_text gained an early return for C keywords on
main (44efa628). It applies to C-family languages only; the new
Haskell operator branch is independent of it.
- The new test keeps its place beside haskell_function in the
extraction suite.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
eval_condition_partial (from #2253) answers UNKNOWN for a condition whose
alias is not bound yet, but it only looked at c->variable. A function
condition is filed under its first argument's alias, so in
MATCH (a)-[:CALLS]->(b) WHERE coalesce(a.no_such_prop, b.name) = 'x'
the seed row saw a bound a, read coalesce(NULL, <unbound b>) as a
definite mismatch and pruned every row before CALLS expansion: zero rows,
while the same predicate after WITH returned the right ones.
Check every function argument's alias as well, the way this branch's
original fix did (condition_bindings_available), inside main's
three-valued leaf.
RED before: cypher_exec_where_func_arg_on_unbound_target, row_count 0,
expected 1. GREEN after; cypher suite 237 passed.
Co-authored-by: lorenzozanee <wyz0707@proton.me>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Conflict in tests/test_cypher.c: both sides added WHERE NOT regression
tests right after cypher_exec_where_not. Kept both sets, main's four
#2253 tests first, then this branch's four, with their RUN_TEST lines in
the same order. src/cypher/cypher.c merged cleanly to main's version.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The early WHERE pass in execute_single runs on seed rows where only the
first alias is bound, so a NOT on the target node or the relationship
pruned every row (#2250). This branch fixed that by skipping the early
pass until every alias the WHERE reads was bound.
While it waited for review, #2253 (8ab555ee) landed the same fix on
main as a three-valued early pass: an unbound alias is UNKNOWN, NOT
keeps it UNKNOWN, and only a definite FALSE prunes. This branch's
regression tests pass on main's production code unchanged, so restore
src/cypher/cypher.c to the branch's base and keep the tests; the merge
of main that follows takes main's implementation.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
safe_stat reports a symlink as CBM_NOT_FOUND by policy, and
walk_dir_process_entry turned any CBM_NOT_FOUND into out->failed when
count_only was set. One symlink anywhere in a tree therefore aborted the
whole count.
cbm_discover_count_bounded is what auto-index admission calls (the MCP
session path and the daemon path), so any repository containing any
symlink could never auto-index: admission logged
reason=unsafe_or_unavailable_path files=-1 and returned. Nothing is unsafe
and the walk is not slow; the count simply refused at the first symlink it
met. Explicit index_repository was unaffected, because count_only is false
there and the same entry is skipped rather than fatal.
A count now skips symlinks and still aborts on an entry it genuinely cannot
observe, which is what the admission guard wants.
Carried from #1803 by Logan G onto current main. #1822 has since given
safe_stat an is_symlink out-param for the ignored-files report, so this
reuses it instead of adding a second is_link parameter; the production
change is the one condition. The regression test
discover_count_bounded_survives_a_symlink is the contributor's, aligned
with the neighbouring symlink tests: a visible SKIP_PLATFORM on Windows
instead of compiling it out, no wall-clock deadline (0 = none), and
th_cleanup of its temp tree.
Co-authored-by: Logan G <logan@gosolto.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The Windows half of ws_approve_sensitive_matches_across_separator_styles
asserted the sensitive-root hint for <cache>/.ssh/cfg/sub without creating
that directory. Containment canonicalizes the candidate, which on Windows
opens it (CreateFileW, OPEN_EXISTING), so a missing descendant was reported
as outside the allowed root and the assertion failed on the Windows shard.
Create it the way its parent is created.
The grant file is now written through cbm_fopen, the wrapper every
repository or user path goes through, in binary mode.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
main landed the same separator-equivalent comparison in 2e6dc087
(ws_prefix_equal), so the conflict in ws_is_ancestor_or_equal resolves to
main's helper; the #1718 comment above the function is kept. The two
test-file conflicts were both sides adding tests at the same place, and
keep both.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Walking top-level arrow components also bound every exported `const`
declaration into the module root scope in pass 1. A root-scope binding
makes the JSX resolver fail closed and the exact same-module call path
skip the name, so a same-file `<Badge />` rendered from a function
component lost its CALLS edge (JSX carriers have no other resolver), and
calls such as `formatLabel()` fell back from lsp_ts_local to same_module.
Walking the arrow bodies does not need that binding, so drop it, and pin
both forms with a test that renders and calls an exported arrow const
from a function component and from an arrow component.
On excalidraw/excalidraw @ e1b7aacb the change keeps every edge it adds
over main (+270 CALLS, 0 removed; 6 more same-file JSX edges than with
the binding) and the 17 lsp_ts_local -> same_module downgrades are gone.
Also joins one declaration line that clang-format reflows.
Refs #2559
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Brings the branch up to date with main; all six conflicts are keep-both:
- pass_calls.c / pass_parallel.c: drop_plain_call keeps the Go
weak-method and Python import-binding (#2127) clauses from main and
ORs the external-import guard after them, identically on both
resolvers. resolve_single_call takes the signature from main, which
already carries the file imports, so the guard reads that array
instead of a second parameter.
- registry.c, pipeline_internal.h: both sides added declarations and
functions at the same anchor; all are kept.
- tests/test_registry.c, tests/test_pipeline.c: both sides added tests
and RUN_TEST lines at the same anchor; all are kept.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Resolve the exact File an explicit relative import names through
cbm_repo_rel_join into stack buffers instead of the allocating relative
resolver, so the raw allocator count of pass_pkgmap.c does not grow
(memory-core ratchet). The helper refuses a path above the repository
root and honours php_only like the other strategies in
resolve_import_strategies, which main gained after the base of this
branch.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Brings the branch onto current main. The only red in the PR's CI run,
incremental_deleted_spawner_drops_its_process, was a main-side failure at
the old base 56700bdb, fixed on main by 2317c2e7.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
When an agent greps for a symbol that the graph knows, the PreToolUse
augmentation (Grep, Glob and Bash searches) lists the matching symbols. It now
closes with one line that is cbm's own statement, outside the untrusted symbol
block, naming the graph tools for the usual next question:
[codebase-memory] For these symbols, trace_path lists callers and callees,
get_code_snippet returns the exact source, and search_graph finds related
symbols by name.
The line carries no repository-derived value and is stated as facts, as Claude
Code asks of hook context. It is appended at the string's real end into
reserved room: the list loop's offset can run past its cap when a row is
truncated, so appending at that offset could have written past the buffer.
Searches without a hit, errors and non-search events are unchanged.
Refs #509
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Review round 4 of #2677 (user decisions: R4-1..R4-4, R4-6, R4-7, R4-9;
R4-5 measured and left out). Every case ends in an UNKNOWN image, never a
wrong one.
Changes not run exactly (helm_template.c):
- A deep merge into a values map is not exact when a change was made at or
below its path (a set storing a map there): every values map a set or a
merge changes keeps its path, and such a merge leaves the images after it
UNKNOWN.
- and / or at an operand whose truth is not known -- UNKNOWN, or a map a
change made unknown -- leave the images after them UNKNOWN when an
operand after it may change maps.
- In an if / with an UNKNOWN condition skips, the else-if / else-with
conditions after it are looked at too: a change there taints, an
assignment there makes its variable UNKNOWN.
- sortAlpha sorts a []string in place: it counts as a change (a list is
never poisoned, so it taints). No other Sprig list function changes its
argument in place.
- A template, a define body or a scanned template file the grammar does not
parse may hide a change: its images are UNKNOWN.
Work (helm_template.c):
- A capture's keys leave the key set one by one; the set is no longer
cleared by walking its whole capacity after each capture.
- Template files are found by a hash of their path, and an include of a
name that is no define is answered once per render.
Known limit, documented (README): each template file renders on its own,
while Helm renders a chart's files with one shared .Values. Running them in
Helm's order is the planned follow-up; the coarse rule across files cost 83
more correct images on the 20 infra corpora and was left out.
On the 20 infra corpora: Image edges identical to round 4 (0 lost, 0
added), no budget trip.
Tests: R4-1..R4-4 and R4-9 fail on 3401def5 by their assertions (each sub
case too); R4-6 and R4-7 are correctness controls, their cost measured
outside the suite.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Conflict in tests/test_pipeline.c: this branch and #2703 (on main) each
registered two Go tests at the same place in the suite list; both pairs
are kept.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Conflict in tests/test_edge_types_probe.c: both sides added tests after
et_handles_exact (the R1 + R2 DATA_FLOWS tests and the #1692 ASP.NET
overload test); both are kept.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The read-only analysis profile and the fast scout profile serve the graph
subagents (codebase-memory-auditor, codebase-memory-scout). Their server
instructions listed tools only. Like the default profile, they now state as
facts which structural questions the graph answers (where a symbol is defined,
who calls a function, what it calls, and for analysis also call chains, data
flow and change impact), keywords first: analysis 190 -> 648 characters, scout
199 -> 427, well within Claude Code's 4,096-character cut. The profile names,
check_index_coverage and the absence of index_repository are unchanged.
Stacked on #2698.
Refs #509
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Design decision 2026-10-18 (R1 + R2): a handler defined in a test file
pairs only with callers in that same file, and a caller in a test file
pairs only with handlers in that same file. A test's request to the
application, a production caller matched to a test's mock server, and
the private mock servers of two tests pairing across files are not
data flows of the application. #2689 links every caller of a route to
every handler of it, so these pairs were made on main.
try_create_data_flow now refuses a pair whose caller or handler is in a
test file (cbm_is_test_file, the per-language rule extraction uses)
when the two files differ. HTTP_CALLS and HANDLES are unchanged; only
the pairing is scoped. The pass log counts the refused pairs
(skipped_test_scope).
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Design decision 2026-10-18 (R1 + R2): a DATA_FLOWS caller in a test file
pairs only with handlers in that same file, and a handler in a test
file pairs only with callers in that same file.
infrascan: test callers by each language rule (a tests/ directory, a
Java *Test.java, a TS .spec.ts) get no flow to a production handler; a
production caller gets no flow to a test mock server; a test reaches
the mock server of its own file but not one in another test file.
Controls: a production caller keeps its flow, and so does a same-file
test pair.
edge_types_probe: a Spring controller, a mock controller under
src/test/, a production TS client and a TS .test.ts client meet on
the same __route__ANY__ nodes. Exactly one flow (production client to
production controller) on the sequential and on the parallel path.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
tests/test_pipeline.c conflict: main moved the surface codec to v3 (#2703), this branch moved the Python facts payload to v2; the codec rows carry both (surface v3, py v2), plus the stale py v1 row this branch added.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
WHY: the multiple-bindings test pinned an UNKNOWN EFFECT fact for `__all__ = ['escaped\n']`, which only existed because the namespace facts proved plain one-line strings harmless and nothing else. A string literal, escapes included, evaluates nothing, which is this branch's rule, so the fact is gone. ALL_UNKNOWN stays: an escaped name is no literal export name. The assertion still pins the exact stream (13 facts, verified against an index of the same source with the base and this branch).
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>