Merge main into fix/ui-index-start-resolved-root for the release batch

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
Martin Vogel
2026-10-07 18:20:00 +02:00
340 changed files with 712847 additions and 613992 deletions
+59
View File
@@ -0,0 +1,59 @@
{
"test_impact": {
"version": 1,
"tests": {
"presets": {
"c-cbm": true
}
},
"rules": [
{
"id": "harness",
"paths": ["tests/test_framework.h", "tests/test_helpers.*", "tests/test_main.c"],
"action": "run_all"
},
{
"id": "engine",
"paths": [".codebase-memory.json", "src/mcp/test_impact*", "src/store/store_impact.*"],
"action": "run_all"
},
{
"id": "build",
"paths": ["scripts/**", "test-infrastructure/**", "pkg/**"],
"action": "run_all"
},
{
"id": "grammars",
"paths": ["internal/cbm/vendored/**"],
"action": "run_all"
},
{
"id": "fixtures",
"paths": ["tests/fixtures/*/**"],
"action": "referencing_tests",
"key": "path_segment:2"
},
{
"id": "contracts",
"paths": ["tests/**/*.{sh,py,json,txt}"],
"action": "ignore"
},
{
"id": "notices",
"paths": ["NOTICE*", "CHANGELOG*", "THIRD_PARTY*"],
"action": "ignore"
}
],
"unmapped_files": "run_all",
"coverage": {
"compatibility_paths": [
"tests/test_framework.h",
"tests/test_main.c",
"tests/test_helpers.h",
"Makefile.cbm",
"scripts/test-impact/coverage-map.py",
"scripts/test-impact/coverage-merge.py"
]
}
}
}
+7
View File
@@ -15,3 +15,10 @@ scripts/hooks/pre-commit text eol=lf
# Chialisp grammar corpus: one case asserts CRLF comment termination, so this
# file must keep its exact bytes on every platform (no eol conversion). (#919)
tools/tree-sitter-chialisp/test/corpus/comments.txt -text
# llvm-profdata text captures: tests/test_profile_text_parser.py checks their
# SHA-256, so they keep their exact bytes on every platform (no eol conversion).
tests/fixtures/profile_text/*.proftext -text
# Golden profile-map outputs: tests/test_profile_map_v2.py compares the
# producer's LF bytes against them, so they keep their exact bytes too.
tests/fixtures/profile_map_v2/*.tsv -text
+15 -3
View File
@@ -3,14 +3,24 @@
name: Lint & Security
on:
workflow_call: {}
workflow_call:
inputs:
lanes:
description: 'PR lane selection (scripts/ci/select-lanes.sh JSON list); "all" = every job (dry run, release)'
type: string
default: all
permissions:
contents: read
jobs:
lint:
runs-on: ubuntu-latest
if: ${{ inputs.lanes == 'all' || contains(fromJSON(inputs.lanes), 'lint') }}
# Pinned, not ubuntu-latest: the LLVM install below adds an
# apt.llvm.org llvm-toolchain-NOBLE repo, which is 24.04 by name. When
# GitHub moves ubuntu-latest to the next LTS this job breaks on a day
# nobody chose. Pin the runner to the release the repo line already names.
runs-on: ubuntu-24.04
# 15 min proved too tight on a slow runner day and a timed-out lint reads
# as "cancelled", which the release graph must treat as a hard stop; keep
# a bound, but one only a genuine hang can hit (normal runtime ~5 min).
@@ -60,7 +70,9 @@ jobs:
# provability (never suppressed — the NOLINT ban applies here too).
# Vendored-tree diagnostics are path-filtered, mirroring .cppcheck.
lint-mem:
runs-on: ubuntu-latest
if: ${{ inputs.lanes == 'all' || contains(fromJSON(inputs.lanes), 'lint-mem') }}
# Pinned for the same reason as `lint`: llvm-toolchain-noble-21 below.
runs-on: ubuntu-24.04
timeout-minutes: 60
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+8 -1
View File
@@ -4,7 +4,12 @@
name: Security Gate
on:
workflow_call: {}
workflow_call:
inputs:
lanes:
description: 'PR lane selection (scripts/ci/select-lanes.sh JSON list); "all" = every job (dry run, release)'
type: string
default: all
permissions:
contents: read
@@ -23,6 +28,7 @@ jobs:
run: scripts/security-vendored.sh
license-gate:
if: ${{ inputs.lanes == 'all' || contains(fromJSON(inputs.lanes), 'license-gate') }}
runs-on: ubuntu-latest
timeout-minutes: 240
steps:
@@ -39,6 +45,7 @@ jobs:
run: scripts/audit-license-provenance.py
codeql-gate:
if: ${{ inputs.lanes == 'all' || contains(fromJSON(inputs.lanes), 'codeql-gate') }}
runs-on: ubuntu-latest
timeout-minutes: 240
# Without security-events:read this job inherits the workflow's
+104 -17
View File
@@ -16,6 +16,18 @@ on:
description: 'Split each C-suite leg across parallel shard jobs (ubuntu x3, windows x2); off = one job per leg, byte-identical to the pre-shard topology'
type: boolean
default: false
shard_profile:
description: 'Shard counts when shard_suites is on: standard = ubuntu x3, windows x2 (dry run, release); pr = ubuntu x86 x1, ubuntu arm64 x2, windows x1 (fewer jobs re-paying setup)'
type: string
default: standard
lanes:
description: 'PR lane selection (scripts/ci/select-lanes.sh JSON list): only those legs and jobs run; "all" = every one (dry run, release)'
type: string
default: all
test_selection:
description: 'PR test selection (smart CI): the artifact the select-tests job uploaded; the test-unix and test-windows legs run only its tests. Empty = every test (dry run, release, and any PR the selection does not narrow)'
type: string
default: ''
permissions:
contents: read
@@ -30,18 +42,26 @@ jobs:
outputs:
unix: ${{ steps.set.outputs.unix }}
windows: ${{ steps.set.outputs.windows }}
tsan: ${{ steps.set.outputs.tsan }}
has_unix: ${{ steps.set.outputs.has_unix }}
has_windows: ${{ steps.set.outputs.has_windows }}
has_tsan: ${{ steps.set.outputs.has_tsan }}
steps:
- name: Compute matrices
id: set
env:
BROAD: ${{ inputs.broad_platforms }}
SHARDS: ${{ inputs.shard_suites }}
SHARD_PROFILE: ${{ inputs.shard_profile }}
LANES: ${{ inputs.lanes }}
run: |
# Every CORE leg is tagged with the lane (scripts/ci/select-lanes.sh)
# it serves; the tag is what lane selection filters on below.
CORE_UNIX='[
{"os":"ubuntu-latest","cc":"gcc","cxx":"g++"},
{"os":"ubuntu-24.04-arm","cc":"gcc","cxx":"g++"},
{"os":"macos-14","cc":"cc","cxx":"c++"},
{"os":"macos-15-intel","cc":"cc","cxx":"c++"}
{"os":"ubuntu-latest","cc":"gcc","cxx":"g++","lane":"unix-x86"},
{"os":"ubuntu-24.04-arm","cc":"gcc","cxx":"g++","lane":"unix-arm64"},
{"os":"macos-14","cc":"cc","cxx":"c++","lane":"unix-macos14"},
{"os":"macos-15-intel","cc":"cc","cxx":"c++","lane":"unix-macos-intel"}
]'
# Broad matrix legs are REQUIRED gates (no optional/continue-on-error
# escape hatches): the release dry run must be all-green, every platform.
@@ -59,7 +79,7 @@ jobs:
# interception crashed (interception_win: unhandled instruction). With
# the native ARM64 toolchain ASan instruments native ARM64 code, so it
# is a real (non-optional) gate, not a tolerated emulated-flake.
CORE_WIN='[{"os":"windows-latest","msystem":"CLANG64","pkg":"x86_64"}]'
CORE_WIN='[{"os":"windows-latest","msystem":"CLANG64","pkg":"x86_64","lane":"windows"}]'
BROAD_WIN='[{"os":"windows-2025","msystem":"CLANG64","pkg":"x86_64"},{"os":"windows-11-arm","msystem":"CLANGARM64","pkg":"aarch64"}]'
if [ "$BROAD" = "true" ]; then
UNIX=$(jq -cn --argjson a "$CORE_UNIX" --argjson b "$BROAD_UNIX" '$a + $b')
@@ -68,6 +88,30 @@ jobs:
UNIX=$(jq -cn --argjson a "$CORE_UNIX" '$a')
WIN=$(jq -cn --argjson a "$CORE_WIN" '$a')
fi
# The ThreadSanitizer legs (test-tsan), tagged the same way.
TSAN='[
{"os":"ubuntu-latest","cc":"clang","cxx":"clang++","lane":"tsan-x86"},
{"os":"ubuntu-24.04-arm","cc":"clang","cxx":"clang++","lane":"tsan-arm"},
{"os":"macos-14","cc":"cc","cxx":"c++","lane":"tsan-mac"}
]'
# Lane selection. "all" (dry run, release) keeps every leg and drops
# the tags, so those matrices -- and their job names -- stay exactly
# as before lane selection. A PR keeps only its selected legs, and
# the tag stays in the job name, where ci-ok reads it back. An
# untagged leg could never be selected: that is a configuration
# error here, never a leg silently dropped.
if [ "$LANES" = "all" ]; then
UNIX=$(jq -c 'map(del(.lane))' <<< "$UNIX")
WIN=$(jq -c 'map(del(.lane))' <<< "$WIN")
TSAN=$(jq -c 'map(del(.lane))' <<< "$TSAN")
else
for M in "$UNIX" "$WIN" "$TSAN"; do
jq -e 'all(.[]; has("lane"))' <<< "$M" > /dev/null || { echo "::error::untagged test leg in $M"; exit 1; }
done
UNIX=$(jq -c --argjson sel "$LANES" 'map(select(.lane | IN($sel[])))' <<< "$UNIX")
WIN=$(jq -c --argjson sel "$LANES" 'map(select(.lane | IN($sel[])))' <<< "$WIN")
TSAN=$(jq -c --argjson sel "$LANES" 'map(select(.lane | IN($sel[])))' <<< "$TSAN")
fi
# Suite sharding (opt-in via the shard_suites input): each platform's
# C-suite run is split across N runner jobs. CBM_TEST_SHARD="i/N"
# slices deterministically inside run-tests-parallel.sh; each
@@ -78,20 +122,37 @@ jobs:
# shards); Windows gets 2 (every extra shard re-pays ~5 min of
# MSYS2 setup); macOS stays at 1 — mac legs are not the critical
# path and mac runner concurrency ceilings are the tightest.
# The pr profile shards less: a PR runs few legs and every shard
# re-pays its setup, so x86 and Windows run whole and only arm64,
# the slowest leg (~72 min unsharded), splits in two.
case "$SHARD_PROFILE" in
standard) N_X86=3; N_ARM=3; N_WIN=2 ;;
pr) N_X86=1; N_ARM=2; N_WIN=1 ;;
*) echo "::error::unknown shard_profile '$SHARD_PROFILE'"; exit 1 ;;
esac
if [ "$SHARDS" = "true" ]; then
UNIX=$(jq -c '[.[] | . as $e
| (if ($e.os | startswith("ubuntu")) then 3 else 1 end) as $n
UNIX=$(jq -c --argjson x86 "$N_X86" --argjson arm "$N_ARM" '[.[] | . as $e
| (if ($e.os | startswith("ubuntu")) then
(if ($e.os | endswith("-arm")) then $arm else $x86 end)
else 1 end) as $n
| range(1; $n + 1) as $i
| $e + {shard: "\($i)/\($n)"}]' <<< "$UNIX")
WIN=$(jq -c '[.[] | . as $e
| range(1; 3) as $i
| $e + {shard: "\($i)/2"}]' <<< "$WIN")
WIN=$(jq -c --argjson n "$N_WIN" '[.[] | . as $e
| range(1; $n + 1) as $i
| $e + {shard: "\($i)/\($n)"}]' <<< "$WIN")
fi
echo "unix={\"include\":$UNIX}" >> "$GITHUB_OUTPUT"
echo "windows={\"include\":$WIN}" >> "$GITHUB_OUTPUT"
echo "tsan={\"include\":$TSAN}" >> "$GITHUB_OUTPUT"
# GitHub rejects an empty matrix, so a job whose legs were all
# deselected is skipped through these flags instead.
echo "has_unix=$(jq 'length > 0' <<< "$UNIX")" >> "$GITHUB_OUTPUT"
echo "has_windows=$(jq 'length > 0' <<< "$WIN")" >> "$GITHUB_OUTPUT"
echo "has_tsan=$(jq 'length > 0' <<< "$TSAN")" >> "$GITHUB_OUTPUT"
test-unix:
needs: setup-matrix
if: ${{ needs.setup-matrix.outputs.has_unix == 'true' }}
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.setup-matrix.outputs.unix) }}
@@ -160,12 +221,20 @@ jobs:
ccache-test-${{ matrix.os }}-${{ matrix.cc }}-${{ github.ref }}-
ccache-test-${{ matrix.os }}-${{ matrix.cc }}-
- name: Fetch the test selection
if: ${{ inputs.test_selection != '' }}
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: ${{ inputs.test_selection }}
path: ${{ runner.temp }}/test-selection
- name: Test
run: scripts/test.sh CC=${{ matrix.cc }} CXX=${{ matrix.cxx }}
env:
CBM_TEST_SHARD: ${{ matrix.shard }}
CBM_TEST_LEG: ${{ matrix.os }}-${{ matrix.cc }}
CBM_SKIP_PERF: ${{ inputs.skip_perf && '1' || '' }}
CBM_TEST_SELECTION_DIR: ${{ inputs.test_selection != '' && format('{0}/test-selection', runner.temp) || '' }}
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 1500M
@@ -205,6 +274,7 @@ jobs:
# inode/flock behavior and Windows handle/link behavior are each gating,
# without adding Node/Go provisioning to every sanitizer/container leg.
test-package-wrappers:
if: ${{ inputs.lanes == 'all' || contains(fromJSON(inputs.lanes), 'pkg-wrappers') }}
strategy:
fail-fast: false
matrix:
@@ -245,7 +315,10 @@ jobs:
# parallel suite — catches what an older compiler-rt can miss. Same
# canonical scripts/test.sh wave as every other leg.
test-diag:
runs-on: ubuntu-latest
if: ${{ inputs.lanes == 'all' || contains(fromJSON(inputs.lanes), 'diag') }}
# Pinned for the same reason as the lint jobs: the pinned diagnostic
# toolchain below installs from llvm-toolchain-noble-21.
runs-on: ubuntu-24.04
timeout-minutes: 120
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -308,6 +381,7 @@ jobs:
# list both venues now share. Zero use-of-uninitialized-value findings were
# reported across the lane on either architecture.
test-msan:
if: ${{ inputs.lanes == 'all' || contains(fromJSON(inputs.lanes), 'msan') }}
runs-on: ubuntu-latest
timeout-minutes: 180
steps:
@@ -332,6 +406,7 @@ jobs:
# so this leg is the ordinary ASan suite built with that toolchain and run
# with detect_leaks=1. Mirrors the local `make -f Makefile.cbm test-lsan`.
test-lsan-macos:
if: ${{ inputs.lanes == 'all' || contains(fromJSON(inputs.lanes), 'lsan-macos') }}
runs-on: macos-latest
timeout-minutes: 120
steps:
@@ -370,13 +445,11 @@ jobs:
key: ccache-lsan-${{ github.ref }}-${{ github.sha }}
test-tsan:
needs: setup-matrix
if: ${{ needs.setup-matrix.outputs.has_tsan == 'true' }}
strategy:
fail-fast: false
matrix:
include:
- { os: ubuntu-latest, cc: clang, cxx: clang++ }
- { os: ubuntu-24.04-arm, cc: clang, cxx: clang++ }
- { os: macos-14, cc: cc, cxx: c++ }
matrix: ${{ fromJSON(needs.setup-matrix.outputs.tsan) }}
runs-on: ${{ matrix.os }}
timeout-minutes: 120
steps:
@@ -429,6 +502,7 @@ jobs:
test-windows:
needs: setup-matrix
if: ${{ needs.setup-matrix.outputs.has_windows == 'true' }}
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.setup-matrix.outputs.windows) }}
@@ -479,6 +553,13 @@ jobs:
ccache-test-${{ matrix.os }}-${{ matrix.msystem }}-${{ github.ref }}-
ccache-test-${{ matrix.os }}-${{ matrix.msystem }}-
- name: Fetch the test selection
if: ${{ inputs.test_selection != '' }}
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: ${{ inputs.test_selection }}
path: ${{ runner.temp }}/test-selection
- name: Test
shell: msys2 {0}
# AddressSanitizer is unavailable on native ARM64 Windows (LLVM ships no
@@ -506,6 +587,7 @@ jobs:
CBM_TEST_SHARD: ${{ matrix.shard }}
CBM_TEST_LEG: ${{ matrix.os }}-${{ matrix.msystem }}
CBM_SKIP_PERF: ${{ inputs.skip_perf && '1' || '' }}
CBM_TEST_SELECTION_DIR: ${{ inputs.test_selection != '' && format('{0}/test-selection', runner.temp) || '' }}
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 1500M
@@ -562,8 +644,12 @@ jobs:
with:
pattern: shard-manifest-*
path: manifests
# A PR with no test leg selected uploads no manifest; the script
# accepts that only when the selection really holds no leg.
- name: Verify cross-shard union per leg
run: repo/scripts/ci/verify-shard-union.sh manifests
env:
LANES: ${{ inputs.lanes }}
run: repo/scripts/ci/verify-shard-union.sh manifests --lanes "$LANES"
# Windows product-surface regression guards. Distinct from test-windows above
# (the sanitizer C suite): these drive a real product binary + external UI pack
@@ -574,6 +660,7 @@ jobs:
# CLI arguments being mangled by the narrow-argv main() (#423/#20, fixed by the
# wide-argv entrypoint that reads GetCommandLineW).
test-windows-guards:
if: ${{ inputs.lanes == 'all' || contains(fromJSON(inputs.lanes), 'windows-guards') }}
runs-on: windows-latest
timeout-minutes: 60
steps:
+2 -2
View File
@@ -29,7 +29,7 @@ jobs:
run: sudo apt-get update && sudo apt-get install -y zlib1g-dev
- name: Initialize CodeQL
uses: github/codeql-action/init@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
uses: github/codeql-action/init@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
with:
languages: c-cpp
build-mode: manual
@@ -38,6 +38,6 @@ jobs:
run: scripts/build.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
uses: github/codeql-action/analyze@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4.38.1
with:
category: "/language:c-cpp"
+199 -33
View File
@@ -1,7 +1,10 @@
# PR validation: security gates + lint + full test suite. Builds, smoke and
# soak stay maintainer-driven via the dry-run workflow_dispatch. Branch
# PR validation: security gates + lint + the test legs this PR's changes need.
# `changes` runs scripts/ci/select-lanes.sh on the PR's file list; every lane
# job below runs only when selected (unknown paths, workflows, the harness,
# Makefile.cbm and vendored code select everything). Builds, smoke and soak at
# full breadth stay maintainer-driven via the dry-run workflow_dispatch. Branch
# protection requires `dco` + `ci-ok` — a single stable summary context that
# fails unless every PR stage succeeded.
# fails unless every PR stage succeeded and every selected lane ran green.
name: PR
on:
@@ -21,6 +24,7 @@ concurrency:
jobs:
security:
needs: [changes]
uses: ./.github/workflows/_security.yml
# The called job requests security-events:read to count code-scanning
# alerts; a reusable workflow cannot request more than its caller grants,
@@ -30,33 +34,91 @@ jobs:
security-events: read
actions: read
secrets: inherit
with:
lanes: ${{ needs.changes.outputs.lanes }}
lint:
needs: [changes]
if: ${{ contains(fromJSON(needs.changes.outputs.lanes), 'lint') || contains(fromJSON(needs.changes.outputs.lanes), 'lint-mem') }}
uses: ./.github/workflows/_lint.yml
with:
lanes: ${{ needs.changes.outputs.lanes }}
# ── Smart CI, PR CI only: run only the tests this change can reach.
# Active only while the repository variable TEST_IMPACT_MODE is `gate`;
# unset (shadow) every test runs and test-impact-shadow only reports.
# test-impact-select.sh --gate narrows only on a well-formed answer: a
# failure of this job, a run-all answer or a lane that selects nothing
# leaves `test` without a selection, and every test runs. ci-ok does not
# need this job: when it breaks, PRs cost more minutes, never coverage.
# Dry runs and releases never pass a selection.
select-tests:
needs: [changes]
if: ${{ vars.TEST_IMPACT_MODE == 'gate' }}
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
contents: read
actions: read # the merge base's team-artifact bundle (test-impact-artifact.yml)
outputs:
selection: ${{ steps.select.outputs.selection }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# The PR merge commit and its base parent: the change to select for.
fetch-depth: 2
persist-credentials: false
- name: Build this checkout's engine
run: |
sudo apt-get update && sudo apt-get install -y zlib1g-dev
scripts/build.sh CC=gcc CXX=g++
- name: Select the tests
id: select
env:
GH_TOKEN: ${{ github.token }}
run: scripts/ci/test-impact-select.sh --binary build/c/codebase-memory-mcp --out "$RUNNER_TEMP/test-impact" --gate "$RUNNER_TEMP/test-selection"
- name: Upload the selection
if: ${{ steps.select.outputs.selection == 'narrowed' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: test-selection
path: ${{ runner.temp }}/test-selection
if-no-files-found: error
overwrite: true
test:
needs: [lint]
needs: [changes, lint, select-tests]
if: ${{ !cancelled() && needs.lint.result == 'success' }}
uses: ./.github/workflows/_test.yml
with:
lanes: ${{ needs.changes.outputs.lanes }}
# Smart CI: only a successful select-tests run that narrowed passes a
# selection; skipped (shadow), failed, run-all and nothing-selected
# answers all run every test.
test_selection: ${{ needs.select-tests.result == 'success' && needs.select-tests.outputs.selection == 'narrowed' && 'test-selection' || '' }}
# Perf assertions are timing-sensitive on shared runners; they stay in
# dry runs and releases where a flaky red does not block a merge.
skip_perf: true
# Iteration speed: split each C-suite leg across parallel shard jobs
# (ubuntu x3, windows x2; coverage re-proven every run by the
# shard-completeness job). Dry runs and releases shard identically.
# Iteration speed: split C-suite legs across parallel shard jobs
# (coverage re-proven every run by the shard-completeness job). The PR
# profile shards only arm64 (x2); x86 and Windows run whole, since each
# shard re-pays its setup. Dry runs and releases keep x3 / x2.
shard_suites: true
shard_profile: pr
# ── Which files changed? Gate the (heavier) build+smoke on product code so
# docs / CI / test-only PRs stay fast. ──
# ── Which lanes does this PR need? The changed files go through
# scripts/ci/select-lanes.sh (tier, lanes, full, reasons); docs / CI /
# test-only PRs stay fast, anything it cannot place runs everything. ──
changes:
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
product: ${{ steps.f.outputs.product }}
tier: ${{ steps.select.outputs.tier }}
full: ${{ steps.select.outputs.full }}
lanes: ${{ steps.select.outputs.lanes }}
smoke: ${{ steps.select.outputs.smoke }}
steps:
- name: Detect product-code changes
id: f
- name: List the changed files
env:
GH_TOKEN: ${{ github.token }}
PR: ${{ github.event.pull_request.number }}
@@ -64,7 +126,9 @@ jobs:
run: |
# The full .diff endpoint rejects large-but-valid PRs at 20k lines.
# The paginated files endpoint remains filename-only for this gate.
if FILES=$(gh api --paginate "repos/$REPO/pulls/$PR/files?per_page=100" --jq '.[].filename'); then
# A rename lists its old path too, as the fallback's --no-renames
# diff does: moving a file away is a change to where it was.
if FILES=$(gh api --paginate "repos/$REPO/pulls/$PR/files?per_page=100" --jq '.[] | .filename, (.previous_filename // empty)'); then
echo "file list: pulls/files API"
else
# The files endpoint renders the diff server-side too, and answers
@@ -81,31 +145,45 @@ jobs:
"https://github.com/$REPO" "refs/pull/$PR/merge"
FILES=$(git --git-dir="$CHANGES_GIT" diff --name-only --no-renames FETCH_HEAD^1 FETCH_HEAD)
fi
printf '%s\n' "$FILES"
# A here-string, not a pipe: under this shell's pipefail, grep -q
# exits on its first match, printf dies of SIGPIPE once the list
# outgrows the pipe buffer, and the MATCH is then reported as a
# failure -- product=false, smoke skipped, on the largest PRs.
if grep -qE '^(src/|internal/|install\.(sh|ps1)|scripts/build\.sh|scripts/smoke-test\.sh|scripts/smoke-local\.sh|scripts/smoke-fixture-server\.py|scripts/gen-third-party-notices\.sh|scripts/env\.sh|test-infrastructure/vm/(vm-smoke\.sh|windows-user-path-guard\.ps1)|Makefile\.cbm)' <<<"$FILES"; then
echo "product=true" >> "$GITHUB_OUTPUT"
else
echo "product=false" >> "$GITHUB_OUTPUT"
fi
printf '%s\n' "$FILES" > "$RUNNER_TEMP/changed-files.txt"
cat "$RUNNER_TEMP/changed-files.txt"
# The selector is PR code: it runs in its own step, after the
# token-bearing one, from a checkout that keeps no credentials.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# The pulls/files API lists at most 3000 files: the PR's own count lets
# the selector notice a cut list and select everything instead.
- name: Select lanes
id: select
env:
CHANGED_FILES: ${{ github.event.pull_request.changed_files }}
run: |
scripts/ci/select-lanes.sh --format github-output --expect-files "$CHANGED_FILES" "$RUNNER_TEMP/changed-files.txt" > "$RUNNER_TEMP/selection.txt"
cat "$RUNNER_TEMP/selection.txt"
cat "$RUNNER_TEMP/selection.txt" >> "$GITHUB_OUTPUT"
# pr-smoke's legs, each tagged with its lane (kept in the job name,
# where ci-ok reads it back); the job gets the selected ones.
SMOKE='[{"os":"ubuntu-latest","lane":"smoke-ubuntu"},{"os":"macos-14","lane":"smoke-mac"},{"os":"windows-latest","lane":"smoke-win"}]'
LANES=$(sed -n 's/^lanes=//p' "$RUNNER_TEMP/selection.txt")
echo "smoke={\"include\":$(jq -c --argjson sel "$LANES" 'map(select(.lane | IN($sel[])))' <<< "$SMOKE")}" >> "$GITHUB_OUTPUT"
# ── Light smoke: build the PRODUCTION binary and run the core smoke on the
# three RELIABLE NATIVE platforms. Catches built-binary regressions at PR
# time (e.g. the Windows CreateProcess argv-quoting class that previously
# only surfaced in the release dry run). The full broad/emulated smoke stays
# in the dry run. Only product-code PRs pay this; every leg gates.
# in the dry run. Only the selected legs run (ubuntu from the extraction
# tier up, all three for platform/packaging changes); every leg gates.
# The maintained local wrappers add a race-free release fixture so the
# download/checksum/install/update phases run here as well. ──
pr-smoke:
needs: [changes]
if: ${{ !cancelled() && needs.changes.outputs.product == 'true' }}
if: ${{ !cancelled() && needs.changes.result == 'success' && (contains(fromJSON(needs.changes.outputs.lanes), 'smoke-ubuntu') || contains(fromJSON(needs.changes.outputs.lanes), 'smoke-mac') || contains(fromJSON(needs.changes.outputs.lanes), 'smoke-win')) }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-14, windows-latest]
matrix: ${{ fromJSON(needs.changes.outputs.smoke) }}
runs-on: ${{ matrix.os }}
timeout-minutes: 30
steps:
@@ -177,27 +255,115 @@ jobs:
# ── Waste sanitizer, REPORT-ONLY: memory + CPU waste on this repository and
# the per-function scaling lane (Linux). Deliberately NOT in ci-ok's needs:
# it uploads evidence and prints the ratchet, it never blocks a merge. Only
# product-code PRs pay for it. ──
# PRs whose selection holds the memwaste lane (memory core, memwaste
# tooling, full runs) pay for it. ──
memwaste:
needs: [changes]
if: ${{ !cancelled() && needs.changes.outputs.product == 'true' }}
if: ${{ !cancelled() && needs.changes.result == 'success' && contains(fromJSON(needs.changes.outputs.lanes), 'memwaste') }}
uses: ./.github/workflows/_memwaste.yml
with:
mode: pr
# ── Test-impact SHADOW, REPORT-ONLY: predicts from the code graph which C
# suites this change can affect (the latest released binary indexes the
# PR checkout; detect_changes at depth 15 against the base, mapped to
# suites, with the selector's run-all triggers) and uploads the
# prediction, so graph-based selection can be judged against what really
# failed before anything gates on it. continue-on-error and NOT in
# ci-ok's needs: it can never block or pass a merge. ──
test-impact-shadow:
runs-on: ubuntu-latest
timeout-minutes: 45
continue-on-error: true
permissions:
contents: read
actions: read # the merge base's team-artifact bundle (test-impact-artifact.yml)
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# The PR merge commit and its base parent: the diff to predict for.
fetch-depth: 2
persist-credentials: false
- name: Fetch the latest released binary
id: release
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
run: scripts/ci/test-impact-shadow.sh --fetch-release "$RUNNER_TEMP/cbm-bin"
- name: Build the binary from this checkout (download failed)
if: ${{ steps.release.outcome == 'failure' }}
run: |
sudo apt-get update && sudo apt-get install -y zlib1g-dev
scripts/build.sh CC=gcc CXX=g++
mkdir -p "$RUNNER_TEMP/cbm-bin"
cp build/c/codebase-memory-mcp "$RUNNER_TEMP/cbm-bin/"
- name: Predict the impacted suites
run: scripts/ci/test-impact-shadow.sh --binary "$RUNNER_TEMP/cbm-bin/codebase-memory-mcp" --out "$RUNNER_TEMP/test-impact"
# The engine (test-impact select) is this checkout's own code: the
# released binary predates it, so the checkout is built either way.
- name: Build this checkout's engine
if: ${{ steps.release.outcome != 'failure' }}
run: |
sudo apt-get update && sudo apt-get install -y zlib1g-dev
scripts/build.sh CC=gcc CXX=g++
- name: Select with the team artifact (engine shadow)
env:
GH_TOKEN: ${{ github.token }}
run: scripts/ci/test-impact-select.sh --binary build/c/codebase-memory-mcp --out "$RUNNER_TEMP/test-impact"
- name: Upload the prediction
if: ${{ always() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: test-impact-prediction
path: |
${{ runner.temp }}/test-impact/prediction.json
${{ runner.temp }}/test-impact/selection.json
${{ runner.temp }}/test-impact/changed-files.txt
${{ runner.temp }}/test-impact/selection-engine.json
${{ runner.temp }}/test-impact/summary-engine.md
if-no-files-found: warn
# ── Docs-only and non-product PRs (tiers T0a, T0b) run the static contract
# steps of the canonical test leg on their own: they police docs
# surfaces (language counts, version metadata), test-infrastructure/ and
# lint config too. Every test leg runs the same steps. ──
contracts:
needs: [changes]
if: ${{ !cancelled() && needs.changes.result == 'success' && contains(fromJSON(needs.changes.outputs.lanes), 'contracts') }}
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Contract steps only
run: scripts/test.sh --contracts-only
ci-ok:
# The one required context (besides dco) — fails unless every PR stage
# succeeded, so matrix renames can never silently deadlock merges. `skipped`
# is OK: pr-smoke skips on docs/CI/test-only PRs (changes.product == false).
needs: [security, lint, test, changes, pr-smoke]
# succeeded, so matrix renames can never silently deadlock merges, and
# every lane the selector chose ran green: `skipped` is OK only for a lane
# nobody selected (a selected lane that skipped is a silent gate loss).
# memwaste and the test-impact shadow are report-only and not needed here.
needs: [changes, security, lint, test, pr-smoke, contracts]
if: ${{ always() }}
runs-on: ubuntu-latest
timeout-minutes: 5
# actions:read lists this run's jobs, the lane-level truth behind `needs`.
permissions:
contents: read
actions: read
steps:
# Needs a checkout (unlike before): the gate logic lives in the canonical
# scripts/ci entry, not inline YAML (venue-parity contract).
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: All PR stages must have succeeded
- name: List this run's jobs
env:
GH_TOKEN: ${{ github.token }}
run: |
gh api --paginate "repos/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID/jobs?filter=latest&per_page=100" \
--jq '.jobs[] | {name, status, conclusion}' > "$RUNNER_TEMP/jobs.jsonl"
- name: All PR stages and every selected lane must have succeeded
env:
RESULTS: ${{ toJSON(needs) }}
LANES: ${{ needs.changes.outputs.lanes }}
JOBS: ${{ runner.temp }}/jobs.jsonl
run: scripts/ci/require-all-green.sh
+13 -1
View File
@@ -236,8 +236,20 @@ jobs:
- name: Generate SBOM
run: python3 scripts/ci/generate-sbom.py "${{ inputs.version }}"
# actions/attest-sbom is deprecated. What it does now is emit
# "::warning::actions/attest-sbom has been deprecated" and forward to
# actions/attest with NODE_OPTIONS set, passing sbom-path straight
# through. Calling attest directly produces the same attestation without
# the wrapper, and sbom-path is a first-class input there (verified
# against the action definition at this exact SHA, which is tag v4.1.0).
#
# This step deliberately carries no continue-on-error: a release must
# fail if its SBOM cannot be attested. An unattested SBOM is worse than
# an absent one, because the artifact still ships looking complete.
- name: Attest SBOM
uses: actions/attest-sbom@c604332985a26aa8cf1bdc465b92731239ec6b9e # v4.1.0
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
env:
NODE_OPTIONS: '--max-http-header-size=32768'
with:
subject-path: '*.tar.gz'
sbom-path: 'sbom.json'
+1 -1
View File
@@ -27,6 +27,6 @@ jobs:
publish_results: true
- name: Upload SARIF results
uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4
uses: github/codeql-action/upload-sarif@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4
with:
sarif_file: results.sarif
@@ -0,0 +1,85 @@
# Team artifact for test selection (smart CI, report-only until the oracles
# and the shadow comparison are green). Every push to main publishes the
# commit's bundle — its frozen graph, built incrementally from the previous
# bundle, and its per-test coverage map, re-run only for the suites the push
# selects — as the Actions artifact test-impact-<sha>. A weekly run rebuilds
# both from scratch and fails when the incremental graph differs from the full
# one. PR selection fetches the bundle of its merge base
# (scripts/ci/test-impact-bundle.sh) and never narrows on anything else.
name: test-impact-artifact
on:
push:
branches: [main]
schedule:
- cron: "23 3 * * 0" # weekly full refresh, Sunday 03:23 UTC
workflow_dispatch:
inputs:
mode:
description: incremental or full
type: choice
options: [incremental, full]
default: full
permissions:
contents: read
actions: read # fetch the previous bundle
concurrency:
# Bundles chain: each push builds on the previous one, so runs never overlap
# and are never cancelled half-way.
group: test-impact-artifact
cancel-in-progress: false
jobs:
publish:
runs-on: ubuntu-24.04
timeout-minutes: 120
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 2
persist-credentials: false
- name: Install build deps and LLVM 21 (coverage toolchain)
run: |
sudo apt-get update && sudo apt-get install -y zlib1g-dev
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
echo "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-21 main" | sudo tee /etc/apt/sources.list.d/llvm-21.list
sudo apt-get update
sudo apt-get install -y clang-21 llvm-21 libclang-rt-21-dev
- name: Build the production binary and the coverage runner
run: |
scripts/build.sh CC=gcc CXX=g++
make -f Makefile.cbm test-runner-cov COV_LLVM_BIN=/usr/lib/llvm-21/bin -j"$(nproc)"
- name: Publish the bundle
env:
GH_TOKEN: ${{ github.token }}
EVENT: ${{ github.event_name }}
BEFORE: ${{ github.event.before }}
REQUESTED: ${{ inputs.mode }}
# The selection lane excludes @perf and PR CI skips perf, so their
# (slowest, longest under coverage) suites are neither mapped nor
# listed: the runner then neither lists nor runs them.
CBM_SKIP_PERF: "1"
run: |
mode=incremental
if [ "$EVENT" = schedule ] || [ "$REQUESTED" = full ]; then mode=full; fi
before="$(git rev-parse HEAD^1)"
if [ "$EVENT" = push ] && [ -n "$BEFORE" ] && [ "$BEFORE" != 0000000000000000000000000000000000000000 ]; then
before="$BEFORE"
fi
scripts/ci/test-impact-publish.sh --binary build/c/codebase-memory-mcp \
--runner build/cov/test-runner --llvm-bin /usr/lib/llvm-21/bin \
--out "$RUNNER_TEMP/bundle" --mode "$mode" --before "$before"
- name: Upload the bundle
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: test-impact-${{ github.sha }}
path: ${{ runner.temp }}/bundle
include-hidden-files: true # the graph lives in .codebase-memory/
retention-days: 30
if-no-files-found: error
+3
View File
@@ -51,6 +51,9 @@ graph-ui/dist/
# Generated by scripts/embed-frontend.sh into the source tree (the linker needs
# it beside the other UI sources). Removed by scripts/clean.sh; never committed.
src/ui/embedded_assets.c
# Raw coverage profiles. A coverage-instrumented binary (make test-runner-cov,
# cbm-cov) run without LLVM_PROFILE_FILE drops default.profraw in its cwd.
*.profraw
# Generated reports
BENCHMARK_REPORT.md
+167 -13
View File
@@ -71,13 +71,33 @@ ifeq ($(TEST_SEAMS),1)
TEST_SEAM_DEFINE := -DCBM_ENABLE_TEST_SEAMS=1
endif
# Per-test coverage for the test-impact map. TEST_COVERAGE=1 compiles clang
# source coverage into OUR code only -- the one-shot sources, the LSP unity
# object and the preprocessor -- in the test runner and in the product binary,
# and turns on the runner's per-test profile hooks (tests/test_framework.h).
# Vendored objects stay uninstrumented: a vendored change selects tests by rule,
# and every counter compiled in is written out again for every test. Like every
# other flavour it needs its own BUILD_DIR; the test-runner-cov and cbm-cov
# targets pin one.
# -fprofile-continuous: a child process started with `%c` in LLVM_PROFILE_FILE
# keeps its counters mapped onto its profile file, so a child that is killed
# still leaves a complete profile. Linux needs the flag for `%c` to work at
# all; with it macOS behaves the same way (tests/test_main.c, "Children").
TEST_COVERAGE ?= 0
TEST_COVERAGE_FLAGS :=
TEST_COVERAGE_DEFINE :=
ifeq ($(TEST_COVERAGE),1)
TEST_COVERAGE_FLAGS := -fprofile-instr-generate -fcoverage-mapping -fprofile-continuous
TEST_COVERAGE_DEFINE := -DCBM_TEST_COVERAGE=1
endif
# Production flags (CFLAGS_EXTRA allows CI to inject -DCBM_VERSION)
# CBM_BIND_TS_ALLOCATOR=1: bind the tree-sitter runtime to mimalloc (#424). Only
# the prod build uses mimalloc (MI_OVERRIDE=1); the test build is CRT+ASan, where
# binding would create an alloc/free mismatch, so the guard is prod-only.
CFLAGS_PROD = $(CFLAGS_COMMON) -O2 -DCBM_BIND_TS_ALLOCATOR=1 $(TEST_SEAM_DEFINE) \
$(GLOBAL_OVERRIDE_DEFINE) $(MEMWASTE_CFLAGS) $(CFLAGS_EXTRA)
CXXFLAGS_PROD = $(CXXFLAGS_COMMON) -O2 $(MEMWASTE_VENDOR_CFLAGS)
$(GLOBAL_OVERRIDE_DEFINE) $(MEMWASTE_CFLAGS) $(TEST_COVERAGE_FLAGS) $(CFLAGS_EXTRA)
CXXFLAGS_PROD = $(CXXFLAGS_COMMON) -O2 $(MEMWASTE_VENDOR_CFLAGS) $(TEST_COVERAGE_FLAGS)
# Test flags: debug + sanitizers (override SANITIZE= to disable on Windows)
SANITIZE = -fsanitize=address,undefined -fno-omit-frame-pointer
@@ -102,8 +122,10 @@ INCREMENTAL_TEST_DEFINE = -DCBM_INCREMENTAL_TEST_API=1
COVERAGE_MARKER_TEST_DEFINE = -DCBM_COVERAGE_MARKER_TEST_API=1
CFLAGS_TEST = $(CFLAGS_COMMON) $(EDITOR_TEST_DEFINES) $(SANITIZED_DEFINE) \
$(KOTLIN_DEDUP_TEST_DEFINE) $(CALL_REFERENCE_LOOKUP_TEST_DEFINE) \
$(INCREMENTAL_TEST_DEFINE) $(COVERAGE_MARKER_TEST_DEFINE) -g -O1 $(SANITIZE)
CXXFLAGS_TEST = $(CXXFLAGS_COMMON) $(SANITIZED_DEFINE) -g -O1 $(SANITIZE) $(CXX_STDLIB_FLAGS)
$(INCREMENTAL_TEST_DEFINE) $(COVERAGE_MARKER_TEST_DEFINE) $(TEST_COVERAGE_DEFINE) \
-g -O1 $(SANITIZE) $(TEST_COVERAGE_FLAGS)
CXXFLAGS_TEST = $(CXXFLAGS_COMMON) $(SANITIZED_DEFINE) -g -O1 $(SANITIZE) $(TEST_COVERAGE_FLAGS) \
$(CXX_STDLIB_FLAGS)
# TSan (can't combine with ASan)
TSAN_SANITIZE = -fsanitize=thread -fno-omit-frame-pointer
@@ -309,6 +331,7 @@ FOUNDATION_SRCS = \
src/foundation/str_util.c \
src/foundation/workspace.c \
src/foundation/platform.c \
src/foundation/path_syntax_internal.c \
src/foundation/system_info.c \
src/foundation/slab_alloc.c \
src/foundation/yaml.c \
@@ -338,6 +361,7 @@ FOUNDATION_SRCS = \
EXTRACTION_SRCS = \
$(CBM_DIR)/cbm.c \
$(CBM_DIR)/extract_defs.c \
$(CBM_DIR)/callable_sig.c \
$(CBM_DIR)/extract_calls.c \
$(CBM_DIR)/extract_imports.c \
$(CBM_DIR)/extract_usages.c \
@@ -351,12 +375,14 @@ EXTRACTION_SRCS = \
$(CBM_DIR)/extract_dbt.c \
$(CBM_DIR)/sql_values.c \
$(CBM_DIR)/helpers.c \
$(CBM_DIR)/cpp_branch_views.c \
$(CBM_DIR)/result_compact.c \
$(CBM_DIR)/result_spill.c \
$(CBM_DIR)/lang_specs.c \
$(CBM_DIR)/macro_table.c \
$(CBM_DIR)/iris_export_xml.c \
$(CBM_DIR)/service_patterns.c
$(CBM_DIR)/service_patterns.c \
$(CBM_DIR)/spawn_patterns.c
# LSP resolvers (compiled as one unit via lsp_all.c)
LSP_SRCS = $(CBM_DIR)/lsp_all.c
@@ -399,13 +425,15 @@ PREPROCESSOR_SRC = $(CBM_DIR)/preprocessor.cpp
SQLITE_WRITER_SRC = $(CBM_DIR)/sqlite_writer.c
# Store module (new)
STORE_SRCS = src/store/store.c
STORE_SRCS = src/store/store.c src/store/store_impact.c src/store/store_graph_digest.c src/store/store_content_digest.c
# Cypher module (new)
CYPHER_SRCS = src/cypher/cypher.c
# MCP server module (new)
MCP_SRCS = src/mcp/mcp.c src/mcp/index_supervisor.c src/mcp/compact_out.c
MCP_SRCS = src/mcp/mcp.c src/mcp/index_supervisor.c src/mcp/compact_out.c \
src/mcp/test_impact.c src/mcp/test_impact_diff.c src/mcp/test_impact_classify.c src/mcp/test_impact_source.c src/mcp/test_impact_seed.c src/mcp/test_impact_engine.c src/mcp/test_impact_engine_snapshot.c src/mcp/test_impact_engine_reach.c src/mcp/test_impact_engine_artifact.c src/mcp/test_impact_cli.c src/mcp/test_impact_artifact.c src/mcp/test_impact_changes.c src/mcp/test_impact_origins.c src/mcp/test_impact_coverage.c src/mcp/test_impact_config.c src/mcp/test_impact_select.c src/mcp/test_impact_policy.c src/mcp/test_impact_git.c src/mcp/test_impact_inventory.c src/mcp/test_impact_inventory_input.c src/mcp/test_impact_tree.c src/mcp/test_impact_tree_audit.c src/mcp/test_impact_tree_read.c src/mcp/test_impact_tree_batch.c src/mcp/test_impact_tree_digest.c src/mcp/test_impact_tree_names.c src/mcp/test_impact_tree_plan.c src/mcp/test_impact_tree_posix.c src/mcp/test_impact_tree_posix_acl.c src/mcp/test_impact_tree_posix_io.c src/mcp/test_impact_tree_util.c src/mcp/test_impact_tree_windows.c src/mcp/test_impact_tree_windows_acl.c src/mcp/test_impact_tree_windows_io.c \
src/mcp/test_impact_result.c src/mcp/test_impact_result_json.c src/mcp/test_impact_result_project.c src/mcp/test_impact_result_receipt.c src/mcp/test_impact_result_rows.c src/mcp/test_impact_result_util.c src/mcp/test_impact_result_validate.c src/mcp/test_impact_result_wire.c
# Shared MCP daemon coordination and local transport
DAEMON_SRCS = \
@@ -424,7 +452,18 @@ DAEMON_SRCS = \
DISCOVER_SRCS = \
src/discover/language.c \
src/discover/userconfig.c \
src/discover/test_conventions.c \
src/discover/gitignore.c \
src/discover/gitignore_checked.c \
src/discover/inventory_filter.c \
src/discover/inventory.c \
src/discover/inventory_guard.c \
src/discover/inventory_metadata.c \
src/discover/inventory_directories.c \
src/discover/inventory_controls.c \
src/discover/gitignore_checked_parse.c \
src/discover/gitignore_core.c \
src/discover/gitignore_guard.c \
src/discover/discover.c
# Graph buffer module (new)
@@ -436,6 +475,7 @@ PIPELINE_SRCS = \
src/pipeline/lsp_surface.c \
src/pipeline/pipeline_delta.c \
src/pipeline/path_alias.c \
src/pipeline/laravel_routing.c \
src/pipeline/registry.c \
src/pipeline/pipeline.c \
src/pipeline/pipeline_incremental.c \
@@ -443,6 +483,7 @@ PIPELINE_SRCS = \
src/pipeline/pass_parallel.c \
src/pipeline/pass_definitions.c \
src/pipeline/pass_calls.c \
src/pipeline/pass_spawns.c \
src/pipeline/pass_lsp_cross.c \
src/pipeline/pass_usages.c \
src/pipeline/pass_semantic.c \
@@ -615,6 +656,8 @@ PROJECT_HDRS = $(wildcard src/*.h src/*/*.h $(CBM_DIR)/*.h $(CBM_DIR)/lsp/*.h te
TEST_FOUNDATION_SRCS = \
tests/test_main.c \
tests/test_coverage_children.c \
tests/test_coverage_parent_failure.c \
tests/test_arena.c \
tests/test_hash_table.c \
tests/test_dyn_array.c \
@@ -633,6 +676,8 @@ TEST_FOUNDATION_SRCS = \
TEST_EXTRACTION_SRCS = \
tests/test_extraction.c \
tests/test_callable_sig.c \
tests/test_test_conventions.c \
tests/test_extraction_inheritance.c \
tests/test_extraction_imports.c \
tests/test_parse_coverage.c \
@@ -644,6 +689,9 @@ TEST_EXTRACTION_SRCS = \
TEST_STORE_SRCS = \
tests/test_store_nodes.c \
tests/test_store_edges.c \
tests/test_store_impact.c \
tests/test_store_scope.c \
tests/test_store_graph_digest.c \
tests/test_store_search.c \
tests/test_store_arch.c \
tests/test_store_bulk.c \
@@ -656,6 +704,23 @@ TEST_CYPHER_SRCS = \
TEST_MCP_SRCS = \
tests/test_mcp.c \
tests/test_test_impact.c \
tests/test_test_impact_profiles.c \
tests/test_test_impact_changes.c \
tests/test_test_impact_origins.c \
tests/test_test_impact_git.c \
tests/test_test_impact_tree.c \
tests/test_test_impact_inventory.c \
tests/test_test_impact_classify.c \
tests/test_test_impact_source.c \
tests/test_test_impact_seed.c \
tests/test_test_impact_engine.c \
tests/test_test_impact_artifact.c \
tests/test_test_impact_inventory_errors.c \
tests/test_test_impact_inventory_support.c \
tests/test_test_impact_runner_filter.c \
tests/test_test_impact_runner_filter_fixture.c \
tests/test_test_impact_runner_filter_process.c \
tests/test_index_supervisor.c
TEST_DAEMON_SRCS = \
@@ -673,12 +738,23 @@ TEST_DISCOVER_SRCS = \
tests/test_language.c \
tests/test_userconfig.c \
tests/test_gitignore.c \
tests/test_gitignore_checked.c \
tests/if_native_git_path.c \
tests/test_inventory_filter.c \
tests/test_inventory_filter_cases.c \
tests/test_inventory_filter_errors.c \
tests/test_inventory_filter_limits.c \
tests/test_inventory_filter_native.c \
tests/test_inventory_filter_native_fixture.c \
tests/test_inventory_filter_native_platform.c \
tests/test_inventory_filter_structure_extra.c \
tests/test_inventory_filter_support.c \
tests/test_git_context.c \
tests/test_discover.c
TEST_GRAPH_BUFFER_SRCS = tests/test_graph_buffer.c
TEST_PIPELINE_SRCS = tests/test_registry.c tests/test_pipeline.c tests/test_importance.c tests/test_cross_repo.c tests/test_fqn.c tests/test_route_canon.c tests/test_path_alias.c tests/test_configlink.c tests/test_doclinks.c tests/test_infrascan.c tests/test_worker_pool.c tests/test_parallel.c tests/test_index_resilience.c tests/test_index_format.c tests/test_call_reference_contract.c tests/repro/repro_call_scope_usages.c tests/repro/repro_call_argument_usages.c tests/repro/repro_reference_precision.c tests/repro/repro_lexical_binding_precision.c tests/repro/repro_call_argument_matrix_a.c tests/repro/repro_call_argument_matrix_b.c tests/repro/repro_call_node_behaviors.c tests/repro/repro_language_registry.c tests/repro/repro_call_node_manifest.c tests/repro/repro_lsp_ordered_signatures.c tests/repro/repro_lsp_ordered_local.c tests/repro/repro_ts_overload_return_chains.c tests/repro/repro_harness_cleanup.c tests/repro/repro_runner_filter.c
TEST_PIPELINE_SRCS = tests/test_registry.c tests/test_pipeline.c tests/test_pipeline_conventions.c tests/test_pipeline_frozen.c tests/test_importance.c tests/test_cross_repo.c tests/test_fqn.c tests/test_route_canon.c tests/test_path_alias.c tests/test_configlink.c tests/test_doclinks.c tests/test_infrascan.c tests/test_worker_pool.c tests/test_parallel.c tests/test_index_resilience.c tests/test_index_format.c tests/test_call_reference_contract.c tests/repro/repro_call_scope_usages.c tests/repro/repro_call_argument_usages.c tests/repro/repro_reference_precision.c tests/repro/repro_lexical_binding_precision.c tests/repro/repro_call_argument_matrix_a.c tests/repro/repro_call_argument_matrix_b.c tests/repro/repro_call_node_behaviors.c tests/repro/repro_language_registry.c tests/repro/repro_call_node_manifest.c tests/repro/repro_lsp_ordered_signatures.c tests/repro/repro_lsp_ordered_local.c tests/repro/repro_ts_overload_return_chains.c tests/repro/repro_harness_cleanup.c tests/repro/repro_runner_filter.c
TEST_WATCHER_SRCS = tests/test_watcher.c
@@ -717,7 +793,7 @@ TEST_JAVA_LSP_SRCS = tests/test_java_lsp.c tests/test_java_lsp_coverage.c
TEST_KOTLIN_LSP_SRCS = tests/test_kotlin_lsp.c
TEST_RUST_LSP_SRCS = tests/test_rust_lsp.c
TEST_INTEGRATION_SRCS = tests/test_integration.c tests/test_incremental.c tests/test_lang_contract.c tests/test_edge_imports.c tests/test_edge_structural.c tests/test_lsp_resolution_probe.c tests/test_node_creation_probe.c tests/test_edge_types_probe.c tests/test_convergence_probe.c tests/test_matrix_known_classes.c tests/test_matrix_new_constructs.c tests/test_grammar_probe_a.c tests/test_grammar_probe_b.c tests/test_grammar_probe_c.c tests/test_grammar_probe_d.c tests/test_grammar_probe_e.c tests/test_grammar_probe_f.c tests/test_grammar_probe_g.c
TEST_INTEGRATION_SRCS = tests/test_integration.c tests/test_incremental.c tests/test_lang_contract.c tests/test_edge_imports.c tests/test_edge_structural.c tests/test_lsp_resolution_probe.c tests/test_node_creation_probe.c tests/test_edge_types_probe.c tests/test_convergence_probe.c tests/test_matrix_known_classes.c tests/test_matrix_new_constructs.c tests/test_grammar_probe_a.c tests/test_grammar_probe_b.c tests/test_grammar_probe_c.c tests/test_grammar_probe_d.c tests/test_grammar_probe_e.c tests/test_grammar_probe_f.c tests/test_grammar_probe_g.c tests/test_conditional_variants.c tests/test_spawns.c
TEST_TRACES_SRCS = tests/test_traces.c
@@ -857,7 +933,7 @@ PP_OBJ_TSAN = $(BUILD_DIR)/tsan_preprocessor.o
# ── Targets ──────────────────────────────────────────────────────
.PHONY: test test-par test-repro test-foundation test-tsan test-daemon-smoke cbm cbm-with-ui frontend embed clean-c lint lint-tidy lint-tidy-diff lint-cppcheck lint-format security fuzz
.PHONY: test-coverage-flush test test-par test-repro test-foundation test-tsan test-daemon-smoke test-runner-cov cbm-cov cbm cbm-with-ui frontend embed clean-c lint lint-tidy lint-tidy-diff lint-cppcheck lint-format security fuzz
$(BUILD_DIR):
mkdir -p $(BUILD_DIR)
@@ -896,7 +972,7 @@ $(BUILD_DIR)/ts_runtime.o: $(TS_RUNTIME_DEPS) | $(BUILD_DIR)
$(CC) $(GRAMMAR_CFLAGS_TEST) -c -o $@ $<
$(BUILD_DIR)/lsp_all.o: $(LSP_UNITY_DEPS) | $(BUILD_DIR)
$(CC) $(GRAMMAR_CFLAGS_TEST) $(SANITIZE) -c -o $@ $<
$(CC) $(GRAMMAR_CFLAGS_TEST) $(SANITIZE) $(TEST_COVERAGE_FLAGS) -c -o $@ $<
$(BUILD_DIR)/preprocessor.o: $(CBM_DIR)/preprocessor.cpp | $(BUILD_DIR)
$(CXX) $(CXXFLAGS_TEST) -w -I$(CBM_DIR)/vendored -c -o $@ $<
@@ -1147,7 +1223,7 @@ $(BUILD_DIR)/prod_ts_runtime.o: $(TS_RUNTIME_DEPS) $(BUILD_DIR)/.build-config |
$(CC) $(GRAMMAR_CFLAGS) -c -o $@ $<
$(BUILD_DIR)/prod_lsp_all.o: $(LSP_UNITY_DEPS) $(BUILD_DIR)/.build-config | $(BUILD_DIR)
$(CC) $(GRAMMAR_CFLAGS) -c -o $@ $<
$(CC) $(GRAMMAR_CFLAGS) $(TEST_COVERAGE_FLAGS) -c -o $@ $<
$(BUILD_DIR)/prod_preprocessor.o: $(CBM_DIR)/preprocessor.cpp $(BUILD_DIR)/.build-config | $(BUILD_DIR)
$(CXX) $(CXXFLAGS_PROD) -w -I$(CBM_DIR)/vendored -c -o $@ $<
@@ -1185,7 +1261,7 @@ MAIN_SRC = src/main.c
# for trap debugging -- silently re-run the previously instrumented binary,
# which during this bisect produced a "crashes without sanitizers too" result
# that was pure artifact and sent the investigation down the wrong path.
BUILD_CONFIG_SIG := TEST_SEAMS=$(TEST_SEAMS)|CFLAGS_EXTRA=$(CFLAGS_EXTRA)|SANITIZE=$(SANITIZE)|MEMWASTE=$(MEMWASTE)|MEMWASTE_ACCESS=$(MEMWASTE_ACCESS)|MEMWASTE_PROFILE=$(MEMWASTE_PROFILE)
BUILD_CONFIG_SIG := TEST_SEAMS=$(TEST_SEAMS)|CFLAGS_EXTRA=$(CFLAGS_EXTRA)|SANITIZE=$(SANITIZE)|MEMWASTE=$(MEMWASTE)|MEMWASTE_ACCESS=$(MEMWASTE_ACCESS)|MEMWASTE_PROFILE=$(MEMWASTE_PROFILE)|TEST_COVERAGE=$(TEST_COVERAGE)
.PHONY: build-config-check
build-config-check:
$(BUILD_DIR)/.build-config: build-config-check | $(BUILD_DIR)
@@ -1381,6 +1457,84 @@ test-lsan:
cd $(CURDIR) && ASAN_OPTIONS="detect_leaks=1:halt_on_error=1" \
./build/lsan/test-runner $(LSAN_SUITES)
# ── Per-test coverage build (test-impact map) ───────────────────
# The instrumented runner and product binary (TEST_COVERAGE=1 above), in their
# own build directory. Homebrew LLVM rather than Apple clang: the raw profile
# format is versioned with the compiler, so llvm-profdata and llvm-cov have to
# come from the toolchain that built the binary.
# LLVM_PROFILE_FILE=/dev/null CBM_TEST_COVERAGE_DIR=<dir> \
# build/cov/test-runner [suite...]
# leaves one profile set per test (tests/test_main.c). The runner names every
# profile itself, but the profile runtime creates its default file before main
# runs: started without LLVM_PROFILE_FILE it leaves an empty default.profraw in
# the working directory.
# No C suite runs the product binary -- they re-exec the runner itself. The
# shell process regressions do, and they find the instrumented one the way they
# find any other: CBM_TEST_BINARY=build/cov/codebase-memory-mcp, with
# LLVM_PROFILE_FILE set by whoever collects the profiles.
COV_LLVM_BIN ?= /opt/homebrew/opt/llvm/bin
COV_BUILD_DIR ?= build/cov
COV_MAKE = $(MAKE) -f Makefile.cbm CC=$(COV_LLVM_BIN)/clang CXX=$(COV_LLVM_BIN)/clang++ \
BUILD_DIR=$(COV_BUILD_DIR) SANITIZE= TEST_COVERAGE=1
test-runner-cov:
$(COV_MAKE) $(COV_BUILD_DIR)/test-runner
cbm-cov:
$(COV_MAKE) $(COV_BUILD_DIR)/codebase-memory-mcp TEST_SEAMS=1
# Genuine LLVM write failures must fail the parent coverage run.
test-coverage-flush: test-runner-cov
@evidence_dir=$$(mktemp -d "$(COV_BUILD_DIR)/coverage-flush.XXXXXX") && \
printf 'Coverage flush evidence: %s/proof\n' "$$evidence_dir" && \
python3 tests/test_coverage_flush.py \
--runner "$(COV_BUILD_DIR)/test-runner" \
--llvm-profdata "$(COV_LLVM_BIN)/llvm-profdata" \
--output-dir "$$evidence_dir/proof"
.PHONY: test-coverage-children
# Genuine child profile/marker and setup-reducer controls; evidence is retained.
test-coverage-children: test-runner-cov
@evidence_dir=$$(mktemp -d "$(COV_BUILD_DIR)/coverage-children.XXXXXX") && \
printf 'Coverage child evidence: %s/proof\n' "$$evidence_dir" && \
python3 tests/test_coverage_children.py \
--runner "$(COV_BUILD_DIR)/test-runner" \
--llvm-profdata "$(COV_LLVM_BIN)/llvm-profdata" \
--reducer scripts/test-impact/coverage-map.py \
--output-dir "$$evidence_dir/proof"
.PHONY: test-coverage-parent-failure
# Positive default plus genuine owned EMFILE; all evidence is retained.
test-coverage-parent-failure: test-runner-cov
@evidence_dir=$$(mktemp -d "$(COV_BUILD_DIR)/coverage-parent-failure.XXXXXX") && \
printf 'Coverage parent failure evidence: %s/proof\n' "$$evidence_dir" && \
python3 tests/test_coverage_parent_failure.py \
--runner "$(COV_BUILD_DIR)/test-runner" \
--llvm-profdata "$(COV_LLVM_BIN)/llvm-profdata" \
--reducer scripts/test-impact/coverage-map.py \
--output-dir "$$evidence_dir/proof"
.PHONY: test-coverage-terminal
# A readable stale terminal profile cannot hide a failed parent write.
test-coverage-terminal: test-runner-cov
@evidence_dir=$$(mktemp -d "$(COV_BUILD_DIR)/coverage-terminal.XXXXXX") && \
printf 'Coverage terminal evidence: %s/proof\n' "$$evidence_dir" && \
python3 tests/test_coverage_terminal.py \
--runner "$(COV_BUILD_DIR)/test-runner" \
--llvm-profdata "$(COV_LLVM_BIN)/llvm-profdata" \
--helper tests/test_coverage_children.py \
--reducer scripts/test-impact/coverage-map.py \
--output-dir "$$evidence_dir/proof"
.PHONY: test-coverage-live-v2
# Native full-identity transport remains explicitly unadmitted; evidence is retained.
test-coverage-live-v2: test-runner-cov
@evidence_dir=$$(mktemp -d "$(COV_BUILD_DIR)/coverage-live-v2.XXXXXX") && \
printf 'Coverage live-v2 evidence: %s/proof\n' "$$evidence_dir" && \
PYTHONDONTWRITEBYTECODE=1 python3 tests/test_coverage_live_v2.py \
--collector scripts/test-impact/coverage-map.py \
--runner "$(COV_BUILD_DIR)/test-runner" \
--llvm-profdata "$(COV_LLVM_BIN)/llvm-profdata" \
--output "$$evidence_dir/proof" $(if $(COV_LIVE_V2_CASE),--case "$(COV_LIVE_V2_CASE)")
# ── Coverage-guided fuzzing (libFuzzer) ─────────────────────────
# One binary, three targets (tests/fuzz/cbm_fuzz.c): the per-file extractor,
# the Cypher parser, the TOML/YAML/JSON config editors. ASan+UBSan as in the
+13 -2
View File
@@ -155,6 +155,8 @@ Watcher registration is controlled separately by `auto_watch` (default `true`).
To turn the watcher off entirely, set `config set watcher_enabled false` (default `true`): the background poll thread never starts and no project is registered, while `auto_index` and manual `index_repository` keep working. Unlike `auto_watch` — which is consulted per session — `watcher_enabled` is read once when the background daemon starts, so run `codebase-memory-mcp daemon stop` after changing it; reconnecting your MCP client alone will not restart the daemon. See [docs/CONFIGURATION.md](docs/CONFIGURATION.md#2-cli-managed-runtime-settings).
The watcher follows **git** projects only by default: a project indexed from a directory that is not a git repository is not watched and stays at its last index until you run `index_repository` again. To have such roots polled too, set `config set watch_non_git true` (default `false`, read when the daemon starts): they are then checked on the same cadence with a file-tree scan that uses the indexer's own ignore rules.
### Keeping Up to Date
**Updates run from the install script on every platform, not from inside the running binary.** `codebase-memory-mcp update` validates your flags and then prints the exact command to run:
@@ -183,7 +185,14 @@ Installed through **npm or pip**? Update with your package manager on every plat
codebase-memory-mcp uninstall
```
Removes owned agent config entries, skills, hooks, instructions, and the installed binary. Existing graph indexes are listed and deleted only after confirmation.
Removes owned agent config entries, skills, hooks, instructions, and the installed binary. Existing graph indexes are listed and **kept by default**. `-y`/`--yes`, `-n`/`--no`, and noninteractive input keep them unless `--delete-indexes` is explicitly given. An interactive terminal without that flag is asked separately; the default answer is to keep them.
```bash
codebase-memory-mcp uninstall -y --delete-indexes # also delete every project index
codebase-memory-mcp uninstall --dry-run --delete-indexes # preview; change nothing
```
`--delete-indexes` is explicit consent and takes precedence over `--no`; `--dry-run` always preserves the files. When indexes are kept, uninstall prints their cache directory (`${CBM_CACHE_DIR:-~/.cache/codebase-memory-mcp}`) and how to remove them.
The install script placed beside the binary is **reported, not deleted** — uninstall prints its path and the `rm` command for it. It is left alone on purpose: it may be your own copy, a symlink into a checkout, or managed by a package manager, and an uninstaller should not delete a file it cannot prove it owns.
@@ -220,6 +229,7 @@ The install script placed beside the binary is **reported, not deleted** — uni
- `USAGE` — an identifier is used, but a unique callable target is not proven (including ambiguous or complex expressions)
- `IMPORTS`, `DEFINES`, `IMPLEMENTS`, `INHERITS`
- `HTTP_CALLS`, `ASYNC_CALLS` (cross-service)
- `SPAWNS` — the caller starts another program (`subprocess.run`, `exec.Command`, `posix_spawn`, `Process.Start`, ...); the target is a `Process` node named after the program, or `<dynamic>` when the program is not a literal
- `EMITS`, `LISTENS_ON` (channels)
- `DATA_FLOWS` with arg-to-param mapping + field access chains
- `SIMILAR_TO` (MinHash + LSH near-clone detection, Jaccard scored)
@@ -731,7 +741,7 @@ mode. `index_status` keeps describing the published graph and its freshness.
### Edge Types
`CONTAINS_PACKAGE`, `CONTAINS_FOLDER`, `CONTAINS_FILE`, `DEFINES`, `DEFINES_METHOD`, `IMPORTS`, `CALLS`, `CALL_REFERENCE`, `HTTP_CALLS`, `ASYNC_CALLS`, `IMPLEMENTS`, `HANDLES`, `USAGE`, `CONFIGURES`, `REFERENCES_FILE`, `WRITES`, `MEMBER_OF`, `TESTS`, `USES_TYPE`, `FILE_CHANGES_WITH`
`CONTAINS_PACKAGE`, `CONTAINS_FOLDER`, `CONTAINS_FILE`, `DEFINES`, `DEFINES_METHOD`, `IMPORTS`, `CALLS`, `CALL_REFERENCE`, `HTTP_CALLS`, `ASYNC_CALLS`, `SPAWNS`, `IMPLEMENTS`, `HANDLES`, `USAGE`, `CONFIGURES`, `REFERENCES_FILE`, `WRITES`, `MEMBER_OF`, `TESTS`, `USES_TYPE`, `FILE_CHANGES_WITH`
### Qualified Names
@@ -763,6 +773,7 @@ codebase-memory-mcp config set auto_index true # auto-index on session
codebase-memory-mcp config set auto_index_limit 50000 # max files for auto-index
codebase-memory-mcp config set auto_watch false # don't register background git watcher (default: true)
codebase-memory-mcp config set watcher_enabled false # stop the watcher thread entirely (default: true)
codebase-memory-mcp config set watch_non_git true # also poll non-git project roots (default: false)
codebase-memory-mcp config set index_max_files 250000 # optional per-index source-file limit
codebase-memory-mcp config set index_max_source_mb 16384 # optional per-index source-size limit
codebase-memory-mcp config reset auto_index # reset to default
+1
View File
@@ -89,6 +89,7 @@ Current keys:
| `auto_index_limit` | `50000` | Maximum file count allowed for automatic indexing of a new project. |
| `auto_watch` | `true` | Register the session's project with the background git watcher on connect. Set `false` to keep a session from registering its project (the watcher still runs for other projects). |
| `watcher_enabled` | `true` | Master switch for the background watcher subsystem. Set `false` to stop the watcher from starting at all — no poll thread and no project registration. Reindex manually with `index_repository` when disabled. |
| `watch_non_git` | `false` | Also poll project roots that are **not git repositories**. By default the watcher only follows git projects, so a project indexed from a plain directory is never refreshed after its first index — reindex it manually with `index_repository`. Set `true` to poll such roots on the same adaptive cadence with a file-tree scan: the indexer's own discovery walk (same skip lists, `.gitignore` and `.cbmignore` rules) hashed over each file's path, size and mtime. Any change reindexes once; paths the indexer skips (including cbm's own `.codebase-memory/` output) never trigger. The first poll after the daemon starts reindexes each such project once, since nothing records which tree state the index holds. The scan walks the whole tree every poll, so it costs more than git polling on very large trees. Read once when the daemon starts, like `watcher_enabled`. |
| `index_max_files` | `off` | Optional maximum number of accepted source files in one discovery run. |
| `index_max_source_mb` | `off` | Optional maximum accepted source size in MiB in one discovery run. |
File diff suppressed because it is too large Load Diff
+83
View File
@@ -0,0 +1,83 @@
/*
* callable_sig.h — signature-qualified callable identity (#2061).
*
* A callable's graph identity is
*
* callable_qn := base_qn [<tparams>] (params) [cvref]
*
* so overloads that share a base QN (Java/C#/C++/Kotlin/Swift/Scala/ObjC)
* become distinct nodes instead of merging into one node that collects every
* overload's CALLS edges. The `name` column stays the bare name.
*
* Every language selects a mode (cbm_callable_identity). A language whose
* mode is CBM_CALLABLE_ID_NONE keeps its historical QN byte for byte; the
* suffix is applied only where a mode is enabled, and the SAME builder
* (cbm_callable_sig) serves the definition QN and the call-scope QN so the two
* can never disagree.
*
* Suffix contract (what cbm_qn_callable_base_len inverts):
* - it never contains '.', "::" or "->" — the separators every QN leaf
* splitter uses — so a splitter that first strips the suffix finds the
* same leaf as before;
* - it is whitespace-normalized: one space only between two identifier
* characters ("unsigned long", "const T&"), none elsewhere;
* - type spelling: comments, annotations and attributes are dropped;
* qualified type paths keep their last segment (java.util.List -> List,
* std::vector -> vector); "->" in function types is spelled "=>"; a
* variadic/rest marker is "[]" for Java (the JVM array it is) and "~"
* everywhere else; C++ top-level cv-qualifiers are dropped and array
* parameters decay to pointers, as the language itself does;
* - it is at most CBM_CALLABLE_SIG_MAX bytes. A longer signature keeps the
* leading whole parameter entries that fit and ends in "#<16 hex>)", the
* FNV-1a 64 of the full uncapped suffix, so identity is preserved.
*/
#ifndef CBM_CALLABLE_SIG_H
#define CBM_CALLABLE_SIG_H
#include "cbm.h"
#include "arena.h"
#include "tree_sitter/api.h"
#include <stddef.h>
typedef enum {
CBM_CALLABLE_ID_NONE = 0, /* historical base QN, no suffix */
CBM_CALLABLE_ID_TYPED, /* (int,String) */
CBM_CALLABLE_ID_LABELED_TYPED, /* (_:Data,to:URL) Swift */
CBM_CALLABLE_ID_LABELED, /* (_:style:) ObjC selector */
CBM_CALLABLE_ID_ARITY, /* (2) dynamic langs */
} CBMCallableIdentity;
enum { CBM_CALLABLE_SIG_MAX = 200 };
/* The identity mode a language uses for callable QNs. Every language is
* CBM_CALLABLE_ID_NONE until its enable change lands (with its index-format
* bump). Kept as a side table, like cbm_string_dispatch_suffixes, rather than
* a CBMLangSpec field: the ~160 positional lang_specs rows would all have to
* spell the new member under -Wmissing-field-initializers. */
CBMCallableIdentity cbm_callable_identity(CBMLanguage lang);
/* The identity suffix for the callable at `func_node` in the language's own
* mode, or NULL when that mode is NONE (or the node carries no parameter
* information the mode can use). Arena-owned. */
const char *cbm_callable_sig(CBMArena *a, TSNode func_node, const char *source, CBMLanguage lang);
/* Same, with an explicit mode — the builder the enable changes and the golden
* tables exercise before a language is switched on. */
const char *cbm_callable_sig_mode(CBMArena *a, TSNode func_node, const char *source,
CBMLanguage lang, CBMCallableIdentity mode);
/* Length of the base QN, i.e. `qn` without its callable identity suffix; equal
* to strlen(qn) when there is none. Purely syntactic (the inverse of the
* builder's contract above); a bare C++ `operator()` leaf is recognised as a
* name, not a suffix. NULL -> 0. */
size_t cbm_qn_callable_base_len(const char *qn);
/* The inverse anchored to the callable's bare `name` (the node's name
* column): the suffix is stripped only when the base QN is `name` or ends in
* "." / "::" + `name`. Other QN text can end in ')' as well — a Java field
* whose name is its declarator `X = f()`, a make `$(VAR)` target — and only
* the anchor makes the inverse exact on it. Use this form wherever the name
* is known; NULL name -> the unanchored form. */
size_t cbm_qn_callable_base_len_named(const char *qn, const char *name);
#endif /* CBM_CALLABLE_SIG_H */
+145 -6
View File
@@ -22,6 +22,7 @@
#include "lsp/rust_lsp.h"
#include "preprocessor.h"
#include "sql_values.h" // #1735: literal INSERT rows kept out of the SQL parse
#include "cpp_branch_views.h"
#include "foundation/compat.h"
#include "foundation/compat_fs.h" // cbm_fopen — crash-supervisor per-file marker write
#include "foundation/hash_table.h" // CBMHashTable — crash-supervisor quarantine set
@@ -243,6 +244,11 @@ void cbm_typeassign_push(CBMTypeAssignArray *arr, CBMArena *a, CBMTypeAssign ta)
arr->items[arr->count++] = ta;
}
void cbm_fieldtype_push(CBMFieldTypeArray *arr, CBMArena *a, CBMFieldType ft) {
GROW_ARRAY(arr, a);
arr->items[arr->count++] = ft;
}
void cbm_stringref_push(CBMStringRefArray *arr, CBMArena *a, CBMStringRef sr) {
GROW_ARRAY(arr, a);
arr->items[arr->count++] = sr;
@@ -2141,13 +2147,110 @@ static bool cbm_sql_values_exclusion_on(const char *rel_path) {
return true;
}
/* Drop the entries a branch view re-extracted from code outside the
* conditionals: a definition with a QN the file already has at the same line,
* an import with the same name and path. */
static void cpp_view_drop_seen(CBMFileResult *r, int defs_before, int imports_before) {
int w = defs_before;
for (int i = defs_before; i < r->defs.count; i++) {
const CBMDefinition *d = &r->defs.items[i];
bool seen = false;
for (int j = 0; j < w && !seen; j++) {
const CBMDefinition *e = &r->defs.items[j];
seen = e->start_line == d->start_line && e->qualified_name && d->qualified_name &&
strcmp(e->qualified_name, d->qualified_name) == 0;
}
if (!seen) {
r->defs.items[w++] = *d;
}
}
r->defs.count = w;
w = imports_before;
for (int i = imports_before; i < r->imports.count; i++) {
const CBMImport *m = &r->imports.items[i];
bool seen = false;
for (int j = 0; j < w && !seen; j++) {
const CBMImport *e = &r->imports.items[j];
seen = e->local_name && m->local_name && e->module_path && m->module_path &&
strcmp(e->local_name, m->local_name) == 0 &&
strcmp(e->module_path, m->module_path) == 0;
}
if (!seen) {
r->imports.items[w++] = *m;
}
}
r->imports.count = w;
}
/* Haskell: the scanner parses the first branch of an #if group and swallows
* the rest up to #endif as one token, so what is written once per
* configuration (a definition, an import, a call in the other branch) was
* seen in its first variant only. Every further branch is parsed as its own
* view (cpp_branch_views.h: same bytes and lines, the other branches and the
* conditional lines blanked) and extracted into the same result, under the
* raw parse's budget. A definition at another line under a QN the file
* already has is that definition's variant; the graph keeps one identity with
* both spans. Calls and usages outside the conditionals come back as
* duplicates, which the pipeline dedups by caller and callee. The raw parse
* alone decides the file's parse status. */
static void extract_cpp_branch_views(const CBMExtractCtx *raw, const TSLanguage *ts_lang,
int64_t timeout_micros) {
int views = cbm_cpp_branch_view_count(raw->arena, raw->source, raw->source_len);
for (int v = 1; v <= views; v++) {
char *view = cbm_cpp_branch_view(raw->arena, raw->source, raw->source_len, v);
TSParser *parser = view ? get_thread_parser(ts_lang, raw->language) : NULL;
if (!parser) {
return;
}
ts_parser_reset(parser);
CBMStringInput input = {view, (uint32_t)raw->source_len};
TSInput ts_input = {&input, cbm_string_read, TSInputEncodingUTF8, NULL};
TSParseOptions opts = {0};
CBMParseBudget budget = {0}; // cppcheck-suppress unreadVariable
if (timeout_micros > 0) {
uint64_t budget_ns = (uint64_t)timeout_micros * USEC_TO_NSEC;
budget.cpu_deadline_ns = cbm_thread_cpu_time_ns() + budget_ns;
budget.wall_ceiling_ns = now_ns() + budget_ns * CBM_PARSE_WALL_CEILING_FACTOR;
opts.payload = &budget;
opts.progress_callback = cbm_timeout_cb;
}
TSTree *tree = ts_parser_parse_with_options(parser, NULL, ts_input, opts);
if (!tree) {
return;
}
CBMExtractCtx vctx = {
.arena = raw->arena,
.scratch = raw->scratch,
.result = raw->result,
.source = view,
.source_len = raw->source_len,
.language = raw->language,
.project = raw->project,
.rel_path = raw->rel_path,
.module_qn = raw->module_qn,
.root = ts_tree_root_node(tree),
.macro_table = raw->macro_table,
.return_type_table = raw->return_type_table,
.walk_budget_nodes = cbm_walk_max_nodes(),
};
int defs_before = raw->result->defs.count;
int imports_before = raw->result->imports.count;
cbm_extract_definitions(&vctx);
cbm_extract_imports(&vctx);
cbm_extract_unified(&vctx);
cpp_view_drop_seen(raw->result, defs_before, imports_before);
ts_tree_delete(tree);
}
}
static CBMFileResult *extract_file_ex_body(const char *source, int source_len, CBMLanguage language,
const char *project, const char *rel_path,
int64_t timeout_micros, const char **extra_defines,
const char **include_paths,
const CBMMacroTable *macro_table,
const CBMReturnTypeTable *return_type_table,
CBMArena *scratch) {
CBMArena *scratch,
const cbm_test_declarations_t *test_declarations) {
// Allocate result on heap (arena inside for all string data)
CBMFileResult *result = cbm_result_alloc();
if (!result) {
@@ -2156,6 +2259,9 @@ static CBMFileResult *extract_file_ex_body(const char *source, int source_len, C
cbm_work_arena_take(&result->arena);
CBMArena *a = &result->arena;
if (!cbm_test_declarations_validate(result, test_declarations)) {
return result;
}
/* Crash-quarantine hard guard (Stage 3c): a file the supervisor pinned as a
* crasher must NEVER be parsed again. Return a clean empty result BEFORE the
@@ -2334,13 +2440,19 @@ static CBMFileResult *extract_file_ex_body(const char *source, int source_len, C
.macro_table = macro_table,
.return_type_table = return_type_table,
.walk_budget_nodes = cbm_walk_max_nodes(),
.test_declarations = test_declarations,
.test_declarations_raw_source = true,
};
// Run extractors: defs + imports use separate walks (unique recursion patterns),
// then a single unified cursor walk handles the remaining 7 extractors.
cbm_extract_definitions(&ctx);
cbm_test_declarations_finish(&ctx);
cbm_extract_imports(&ctx);
cbm_extract_unified(&ctx);
if (cbm_lang_needs_cpp_branch_views(language)) {
extract_cpp_branch_views(&ctx, ts_lang, timeout_micros);
}
result->tree_nodes = ts_node_descendant_count(root);
result->walk_nodes_visited = ctx.walk_nodes_visited;
if (ctx.walk_budget_exhausted) {
@@ -2373,8 +2485,20 @@ static CBMFileResult *extract_file_ex_body(const char *source, int source_len, C
cbm_run_go_lsp(a, result, source, source_len, root);
}
if (language == CBM_LANG_C || language == CBM_LANG_CPP || language == CBM_LANG_CUDA) {
cbm_run_c_lsp(a, result, source, source_len, root, language != CBM_LANG_C,
CBM_SOURCE_ORIGIN_RAW);
if (!cbm_run_c_lsp_with_test_owners(a, result, source, source_len, root,
language != CBM_LANG_C, CBM_SOURCE_ORIGIN_RAW,
result)) {
result->has_error = true;
if (result->test_declarations_status == CBM_TEST_EXTRACT_OK) {
result->test_declarations_status = CBM_TEST_EXTRACT_UNSUPPORTED_FORM;
result->test_declaration_index = -1;
result->test_declaration_line = 0;
result->error_msg =
cbm_arena_strdup(a, "configured owner source identity mismatch");
if (!result->error_msg)
result->test_declarations_status = CBM_TEST_EXTRACT_OOM;
}
}
}
if (language == CBM_LANG_PHP) {
cbm_run_php_lsp(a, result, source, source_len, root);
@@ -2485,6 +2609,10 @@ static CBMFileResult *extract_file_ex_body(const char *source, int source_len, C
.rel_path = rel_path,
.module_qn = result->module_qn,
.root = pp_root,
/* Preset switches still apply, but expanded bytes
* cannot acquire a raw configured definition identity. */
.test_declarations = test_declarations,
.test_declarations_raw_source = false,
};
// Re-run unified extraction on expanded source.
// This adds macro-expanded calls; duplicates with original calls are
@@ -3046,6 +3174,16 @@ CBMFileResult *cbm_extract_file_ex(const char *source, int source_len, CBMLangua
int64_t timeout_micros, const char **extra_defines,
const char **include_paths, const CBMMacroTable *macro_table,
const CBMReturnTypeTable *return_type_table) {
return cbm_extract_file_ex_with_tests(source, source_len, language, project, rel_path,
timeout_micros, extra_defines, include_paths, macro_table,
return_type_table, NULL);
}
CBMFileResult *cbm_extract_file_ex_with_tests(
const char *source, int source_len, CBMLanguage language, const char *project,
const char *rel_path, int64_t timeout_micros, const char **extra_defines,
const char **include_paths, const CBMMacroTable *macro_table,
const CBMReturnTypeTable *return_type_table, const cbm_test_declarations_t *test_declarations) {
CBMArena scratch;
if (tl_scratch_live && tl_scratch_slot) {
scratch = *tl_scratch_slot;
@@ -3054,9 +3192,9 @@ CBMFileResult *cbm_extract_file_ex(const char *source, int source_len, CBMLangua
} else {
cbm_arena_init_lazy(&scratch, CBM_EXTRACT_SCRATCH_BLOCK);
}
CBMFileResult *result = extract_file_ex_body(source, source_len, language, project, rel_path,
timeout_micros, extra_defines, include_paths,
macro_table, return_type_table, &scratch);
CBMFileResult *result = extract_file_ex_body(
source, source_len, language, project, rel_path, timeout_micros, extra_defines,
include_paths, macro_table, return_type_table, &scratch, test_declarations);
/* !tl_scratch_live: a nested extraction (an embedded language inside this
* file) may already have parked its own; never overwrite it. */
/* Kept up to CBM_EXTRACT_SCRATCH_KEEP_BYTES, grown blocks included: the
@@ -3077,6 +3215,7 @@ CBMFileResult *cbm_extract_file_ex(const char *source, int source_len, CBMLangua
} else {
cbm_arena_destroy(&scratch);
}
cbm_test_declarations_degrade(result);
return result;
}
+132
View File
@@ -17,6 +17,27 @@ TSNode cbm_ts_child_by_field_name(TSNode node, const char *name, uint32_t name_l
* (TS_FIELD("body") expands to "body", 4), which must expand before the call. */
#define ts_node_child_by_field_name(...) cbm_ts_child_by_field_name(__VA_ARGS__)
/* Immutable, explicitly supplied test-declaration snapshot. */
typedef struct cbm_test_declarations cbm_test_declarations_t;
typedef enum {
CBM_TEST_ROLE_NONE = 0,
CBM_TEST_ROLE_CASE,
CBM_TEST_ROLE_SUITE
} CBMTestDefinitionRole;
typedef enum {
CBM_TEST_EXTRACT_OK = 0,
CBM_TEST_EXTRACT_UNSUPPORTED_LANGUAGE,
CBM_TEST_EXTRACT_UNSUPPORTED_PRESET,
CBM_TEST_EXTRACT_UNSUPPORTED_NAME,
CBM_TEST_EXTRACT_MISSING_ARGUMENT,
CBM_TEST_EXTRACT_UNSUPPORTED_ARGUMENT,
CBM_TEST_EXTRACT_AMBIGUOUS,
CBM_TEST_EXTRACT_UNSUPPORTED_FORM,
CBM_TEST_EXTRACT_OOM
} CBMTestExtractStatus;
// Language enum mirrors lang.Language in Go.
// Order must match lang_specs.c tables.
typedef enum {
@@ -246,6 +267,15 @@ typedef struct {
* HTTP_CALLS edge to base + path. Tail fields: zero-init stays valid. */
const char *http_client;
const char *http_base_url;
/* Callable identity (#2061): offset of the signature suffix inside
* qualified_name (base QN = the first qn_sig_off bytes); 0 = no suffix.
* Always 0 until a language enables its callable_identity mode. */
uint32_t qn_sig_off;
CBMTestDefinitionRole test_role; /* NONE preserves legacy helper/test-file semantics */
/* Configured raw definitions only; zero for all legacy rows. Exact spans
* are bound to the owning result's source identity before cross-file use. */
uint32_t test_name_start_byte, test_name_end_byte;
uint32_t test_body_start_byte, test_body_end_byte;
} CBMDefinition;
/* Argument captured from a call expression */
@@ -304,10 +334,20 @@ typedef struct {
// guard's unique-name exemption. Default false.
} CBMCall;
// What an import statement names, when the syntax says so. Only PHP's
// `use function` / `use const` set a non-default kind today: they name a
// namespace MEMBER, so they must never be mapped to a class file (#1186).
typedef enum {
CBM_IMPORT_KIND_DEFAULT = 0, // module / class / namespace (language default)
CBM_IMPORT_KIND_FUNCTION, // PHP `use function A\b`
CBM_IMPORT_KIND_CONST, // PHP `use const A\B`
} CBMImportKind;
typedef struct {
const char *local_name; // local alias or name
const char *module_path; // resolved module path / QN
bool is_default; // ES default import (`import X from "Y"`), JS/TS only (#1916)
CBMImportKind kind; // CBM_IMPORT_KIND_DEFAULT unless the syntax names a member kind
} CBMImport;
typedef enum {
@@ -405,6 +445,17 @@ typedef struct {
CBMChannelDirection direction;
} CBMChannel;
/* Python: one annotated instance field of a class -- `x: T` or `x: T = v` in
* the class body, `self.x: T = v` in __init__, or `self.x = p` where `p` is an
* annotated __init__ parameter. Not a graph node: it only carries the field's
* declared type to the cross-file LSP, so `obj.x.m()` on a class imported
* from another file can be typed (#1277). */
typedef struct {
const char *class_qn; // QN of the owning class
const char *field_name; // attribute name
const char *type_text; // raw annotation text, resolved later per file
} CBMFieldType;
// Rust: impl Trait for Struct
typedef struct {
const char *trait_name; // trait name (raw text)
@@ -418,6 +469,10 @@ typedef struct {
typedef enum {
CBM_RESOLVED_INVOCATION = 0,
CBM_RESOLVED_CALL_REFERENCE,
/* C-family member access `a.b` / `a->b`: callee_qn is the Field `b` of the
* type of `a`. One row per (caller, field), not per occurrence, and no site
* span: the join is by enclosing function and member name. */
CBM_RESOLVED_FIELD_REFERENCE,
} CBMResolvedKind;
// LSP-resolved invocation/reference: high-confidence type-aware resolution.
@@ -431,6 +486,10 @@ typedef struct {
uint32_t site_start_byte; // exact source occurrence; end > start when present
uint32_t site_end_byte; // exclusive byte offset in the source file
CBMSourceOrigin source_origin; // raw source or C-family preprocessed buffer
/* Callable identity (#2061): the resolved overload's signature suffix,
* appended to callee_qn when the edge is written; NULL = none (always,
* until a language enables its callable_identity mode). */
const char *callee_sig;
} CBMResolvedCall;
typedef struct {
@@ -518,6 +577,12 @@ typedef struct {
int cap;
} CBMChannelArray;
typedef struct {
CBMFieldType *items;
int count;
int cap;
} CBMFieldTypeArray;
// Full extraction result for one file.
typedef struct CBMFileResult {
CBMArena arena; // owns local memory; composites may also retain child arenas below
@@ -536,6 +601,7 @@ typedef struct CBMFileResult {
CBMStringRefArray string_refs; // URL/config string literals from AST
CBMInfraBindingArray infra_bindings; // topic→URL pairs from IaC configs
CBMChannelArray channels; // Socket.IO / EventEmitter pub/sub participation
CBMFieldTypeArray field_types; // Python: annotated instance fields (#1277)
const char *module_qn; // module qualified name
const char *namespace_name; // declared namespace/package (Java/Kotlin/C#/PHP), NULL if none
@@ -606,6 +672,29 @@ typedef struct CBMFileResult {
// by cbm_free_result(); ordinary single-file results leave these zeroed.
struct CBMFileResult **owned_results;
int owned_result_count;
/* The file's own doc, set on its File node: the Go package comment or
* the Rust inner docs (//!). NULL for other languages and undocumented
* files. */
const char *module_doc;
/* A consumed configured definition could not be mapped safely. Only an
* allocation failure (OOM) still aborts publication: any other status is
* degraded per file by cbm_test_declarations_degrade. error_msg is
* arena-owned as usual. */
CBMTestExtractStatus test_declarations_status;
int test_declaration_index; /* meaningful on failure; -1 = preset */
uint32_t test_declaration_line; /* 0 = snapshot preflight */
bool has_test_definition_owners;
/* has_error was raised by a configured-definition issue alone. */
bool test_error_only;
/* The file's configured test forms could not be mapped: it carries no
* configured test roles; the status names why (no pointer: compaction
* and spill relocate only the strings they know). */
bool test_declarations_degraded;
CBMTestExtractStatus test_declarations_degraded_status;
int test_owner_source_len;
CBMLanguage test_owner_language;
char test_owner_source_sha256[65]; /* exact raw source; identity, not authentication */
} CBMFileResult;
// --- Enclosing function cache ---
@@ -692,8 +781,40 @@ typedef struct {
/* How many nodes the unified walk actually visited (whether or not it ran
* out of budget) — the measurement the budget has to be expressed in. */
uint32_t walk_nodes_visited;
/* Doc-comment lookup state (extract_defs.c), NULL until first used and
* allocated in `scratch`: the memo of parents' child arrays and the Perl
* POD section index. */
void *doc_memo;
void *doc_pod_index;
const cbm_test_declarations_t *test_declarations; /* borrowed for this call only */
bool test_declarations_raw_source;
struct CBMTestDefinitionMatch *test_definition_matches; /* traversal scratch */
int test_definition_match_count;
int test_definition_match_cap;
} CBMExtractCtx;
/* Internal configured-definition seams. No declarations pointer enters a result. */
/* A configured test form the extractor cannot map degrades the FILE, not
* the index (user decision 2026-10-04): its configured test roles, owner
* spans and owner flag are dropped, the status reads OK again, an error the
* issue alone raised is cleared, and test_declarations_degraded keeps the
* reason for a per-file diagnostic. OK and OOM results are left unchanged:
* an allocation failure still fails. Idempotent. */
void cbm_test_declarations_degrade(CBMFileResult *result);
/* The fixed description of a configured-definition status (static). */
const char *cbm_test_extract_status_message(CBMTestExtractStatus status);
bool cbm_test_declarations_validate(CBMFileResult *result,
const cbm_test_declarations_t *declarations);
void cbm_test_declarations_finish(CBMExtractCtx *ctx);
/* Raw AST candidate only: no declaration/configuration decisions. A C split
* invocation uses its adjacent compound body as scope; ordinary definitions
* use their function node. Callers still validate raw bytes before ownership. */
bool cbm_test_definition_candidate(TSNode scope, CBMLanguage language, TSNode *name, TSNode *body);
const char *cbm_test_definition_qn(CBMExtractCtx *ctx, TSNode function);
bool cbm_test_definition_owners_match(const CBMFileResult *owners, const char *source,
int source_len, bool cpp_mode, const char *module_qn);
const char *cbm_test_definition_owner_qn(const CBMFileResult *owners, TSNode function);
// --- Public API ---
// Bind third-party allocators (tree-sitter, sqlite3) to mimalloc as
@@ -796,6 +917,16 @@ CBMFileResult *cbm_extract_file_ex(
const CBMReturnTypeTable *return_type_table // OS return types, or NULL
);
/* Additive immutable-snapshot entry point. NULL preserves legacy behavior.
* Snapshot must outlive this call; returned data is independently result-owned.
* A non-OK result status is an explicit configured-mapping failure, not a
* successful empty inventory. error_msg may be NULL on diagnostic OOM. */
CBMFileResult *cbm_extract_file_ex_with_tests(
const char *source, int source_len, CBMLanguage language, const char *project,
const char *rel_path, int64_t timeout_micros, const char **extra_defines,
const char **include_paths, const CBMMacroTable *macro_table,
const CBMReturnTypeTable *return_type_table, const cbm_test_declarations_t *test_declarations);
// Free all memory associated with a result.
void cbm_free_result(CBMFileResult *result);
@@ -892,6 +1023,7 @@ void cbm_rw_push(CBMRWArray *arr, CBMArena *a, CBMReadWrite rw);
void cbm_typerefs_push(CBMTypeRefArray *arr, CBMArena *a, CBMTypeRef tr);
void cbm_envaccess_push(CBMEnvAccessArray *arr, CBMArena *a, CBMEnvAccess ea);
void cbm_typeassign_push(CBMTypeAssignArray *arr, CBMArena *a, CBMTypeAssign ta);
void cbm_fieldtype_push(CBMFieldTypeArray *arr, CBMArena *a, CBMFieldType ft);
void cbm_stringref_push(CBMStringRefArray *arr, CBMArena *a, CBMStringRef sr);
void cbm_infrabinding_push(CBMInfraBindingArray *arr, CBMArena *a, CBMInfraBinding ib);
void cbm_impltrait_push(CBMImplTraitArray *arr, CBMArena *a, CBMImplTrait it);
+183
View File
@@ -0,0 +1,183 @@
/*
* cpp_branch_views.c — one parse view per #if/#else branch (see the header).
*/
#include "cpp_branch_views.h"
#include <string.h>
enum { CPP_LINE_CODE, CPP_LINE_START, CPP_LINE_ELSE, CPP_LINE_END };
enum { CPP_NEST_MAX = 64 };
bool cbm_lang_needs_cpp_branch_views(CBMLanguage lang) {
return lang == CBM_LANG_HASKELL;
}
static bool cpp_word_char(char c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
}
// Classify the line at src[pos..end): a conditional directive is a '#' in
// column 0, optional blanks, then the directive word — the same lines the
// Haskell scanner treats as conditionals.
static int cpp_line_kind(const char *src, int pos, int end) {
if (pos >= end || src[pos] != '#') {
return CPP_LINE_CODE;
}
int p = pos + 1;
while (p < end && (src[p] == ' ' || src[p] == '\t')) {
p++;
}
int w = p;
while (w < end && cpp_word_char(src[w])) {
w++;
}
static const struct {
const char *word;
int kind;
} words[] = {
{"if", CPP_LINE_START}, {"ifdef", CPP_LINE_START}, {"ifndef", CPP_LINE_START},
{"else", CPP_LINE_ELSE}, {"elif", CPP_LINE_ELSE}, {"elifdef", CPP_LINE_ELSE},
{"elifndef", CPP_LINE_ELSE}, {"endif", CPP_LINE_END},
};
size_t n = (size_t)(w - p);
for (size_t i = 0; i < sizeof(words) / sizeof(words[0]); i++) {
if (strlen(words[i].word) == n && memcmp(src + p, words[i].word, n) == 0) {
return words[i].kind;
}
}
return CPP_LINE_CODE;
}
static int cpp_line_end(const char *src, int pos, int len) {
const char *nl = memchr(src + pos, '\n', (size_t)(len - pos));
return nl ? (int)(nl - src) : len;
}
typedef struct {
int group; // groups are numbered in order of their #if line
int branch; // branch the current line is in
bool outer; // the enclosing context is kept
} cpp_frame_t;
typedef struct {
cpp_frame_t frames[CPP_NEST_MAX];
int depth;
int overflow; // groups nested past CPP_NEST_MAX are not tracked
int next;
} cpp_nest_t;
// Track one line. Returns the innermost tracked group when the line is one of
// its #else/#elif lines, else NULL.
static cpp_frame_t *cpp_nest_step(cpp_nest_t *n, int kind, bool active) {
switch (kind) {
case CPP_LINE_START:
if (n->depth < CPP_NEST_MAX && n->overflow == 0) {
n->frames[n->depth++] = (cpp_frame_t){n->next, 0, active};
} else {
n->overflow++;
}
n->next++;
return NULL;
case CPP_LINE_ELSE:
if (n->overflow > 0 || n->depth == 0) {
return NULL;
}
n->frames[n->depth - 1].branch++;
return &n->frames[n->depth - 1];
case CPP_LINE_END:
if (n->overflow > 0) {
n->overflow--;
} else if (n->depth > 0) {
n->depth--;
}
return NULL;
default:
return NULL;
}
}
// Branch count per group. Returns the group count, or -1 on allocation failure.
static int cpp_group_branches(CBMArena *a, const char *src, int len, int **out) {
int groups = 0;
for (int pos = 0; pos < len;) {
int end = cpp_line_end(src, pos, len);
groups += cpp_line_kind(src, pos, end) == CPP_LINE_START;
pos = end + 1;
}
*out = NULL;
if (groups == 0) {
return 0;
}
int *branches = cbm_arena_alloc(a, sizeof(int) * (size_t)groups);
if (!branches) {
return -1;
}
for (int g = 0; g < groups; g++) {
branches[g] = 1;
}
cpp_nest_t nest = {.depth = 0};
for (int pos = 0; pos < len;) {
int end = cpp_line_end(src, pos, len);
cpp_frame_t *frame = cpp_nest_step(&nest, cpp_line_kind(src, pos, end), true);
if (frame) {
branches[frame->group] = frame->branch + 1;
}
pos = end + 1;
}
*out = branches;
return groups;
}
int cbm_cpp_branch_view_count(CBMArena *a, const char *src, int len) {
int *branches = NULL;
int groups = cpp_group_branches(a, src, len, &branches);
int most = 1;
for (int g = 0; g < groups; g++) {
if (branches[g] > most) {
most = branches[g];
}
}
int views = most - 1;
return views > CBM_CPP_BRANCH_VIEWS_MAX ? CBM_CPP_BRANCH_VIEWS_MAX : views;
}
// The current line belongs to the view: every enclosing group sits in the
// branch the view keeps.
static bool cpp_view_keeps(const cpp_nest_t *n, const int *branches, int view) {
if (n->depth == 0) {
return true;
}
const cpp_frame_t *top = &n->frames[n->depth - 1];
int nb = branches[top->group];
int kept = 0;
if (nb >= 2) {
kept = view < nb - 1 ? view : nb - 1;
}
return top->outer && top->branch == kept;
}
char *cbm_cpp_branch_view(CBMArena *a, const char *src, int len, int view) {
int *branches = NULL;
int groups = cpp_group_branches(a, src, len, &branches);
char *buf = groups >= 0 ? cbm_arena_alloc(a, (size_t)len + 1) : NULL;
if (!buf) {
return NULL;
}
memcpy(buf, src, (size_t)len);
buf[len] = '\0';
cpp_nest_t nest = {.depth = 0};
for (int pos = 0; pos < len;) {
int end = cpp_line_end(src, pos, len);
bool kept = cpp_view_keeps(&nest, branches, view);
int kind = cpp_line_kind(src, pos, end);
cpp_nest_step(&nest, kind, kept);
if (kind != CPP_LINE_CODE || !kept) {
for (int i = pos; i < end; i++) {
buf[i] = buf[i] == '\r' ? '\r' : ' ';
}
}
pos = end + 1;
}
return buf;
}
+34
View File
@@ -0,0 +1,34 @@
#ifndef CBM_CPP_BRANCH_VIEWS_H
#define CBM_CPP_BRANCH_VIEWS_H
#include "cbm.h"
/*
* Branch views of a source whose grammar parses only the first branch of an
* #if/#else group (the Haskell scanner swallows everything from #else to
* #endif as one token, so a definition written once per configuration is seen
* in its first variant only).
*
* A view keeps the source byte for byte and line for line: the branch it
* selects in every group stays, the other branches and all conditional lines
* (#if/#ifdef/#ifndef/#else/#elif.../#endif) become spaces, newlines stay. So
* every span extracted from a view is a raw-source span.
*
* View v (1-based) selects branch min(v, branches - 1) of a group with at
* least two branches, and the only branch of a group without one.
*/
#define CBM_CPP_BRANCH_VIEWS_MAX 3
// True when `lang`'s grammar parses only the first branch of a conditional.
bool cbm_lang_needs_cpp_branch_views(CBMLanguage lang);
// Number of views the source needs (0 when no group has a second branch),
// at most CBM_CPP_BRANCH_VIEWS_MAX.
int cbm_cpp_branch_view_count(CBMArena *a, const char *src, int len);
// View `view` (1..count) as an arena buffer of exactly `len` bytes plus a NUL,
// or NULL on allocation failure.
char *cbm_cpp_branch_view(CBMArena *a, const char *src, int len, int view);
#endif
+2880 -118
View File
File diff suppressed because it is too large Load Diff
+61 -14
View File
@@ -13,6 +13,8 @@
enum {
USE_PREFIX_LEN = 4, /* strlen("use ") */
MIN_WOLFRAM_CHILDREN = 2,
/* Nested `#if` branches followed while collecting C includes. */
C_INCLUDE_NEST_LIMIT = 64,
SECOND_IDX = 1,
};
@@ -45,7 +47,7 @@ static void parse_lisp_imports(CBMExtractCtx *ctx);
static void parse_starlark_imports(CBMExtractCtx *ctx);
static void parse_tcl_imports(CBMExtractCtx *ctx);
static void parse_teal_imports(CBMExtractCtx *ctx);
static void parse_zsh_imports(CBMExtractCtx *ctx);
static void parse_shell_source_imports(CBMExtractCtx *ctx);
static void parse_css_imports(CBMExtractCtx *ctx);
static void parse_html_imports(CBMExtractCtx *ctx);
static void parse_cmake_imports(CBMExtractCtx *ctx);
@@ -663,10 +665,22 @@ static char *strip_angle_brackets(CBMArena *a, char *path) {
return path;
}
static void parse_c_imports(CBMExtractCtx *ctx) {
/* Node kinds that only group file-level items: the branches of `#if` /
* `#ifdef` (an include guard is one) and the body of `extern "C" { ... }`. */
static bool is_c_include_container(const char *kind) {
return strcmp(kind, "preproc_if") == 0 || strcmp(kind, "preproc_ifdef") == 0 ||
strcmp(kind, "preproc_else") == 0 || strcmp(kind, "preproc_elif") == 0 ||
strcmp(kind, "preproc_elifdef") == 0 || strcmp(kind, "linkage_specification") == 0 ||
strcmp(kind, "declaration_list") == 0;
}
/* Collect the includes among `parent`'s children, descending into
* preprocessor branches: every header keeps its includes inside its include
* guard, so the top level alone gave a guarded header no imports at all. */
static void parse_c_imports_in(CBMExtractCtx *ctx, TSNode parent, int depth) {
CBMArena *a = ctx->arena;
TSTreeCursor cursor = ts_tree_cursor_new(ctx->root);
TSTreeCursor cursor = ts_tree_cursor_new(parent);
if (!ts_tree_cursor_goto_first_child(&cursor)) {
ts_tree_cursor_delete(&cursor);
return;
@@ -675,6 +689,9 @@ static void parse_c_imports(CBMExtractCtx *ctx) {
TSNode node = ts_tree_cursor_current_node(&cursor);
const char *kind = ts_node_type(node);
if (strcmp(kind, "preproc_include") != 0 && strcmp(kind, "preproc_import") != 0) {
if (depth < C_INCLUDE_NEST_LIMIT && is_c_include_container(kind)) {
parse_c_imports_in(ctx, node, depth + SKIP_ONE);
}
continue;
}
@@ -695,6 +712,10 @@ static void parse_c_imports(CBMExtractCtx *ctx) {
ts_tree_cursor_delete(&cursor);
}
static void parse_c_imports(CBMExtractCtx *ctx) {
parse_c_imports_in(ctx, ctx->root, 0);
}
// --- Ruby imports ---
// call nodes: require("X"), require_relative("X")
@@ -1205,7 +1226,29 @@ static void parse_wolfram_imports(CBMExtractCtx *ctx) {
// require/include forms remain `expression_statement`s and are still handled by
// the text fallback. Take the first qualified_name/name descendant of each
// clause as the module path.
static void emit_php_use_clause(CBMExtractCtx *ctx, TSNode clause, const char *group_prefix) {
// `use function` / `use const` carry the keyword as an anonymous direct child
// of the declaration (flat form) or of the clause (grouped form
// `use A\{function f, const C, K}`). DEFAULT when neither is present.
static CBMImportKind php_use_kind(TSNode node) {
uint32_t n = ts_node_child_count(node);
for (uint32_t i = 0; i < n; i++) {
TSNode c = ts_node_child(node, i);
if (ts_node_is_named(c)) {
continue;
}
const char *k = ts_node_type(c);
if (strcmp(k, "function") == 0) {
return CBM_IMPORT_KIND_FUNCTION;
}
if (strcmp(k, "const") == 0) {
return CBM_IMPORT_KIND_CONST;
}
}
return CBM_IMPORT_KIND_DEFAULT;
}
static void emit_php_use_clause(CBMExtractCtx *ctx, TSNode clause, const char *group_prefix,
CBMImportKind decl_kind) {
CBMArena *a = ctx->arena;
// The path node is the qualified_name / namespace_name / name child.
TSNode path_node = clause;
@@ -1230,12 +1273,14 @@ static void emit_php_use_clause(CBMExtractCtx *ctx, TSNode clause, const char *g
TSNode alias = ts_node_child_by_field_name(clause, TS_FIELD("alias"));
const char *local =
!ts_node_is_null(alias) ? cbm_node_text(a, alias, ctx->source) : path_last(a, path);
CBMImport imp = {.local_name = local, .module_path = path};
CBMImportKind kind = decl_kind != CBM_IMPORT_KIND_DEFAULT ? decl_kind : php_use_kind(clause);
CBMImport imp = {.local_name = local, .module_path = path, .kind = kind};
cbm_imports_push(&ctx->result->imports, a, imp);
}
static void emit_php_use_decl(CBMExtractCtx *ctx, TSNode decl) {
CBMArena *a = ctx->arena;
const CBMImportKind decl_kind = php_use_kind(decl);
// Grouped form: namespace_use_group with a leading prefix qualified_name.
TSNode group = decl;
if (find_first_descendant_of(decl, "namespace_use_group", &group)) {
@@ -1257,7 +1302,7 @@ static void emit_php_use_decl(CBMExtractCtx *ctx, TSNode decl) {
const char *ck = ts_node_type(clause);
if (strcmp(ck, "namespace_use_group_clause") == 0 ||
strcmp(ck, "namespace_use_clause") == 0) {
emit_php_use_clause(ctx, clause, prefix);
emit_php_use_clause(ctx, clause, prefix, decl_kind);
}
}
return;
@@ -1268,13 +1313,13 @@ static void emit_php_use_decl(CBMExtractCtx *ctx, TSNode decl) {
for (uint32_t i = 0; i < dc; i++) {
TSNode clause = ts_node_named_child(decl, i);
if (strcmp(ts_node_type(clause), "namespace_use_clause") == 0) {
emit_php_use_clause(ctx, clause, NULL);
emit_php_use_clause(ctx, clause, NULL, decl_kind);
any = true;
}
}
if (!any) {
// Some grammar versions inline the path directly under the declaration.
emit_php_use_clause(ctx, decl, NULL);
emit_php_use_clause(ctx, decl, NULL, decl_kind);
}
}
@@ -2142,10 +2187,13 @@ static void parse_teal_imports(CBMExtractCtx *ctx) {
}
}
// --- Zsh imports ---
// --- Bash / Zsh imports ---
// source file / . file — `command` nodes whose command_name is "source" or ".".
// The argument field carries the sourced path.
static void parse_zsh_imports(CBMExtractCtx *ctx) {
// The argument field carries the sourced path. Every other command is a call,
// not an import: read as an import, `set -e` imported "set", which the import
// resolver then bound to whatever project symbol is named set (a TSX method),
// and every `set` call in the script followed it.
static void parse_shell_source_imports(CBMExtractCtx *ctx) {
CBMArena *a = ctx->arena;
TSNodeStack stack;
ts_nstack_init(&stack, ctx, CBM_SZ_512);
@@ -3004,8 +3052,7 @@ void cbm_extract_imports(CBMExtractCtx *ctx) {
parse_generic_imports(ctx, "call");
break;
case CBM_LANG_BASH:
// source/. commands
parse_generic_imports(ctx, "command");
parse_shell_source_imports(ctx);
break;
case CBM_LANG_ZIG:
parse_zig_imports(ctx);
@@ -3075,7 +3122,7 @@ void cbm_extract_imports(CBMExtractCtx *ctx) {
parse_teal_imports(ctx);
break;
case CBM_LANG_ZSH:
parse_zsh_imports(ctx);
parse_shell_source_imports(ctx);
break;
case CBM_LANG_CMAKE:
parse_cmake_imports(ctx);
+20 -4
View File
@@ -918,6 +918,13 @@ static const char *objectscript_get_method_qn(CBMExtractCtx *ctx, TSNode node,
// Compute function QN for scope tracking (mirrors cbm_enclosing_func_qn logic).
static const char *compute_func_qn(CBMExtractCtx *ctx, TSNode node, const CBMLangSpec *spec,
WalkState *state) {
/* Raw configured definitions already own the canonical QN, including
* namespace scope. Never rederive it from the bare macro identifier. */
const char *configured_qn = cbm_test_definition_qn(ctx, node);
if (configured_qn) {
return configured_qn;
}
(void)spec;
if (ctx->language == CBM_LANG_WOLFRAM) {
return compute_wolfram_func_qn(ctx, node);
@@ -1103,9 +1110,13 @@ static const char *compute_func_qn(CBMExtractCtx *ctx, TSNode node, const CBMLan
return cbm_arena_sprintf(ctx->arena, "%s.%s", state->enclosing_class_qn, qn_name);
}
/* Java/Go: directory-based module so this enclosing-func QN matches the def
* QN and the LSP caller_qn (the lsp_resolve join keys on exact equality). */
return cbm_fqn_compute_source_lang(ctx->arena, ctx->project, ctx->rel_path, qn_name,
ctx->language);
* QN and the LSP caller_qn (the lsp_resolve join keys on exact equality).
* C-family platform variants share their platform-neutral QN — the same
* rule extract_func_def applies, so in-body calls source to the def. */
const char *qn = cbm_fqn_compute_source_lang(ctx->arena, ctx->project, ctx->rel_path, qn_name,
ctx->language);
return cbm_platform_variant_qn(ctx->arena, ctx->language, ctx->project, ctx->rel_path, qn_name,
qn, node);
}
// Compute class QN for scope tracking.
@@ -2370,7 +2381,12 @@ static void push_lexical_boundary(TSNode node, WalkState *state, uint32_t depth)
static void push_boundary_scopes(CBMExtractCtx *ctx, TSNode node, const CBMLangSpec *spec,
WalkState *state, uint32_t depth,
const CBMInvocationDescriptor *invocation) {
if (spec->function_node_types && cbm_kind_in_set(node, spec->function_node_types)) {
const char *configured_body_qn = NULL;
if (ctx->language == CBM_LANG_C && strcmp(ts_node_type(node), "compound_statement") == 0)
configured_body_qn = cbm_test_definition_qn(ctx, node);
if (configured_body_qn) {
(void)push_function_scope(state, depth, configured_body_qn, node);
} else if (spec->function_node_types && cbm_kind_in_set(node, spec->function_node_types)) {
/* OCaml: a nested local `let x = e in ...` is itself a value_definition,
* but the def walk does not descend into function bodies, so it emits no
* node for it. Pushing a func scope here would attribute in-body calls to
+152
View File
@@ -1300,6 +1300,18 @@ const char *cbm_enclosing_func_qn(CBMArena *a, TSNode node, CBMLanguage lang, co
// --- Cached enclosing function QN ---
const char *cbm_enclosing_func_qn_cached(CBMExtractCtx *ctx, TSNode node) {
if (ctx->test_definition_match_count > 0) {
const char **function_kinds = func_kinds_for_lang(ctx->language);
for (TSNode current = node; !ts_node_is_null(current); current = ts_node_parent(current)) {
const char *configured_qn = cbm_test_definition_qn(ctx, current);
if (configured_qn)
return configured_qn;
/* A nested native function owns its own body; do not inherit a
* surrounding configured macro's QN through that boundary. */
if (cbm_kind_in_set(current, function_kinds))
break;
}
}
uint32_t pos = ts_node_start_byte(node);
// Check cache: find a function range that contains this position.
@@ -1666,6 +1678,146 @@ char *cbm_fqn_compute_source_lang(CBMArena *a, const char *project, const char *
return cbm_arena_sprintf(a, "%s.%s", module, name);
}
/* Operating systems and CPU architectures only: a token names a platform, so a
* path segment or stem suffix made of one marks a platform alternative. Vague
* words (generic, default, common, stub) are deliberately absent. */
static const char *const platform_tokens[] = {
"win", "win32", "win64", "windows", "wince", "nt", "posix", "unix",
"linux", "darwin", "mac", "macos", "macosx", "osx", "apple", "ios",
"android", "bsd", "freebsd", "openbsd", "netbsd", "dragonfly", "solaris", "sunos",
"aix", "hpux", "haiku", "fuchsia", "cygwin", "mingw", "msvc", "wasm",
"wasi", "emscripten", "x86", "x86_64", "x64", "amd64", "i386", "i686",
"ia32", "arm", "arm64", "aarch64", "armv7", "riscv", "riscv64", "mips",
"mips64", "ppc", "ppc64", "powerpc", "s390", "s390x", "sparc", "sparc64",
"loongarch", "loongarch64", NULL};
static bool platform_token_eq(const char *s, size_t len) {
for (int i = 0; platform_tokens[i]; i++) {
const char *t = platform_tokens[i];
size_t tl = strlen(t);
if (tl != len) {
continue;
}
size_t k = 0;
while (k < len && tolower((unsigned char)s[k]) == t[k]) {
k++;
}
if (k == len) {
return true;
}
}
return false;
}
/* Length of `stem` without a trailing `_<token>` / `-<token>` / `.<token>`
* platform suffix (the longest one), or `len` when it has none. */
static size_t platform_stem_len(const char *stem, size_t len) {
size_t best = len;
for (size_t sep = 1; sep < len; sep++) {
char c = stem[sep - 1];
if ((c == '_' || c == '-' || c == '.') && platform_token_eq(stem + sep, len - sep)) {
if (sep - 1 < best) {
best = sep - 1;
}
}
}
return best;
}
static bool c_family_variant_lang(CBMLanguage lang) {
return lang == CBM_LANG_C || lang == CBM_LANG_CPP || lang == CBM_LANG_CUDA ||
lang == CBM_LANG_OBJC;
}
static bool func_is_static(TSNode func_node) {
uint32_t n = ts_node_child_count(func_node);
for (uint32_t i = 0; i < n; i++) {
TSNode child = ts_node_child(func_node, i);
if (strcmp(ts_node_type(child), "storage_class_specifier") == 0 &&
ts_node_child_count(child) > 0 &&
strcmp(ts_node_type(ts_node_child(child, 0)), "static") == 0) {
return true;
}
}
return false;
}
const char *cbm_platform_variant_qn(CBMArena *a, CBMLanguage lang, const char *project,
const char *rel_path, const char *name, const char *qn,
TSNode func_node) {
if (!c_family_variant_lang(lang) || !rel_path || !name || !name[0] || !qn ||
ts_node_is_null(func_node) || func_is_static(func_node)) {
return qn;
}
char *plain = cbm_fqn_compute(a, project, rel_path, name);
if (!plain || strcmp(plain, qn) != 0) {
return qn; /* namespaced or otherwise scoped: not a file-scope function */
}
/* Rebuild the path without platform tokens: a directory segment that is
* one is dropped; a filename stem loses its platform suffix, and a stem
* that IS a token leaves the function at its directory. */
size_t len = strlen(rel_path);
char *neutral = (char *)cbm_arena_alloc(a, len + SKIP_ONE);
if (!neutral) {
return qn;
}
size_t out = 0;
bool changed = false;
bool dir_level = false;
const char *seg = rel_path;
while (*seg) {
const char *slash = strchr(seg, '/');
size_t seg_len = slash ? (size_t)(slash - seg) : strlen(seg);
if (slash) {
if (platform_token_eq(seg, seg_len)) {
changed = true;
} else {
memcpy(neutral + out, seg, seg_len);
out += seg_len;
neutral[out++] = '/';
}
seg = slash + SKIP_ONE;
continue;
}
const char *dot = NULL;
for (const char *p = seg + seg_len; p > seg; p--) {
if (p[-1] == '.') {
dot = p - 1;
break;
}
}
size_t stem_len = dot && dot > seg ? (size_t)(dot - seg) : seg_len;
size_t keep = platform_token_eq(seg, stem_len) ? 0 : platform_stem_len(seg, stem_len);
if (keep != stem_len) {
changed = true;
}
if (keep == 0) {
dir_level = true; /* the function lives at the directory */
if (out > 0 && neutral[out - SKIP_ONE] == '/') {
out--;
}
} else {
memcpy(neutral + out, seg, keep);
out += keep;
memcpy(neutral + out, seg + stem_len, seg_len - stem_len);
out += seg_len - stem_len;
}
break;
}
neutral[out] = '\0';
if (!changed) {
return qn;
}
if (dir_level) {
/* No file stem left: the folder form keeps every directory segment
* (cbm_fqn_compute would strip a dotted segment as an extension). */
char *folder = cbm_fqn_folder(a, project ? project : "", neutral);
return folder ? cbm_arena_sprintf(a, "%s.%s", folder, name) : qn;
}
char *variant = cbm_fqn_compute(a, project, neutral, name);
return variant ? variant : qn;
}
char *cbm_fqn_folder(CBMArena *a, const char *project, const char *rel_dir) {
// project.dir1.dir2
size_t proj_len = strlen(project);
+11
View File
@@ -250,6 +250,17 @@ char *cbm_fqn_module_source_lang(CBMArena *a, const char *project, const char *r
char *cbm_fqn_compute_source_lang(CBMArena *a, const char *project, const char *rel_path,
const char *name, CBMLanguage lang);
// C-family platform variants: the same external-linkage function defined in
// files that differ only by a platform token (foo_win.c / foo_posix.c,
// src/unix/fs.c / src/win/fs.c) is ONE function with platform variants — two
// external definitions of one name cannot link into one program, so they are
// alternatives by construction. Returns the platform-neutral QN (the path with
// its platform tokens removed) for a non-static file-scope function of
// C/C++/CUDA/ObjC whose `qn` is the plain file-stem form; otherwise `qn`.
const char *cbm_platform_variant_qn(CBMArena *a, CBMLanguage lang, const char *project,
const char *rel_path, const char *name, const char *qn,
TSNode func_node);
// Folder QN: project.dir_parts
char *cbm_fqn_folder(CBMArena *a, const char *project, const char *rel_dir);
+22
View File
@@ -2737,6 +2737,28 @@ static const CBMLangSpec lang_specs[CBM_LANG_COUNT] = {
_Static_assert(sizeof(lang_specs) / sizeof(lang_specs[0]) == CBM_LANG_COUNT,
"lang_specs array size must match CBM_LANG_COUNT");
/* Conditional-compilation blocks per grammar (the `*_in_field_declaration_list`
* / `*_in_top_level` forms are aliased to these names by the grammars). */
static const char *c_family_conditional_types[] = {
"preproc_if", "preproc_ifdef", "preproc_else", "preproc_elif", "preproc_elifdef", NULL};
const char **cbm_conditional_block_types(CBMLanguage lang) {
switch (lang) {
case CBM_LANG_C:
case CBM_LANG_CPP:
case CBM_LANG_CUDA:
case CBM_LANG_OBJC:
case CBM_LANG_CSHARP:
case CBM_LANG_GLSL:
case CBM_LANG_HLSL:
case CBM_LANG_ISPC:
case CBM_LANG_SLANG:
return c_family_conditional_types;
default:
return NULL;
}
}
const CBMLangSpec *cbm_lang_spec(CBMLanguage lang) {
if (lang < 0 || lang >= CBM_LANG_COUNT) {
return NULL;
+7
View File
@@ -48,6 +48,13 @@ typedef struct {
// language rows; the table lives in extract_calls.c next to the dispatch code.
const char **cbm_string_dispatch_suffixes(CBMLanguage lang);
// Returns a NULL-terminated list of node types that wrap conditionally
// compiled alternatives (#if/#ifdef/#else/#elif blocks) for a language, or
// NULL when the grammar has none. A definition inside one is still a member
// of its enclosing scope; its #else twin is a variant of the same definition.
// Side table for the same -Wmissing-field-initializers reason as above.
const char **cbm_conditional_block_types(CBMLanguage lang);
// Get the language spec for a given language. Returns NULL for unsupported.
const CBMLangSpec *cbm_lang_spec(CBMLanguage lang);
+884 -125
View File
File diff suppressed because it is too large Load Diff
+44
View File
@@ -63,9 +63,28 @@ typedef struct {
int neg_memo_cap; // power-of-two; 0 until first insert
int neg_memo_count; // live entries (grow by rehash at 70% load)
// Field references already emitted for this file, keyed by (enclosing
// function, owner type, field). The function -> Field edge needs one row
// per pair; a row per occurrence would cost a record for every member
// access in the corpus. Same uint64 hash-set shape and ownership as
// neg_memo; freed at end of c_lsp_process_file.
uint64_t *field_ref_seen;
int field_ref_seen_cap;
int field_ref_seen_count;
// The type an initializer_list is about to initialize, handed from the
// declarator / compound literal / enclosing list that knows it to the walk
// of that list. Bound to the list's node id: a hint nothing consumed must
// never type a later, unrelated list.
const CBMType *init_list_type;
const void *init_list_id;
// Output
CBMResolvedCallArray *resolved_calls;
CBMSourceOrigin source_origin; // source buffer represented by emitted occurrence spans
const CBMFileResult *test_definition_owners; /* borrowed, prevalidated raw source identity */
int test_owner_seen_count;
bool test_owner_error;
// Function pointer targets: lexical binding -> exact target function QN.
// A NULL target is an explicitly unknown/ambiguous binding and must shadow
@@ -154,6 +173,31 @@ void cbm_run_c_lsp_cross_with_registry(CBMArena *arena, const char *source, int
TSTree *cached_tree, // NULL = parse internally
CBMResolvedCallArray *out);
/* Additive configured-owner entrypoints. Owners must outlive the call. With
* owners, RAW origin, exact source identity and module identity are required.
* Configured owners fresh-parse verified source: supplied roots/cached trees
* have no source provenance and are not trusted. NULL owners retain caching.
* False leaves output untouched when identity validation fails. A later
* mapping/OOM failure may leave partial output that callers must discard. QNs
* in successful output belong to the supplied output arena. Callers must
* abort publication on false. Legacy wrappers pass NULL and retain previous behavior. */
bool cbm_run_c_lsp_with_test_owners(
CBMArena *arena, CBMFileResult *result, const char *source, int source_len,
TSNode root, bool cpp_mode, CBMSourceOrigin source_origin,
const CBMFileResult *test_owners);
bool cbm_run_c_lsp_cross_with_test_owners(
CBMArena *arena, const char *source, int source_len, const char *module_qn,
bool cpp_mode, CBMLSPDef *defs, int def_count, const char **include_paths,
const char **include_ns_qns, int include_count, TSTree *cached_tree,
CBMResolvedCallArray *out, CBMSourceOrigin source_origin,
const CBMFileResult *test_owners);
bool cbm_run_c_lsp_cross_with_registry_with_test_owners(
CBMArena *arena, const char *source, int source_len, const char *module_qn,
bool cpp_mode, CBMTypeRegistry *reg, const char **include_paths,
const char **include_ns_qns, int include_count, TSTree *cached_tree,
CBMResolvedCallArray *out, CBMSourceOrigin source_origin,
const CBMFileResult *test_owners);
// Register C stdlib types and functions into a registry.
void cbm_c_stdlib_register(CBMTypeRegistry *reg, CBMArena *arena);
+3
View File
@@ -99,6 +99,9 @@ typedef struct {
* a decorator rebinds the function name, so the undecorated definition is
* not automatically an exact callable-value target. */
const char **decorators;
/* Callable identity (#2061): signature suffix of this def's graph QN;
* qualified_name stays the base QN. NULL = none (always, until enabled). */
const char *sig;
} CBMLSPDef;
// Parse source, build registry from defs + stdlib, run LSP.
+42
View File
@@ -4965,6 +4965,47 @@ static const char **py_split_pipe(CBMArena *arena, const char *text) {
return out;
}
/* Split a class def's field_defs ("name:QN|name:QN", built by the Python
* field fold in pass_lsp_cross.c) into the parallel NULL-terminated arrays a
* CBMRegisteredType carries. The types are already project QNs, so each one
* becomes a NAMED type verbatim. Without this, `obj.field.method()` on a class
* from another file lost the field's type (#1277); same-file classes get their
* fields from the per-file walk instead. Pure string work: no registry lookup
* happens here, so it is safe inside the type-registration pass. */
static void py_split_field_defs(CBMArena *arena, const char *field_defs, const char ***names_out,
const CBMType ***types_out) {
*names_out = NULL;
*types_out = NULL;
const char **pairs = py_split_pipe(arena, field_defs);
if (!pairs)
return;
int n = 0;
while (pairs[n])
n++;
const char **names =
(const char **)cbm_arena_alloc(arena, (size_t)(n + 1) * sizeof(const char *));
const CBMType **types =
(const CBMType **)cbm_arena_alloc(arena, (size_t)(n + 1) * sizeof(const CBMType *));
if (!names || !types)
return;
int kept = 0;
for (int i = 0; i < n; i++) {
char *colon = strchr(pairs[i], ':');
if (!colon || colon == pairs[i] || !colon[1])
continue;
*colon = '\0'; /* pairs[i] is this call's own arena copy */
names[kept] = pairs[i];
types[kept] = cbm_type_named(arena, colon + 1);
kept++;
}
if (kept == 0)
return;
names[kept] = NULL;
types[kept] = NULL;
*names_out = names;
*types_out = types;
}
/* Build a registry from CBMLSPDef[] supplied by the caller — covers both
* the source file's own defs and cross-file referenced defs. */
static void py_register_lsp_defs(CBMArena *arena, CBMArena *idx_arena, CBMTypeRegistry *reg,
@@ -4990,6 +5031,7 @@ static void py_register_lsp_defs(CBMArena *arena, CBMArena *idx_arena, CBMTypeRe
if (d->method_names_str && d->method_names_str[0]) {
rt.method_names = py_split_pipe(arena, d->method_names_str);
}
py_split_field_defs(arena, d->field_defs, &rt.field_names, &rt.field_types);
cbm_registry_add_type(reg, rt);
}
}
+97 -17
View File
@@ -1998,6 +1998,22 @@ static const CBMType *ts_new_bare_class_type(TSLSPContext *ctx, const char *cnam
return cbm_type_named(ctx->arena, local_qn);
}
/* Type of a `new_expression`: a bare constructor name follows TS scoping
* (ts_new_bare_class_type), a dotted one is taken as spelled. NULL when the
* node carries no constructor. Shared by expression evaluation and by the
* field-initializer inference in ast_sweep_shapes, so `const t = new C()`
* and `private t = new C()` type the instance identically. */
static const CBMType *ts_new_expression_type(TSLSPContext *ctx, TSNode node) {
TSNode ctor = ts_node_child_by_field_name(node, "constructor", TS_LSP_FIELD_LEN("constructor"));
if (ts_node_is_null(ctor))
return NULL;
char *cname = node_text(ctx, ctor);
if (!cname)
return NULL;
return strchr(cname, '.') == NULL ? ts_new_bare_class_type(ctx, cname)
: cbm_type_named(ctx->arena, cname);
}
static const CBMRegisteredFunc *ts_lookup_namespace_call(TSLSPContext *ctx, const char *object_name,
const char *method_name, TSNode args,
bool *out_is_import) {
@@ -2223,15 +2239,9 @@ const CBMType *ts_eval_expr_type(TSLSPContext *ctx, TSNode node) {
result = lookup_member_type(ctx, recv, pname);
}
} else if (strcmp(kind, "new_expression") == 0) {
TSNode ctor =
ts_node_child_by_field_name(node, "constructor", TS_LSP_FIELD_LEN("constructor"));
if (!ts_node_is_null(ctor)) {
char *cname = node_text(ctx, ctor);
if (cname) {
result = strchr(cname, '.') == NULL ? ts_new_bare_class_type(ctx, cname)
: cbm_type_named(ctx->arena, cname);
}
}
const CBMType *nt = ts_new_expression_type(ctx, node);
if (nt)
result = nt;
} else if (strcmp(kind, "call_expression") == 0) {
TSNode fn = ts_node_child_by_field_name(node, "function", TS_LSP_FIELD_LEN("function"));
if (!ts_node_is_null(fn)) {
@@ -5357,6 +5367,76 @@ static const CBMType *interface_method_signature_from_ast(TSLSPContext *ctx, TSN
return cbm_type_func(ctx->arena, NULL, param_count ? param_types : NULL, return_types);
}
/* Parsed type of a `type_annotation` field (`: T`), or unknown. */
static const CBMType *ts_annotation_field_type(TSLSPContext *ctx, TSNode owner) {
TSNode tann = ts_node_child_by_field_name(owner, "type", TS_LSP_FIELD_LEN("type"));
if (ts_node_is_null(tann))
return cbm_type_unknown();
TSNode tn =
strcmp(ts_node_type(tann), "type_annotation") == 0 ? ts_node_named_child(tann, 0) : tann;
return ts_node_is_null(tn) ? cbm_type_unknown() : ts_parse_type_node(ctx, tn);
}
/* Declared type of a class field (`public_field_definition`) or interface
* property: the annotation when present, else, for an unannotated field
* initialised with `new C()`, the type of that instance (#514). No other
* initializer is inferred: `new` names its type exactly, arbitrary
* expressions do not. */
static const CBMType *ts_field_decl_type(TSLSPContext *ctx, TSNode m) {
if (!ts_node_is_null(ts_node_child_by_field_name(m, "type", TS_LSP_FIELD_LEN("type"))))
return ts_annotation_field_type(ctx, m);
TSNode val = ts_node_child_by_field_name(m, "value", TS_LSP_FIELD_LEN("value"));
if (ts_node_is_null(val) || strcmp(ts_node_type(val), "new_expression") != 0)
return cbm_type_unknown();
const CBMType *nt = ts_new_expression_type(ctx, val);
return nt ? nt : cbm_type_unknown();
}
/* A constructor parameter is a TS parameter property, i.e. also declares a
* class field, iff it carries an accessibility modifier, `readonly` or
* `override`. */
static bool ts_param_is_property(TSNode p) {
uint32_t pc = ts_node_child_count(p);
for (uint32_t ci = 0; ci < pc; ci++) {
const char *ck = ts_node_type(ts_node_child(p, ci));
if (strcmp(ck, "accessibility_modifier") == 0 || strcmp(ck, "readonly") == 0 ||
strcmp(ck, "override_modifier") == 0)
return true;
}
return false;
}
/* Append the parameter properties of a `constructor` method_definition to
* the field arrays (#514): `constructor(private readonly svc: Svc)` declares
* field `svc: Svc`, the dominant NestJS injection shape. Any other member is
* ignored. Returns the new field count. */
static int ts_collect_param_properties(TSLSPContext *ctx, TSNode m, const char **names,
const CBMType **types, int count, int cap) {
TSNode mname = ts_node_child_by_field_name(m, "name", TS_LSP_FIELD_LEN("name"));
char *mnm = ts_node_is_null(mname) ? NULL : node_text(ctx, mname);
if (!mnm || strcmp(mnm, "constructor") != 0)
return count;
TSNode params = ts_node_child_by_field_name(m, "parameters", TS_LSP_FIELD_LEN("parameters"));
uint32_t pnc = ts_node_is_null(params) ? 0 : ts_node_named_child_count(params);
for (uint32_t pi = 0; pi < pnc && count < cap; pi++) {
TSNode p = ts_node_named_child(params, pi);
const char *pk = ts_node_type(p);
if ((strcmp(pk, "required_parameter") != 0 && strcmp(pk, "optional_parameter") != 0) ||
!ts_param_is_property(p))
continue;
TSNode pat = ts_node_child_by_field_name(p, "pattern", TS_LSP_FIELD_LEN("pattern"));
if (ts_node_is_null(pat) || strcmp(ts_node_type(pat), "identifier") != 0)
continue;
char *pnm = node_text(ctx, pat);
if (!pnm)
continue;
names[count] = pnm;
types[count] = ts_annotation_field_type(ctx, p);
count++;
}
return count;
}
// AST sweep: walk class/interface bodies to collect field names+types and refine
// embedded_types / type_param_names.
static void ast_sweep_shapes(TSLSPContext *ctx, TSNode root, CBMTypeRegistry *reg) {
@@ -5469,24 +5549,24 @@ static void ast_sweep_shapes(TSLSPContext *ctx, TSNode root, CBMTypeRegistry *re
if (strcmp(mk, "public_field_definition") == 0 ||
strcmp(mk, "property_signature") == 0) {
TSNode fname = ts_node_child_by_field_name(m, "name", TS_LSP_FIELD_LEN("name"));
TSNode ftype = ts_node_child_by_field_name(m, "type", TS_LSP_FIELD_LEN("type"));
if (ts_node_is_null(fname))
continue;
char *fnm = node_text(ctx, fname);
if (!fnm)
continue;
const CBMType *ft = cbm_type_unknown();
if (!ts_node_is_null(ftype)) {
TSNode tch = ts_node_named_child(ftype, 0);
if (!ts_node_is_null(tch))
ft = ts_parse_type_node(ctx, tch);
}
field_names[field_count] = fnm;
field_types[field_count] = ft;
field_types[field_count] = ts_field_decl_type(ctx, m);
field_count++;
continue;
}
// Constructor parameter properties are class fields too (#514).
if (!is_interface && strcmp(mk, "method_definition") == 0) {
field_count =
ts_collect_param_properties(ctx, m, field_names, field_types, field_count, 63);
continue;
}
// Interface method signature: `methodName(params): ReturnType;`
// Or class method declaration in `.d.ts` ambient mode.
if (is_interface && (strcmp(mk, "method_signature") == 0)) {
+21 -1
View File
@@ -677,8 +677,28 @@ CBMRegisteredType *cbm_registry_type_for_update(CBMTypeRegistry *head, const cha
for (const CBMTypeRegistry *r = head->fallback; r; r = r->fallback) {
const CBMRegisteredType *base = lookup_type_self(r, qualified_name);
if (base) {
CBMRegisteredType copy = *base;
/* Field refinements overwrite entries in this array. The type
* objects remain immutable, but the writable overlay must own
* the pointer array rather than mutate its shared fallback. */
if (base->field_types) {
size_t count = 0;
/* Types are parallel to names; a named field may have no
* type, so an interior NULL does not terminate that array. */
while (base->field_names ? base->field_names[count] != NULL
: base->field_types[count] != NULL) {
count++;
}
const CBMType **field_types = (const CBMType **)cbm_arena_alloc(
head->arena, (count + 1) * sizeof(*field_types));
if (!field_types) {
return NULL;
}
memcpy(field_types, base->field_types, (count + 1) * sizeof(*field_types));
copy.field_types = field_types;
}
int before = head->type_count;
cbm_registry_add_type(head, *base);
cbm_registry_add_type(head, copy);
if (head->type_count == before + 1) {
return &head->types[before];
}
+3
View File
@@ -40,6 +40,9 @@ typedef struct {
* remain NULL when raw cross-file provenance is ambiguous; the Rust trait
* flag still prevents that method from being mistaken for inherent. */
const char *impl_trait_qn;
/* Callable identity (#2061): this overload's signature suffix; its graph
* QN is qualified_name + sig. NULL = none (always, until enabled). */
const char *sig;
} CBMRegisteredFunc;
// Registered type with fields and method names.
+9
View File
@@ -357,6 +357,7 @@ static void cr_walk(cr_ctx_t *c, CBMFileResult *r) {
cr_str(c, &r->resolved_calls.items[i].callee_qn);
cr_str(c, &r->resolved_calls.items[i].strategy);
cr_str(c, &r->resolved_calls.items[i].reason);
cr_str(c, &r->resolved_calls.items[i].callee_sig);
}
cr_array(c, (void **)&r->string_refs.items, r->string_refs.count, sizeof(CBMStringRef));
for (int i = 0; i < r->string_refs.count && r->string_refs.items; i++) {
@@ -377,6 +378,12 @@ static void cr_walk(cr_ctx_t *c, CBMFileResult *r) {
cr_str(c, &r->channels.items[i].transport);
cr_str(c, &r->channels.items[i].enclosing_func_qn);
}
cr_array(c, (void **)&r->field_types.items, r->field_types.count, sizeof(CBMFieldType));
for (int i = 0; i < r->field_types.count && r->field_types.items; i++) {
cr_str(c, &r->field_types.items[i].class_qn);
cr_str(c, &r->field_types.items[i].field_name);
cr_str(c, &r->field_types.items[i].type_text);
}
cr_str(c, &r->module_qn);
cr_str(c, &r->namespace_name);
cr_list(c, &r->exports);
@@ -385,6 +392,7 @@ static void cr_walk(cr_ctx_t *c, CBMFileResult *r) {
cr_list(c, &r->macros);
cr_str(c, &r->error_msg);
cr_str(c, &r->error_ranges);
cr_str(c, &r->module_doc);
cr_blob(c, (const void **)&r->source, r->source ? (size_t)r->source_len + SKIP_ONE : 0);
}
@@ -523,6 +531,7 @@ void cbm_result_compact(CBMFileResult *result) {
tmp.string_refs.cap = tmp.string_refs.count;
tmp.infra_bindings.cap = tmp.infra_bindings.count;
tmp.channels.cap = tmp.channels.count;
tmp.field_types.cap = tmp.field_types.count;
/* A composite kept its per-unit results only so shallow-copied strings
* stayed valid; every string is now a copy of its own. */
+141 -3
View File
@@ -41,6 +41,11 @@ static const lib_pattern_t http_libraries[] = {
/* JavaScript / TypeScript */
{"axios", CBM_SVC_HTTP, NULL},
/* `import Axios from "axios"`: the capitalized default-import binding
* (also axios's own Axios / AxiosInstance types). Matching is
* case-sensitive, so without this entry `Axios.get(url)` fell through to
* the `.get` route-suffix fallback and became a route registration. */
{"Axios", CBM_SVC_HTTP, NULL},
{"superagent", CBM_SVC_HTTP, NULL},
{"needle", CBM_SVC_HTTP, NULL},
{"node-fetch", CBM_SVC_HTTP, NULL},
@@ -752,9 +757,9 @@ static bool has_filesystem_extension(const char *path) {
ext[ext_len] = '\0';
static const char *const hard_file_exts[] = {
".cfg", ".conf", ".credentials", ".crt", ".db", ".env",
".ini", ".key", ".pem", ".pid", ".properties", ".service",
".sock", ".socket", ".sqlite", ".toml", NULL};
".cfg", ".conf", ".credentials", ".crt", ".db", ".env", ".ini", ".key",
".log", ".md", ".pdf", ".pem", ".pid", ".properties", ".rst", ".service",
".sock", ".socket", ".sqlite", ".toml", ".txt", NULL};
for (int i = 0; hard_file_exts[i]; i++) {
if (path_ext_matches(ext, hard_file_exts[i])) {
return true;
@@ -865,6 +870,139 @@ bool cbm_service_pattern_is_http_route_literal(const char *literal, const char *
return true;
}
/* ── GraphQL operation identity (#598) ─────────────────────────── */
static bool gql_name_start(unsigned char c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
}
static bool gql_name_char(unsigned char c) {
return gql_name_start(c) || (c >= '0' && c <= '9');
}
/* Skip one lexical unit whose braces are not structure: a # comment, a "..."
* or """...""" string, or a JS ${...} template interpolation. Returns p
* unchanged when p starts none of them. */
static const char *gql_skip_opaque(const char *p) {
if (*p == '#') {
while (*p && *p != '\n' && *p != '\r') {
p++;
}
return p;
}
if (strncmp(p, "\"\"\"", 3) == 0) {
const char *end = strstr(p + 3, "\"\"\"");
return end ? end + 3 : p + strlen(p);
}
if (*p == '"') {
p++;
while (*p && *p != '"') {
p += (*p == '\\' && p[1]) ? 2 : 1;
}
return *p ? p + 1 : p;
}
if (p[0] == '$' && p[1] == '{') {
int depth = 1;
p += 2;
while (*p && depth > 0) {
depth += (*p == '{') - (*p == '}');
p++;
}
return p;
}
return p;
}
/* Skip GraphQL "ignored tokens" (whitespace, commas, comments). */
static const char *gql_skip_ignored(const char *p) {
for (;;) {
while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == ',') {
p++;
}
if (*p != '#') {
return p;
}
p = gql_skip_opaque(p);
}
}
/* Copy the Name token at p (bounded). Returns false when p is not a Name. */
static bool gql_copy_name(const char *p, char *name_buf, size_t name_sz) {
if (!gql_name_start((unsigned char)*p) || !name_buf || name_sz == 0) {
return false;
}
size_t n = 0;
while (gql_name_char((unsigned char)p[n]) && n + 1 < name_sz) {
name_buf[n] = p[n];
n++;
}
name_buf[n] = '\0';
return true;
}
static const char *gql_operation_keyword(const char *tok, size_t len) {
static const char *const kws[] = {"query", "mutation", "subscription", NULL};
for (int i = 0; kws[i]; i++) {
if (strlen(kws[i]) == len && strncmp(tok, kws[i], len) == 0) {
return kws[i];
}
}
return NULL;
}
bool cbm_service_pattern_graphql_operation(const char *doc, const char **op_type, char *name_buf,
size_t name_sz) {
*op_type = "operation";
if (name_buf && name_sz > 0) {
name_buf[0] = '\0';
}
if (!doc) {
return false;
}
int depth = 0;
bool in_fragment = false;
const char *p = doc;
while (*p) {
const char *q = gql_skip_opaque(p);
if (q != p) {
p = q;
continue;
}
unsigned char c = (unsigned char)*p;
if (c == '{' && depth == 0 && !in_fragment) {
*op_type = "query"; /* `{ ... }` shorthand is an anonymous query */
return false;
}
if (c == '{' || c == '}') {
depth += (c == '{') ? 1 : -1;
depth = depth < 0 ? 0 : depth;
in_fragment = in_fragment && depth > 0;
p++;
continue;
}
if (depth > 0 || in_fragment || !gql_name_start(c)) {
p++;
continue;
}
const char *tok = p;
while (gql_name_char((unsigned char)*p)) {
p++;
}
size_t len = (size_t)(p - tok);
if (len == strlen("fragment") && strncmp(tok, "fragment", len) == 0) {
in_fragment = true;
continue;
}
const char *kw = gql_operation_keyword(tok, len);
if (!kw) {
return false; /* not a GraphQL document (URL, dict key, variable ...) */
}
*op_type = kw;
return gql_copy_name(gql_skip_ignored(p), name_buf, name_sz);
}
return false;
}
/* ── Public API ────────────────────────────────────────────────── */
/* Per-worker TLS cache of cbm_service_pattern_match results.
+14
View File
@@ -12,6 +12,7 @@
#define CBM_SERVICE_PATTERNS_H
#include <stdbool.h>
#include <stddef.h>
/* Edge type returned by pattern match. */
typedef enum {
@@ -77,4 +78,17 @@ const char *cbm_service_pattern_route_method(const char *callee_name);
* Returns NULL if not an async pattern. */
const char *cbm_service_pattern_broker(const char *resolved_qn);
/* GraphQL operation identity of a client call argument (#598).
* Scans `doc` as a GraphQL document (skipping comments, strings, `${...}`
* template interpolations and leading fragment definitions) and reports the
* first operation: *op_type is "query", "mutation" or "subscription" ("query"
* for the `{ ... }` shorthand). Returns true and writes the operation name to
* name_buf when the operation is named. Returns false for an anonymous
* operation, or with *op_type = "operation" when `doc` is not a GraphQL
* document (NULL, a URL, a bare word such as a dict key ...). The operation
* text itself is never copied, so callers can key Routes on a bounded
* identity. */
bool cbm_service_pattern_graphql_operation(const char *doc, const char **op_type, char *name_buf,
size_t name_sz);
#endif /* CBM_SERVICE_PATTERNS_H */
+744
View File
@@ -0,0 +1,744 @@
/*
* spawn_patterns.c — process-spawning call sites, per language (see the header).
*
* Spellings come from what the extractor records as the callee (checked per
* language against real calls): Rust `Command::new`, Go `exec.Command`, Java
* `Runtime.getRuntime().exec`, a constructor as its class name.
*/
#include "spawn_patterns.h"
#include "foundation/constants.h"
#include <stdio.h>
#include <string.h>
#define Q CBM_SPAWN_QUALIFIED
#define U CBM_SPAWN_BARE
#define I CBM_SPAWN_BARE_IMPORTED
#define S CBM_SPAWN_STRING_RECEIVER
/* libc / POSIX / Win32. posix_spawn and _spawn* take the path second;
* CreateProcess names the program first or only in its command line. */
static const cbm_spawn_api_t c_family[] = {
{"system", NULL, U, 0, -1}, {"std::system", NULL, U, 0, -1},
{"_wsystem", NULL, U, 0, -1}, {"popen", NULL, U, 0, -1},
{"_popen", NULL, U, 0, -1}, {"_wpopen", NULL, U, 0, -1},
{"execl", NULL, U, 0, -1}, {"execle", NULL, U, 0, -1},
{"execlp", NULL, U, 0, -1}, {"execv", NULL, U, 0, -1},
{"execve", NULL, U, 0, -1}, {"execvp", NULL, U, 0, -1},
{"execvpe", NULL, U, 0, -1}, {"fexecve", NULL, U, 0, -1},
{"posix_spawn", NULL, U, 1, -1}, {"posix_spawnp", NULL, U, 1, -1},
{"_execl", NULL, U, 0, -1}, {"_execv", NULL, U, 0, -1},
{"_execlp", NULL, U, 0, -1}, {"_execvp", NULL, U, 0, -1},
{"_spawnl", NULL, U, 1, -1}, {"_spawnle", NULL, U, 1, -1},
{"_spawnlp", NULL, U, 1, -1}, {"_spawnv", NULL, U, 1, -1},
{"_spawnve", NULL, U, 1, -1}, {"_spawnvp", NULL, U, 1, -1},
{"_wspawnv", NULL, U, 1, -1}, {"_wspawnvp", NULL, U, 1, -1},
{"CreateProcess", NULL, U, 0, 1}, {"CreateProcessA", NULL, U, 0, 1},
{"CreateProcessW", NULL, U, 0, 1}, {"ShellExecute", NULL, U, 2, -1},
{"ShellExecuteA", NULL, U, 2, -1}, {"ShellExecuteW", NULL, U, 2, -1},
{"ShellExecuteExA", NULL, U, 0, -1}, {"ShellExecuteExW", NULL, U, 0, -1},
{"WinExec", NULL, U, 0, -1}, {NULL, NULL, 0, 0, -1},
};
static const cbm_spawn_api_t python[] = {
{"subprocess.run", NULL, Q, 0, -1},
{"subprocess.call", NULL, Q, 0, -1},
{"subprocess.check_call", NULL, Q, 0, -1},
{"subprocess.check_output", NULL, Q, 0, -1},
{"subprocess.Popen", NULL, Q, 0, -1},
{"subprocess.getoutput", NULL, Q, 0, -1},
{"subprocess.getstatusoutput", NULL, Q, 0, -1},
{"os.system", NULL, Q, 0, -1},
{"os.popen", NULL, Q, 0, -1},
{"os.startfile", NULL, Q, 0, -1},
{"os.execl", NULL, Q, 0, -1},
{"os.execle", NULL, Q, 0, -1},
{"os.execlp", NULL, Q, 0, -1},
{"os.execlpe", NULL, Q, 0, -1},
{"os.execv", NULL, Q, 0, -1},
{"os.execve", NULL, Q, 0, -1},
{"os.execvp", NULL, Q, 0, -1},
{"os.execvpe", NULL, Q, 0, -1},
{"os.posix_spawn", NULL, Q, 0, -1},
{"os.posix_spawnp", NULL, Q, 0, -1},
{"os.spawnl", NULL, Q, 1, -1},
{"os.spawnle", NULL, Q, 1, -1},
{"os.spawnlp", NULL, Q, 1, -1},
{"os.spawnlpe", NULL, Q, 1, -1},
{"os.spawnv", NULL, Q, 1, -1},
{"os.spawnve", NULL, Q, 1, -1},
{"os.spawnvp", NULL, Q, 1, -1},
{"os.spawnvpe", NULL, Q, 1, -1},
{"asyncio.create_subprocess_exec", NULL, Q, 0, -1},
{"asyncio.create_subprocess_shell", NULL, Q, 0, -1},
{"pexpect.spawn", NULL, Q, 0, -1},
{"pexpect.run", NULL, Q, 0, -1},
{NULL, NULL, 0, 0, -1},
};
/* Node's child_process (also behind `node:`), the destructured-require form,
* execa / cross-spawn, and the Bun and Deno globals. */
static const cbm_spawn_api_t js_family[] = {
{"child_process.spawn", NULL, Q, 0, -1},
{"child_process.spawnSync", NULL, Q, 0, -1},
{"child_process.exec", NULL, Q, 0, -1},
{"child_process.execSync", NULL, Q, 0, -1},
{"child_process.execFile", NULL, Q, 0, -1},
{"child_process.execFileSync", NULL, Q, 0, -1},
{"child_process.fork", NULL, Q, 0, -1},
{"spawn", "child_process", I, 0, -1},
{"spawnSync", "child_process", I, 0, -1},
{"exec", "child_process", I, 0, -1},
{"execSync", "child_process", I, 0, -1},
{"execFile", "child_process", I, 0, -1},
{"execFileSync", "child_process", I, 0, -1},
{"fork", "child_process", I, 0, -1},
{"execa.execa", NULL, Q, 0, -1},
{"execa.execaSync", NULL, Q, 0, -1},
{"execa.execaCommand", NULL, Q, 0, -1},
{"execa.execaCommandSync", NULL, Q, 0, -1},
{"cross-spawn.spawn", NULL, Q, 0, -1},
{"cross-spawn.sync", NULL, Q, 0, -1},
{"Bun.spawn", NULL, Q, 0, -1},
{"Bun.spawnSync", NULL, Q, 0, -1},
{"Deno.Command", NULL, Q, 0, -1},
{"Deno.run", NULL, Q, 0, -1},
{NULL, NULL, 0, 0, -1},
};
static const cbm_spawn_api_t go[] = {
{"os/exec.Command", NULL, Q, 0, -1},
{"os/exec.CommandContext", NULL, Q, 1, -1},
{"golang.org/x/sys/execabs.Command", NULL, Q, 0, -1},
{"golang.org/x/sys/execabs.CommandContext", NULL, Q, 1, -1},
{"os.StartProcess", NULL, Q, 0, -1},
{"syscall.Exec", NULL, Q, 0, -1},
{"syscall.ForkExec", NULL, Q, 0, -1},
{"syscall.StartProcess", NULL, Q, 0, -1},
{NULL, NULL, 0, 0, -1},
};
static const cbm_spawn_api_t rust[] = {
{"std::process::Command::new", NULL, Q, 0, -1},
{"tokio::process::Command::new", NULL, Q, 0, -1},
{"async_process::Command::new", NULL, Q, 0, -1},
{"async_std::process::Command::new", NULL, Q, 0, -1},
{"smol::process::Command::new", NULL, Q, 0, -1},
{"duct::cmd", NULL, Q, 0, -1},
{NULL, NULL, 0, 0, -1},
};
/* java.lang is implicit, so the class name alone is the spelling. */
static const cbm_spawn_api_t jvm[] = {
{"ProcessBuilder", NULL, U, 0, -1},
{"java.lang.ProcessBuilder", NULL, Q, 0, -1},
{"Runtime.getRuntime().exec", NULL, U, 0, -1},
{".execute", NULL, S, 0, -1},
{NULL, NULL, 0, 0, -1},
};
static const cbm_spawn_api_t dotnet[] = {
{"Process.Start", NULL, U, 0, -1}, {"System.Diagnostics.Process.Start", NULL, Q, 0, -1},
{"ProcessStartInfo", NULL, U, 0, -1}, {"System.Diagnostics.ProcessStartInfo", NULL, Q, 0, -1},
{"Cli.Wrap", NULL, U, 0, -1}, {NULL, NULL, 0, 0, -1},
};
static const cbm_spawn_api_t ruby[] = {
{"system", NULL, U, 0, -1}, {"exec", NULL, U, 0, -1},
{"spawn", NULL, U, 0, -1}, {"Kernel.system", NULL, U, 0, -1},
{"Kernel.exec", NULL, U, 0, -1}, {"Kernel.spawn", NULL, U, 0, -1},
{"Process.spawn", NULL, U, 0, -1}, {"Process.exec", NULL, U, 0, -1},
{"IO.popen", NULL, U, 0, -1}, {"PTY.spawn", NULL, U, 0, -1},
{"Open3.popen2", NULL, U, 0, -1}, {"Open3.popen2e", NULL, U, 0, -1},
{"Open3.popen3", NULL, U, 0, -1}, {"Open3.capture2", NULL, U, 0, -1},
{"Open3.capture2e", NULL, U, 0, -1}, {"Open3.capture3", NULL, U, 0, -1},
{"Open3.pipeline", NULL, U, 0, -1}, {NULL, NULL, 0, 0, -1},
};
static const cbm_spawn_api_t php[] = {
{"exec", NULL, U, 0, -1},
{"shell_exec", NULL, U, 0, -1},
{"system", NULL, U, 0, -1},
{"passthru", NULL, U, 0, -1},
{"proc_open", NULL, U, 0, -1},
{"popen", NULL, U, 0, -1},
{"pcntl_exec", NULL, U, 0, -1},
{"Symfony\\Component\\Process\\Process", NULL, Q, 0, -1},
{"Symfony\\Component\\Process\\Process::fromShellCommandline", NULL, Q, 0, -1},
{NULL, NULL, 0, 0, -1},
};
static const cbm_spawn_api_t perl[] = {
{"system", NULL, U, 0, -1},
{"exec", NULL, U, 0, -1},
{NULL, NULL, 0, 0, -1},
};
static const cbm_spawn_api_t lua[] = {
{"os.execute", NULL, U, 0, -1},
{"io.popen", NULL, U, 0, -1},
{NULL, NULL, 0, 0, -1},
};
static const cbm_spawn_api_t r_lang[] = {
{"system", NULL, U, 0, -1},
{"system2", NULL, U, 0, -1},
{"shell", NULL, U, 0, -1},
{"processx::run", NULL, Q, 0, -1},
{"sys::exec_wait", NULL, Q, 0, -1},
{"sys::exec_internal", NULL, Q, 0, -1},
{NULL, NULL, 0, 0, -1},
};
static const cbm_spawn_api_t elixir[] = {
{"System.cmd", NULL, U, 0, -1},
{"System.shell", NULL, U, 0, -1},
{"Port.open", NULL, U, 0, CBM_SPAWN_ANY_LITERAL},
{NULL, NULL, 0, 0, -1},
};
static const cbm_spawn_api_t erlang[] = {
{"os:cmd", NULL, Q, 0, -1},
{"open_port", NULL, U, 0, -1},
{"erlang:open_port", NULL, Q, 0, -1},
{NULL, NULL, 0, 0, -1},
};
static const cbm_spawn_api_t haskell[] = {
{"callProcess", "System.Process", I, 0, -1},
{"callCommand", "System.Process", I, 0, -1},
{"readProcess", "System.Process", I, 0, -1},
{"readProcessWithExitCode", "System.Process", I, 0, -1},
{"readCreateProcess", "System.Process", I, 0, -1},
{"readCreateProcessWithExitCode", "System.Process", I, 0, -1},
{"createProcess", "System.Process", I, 0, -1},
{"spawnProcess", "System.Process", I, 0, -1},
{"spawnCommand", "System.Process", I, 0, -1},
{"system", "System.Process", I, 0, -1},
{"rawSystem", "System.Process", I, 0, -1},
{"runProcess", "System.Process", I, 0, -1},
{"runCommand", "System.Process", I, 0, -1},
{"runInteractiveProcess", "System.Process", I, 0, -1},
{"runInteractiveCommand", "System.Process", I, 0, -1},
{"runProcess", "System.Process.Typed", I, 0, -1},
{"readProcess", "System.Process.Typed", I, 0, -1},
{"startProcess", "System.Process.Typed", I, 0, -1},
{"system", "System.Cmd", I, 0, -1},
{"rawSystem", "System.Cmd", I, 0, -1},
{NULL, NULL, 0, 0, -1},
};
static const cbm_spawn_api_t dlang[] = {
{"execute", "std.process", I, 0, -1},
{"executeShell", "std.process", I, 0, -1},
{"spawnProcess", "std.process", I, 0, -1},
{"spawnShell", "std.process", I, 0, -1},
{"pipeProcess", "std.process", I, 0, -1},
{"pipeShell", "std.process", I, 0, -1},
{NULL, NULL, 0, 0, -1},
};
/* Foundation's Process: the program is a property set after construction,
* so a bare construction is a spawn site with a dynamic program. */
static const cbm_spawn_api_t swift[] = {
{"Process.launchedProcess", NULL, U, 0, -1},
{"Process.run", NULL, U, 0, -1},
{"Process", "Foundation", I, 0, -1},
{NULL, NULL, 0, 0, -1},
};
static const cbm_spawn_api_t crystal[] = {
{"Process.run", NULL, U, 0, -1}, {"Process.new", NULL, U, 0, -1},
{"Process.exec", NULL, U, 0, -1}, {"system", NULL, U, 0, -1},
{NULL, NULL, 0, 0, -1},
};
static const cbm_spawn_api_t zig[] = {
{"std.process.Child.init", NULL, Q, 0, -1},
{"std.process.Child.run", NULL, Q, 0, CBM_SPAWN_ANY_LITERAL},
{"std.ChildProcess.init", NULL, Q, 0, -1},
{"std.ChildProcess.run", NULL, Q, 0, CBM_SPAWN_ANY_LITERAL},
{"std.ChildProcess.exec", NULL, Q, 0, CBM_SPAWN_ANY_LITERAL},
{"std.process.execv", NULL, Q, 1, -1},
{NULL, NULL, 0, 0, -1},
};
static const cbm_spawn_api_t ocaml[] = {
{"Sys.command", NULL, U, 0, -1},
{"Unix.system", NULL, U, 0, -1},
{"Unix.create_process", NULL, U, 0, -1},
{"Unix.create_process_env", NULL, U, 0, -1},
{"Unix.open_process", NULL, U, 0, -1},
{"Unix.open_process_in", NULL, U, 0, -1},
{"Unix.open_process_out", NULL, U, 0, -1},
{"Unix.open_process_full", NULL, U, 0, -1},
{"Unix.open_process_args", NULL, U, 0, -1},
{"Unix.execv", NULL, U, 0, -1},
{"Unix.execvp", NULL, U, 0, -1},
{NULL, NULL, 0, 0, -1},
};
static const cbm_spawn_api_t clojure[] = {
{"clojure.java.shell/sh", NULL, Q, 0, -1},
{"sh", "clojure.java.shell", I, 0, -1},
{"babashka.process/process", NULL, Q, 0, -1},
{"babashka.process/shell", NULL, Q, 0, -1},
{NULL, NULL, 0, 0, -1},
};
static const cbm_spawn_api_t tcl[] = {
{"exec", NULL, U, 0, -1},
{NULL, NULL, 0, 0, -1},
};
static const cbm_spawn_api_t fortran[] = {
{"execute_command_line", NULL, U, 0, -1},
{"system", NULL, U, 0, -1},
{NULL, NULL, 0, 0, -1},
};
static const cbm_spawn_api_t julia[] = {
{"run", NULL, U, 0, -1},
{NULL, NULL, 0, 0, -1},
};
static const cbm_spawn_api_t powershell[] = {
{"Start-Process", NULL, U, 0, -1},
{NULL, NULL, 0, 0, -1},
};
#undef Q
#undef U
#undef I
#undef S
static const cbm_spawn_api_t *spawn_table(CBMLanguage lang) {
switch (lang) {
case CBM_LANG_C:
case CBM_LANG_CPP:
case CBM_LANG_CUDA:
case CBM_LANG_OBJC:
return c_family;
case CBM_LANG_PYTHON:
return python;
case CBM_LANG_JAVASCRIPT:
case CBM_LANG_TYPESCRIPT:
case CBM_LANG_TSX:
case CBM_LANG_ARKTS:
return js_family;
case CBM_LANG_GO:
return go;
case CBM_LANG_RUST:
return rust;
case CBM_LANG_JAVA:
case CBM_LANG_KOTLIN:
case CBM_LANG_SCALA:
case CBM_LANG_GROOVY:
return jvm;
case CBM_LANG_CSHARP:
case CBM_LANG_FSHARP:
return dotnet;
case CBM_LANG_RUBY:
return ruby;
case CBM_LANG_PHP:
return php;
case CBM_LANG_PERL:
return perl;
case CBM_LANG_LUA:
case CBM_LANG_LUAU:
case CBM_LANG_TEAL:
return lua;
case CBM_LANG_R:
return r_lang;
case CBM_LANG_ELIXIR:
return elixir;
case CBM_LANG_ERLANG:
return erlang;
case CBM_LANG_HASKELL:
return haskell;
case CBM_LANG_DLANG:
return dlang;
case CBM_LANG_SWIFT:
return swift;
case CBM_LANG_CRYSTAL:
return crystal;
case CBM_LANG_ZIG:
return zig;
case CBM_LANG_OCAML:
return ocaml;
case CBM_LANG_CLOJURE:
return clojure;
case CBM_LANG_TCL:
return tcl;
case CBM_LANG_FORTRAN:
return fortran;
case CBM_LANG_JULIA:
return julia;
case CBM_LANG_POWERSHELL:
return powershell;
default:
return NULL;
}
}
/* An import value without Node's `node:` scheme. */
static const char spawn_node_scheme[] = "node:";
static const char *spawn_module(const char *val) {
size_t n = sizeof(spawn_node_scheme) - 1;
return strncmp(val, spawn_node_scheme, n) == 0 ? val + n : val;
}
static const char *const spawn_separators[] = {"::", ".", ":", "/", "\\", NULL};
/* Length of the callee's head (before its first separator) and the
* separator's offset, or 0 when the callee has none. */
static size_t spawn_head_len(const char *callee) {
size_t best = 0;
for (int i = 0; spawn_separators[i]; i++) {
const char *at = strstr(callee, spawn_separators[i]);
if (at && at > callee && (best == 0 || (size_t)(at - callee) < best)) {
best = (size_t)(at - callee);
}
}
return best;
}
/* True when `val` names `name` as its last segment ("subprocess.run" for
* run, "java.lang.ProcessBuilder" for ProcessBuilder). */
static bool spawn_val_names(const char *val, const char *name) {
size_t vl = strlen(val);
size_t nl = strlen(name);
if (vl <= nl || strcmp(val + vl - nl, name) != 0) {
return false;
}
char sep = val[vl - nl - 1];
return sep == '.' || sep == ':' || sep == '/' || sep == '\\';
}
/* The callee as its import makes it: `sp.run` -> subprocess.run (an alias),
* `run` -> subprocess.run (a member import), `spawn` -> child_process.spawn
* (a named import mapped to its module). Empty when no import applies. */
static void spawn_expand(const char *callee, const CBMImport *imports, int count, char *out,
size_t n) {
out[0] = '\0';
size_t head = spawn_head_len(callee);
size_t key_len = head ? head : strlen(callee);
for (int i = 0; i < count; i++) {
const char *key = imports[i].local_name;
if (!key || !imports[i].module_path || strlen(key) != key_len ||
strncmp(key, callee, key_len) != 0) {
continue;
}
const char *val = spawn_module(imports[i].module_path);
if (head) {
snprintf(out, n, "%s%s", val, callee + head);
} else if (spawn_val_names(val, callee)) {
snprintf(out, n, "%s", val);
} else {
snprintf(out, n, "%s.%s", val, callee);
}
return;
}
}
static bool spawn_imports_module(const CBMImport *imports, int count, const char *module) {
size_t ml = strlen(module);
for (int i = 0; i < count; i++) {
if (!imports[i].module_path) {
continue;
}
const char *val = spawn_module(imports[i].module_path);
if (strncmp(val, module, ml) == 0 &&
(val[ml] == '\0' || val[ml] == '.' || val[ml] == ':' || val[ml] == '/')) {
return true;
}
}
return false;
}
/* Groovy `"git status".execute`: a quoted receiver, then the api suffix. */
static bool spawn_string_receiver(const char *callee, const char *suffix) {
size_t cl = strlen(callee);
size_t sl = strlen(suffix);
return (callee[0] == '"' || callee[0] == '\'') && cl > sl + 1 &&
strcmp(callee + cl - sl, suffix) == 0 && callee[cl - sl - 1] == callee[0];
}
static bool spawn_entry_matches(const cbm_spawn_api_t *e, const char *callee, const char *expanded,
const CBMImport *imports, int count) {
switch (e->rule) {
case CBM_SPAWN_QUALIFIED:
return strcmp(callee, e->api) == 0 || (expanded[0] && strcmp(expanded, e->api) == 0);
case CBM_SPAWN_BARE:
return strcmp(callee, e->api) == 0;
case CBM_SPAWN_BARE_IMPORTED:
return strcmp(callee, e->api) == 0 && spawn_imports_module(imports, count, e->module);
case CBM_SPAWN_STRING_RECEIVER:
return spawn_string_receiver(callee, e->api);
default:
return false;
}
}
const cbm_spawn_api_t *cbm_spawn_match(CBMLanguage lang, const char *callee,
const CBMImport *imports, int count) {
const cbm_spawn_api_t *table = spawn_table(lang);
if (!table || !callee || !callee[0]) {
return NULL;
}
if (!imports) {
count = 0;
}
char expanded[CBM_SZ_512];
spawn_expand(callee, imports, count, expanded, sizeof(expanded));
for (const cbm_spawn_api_t *e = table; e->api; e++) {
if (spawn_entry_matches(e, callee, expanded, imports, count)) {
return e;
}
}
return NULL;
}
/* The first quoted literal in `text` ("..", '..' or `..`), copied without
* its quotes. False when there is none. */
static bool spawn_first_literal(const char *text, char *out, size_t n) {
for (const char *p = text; *p; p++) {
if (*p != '"' && *p != '\'' && *p != '`') {
continue;
}
const char *end = p + 1;
while (*end && *end != *p) {
end += (*end == '\\' && end[1]) ? 2 : 1;
}
if (!*end) {
return false;
}
size_t len = (size_t)(end - p - 1);
if (len >= n) {
len = n - 1;
}
memcpy(out, p + 1, len);
out[len] = '\0';
return true;
}
return false;
}
static const char *spawn_arg_expr(const CBMCall *call, int index) {
for (int i = 0; i < call->arg_count; i++) {
if (call->args[i].index == index && call->args[i].expr) {
return call->args[i].expr;
}
}
return NULL;
}
static bool spawn_word_char(char c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
}
static const char *spawn_skip_space(const char *p) {
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
p++;
}
return p;
}
/* Past a keyword label in front of an argument (`launchPath: `, `args=`). */
static const char *spawn_skip_label(const char *p) {
const char *q = p;
while (spawn_word_char(*q)) {
q++;
}
if (q == p) {
return p;
}
q = spawn_skip_space(q);
if ((*q == ':' && q[1] != ':') || (*q == '=' && q[1] != '=')) {
return spawn_skip_space(q + 1);
}
return p;
}
/* A string literal at the start of `p`, after a prefix such as r, b, f, L
* or @ (r"..", L"..", @".."). */
static bool spawn_leading_literal(const char *p, char *out, size_t n) {
for (int i = 0; i < 2 && *p && strchr("rRbBuUfFL@$", *p); i++) {
p++;
}
return (*p == '"' || *p == '\'' || *p == '`') && spawn_first_literal(p, out, n);
}
/* What may stand before a collection's bracket: nothing, Zig's `&.` / `.`,
* Java's `new String[]`, or a collection constructor. A name before `[` is a
* subscript (`record["argv"]`) and any other call's arguments are not the
* command (`os.path.join("bin", tool)`). */
static bool spawn_collection_prefix(const char *p, size_t len) {
static const char *const ctors[] = {
"listOf", "arrayOf", "mutableListOf", "List.of", "Arrays.asList", "vec!", "c", "Cmd", NULL};
if (len == 0 || (len <= 2 && strspn(p, "&.") == len) ||
(len > 2 && strncmp(p + len - 2, "[]", 2) == 0 && strncmp(p, "new ", 4) == 0)) {
return true;
}
for (int i = 0; ctors[i]; i++) {
if (strlen(ctors[i]) == len && strncmp(p, ctors[i], len) == 0) {
return true;
}
}
return false;
}
/* The first element of a collection written at `p` ([..], (..), {..},
* listOf(..), &.{..}), copied into `out`. False when `p` is no collection. */
static bool spawn_first_element(const char *p, char *out, size_t n) {
const char *open = p;
while (*open && (spawn_word_char(*open) || strchr("!.&:<> ", *open) ||
(open[0] == '[' && open[1] == ']'))) {
open += (open[0] == '[') ? 2 : 1;
}
if ((*open != '[' && *open != '(' && *open != '{') ||
!spawn_collection_prefix(p, (size_t)(open - p))) {
return false;
}
p = open;
const char *start = spawn_skip_space(p + 1);
int depth = 0;
const char *end = start;
for (; *end; end++) {
if (*end == '"' || *end == '\'' || *end == '`') {
const char *close = strchr(end + 1, *end);
if (!close) {
break;
}
end = close;
} else if (strchr("[({", *end)) {
depth++;
} else if (strchr("])}", *end)) {
if (depth-- == 0) {
break;
}
} else if (*end == ',' && depth == 0) {
break;
}
}
size_t len = (size_t)(end - start);
if (len == 0 || len >= n) {
return false;
}
memcpy(out, start, len);
out[len] = '\0';
return true;
}
/* The program literal an argument spells: a leading literal, or the first
* element of a collection when that element is one. A program held in a
* variable (`[binary, "--version"]`) is no literal: the arguments after it
* are not the program. */
static bool spawn_expr_literal(const char *expr, char *out, size_t n) {
char element[CBM_SZ_256];
for (int depth = 0; depth < 3; depth++) {
expr = spawn_skip_label(spawn_skip_space(expr));
if (spawn_leading_literal(expr, out, n)) {
return true;
}
if (!spawn_first_element(expr, element, sizeof(element))) {
return false;
}
expr = element;
}
return false;
}
/* A literal from the argument at `index`. Tcl words are literals unquoted;
* elsewhere an unquoted argument counts only when the extractor itself read
* it as the call's string (Perl's `system("make")`). */
static bool spawn_arg_literal(const CBMCall *call, CBMLanguage lang, int index, bool anywhere,
char *out, size_t n) {
const char *expr = spawn_arg_expr(call, index);
if (!expr) {
if (call->arg_count == 0 && index == 0 && call->first_string_arg) {
snprintf(out, n, "%s", call->first_string_arg);
return true;
}
return false;
}
if (anywhere ? spawn_first_literal(expr, out, n) : spawn_expr_literal(expr, out, n)) {
return true;
}
bool bareword = lang == CBM_LANG_TCL && strpbrk(expr, "$[") == NULL;
if (bareword || (call->first_string_arg && strcmp(expr, call->first_string_arg) == 0)) {
snprintf(out, n, "%s", expr);
return true;
}
return false;
}
/* A shell word that is not the program: an environment assignment
* (`LC_ALL=C make`) or the `env` that runs the program after them
* (`/usr/bin/env make`). */
static bool spawn_prefix_word(const char *word, size_t len) {
static const char env[] = "env";
size_t env_len = sizeof(env) - 1;
if (len >= env_len && strncmp(word + len - env_len, env, env_len) == 0 &&
(len == env_len || word[len - env_len - 1] == '/')) {
return true;
}
size_t i = 0;
while (i < len && spawn_word_char(word[i])) {
i++;
}
return i > 0 && i < len && word[i] == '=';
}
/* The program word of a command line: its first word that is not an
* environment prefix, without directories. Anything built at run time is not
* a program name. */
static void spawn_program_word(const char *literal, char *buf, size_t n) {
size_t len = 0;
for (;;) {
while (*literal == ' ' || *literal == '\t') {
literal++;
}
len = strcspn(literal, " \t\r\n");
if (!spawn_prefix_word(literal, len) || !literal[len]) {
break;
}
literal += len;
}
const char *start = literal;
for (size_t i = 0; i < len; i++) {
if (literal[i] == '/' || literal[i] == '\\') {
start = literal + i + 1;
}
}
len -= (size_t)(start - literal);
bool dynamic = len == 0 || len >= CBM_SPAWN_PROGRAM_MAX || len >= n;
for (size_t i = 0; i < len && !dynamic; i++) {
dynamic = strchr("$%{}`()<>|;&*?[]\"'", start[i]) != NULL;
}
if (dynamic) {
snprintf(buf, n, "%s", CBM_SPAWN_DYNAMIC);
return;
}
memcpy(buf, start, len);
buf[len] = '\0';
}
void cbm_spawn_program(const cbm_spawn_api_t *api, CBMLanguage lang, const CBMCall *call, char *buf,
size_t n) {
char literal[CBM_SZ_256];
bool found = false;
if (api->rule == CBM_SPAWN_STRING_RECEIVER) {
found = spawn_first_literal(call->callee_name, literal, sizeof(literal));
} else {
bool anywhere = api->alt == CBM_SPAWN_ANY_LITERAL;
found = spawn_arg_literal(call, lang, api->arg, anywhere, literal, sizeof(literal)) ||
(api->alt >= 0 &&
spawn_arg_literal(call, lang, api->alt, false, literal, sizeof(literal)));
}
if (!found) {
snprintf(buf, n, "%s", CBM_SPAWN_DYNAMIC);
return;
}
spawn_program_word(literal, buf, n);
}
+71
View File
@@ -0,0 +1,71 @@
/*
* spawn_patterns.h — process-spawning call sites, per language.
*
* Like the HTTP client tables in service_patterns.h: a call that starts
* another program (subprocess.run, exec.Command, posix_spawn, Process.Start,
* Command::new, ...) becomes a SPAWNS edge to a Process node named after the
* program it starts. Nothing resolves the program to code: the edge records
* that the caller leaves the graph through a process boundary, and which
* program it starts when the call spells it as a literal.
*/
#ifndef CBM_SPAWN_PATTERNS_H
#define CBM_SPAWN_PATTERNS_H
#include "cbm.h"
#include <stdbool.h>
#include <stddef.h>
/* How a table spelling matches a call. */
typedef enum {
/* The full spelling, written out or reached through the file's imports
* (`sp.run` with `import subprocess as sp`, `run` from
* `from subprocess import run`, Go `exec.Command` from "os/exec"). */
CBM_SPAWN_QUALIFIED = 0,
/* A bare name (`system`, `popen`, `exec`): the spelling alone. A
* definition of that name in the calling module (or one the call reaches
* through an import) still makes it an ordinary call. */
CBM_SPAWN_BARE,
/* A bare name that counts when the file imports `module`
* (Haskell `callProcess` with System.Process, a destructured
* `const { spawn } = require("child_process")`). */
CBM_SPAWN_BARE_IMPORTED,
/* Groovy `"git status".execute()`: the receiver is the command line. */
CBM_SPAWN_STRING_RECEIVER,
} cbm_spawn_rule_t;
typedef struct {
const char *api; /* canonical spelling: "subprocess.run", "os/exec.Command", "system" */
const char *module; /* the import a CBM_SPAWN_BARE_IMPORTED name needs, else NULL */
unsigned char rule; /* cbm_spawn_rule_t */
signed char arg; /* argument holding the program or command line */
signed char alt; /* second place to look, -1 for none (CreateProcess: name or line),
* or CBM_SPAWN_ANY_LITERAL */
} cbm_spawn_api_t;
/* `alt` value: the program is the first literal anywhere in the argument,
* which is an options tuple or struct (Elixir {:spawn, "cat"}, Zig
* .{ .argv = &.{"ls"} }). Elsewhere only a leading literal or a collection's
* first element is the program. */
#define CBM_SPAWN_ANY_LITERAL (-2)
/* The spawn API a call names, or NULL. `imports` are the calling file's own
* import statements (local name -> module path) as extracted, external
* modules included: subprocess or os/exec never resolve to project nodes, so
* the resolver's import map does not hold them. Whether the call is a spawn
* after all is the resolver's to decide: an evidence-backed resolution onto
* project code (an import, the same module, the LSP; never a name guess)
* makes it an ordinary call (src/pipeline/pass_spawns.c). */
const cbm_spawn_api_t *cbm_spawn_match(CBMLanguage lang, const char *callee,
const CBMImport *imports, int count);
/* The program a matched spawn starts: the first literal in its program
* argument, that literal's first word, without directories
* ("/usr/bin/git status" -> "git"). CBM_SPAWN_DYNAMIC when the program is
* not a literal. Writes into buf (at least CBM_SPAWN_PROGRAM_MAX bytes). */
#define CBM_SPAWN_DYNAMIC "<dynamic>"
#define CBM_SPAWN_PROGRAM_MAX 64
void cbm_spawn_program(const cbm_spawn_api_t *api, CBMLanguage lang, const CBMCall *call, char *buf,
size_t n);
#endif
+21 -1
View File
@@ -30,6 +30,26 @@ The grammars were originally vendored as bare `parser.c`+`scanner.c` with **no r
The table uses `UPSTREAM-RELEASE` because this refresh verified the tagged
upstream release itself but did not independently verify a current registry
pin; it intentionally does not claim `VERIFIED-NVIM` or `VERIFIED-BOTH`.
- **c_sharp** (refreshed 2026-09-25, #1748): byte-for-byte generated bundle
(`parser.c`, `scanner.c`, `tree_sitter/{alloc,array,parser}.h`) from
[tree-sitter/tree-sitter-c-sharp](https://github.com/tree-sitter/tree-sitter-c-sharp)
tag `v0.23.5`, full commit `cac6d5fb595f5811a076336682d5d595ac1c9e85` (ABI 15,
unchanged), replacing `88366631d598`. The refresh picks up upstream's C# 12
collection-expression support (#402, first released in v0.23.4): new named
nodes `collection_expression`, `collection_element`, `expression_element`,
`spread_element` (purely additive; no node type was removed or renamed, and
`scanner.c` is content-identical). Before it, `[...]` literals only parsed as
`element_binding_expression`, so `c ? [] : x` and similar conditionals were
reported as parse_partial. Upstream HEAD (`9150f7d56bb4`, C# 14 support) was
checked and gives identical results on the #1748 cases and the dotnet/runtime
corpus residuals, so the release tag was taken. Upstream's generated files
were copied from the tag checkout; no upstream JS/build code was run. The
existing MIT `LICENSE` is byte-identical to upstream at the tag (SHA-256
`778fb7d63b8c1844da315648c02f325c4713a6f4a5d19644fd413421422776d3`).
Previous vendored copies differed from the pin only by stripped trailing
newlines; the new files keep upstream's bytes exactly. Per-file SHA-256
values are pinned in `scripts/vendored-checksums.txt`. Regression coverage:
the `cs_*_issue1748` tests in `tests/test_parse_coverage.c`.
- **objectscript_udl / objectscript_routine** (added 2026-06-24): vendored from [intersystems/tree-sitter-objectscript](https://github.com/intersystems/tree-sitter-objectscript) @ `a7ffcdf` — MIT, the InterSystems-official grammars (a niche vendor language, hence `vendor-maintained`, not in nvim-treesitter/Helix). **Re-vendor note:** each `scanner.c`'s upstream `#include "../../common/scanner.h"` is repointed to a per-directory `objectscript_common.h` (a verbatim copy of upstream `common/scanner.h`), because this repo's shared `vendored/common/scanner.h` belongs to the cfml/fsharp grammars and differs. The generated `parser.c`/`scanner.c` are otherwise byte-for-byte upstream — on re-vendor, re-apply only that single include rename. **Local modification (2026-07-16):** in `objectscript_common.h`, two loop counters `uint8_t i` were widened to `int i` (the `reverse_marker` scan and the `html_marker_buffer` reversal) to clear CodeQL `cpp/comparison-with-wider-type` — a false positive in practice (both lengths are hard-bounded by `MARKER_BUFFER_MAX_LEN = 30`, so `uint8_t` could never wrap), fixed for cleanliness. On re-vendor, re-apply this widening too (or upstream it at intersystems/tree-sitter-objectscript).
- **mojo** (added 2026-07-01): vendored from [lsh/tree-sitter-mojo](https://github.com/lsh/tree-sitter-mojo) @ `33193a99afe6` — MIT, ABI 15. Helix tracks `lsh/tree-sitter-mojo` as its Mojo grammar source, but the Helix-pinned commit (`3d7c53b8038f`) no longer resolves in the upstream repository after a force-push, so this vendor uses current upstream `main` rather than the stale registry SHA. Security review covered only the vendored C surface (`parser.c`, `scanner.c`, `tree_sitter/*.h`) plus upstream license/provenance metadata; no package manager hooks, workflow files, prompt/agent instruction files, or generated lockfiles were vendored.
- **arkts** (added 2026-08-26): **first-party derivative** — a fork of [tree-sitter/tree-sitter-typescript](https://github.com/tree-sitter/tree-sitter-typescript)'s `typescript` dialect pinned @ `75b3874edb2d` (v0.23.2, the same commit our vendored typescript/tsx come from), on the tree-sitter-javascript base @ `3a837b6f3658` (v0.23.1, the version upstream's own package-lock pins), extended with ArkTS/ArkUI syntax (`@Component struct` declarations, UI-DSL trailing-closure calls with post-block attribute chains, decorated function declarations, `import lazy`, `@Extend`/`@Styles` leading-dot attribute chains, anonymous `stateStyles` style blocks). Grammar source + corpus tests live in `tools/tree-sitter-arkts/`; regenerate with `npx tree-sitter-cli@0.25.10 generate` (ABI 15). **External scanner:** `scanner.c` is a symbol-rename trampoline; `_common_scanner.h` is byte-identical (same SHA-256) to the reviewed `typescript/_common_scanner.h` already shipped. **LICENSE:** tree-sitter-typescript's MIT text **verbatim and byte-identical** (© 2017 Max Brunsfeld) — which is what MIT requires of a derivative work, and what lets the provenance audit byte-verify it against upstream rather than take a note on trust. The fork is registered in the audit's `FORKS` map. Our own copyright for the ArkTS additions, and the tree-sitter-javascript attribution (© 2014 Max Brunsfeld), live with the SOURCE in `tools/tree-sitter-arkts/grammar.js` and in `THIRD_PARTY.md`, which is what ships in the release archives.
@@ -109,7 +129,7 @@ row instead.
| bitbake | 14 | tree-sitter-grammars/tree-sitter-bitbake | `a5d04fdb5a69` | VERIFIED-BOTH | ✅ |
| blade | 15 | EmranMR/tree-sitter-blade | `b9436b7b9369` | VERIFIED-BOTH | ✅ |
| c | 15 | tree-sitter/tree-sitter-c | `ae19b676b13b` | VERIFIED-BOTH | ✅ |
| c_sharp | 15 | tree-sitter/tree-sitter-c-sharp | `88366631d598` | VERIFIED-BOTH | ✅ |
| c_sharp | 15 | tree-sitter/tree-sitter-c-sharp | `cac6d5fb595f` | UPSTREAM-RELEASE | ✅ |
| cairo | 14 | tree-sitter-grammars/tree-sitter-cairo | `6238f609bea2` | VERIFIED-NVIM | ✅ |
| capnp | 14 | tree-sitter-grammars/tree-sitter-capnp | `7b0883c03e5e` | VERIFIED-BOTH | ✅ |
| clojure | 14 | sogaiu/tree-sitter-clojure | `e43eff80d17c` | VERIFIED-BOTH | ✅ |
+620788 -611671
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -418,4 +418,4 @@ bool tree_sitter_c_sharp_external_scanner_scan(void *payload, TSLexer *lexer, co
}
return false;
}
}
@@ -51,4 +51,4 @@ extern void (*ts_current_free)(void *ptr);
}
#endif
#endif // TREE_SITTER_ALLOC_H_
#endif // TREE_SITTER_ALLOC_H_
@@ -60,7 +60,13 @@ extern "C" {
/// Free any memory allocated for this array. Note that this does not free any
/// memory allocated for the array's contents.
#define array_delete(self) _array__delete((self), (void *)(self)->contents, sizeof(*self))
#define array_delete(self) \
do { \
if ((self)->contents) ts_free((self)->contents); \
(self)->contents = NULL; \
(self)->size = 0; \
(self)->capacity = 0; \
} while (0)
/// Push a new `element` onto the end of the array.
#define array_push(self, element) \
@@ -130,12 +136,11 @@ extern "C" {
/// Swap one array with another
#define array_swap(self, other) \
do { \
struct Swap swapped_contents = _array__swap( \
(void *)(self)->contents, &(self)->size, &(self)->capacity, \
(void *)(other)->contents, &(other)->size, &(other)->capacity \
); \
(self)->contents = swapped_contents.self_contents; \
(other)->contents = swapped_contents.other_contents; \
void *_array_swap_tmp = (void *)(self)->contents; \
(self)->contents = (other)->contents; \
(other)->contents = _array_swap_tmp; \
_array__swap(&(self)->size, &(self)->capacity, \
&(other)->size, &(other)->capacity); \
} while (0)
/// Get the size of the array contents
@@ -188,12 +193,6 @@ extern "C" {
// The `Array` type itself was not altered as a solution in order to avoid breakage
// with existing consumers (in particular, parsers with external scanners).
/// This is not what you're looking for, see `array_delete`.
static inline void _array__delete(void *self, void *contents, size_t self_size) {
if (contents) ts_free(contents);
if (self) memset(self, 0, self_size);
}
/// This is not what you're looking for, see `array_erase`.
static inline void _array__erase(void* self_contents, uint32_t *size,
size_t element_size, uint32_t index) {
@@ -228,31 +227,15 @@ static inline void *_array__assign(void* self_contents, uint32_t *self_size, uin
return new_contents;
}
struct Swap {
void *self_contents;
void *other_contents;
};
/// This is not what you're looking for, see `array_swap`.
// static inline void _array__swap(Array *self, Array *other) {
static inline struct Swap _array__swap(void *self_contents, uint32_t *self_size, uint32_t *self_capacity,
void *other_contents, uint32_t *other_size, uint32_t *other_capacity) {
void *new_self_contents = other_contents;
uint32_t new_self_size = *other_size;
uint32_t new_self_capacity = *other_capacity;
void *new_other_contents = self_contents;
*other_size = *self_size;
*other_capacity = *self_capacity;
*self_size = new_self_size;
*self_capacity = new_self_capacity;
struct Swap out = {
.self_contents = new_self_contents,
.other_contents = new_other_contents,
};
return out;
static inline void _array__swap(uint32_t *self_size, uint32_t *self_capacity,
uint32_t *other_size, uint32_t *other_capacity) {
uint32_t tmp_size = *self_size;
uint32_t tmp_capacity = *self_capacity;
*self_size = *other_size;
*self_capacity = *other_capacity;
*other_size = tmp_size;
*other_capacity = tmp_capacity;
}
/// This is not what you're looking for, see `array_push` or `array_grow_by`.
@@ -344,4 +327,4 @@ static inline void *_array__splice(void *self_contents, uint32_t *size, uint32_t
}
#endif
#endif // TREE_SITTER_ARRAY_H_
#endif // TREE_SITTER_ARRAY_H_
@@ -283,4 +283,4 @@ static inline bool set_contains(const TSCharacterRange *ranges, uint32_t len, in
}
#endif
#endif // TREE_SITTER_PARSER_H_
#endif // TREE_SITTER_PARSER_H_
+3 -1
View File
@@ -55,9 +55,11 @@ codebase-memory-mcp install # configure all detected coding agents
codebase-memory-mcp --version
codebase-memory-mcp --help
codebase-memory-mcp update # update to latest release
codebase-memory-mcp uninstall # remove agent configs
codebase-memory-mcp uninstall # remove agent configs; keep indexes by default
```
Uninstall keeps project indexes with `--yes`, `--no`, or noninteractive input unless you explicitly add `--delete-indexes`. An interactive terminal without the flag asks separately and defaults to keeping them. `--delete-indexes` overrides `--no`; add `--dry-run` to preview without changing files. Kept indexes remain in the cache directory printed by uninstall.
### CLI Mode
Every MCP tool is also available directly from the command line:
+231 -29
View File
@@ -8,7 +8,7 @@ const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const os = require('os');
const { execFileSync } = require('child_process');
const { execFileSync, spawn } = require('child_process');
const { pipeline } = require('stream');
const REPO = 'DeusData/codebase-memory-mcp';
@@ -18,6 +18,20 @@ const MAX_REDIRECTS = 5;
const DOWNLOAD_HOP_TIMEOUT_MS = 120_000;
const CANDIDATE_TIMEOUT_MS = 15_000;
const MAX_CHECKSUM_MANIFEST_BYTES = 1024 * 1024;
// Release archive resource limits. Keep in sync with the Go wrapper
// (pkg/go/cmd/codebase-memory-mcp/main.go) and the PyPI wrapper (_cli.py).
const MAX_RELEASE_ARCHIVE_BYTES = 256 * 1024 * 1024;
const MAX_ARCHIVE_MEMBERS = 64;
const MAX_ARCHIVE_MEMBER_BYTES = 256 * 1024 * 1024;
const MAX_ARCHIVE_EXPANDED_BYTES = 512 * 1024 * 1024;
const DEFAULT_ARCHIVE_LIMITS = Object.freeze({
compressedBytes: MAX_RELEASE_ARCHIVE_BYTES,
members: MAX_ARCHIVE_MEMBERS,
memberBytes: MAX_ARCHIVE_MEMBER_BYTES,
expandedBytes: MAX_ARCHIVE_EXPANDED_BYTES,
});
// Bytes of tar's own diagnostics kept for the failure message.
const MAX_TAR_STDERR_CHARS = 4096;
const WINDOWS_BINARY_NAME = 'codebase-memory-mcp.exe';
const UNIX_ARCHIVE_NAMES = [
'codebase-memory-mcp',
@@ -90,21 +104,131 @@ function validateExactTarMemberListing(listing, expectedNames) {
}
}
function extractExactTarArchive(
archivePath, destPath, expectedNames, targetName, runFile = execFileSync,
function validateArchiveLimits(limits) {
if (!limits || !(limits.compressedBytes > 0) || !(limits.members > 0) ||
!(limits.memberBytes > 0) || !(limits.expandedBytes > 0)) {
throw new Error('invalid archive resource safety limits');
}
}
function requireCompressedArchiveWithinLimit(archivePath, limits) {
const status = fs.statSync(archivePath);
if (!status.isFile()) {
throw new Error('release archive is not a regular file');
}
if (status.size > limits.compressedBytes) {
throw new Error(
`release archive exceeds the ${limits.compressedBytes}-byte compressed safety limit`,
);
}
}
// Streams one member through `tar -xzOf` into a file this call creates,
// counting every byte against the per-member limit. The file is removed only
// when this call created it: a pre-existing target fails untouched.
function extractTarMemberWithLimit(
archivePath, target, memberName, limits, spawnFile = spawn,
) {
return new Promise((resolve, reject) => {
let fd;
try {
fd = fs.openSync(target, 'wx', 0o600);
} catch (err) {
reject(err);
return;
}
let written = 0;
let failure = null;
let settled = false;
let stderr = '';
const finish = (err) => {
if (settled) return;
settled = true;
try { fs.closeSync(fd); } catch (closeError) { err = err || closeError; }
if (err) {
try { fs.unlinkSync(target); } catch (_) { /* already gone */ }
reject(err);
return;
}
resolve(written);
};
const child = spawnFile('tar', ['-xzOf', archivePath, memberName], {
stdio: ['ignore', 'pipe', 'pipe'],
windowsHide: true,
});
const fail = (err) => {
if (failure) return;
failure = err;
child.kill();
};
child.stdout.on('data', (chunk) => {
if (failure) return;
written += chunk.length;
if (written > limits.memberBytes) {
fail(new Error(
`archive member ${JSON.stringify(memberName)} exceeds the ${limits.memberBytes}-byte expanded safety limit`,
));
return;
}
try {
let offset = 0;
while (offset < chunk.length) {
offset += fs.writeSync(fd, chunk, offset, chunk.length - offset);
}
} catch (err) {
fail(err);
}
});
child.stderr.setEncoding('utf8');
child.stderr.on('data', (text) => {
if (stderr.length < MAX_TAR_STDERR_CHARS) stderr += text;
});
child.on('error', (err) => {
failure = failure || err;
// A process that never started (no tar on PATH) has no exit to wait for.
if (child.pid === undefined) finish(failure);
});
child.on('close', (code, signal) => {
const detail = stderr.trim();
let err = failure;
if (!err && signal) {
err = new Error(
`tar was terminated by ${signal} while extracting ${memberName}: ${detail}`,
);
} else if (!err && code !== 0) {
err = new Error(
`tar exited with status ${code} while extracting ${memberName}: ${detail}`,
);
} else if (!err && written === 0) {
err = new Error(`archive member ${memberName} is empty`);
}
finish(err);
});
});
}
async function extractExactTarArchive(
archivePath, destPath, expectedNames, targetName,
runFile = execFileSync, limits = DEFAULT_ARCHIVE_LIMITS, spawnFile = spawn,
) {
validateArchiveLimits(limits);
requireCompressedArchiveWithinLimit(archivePath, limits);
// The exact root namespace is the member bound: its four names lie well
// within MAX_ARCHIVE_MEMBERS, which the zip path enforces by count.
if (expectedNames.length > limits.members) {
throw new Error(`archive namespace exceeds the ${limits.members}-member safety limit`);
}
const listing = runFile('tar', ['-tzf', archivePath], {
encoding: 'utf8',
maxBuffer: MAX_CHECKSUM_MANIFEST_BYTES,
windowsHide: true,
});
validateExactTarMemberListing(listing, expectedNames);
// Extract only the fixed root executable. Even a malformed tar implementation
// cannot write an unvalidated companion member outside the private temp tree.
runFile('tar', ['-xzf', archivePath, '-C', destPath, targetName], {
stdio: 'inherit',
windowsHide: true,
});
// Only the fixed root executable is ever written, through the counted
// writer; the companions are listed but never extracted.
const target = path.join(destPath, targetName);
await extractTarMemberWithLimit(archivePath, target, targetName, limits, spawnFile);
fs.chmodSync(target, 0o755);
}
function downloadHop(url, dest, maxBytes) {
@@ -123,7 +247,11 @@ function downloadHop(url, dest, maxBytes) {
const req = https.get(url, (res) => {
response = res;
res.on('error', (err) => finish(err));
let piped = false;
// Before the body is piped, a response error ends the hop directly;
// afterwards the pipeline reports it, so the partial file is discarded
// before the hop settles.
res.on('error', (err) => { if (!piped) finish(err); });
const redirectCodes = new Set([301, 302, 303, 307, 308]);
if (redirectCodes.has(res.statusCode)) {
const location = res.headers.location;
@@ -149,6 +277,12 @@ function downloadHop(url, dest, maxBytes) {
finish(new Error(`HTTP ${res.statusCode} for ${url}`));
return;
}
const declared = Number(res.headers['content-length']);
if (maxBytes && Number.isFinite(declared) && declared > maxBytes) {
res.destroy();
finish(new Error(`Download exceeds the ${maxBytes}-byte safety limit`));
return;
}
let received = 0;
res.on('data', (chunk) => {
@@ -158,8 +292,20 @@ function downloadHop(url, dest, maxBytes) {
}
});
const file = fs.createWriteStream(dest, { flags: 'w' });
piped = true;
pipeline(res, file, (err) => {
finish(err, { redirect: null });
if (!err) {
finish(null, { redirect: null });
return;
}
// Discard the partial file once its handle is closed (Windows refuses
// to unlink an open file), then report the failure.
const discard = () => {
try { fs.unlinkSync(dest); } catch (_) { /* nothing was written */ }
finish(err);
};
if (file.closed) discard();
else file.once('close', discard);
});
});
req.on('error', (err) => finish(err));
@@ -457,19 +603,37 @@ function installWindowsBinaryAtomically(sourceDir, destDir, verifier = verifyCan
}
}
function extractZipOnWindows(archivePath, destPath, requiredNames, extractNames) {
function extractZipOnWindows(
archivePath, destPath, requiredNames, extractNames,
limits = DEFAULT_ARCHIVE_LIMITS, shell = 'powershell',
) {
validateArchiveLimits(limits);
requireCompressedArchiveWithinLimit(archivePath, limits);
// -EncodedCommand is a constant program. Paths travel only through the child
// environment and are consumed with -LiteralPath, so PowerShell never parses
// user/TEMP path bytes as source code or wildcard syntax.
const script = [
"$ErrorActionPreference = 'Stop'",
'Add-Type -AssemblyName System.IO.Compression.FileSystem',
'$maxMembers = [int]$env:CBM_NPM_MAX_MEMBERS',
'$maxMemberBytes = [long]$env:CBM_NPM_MAX_MEMBER_BYTES',
'$maxExpandedBytes = [long]$env:CBM_NPM_MAX_EXPANDED_BYTES',
'$memberCount = 0',
'$declaredExpanded = [long]0',
'$zip = [System.IO.Compression.ZipFile]::OpenRead($env:CBM_NPM_ARCHIVE_PATH)',
"$seen = New-Object 'System.Collections.Generic.HashSet[string]' ([System.StringComparer]::OrdinalIgnoreCase)",
"$requiredNames = @($env:CBM_NPM_REQUIRED_NAMES.Split('|'))",
'$targetCounts = @{}',
'foreach ($requiredName in $requiredNames) { $targetCounts[$requiredName] = 0 }',
'try { foreach ($entry in $zip.Entries) { ' +
'$memberCount = $memberCount + 1; ' +
'if ($memberCount -gt $maxMembers) { ' +
'throw "archive exceeds the $maxMembers-member safety limit" }; ' +
'if ($entry.Length -gt $maxMemberBytes) { ' +
'throw "archive member $($entry.FullName) exceeds the $maxMemberBytes-byte expanded safety limit" }; ' +
'if ($declaredExpanded -gt ($maxExpandedBytes - $entry.Length)) { ' +
'throw "archive exceeds the $maxExpandedBytes-byte aggregate expanded safety limit" }; ' +
'$declaredExpanded = $declaredExpanded + $entry.Length; ' +
"$name = $entry.FullName.Replace('\\', '/'); " +
"$directory = $name.EndsWith('/'); " +
"$segmentsPath = if ($directory) { $name.TrimEnd('/') } else { $name }; " +
@@ -491,27 +655,61 @@ function extractZipOnWindows(archivePath, destPath, requiredNames, extractNames)
'if ($seen.Count -ne $requiredNames.Count) { ' +
'throw "archive does not match the exact release root-file allowlist" }',
"$extractNames = @($env:CBM_NPM_EXTRACT_NAMES.Split('|'))",
'$actualExpanded = [long]0',
'$buffer = New-Object byte[] 65536',
'$extractZip = [System.IO.Compression.ZipFile]::OpenRead($env:CBM_NPM_ARCHIVE_PATH)',
// Copy by hand with a byte counter: ExtractToFile would write whatever the
// compressed stream yields, regardless of the declared size.
'try { foreach ($extractName in $extractNames) { ' +
'$entry = @($extractZip.Entries | Where-Object { $_.FullName -ceq $extractName })[0]; ' +
'[System.IO.Compression.ZipFileExtensions]::ExtractToFile(' +
'$entry, (Join-Path $env:CBM_NPM_DEST_PATH $extractName), $false) ' +
'$targetPath = Join-Path $env:CBM_NPM_DEST_PATH $extractName; ' +
'$remaining = $maxExpandedBytes - $actualExpanded; ' +
'$copied = [long]0; ' +
'$source = $entry.Open(); ' +
'$target = [System.IO.File]::Open($targetPath, [System.IO.FileMode]::CreateNew, [System.IO.FileAccess]::Write); ' +
'try { while ($true) { ' +
'$count = $source.Read($buffer, 0, $buffer.Length); ' +
'if ($count -le 0) { break }; ' +
'$copied = $copied + $count; ' +
'if ($copied -gt $maxMemberBytes) { ' +
'throw "archive member $extractName exceeds the $maxMemberBytes-byte actual expanded safety limit" }; ' +
'if ($copied -gt $remaining) { ' +
'throw "archive exceeds the $maxExpandedBytes-byte aggregate actual expanded safety limit" }; ' +
'$target.Write($buffer, 0, $count) } ' +
'} catch { $target.Dispose(); ' +
'Remove-Item -LiteralPath $targetPath -Force -ErrorAction SilentlyContinue; throw ' +
'} finally { $target.Dispose(); $source.Dispose() }; ' +
'if ($copied -ne $entry.Length) { ' +
'Remove-Item -LiteralPath $targetPath -Force -ErrorAction SilentlyContinue; ' +
'throw "archive member $extractName actual size $copied does not match declared size $($entry.Length)" }; ' +
'$actualExpanded = $actualExpanded + $copied ' +
'} } finally { $extractZip.Dispose() }',
].join('; ');
const encoded = Buffer.from(script, 'utf16le').toString('base64');
execFileSync('powershell', [
'-NoLogo', '-NoProfile', '-NonInteractive', '-EncodedCommand', encoded,
], {
env: {
...process.env,
CBM_NPM_ARCHIVE_PATH: archivePath,
CBM_NPM_DEST_PATH: destPath,
CBM_NPM_REQUIRED_NAMES: requiredNames.join('|'),
CBM_NPM_EXTRACT_NAMES: extractNames.join('|'),
},
stdio: 'inherit',
windowsHide: true,
});
try {
execFileSync(shell, [
'-NoLogo', '-NoProfile', '-NonInteractive', '-EncodedCommand', encoded,
], {
env: {
...process.env,
CBM_NPM_ARCHIVE_PATH: archivePath,
CBM_NPM_DEST_PATH: destPath,
CBM_NPM_REQUIRED_NAMES: requiredNames.join('|'),
CBM_NPM_EXTRACT_NAMES: extractNames.join('|'),
CBM_NPM_MAX_MEMBERS: String(limits.members),
CBM_NPM_MAX_MEMBER_BYTES: String(limits.memberBytes),
CBM_NPM_MAX_EXPANDED_BYTES: String(limits.expandedBytes),
},
encoding: 'utf8',
stdio: ['ignore', 'inherit', 'pipe'],
windowsHide: true,
});
} catch (err) {
// Surface the script's own reason (for example which limit was hit)
// instead of a bare "Command failed".
const detail = err && err.stderr ? String(err.stderr).trim() : '';
throw new Error(detail ? `zip extraction failed: ${detail}` : err.message);
}
}
// Fetch checksums.txt and verify the archive hash.
@@ -592,12 +790,12 @@ async function main() {
const tmpArchive = path.join(tmpDir, `cbm.${ext}`);
try {
await download(url, tmpArchive);
await download(url, tmpArchive, MAX_RELEASE_ARCHIVE_BYTES);
await verifyChecksum(tmpArchive, archive);
// Validate the complete archive namespace, then extract only executables.
if (ext === 'tar.gz') {
extractExactTarArchive(
await extractExactTarArchive(
tmpArchive, tmpDir, archiveNames, binName,
);
} else {
@@ -674,9 +872,13 @@ if (require.main === module || module.parent == null) {
}
module.exports = {
DEFAULT_ARCHIVE_LIMITS,
UNIX_ARCHIVE_NAMES,
WINDOWS_ARCHIVE_NAMES,
WINDOWS_BINARY_NAME,
download,
extractExactTarArchive,
extractZipOnWindows,
installWindowsBinaryAtomically,
validateExactTarMemberListing,
windowsBinaryReady,
+126
View File
@@ -0,0 +1,126 @@
'use strict';
// Small synthetic release archives for the wrapper tests.
const { execFileSync } = require('node:child_process');
const fs = require('node:fs');
const path = require('node:path');
// The npm wrapper takes the zip path whenever Node reports win32 (see
// getPlatform and `ext` in install.js), so the tar path is unreachable on a
// Windows host and the tar cases would test nothing a user can reach there.
// They cannot even build their fixtures on the Windows runner: the first
// `tar` on its PATH is Git for Windows' GNU tar, which reads a drive-letter
// path such as C:\... as a remote host ("tar (child): Cannot connect to C:
// resolve failed"; every tar case failed that way in the first Windows run),
// while Windows' own bsdtar accepts it. Under WSL Node is a Linux binary,
// takes the tar path with POSIX paths, and these cases run there.
const TAR_PATH_SKIP = process.platform === 'win32'
? 'the npm wrapper never takes the tar path on win32 (it installs the zip asset)'
: false;
// Deterministic, poorly compressible bytes (xorshift32), so a fixture of a
// few hundred KiB still spans several pipe chunks after gzip.
function patternBytes(length, seed = 0x9e3779b9) {
const out = Buffer.alloc(length);
let state = seed >>> 0;
for (let index = 0; index < length; index += 1) {
state ^= state << 13;
state >>>= 0;
state ^= state >>> 17;
state ^= state << 5;
state >>>= 0;
out[index] = state & 0xff;
}
return out;
}
// Writes entries ({ name, data }) into a staging directory under `root` and
// packs them with the system tar, the producer the release itself uses.
function writeTarGz(root, archiveName, entries) {
const stage = fs.mkdtempSync(path.join(root, 'stage-'));
for (const entry of entries) {
fs.writeFileSync(path.join(stage, entry.name), entry.data);
}
const archive = path.join(root, archiveName);
execFileSync(
'tar',
['-czf', archive, '-C', stage, ...entries.map((entry) => entry.name)],
{
env: { ...process.env, COPYFILE_DISABLE: '1' },
stdio: ['ignore', 'ignore', 'inherit'],
windowsHide: true,
},
);
return archive;
}
const CRC_TABLE = (() => {
const table = new Int32Array(256);
for (let n = 0; n < 256; n += 1) {
let c = n;
for (let k = 0; k < 8; k += 1) {
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
}
table[n] = c;
}
return table;
})();
function crc32(buffer) {
let crc = -1;
for (const byte of buffer) {
crc = CRC_TABLE[(crc ^ byte) & 0xff] ^ (crc >>> 8);
}
return (crc ^ -1) >>> 0;
}
// entries: [{ name, data, declaredSize = data.length }]; stored (no
// compression). Hand-rolled so the directory can claim a size that differs
// from the bytes the entry carries, which no zip writer would produce.
function buildZip(entries) {
const locals = [];
const centrals = [];
let offset = 0;
for (const entry of entries) {
const data = Buffer.isBuffer(entry.data) ? entry.data : Buffer.from(entry.data);
const name = Buffer.from(entry.name, 'utf8');
const declaredSize = entry.declaredSize === undefined
? data.length
: entry.declaredSize;
const crc = crc32(data);
const local = Buffer.alloc(30 + name.length);
local.writeUInt32LE(0x04034b50, 0);
local.writeUInt16LE(20, 4);
local.writeUInt32LE(crc, 14);
local.writeUInt32LE(data.length, 18);
local.writeUInt32LE(declaredSize, 22);
local.writeUInt16LE(name.length, 26);
name.copy(local, 30);
const central = Buffer.alloc(46 + name.length);
central.writeUInt32LE(0x02014b50, 0);
central.writeUInt16LE(20, 4);
central.writeUInt16LE(20, 6);
central.writeUInt32LE(crc, 16);
central.writeUInt32LE(data.length, 20);
central.writeUInt32LE(declaredSize, 24);
central.writeUInt16LE(name.length, 28);
central.writeUInt32LE(offset, 42);
name.copy(central, 46);
locals.push(local, data);
centrals.push(central);
offset += local.length + data.length;
}
const directory = Buffer.concat(centrals);
const end = Buffer.alloc(22);
end.writeUInt32LE(0x06054b50, 0);
end.writeUInt16LE(entries.length, 8);
end.writeUInt16LE(entries.length, 10);
end.writeUInt32LE(directory.length, 12);
end.writeUInt32LE(offset, 16);
return Buffer.concat([...locals, directory, end]);
}
module.exports = { TAR_PATH_SKIP, buildZip, patternBytes, writeTarGz };
+350
View File
@@ -0,0 +1,350 @@
'use strict';
const assert = require('node:assert/strict');
const { EventEmitter } = require('node:events');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const { Readable } = require('node:stream');
const test = require('node:test');
const vm = require('node:vm');
const {
DEFAULT_ARCHIVE_LIMITS,
UNIX_ARCHIVE_NAMES,
WINDOWS_ARCHIVE_NAMES,
WINDOWS_BINARY_NAME,
extractExactTarArchive,
extractZipOnWindows,
} = require('../install.js');
const {
TAR_PATH_SKIP, buildZip, patternBytes, writeTarGz,
} = require('./archive-fixtures.js');
const EXECUTABLE = 'codebase-memory-mcp';
// Spans several pipe chunks, so an over-limit member is cut off mid-stream.
const LARGE_EXECUTABLE = patternBytes(300 * 1024);
function withScratch(callback) {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cbm-npm-archive-test-'));
return Promise.resolve()
.then(() => callback(root))
.finally(() => fs.rmSync(root, { recursive: true, force: true }));
}
function limitsWith(overrides) {
return { ...DEFAULT_ARCHIVE_LIMITS, ...overrides };
}
function releaseEntries(names, executableData) {
return names.map((name) => ({
name,
data: executableData !== undefined && (name === EXECUTABLE || name === WINDOWS_BINARY_NAME)
? executableData
: `payload of ${name}`,
}));
}
function makeDestination(root) {
const destination = path.join(root, 'extract');
fs.mkdirSync(destination);
return destination;
}
// Stands in for the system tar listing where the archive itself is damaged
// on purpose and the real listing would fail before the writer runs.
function fakeTarListing(names) {
return (command, args) => {
assert.equal(command, 'tar');
assert.equal(args[0], '-tzf');
return `${names.join('\n')}\n`;
};
}
function neverLists() {
throw new Error('the listing must not run');
}
test('tar extraction refuses an archive larger than the compressed limit', { skip: TAR_PATH_SKIP }, () =>
withScratch(async (root) => {
const archive = writeTarGz(root, 'release.tar.gz', releaseEntries(UNIX_ARCHIVE_NAMES));
const destination = makeDestination(root);
await assert.rejects(
extractExactTarArchive(
archive, destination, UNIX_ARCHIVE_NAMES, EXECUTABLE,
neverLists, limitsWith({ compressedBytes: 16 }),
),
/16-byte compressed safety limit/,
);
assert.deepEqual(fs.readdirSync(destination), []);
}));
test('tar extraction refuses an exact namespace wider than the member limit', { skip: TAR_PATH_SKIP }, () =>
withScratch(async (root) => {
const archive = writeTarGz(root, 'release.tar.gz', releaseEntries(UNIX_ARCHIVE_NAMES));
const destination = makeDestination(root);
await assert.rejects(
extractExactTarArchive(
archive, destination, UNIX_ARCHIVE_NAMES, EXECUTABLE,
neverLists, limitsWith({ members: 2 }),
),
/2-member safety limit/,
);
assert.deepEqual(fs.readdirSync(destination), []);
}));
test('tar extraction stops an over-limit member in the counted writer and leaves no file', { skip: TAR_PATH_SKIP }, () =>
withScratch(async (root) => {
const archive = writeTarGz(
root, 'release.tar.gz', releaseEntries(UNIX_ARCHIVE_NAMES, LARGE_EXECUTABLE),
);
const destination = makeDestination(root);
// The real listing passes (names only); the limit trips while tar is
// still streaming the executable into the writer.
await assert.rejects(
extractExactTarArchive(
archive, destination, UNIX_ARCHIVE_NAMES, EXECUTABLE,
undefined, limitsWith({ memberBytes: 64 * 1024 }),
),
/"codebase-memory-mcp" exceeds the 65536-byte expanded safety limit/,
);
assert.deepEqual(fs.readdirSync(destination), []);
}));
test('tar extraction leaves a pre-existing target untouched', { skip: TAR_PATH_SKIP }, () =>
withScratch(async (root) => {
const archive = writeTarGz(root, 'release.tar.gz', releaseEntries(UNIX_ARCHIVE_NAMES));
const destination = makeDestination(root);
const target = path.join(destination, EXECUTABLE);
fs.writeFileSync(target, 'keep');
await assert.rejects(
extractExactTarArchive(archive, destination, UNIX_ARCHIVE_NAMES, EXECUTABLE),
/EEXIST/,
);
assert.equal(fs.readFileSync(target, 'utf8'), 'keep');
}));
test('tar extraction leaves no file when tar fails mid-stream', { skip: TAR_PATH_SKIP }, () =>
withScratch(async (root) => {
const complete = writeTarGz(
root, 'complete.tar.gz', releaseEntries(UNIX_ARCHIVE_NAMES, LARGE_EXECUTABLE),
);
const bytes = fs.readFileSync(complete);
const archive = path.join(root, 'truncated.tar.gz');
fs.writeFileSync(archive, bytes.subarray(0, Math.floor(bytes.length * 0.6)));
const destination = makeDestination(root);
await assert.rejects(
extractExactTarArchive(
archive, destination, UNIX_ARCHIVE_NAMES, EXECUTABLE,
fakeTarListing(UNIX_ARCHIVE_NAMES),
),
// Non-zero exit, and tar's own diagnostic text after the colon.
/tar exited with status [1-9]\d* while extracting codebase-memory-mcp: \S/,
);
assert.deepEqual(fs.readdirSync(destination), []);
}));
test('tar extraction rejects an empty executable', { skip: TAR_PATH_SKIP }, () =>
withScratch(async (root) => {
const archive = writeTarGz(
root, 'release.tar.gz', releaseEntries(UNIX_ARCHIVE_NAMES, Buffer.alloc(0)),
);
const destination = makeDestination(root);
await assert.rejects(
extractExactTarArchive(archive, destination, UNIX_ARCHIVE_NAMES, EXECUTABLE),
/archive member codebase-memory-mcp is empty/,
);
assert.deepEqual(fs.readdirSync(destination), []);
}));
test('tar extraction installs the executable byte-identical with the executable bit set', { skip: TAR_PATH_SKIP }, () =>
withScratch(async (root) => {
const archive = writeTarGz(
root, 'release.tar.gz', releaseEntries(UNIX_ARCHIVE_NAMES, LARGE_EXECUTABLE),
);
const destination = makeDestination(root);
await extractExactTarArchive(archive, destination, UNIX_ARCHIVE_NAMES, EXECUTABLE);
const target = path.join(destination, EXECUTABLE);
assert.ok(fs.readFileSync(target).equals(LARGE_EXECUTABLE));
if (process.platform !== 'win32') {
// Windows has no mode bits; everywhere else the file must be runnable.
assert.equal(fs.statSync(target).mode & 0o111, 0o111);
}
assert.deepEqual(fs.readdirSync(destination), [EXECUTABLE]);
}));
function loadInstallerWithHttps(fakeHttps) {
const installPath = path.join(__dirname, '..', 'install.js');
const source = fs.readFileSync(installPath, 'utf8');
const sandbox = {
Buffer,
URL,
__dirname: path.dirname(installPath),
__filename: installPath,
clearTimeout,
console,
exports: {},
// A parent keeps the postinstall entry point from running on load.
module: { exports: {}, parent: {} },
process,
require: (specifier) => {
if (specifier === './package.json') return { version: '0.0.0-test' };
if (specifier === 'https') return fakeHttps;
return require(specifier);
},
setTimeout,
};
vm.runInNewContext(source, sandbox, { filename: installPath });
return sandbox.module.exports;
}
// Answers every request with one HTTP 200 response shaped by `configure`.
function fakeHttpsServing(configure) {
return {
get(url, callback) {
const request = new EventEmitter();
request.destroy = (err) => { if (err) request.emit('error', err); };
setImmediate(() => {
const response = new Readable({ read() {} });
response.statusCode = 200;
response.headers = {};
configure(response);
callback(response);
});
return request;
},
};
}
test('download refuses a response whose declared length exceeds the limit', () =>
withScratch(async (root) => {
const { download } = loadInstallerWithHttps(fakeHttpsServing((response) => {
response.headers['content-length'] = '9';
response.push(null);
}));
const destination = path.join(root, 'release.tar.gz');
await assert.rejects(
download('https://example.invalid/release.tar.gz', destination, 8),
/8-byte safety limit/,
);
assert.equal(fs.existsSync(destination), false);
}));
test('download refuses a body that exceeds the limit and removes the partial file', () =>
withScratch(async (root) => {
const { download } = loadInstallerWithHttps(fakeHttpsServing((response) => {
response.push(Buffer.alloc(1024, 0x78));
response.push(null);
}));
const destination = path.join(root, 'release.tar.gz');
await assert.rejects(
download('https://example.invalid/release.tar.gz', destination, 8),
/8-byte safety limit/,
);
assert.equal(fs.existsSync(destination), false);
}));
test('download within the limit stores the body', () =>
withScratch(async (root) => {
const { download } = loadInstallerWithHttps(fakeHttpsServing((response) => {
response.headers['content-length'] = '7';
response.push('payload');
response.push(null);
}));
const destination = path.join(root, 'release.tar.gz');
await download('https://example.invalid/release.tar.gz', destination, 1024);
assert.equal(fs.readFileSync(destination, 'utf8'), 'payload');
}));
// The Windows path runs a constant PowerShell program through powershell.exe,
// which only a Windows host provides (pwsh is not a wrapper dependency, and
// no PowerShell is installed on the Unix wrapper hosts), so these cases run
// in the Windows wrapper leg and are skipped elsewhere.
const WINDOWS_ONLY = process.platform === 'win32'
? false
: 'zip extraction runs through powershell.exe, which this host does not provide';
test('Windows zip extraction refuses an archive with more members than the limit', { skip: WINDOWS_ONLY }, () =>
withScratch(async (root) => {
const archive = path.join(root, 'members.zip');
fs.writeFileSync(archive, buildZip([
{ name: 'one', data: 'a' },
{ name: 'two', data: 'b' },
{ name: 'three', data: 'c' },
]));
const destination = makeDestination(root);
assert.throws(
() => extractZipOnWindows(
archive, destination, ['one', 'two', 'three'], ['one'],
limitsWith({ members: 2 }),
),
/2-member safety limit/,
);
assert.deepEqual(fs.readdirSync(destination), []);
}));
test('Windows zip extraction refuses a member whose declared size exceeds the limit', { skip: WINDOWS_ONLY }, () =>
withScratch(async (root) => {
const archive = path.join(root, 'member.zip');
fs.writeFileSync(archive, buildZip([
{ name: WINDOWS_BINARY_NAME, data: Buffer.alloc(65, 0x78) },
]));
const destination = makeDestination(root);
assert.throws(
() => extractZipOnWindows(
archive, destination, [WINDOWS_BINARY_NAME], [WINDOWS_BINARY_NAME],
limitsWith({ memberBytes: 64 }),
),
/64-byte expanded safety limit/,
);
assert.deepEqual(fs.readdirSync(destination), []);
}));
test('Windows zip extraction stops a member that yields more bytes than it declares', { skip: WINDOWS_ONLY }, () =>
withScratch(async (root) => {
const archive = path.join(root, 'longer.zip');
fs.writeFileSync(archive, buildZip([
{ name: WINDOWS_BINARY_NAME, data: Buffer.alloc(600, 0x78), declaredSize: 10 },
]));
const destination = makeDestination(root);
assert.throws(
() => extractZipOnWindows(
archive, destination, [WINDOWS_BINARY_NAME], [WINDOWS_BINARY_NAME],
limitsWith({ memberBytes: 64 }),
),
/64-byte actual expanded safety limit/,
);
assert.deepEqual(fs.readdirSync(destination), []);
}));
test('Windows zip extraction within the limits installs only the executable', { skip: WINDOWS_ONLY }, () =>
withScratch(async (root) => {
const archive = path.join(root, 'release.zip');
fs.writeFileSync(archive, buildZip(releaseEntries(WINDOWS_ARCHIVE_NAMES)));
const destination = makeDestination(root);
extractZipOnWindows(
archive, destination, WINDOWS_ARCHIVE_NAMES, [WINDOWS_BINARY_NAME],
);
assert.equal(
fs.readFileSync(path.join(destination, WINDOWS_BINARY_NAME), 'utf8'),
`payload of ${WINDOWS_BINARY_NAME}`,
);
assert.deepEqual(fs.readdirSync(destination), [WINDOWS_BINARY_NAME]);
}));
+30 -15
View File
@@ -13,6 +13,7 @@ const {
installWindowsBinaryAtomically,
validateExactTarMemberListing,
} = require('../install.js');
const { TAR_PATH_SKIP, writeTarGz } = require('./archive-fixtures.js');
function exactUnixListing(extra = []) {
return [...UNIX_ARCHIVE_NAMES, ...extra].join('\n') + '\n';
@@ -33,23 +34,37 @@ test('Unix archive validation rejects traversal and unexpected members', () => {
);
});
test('Unix extraction requests only the validated root executable', () => {
const calls = [];
const runner = (command, args) => {
calls.push({ command, args: [...args] });
return calls.length === 1 ? exactUnixListing() : Buffer.alloc(0);
};
test('Unix extraction lists through the system tar and writes only the root executable', { skip: TAR_PATH_SKIP }, async () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cbm-npm-extract-test-'));
try {
const archive = writeTarGz(
root, 'release.tar.gz',
UNIX_ARCHIVE_NAMES.map((name) => ({ name, data: `member:${name}` })),
);
const destination = path.join(root, 'extract');
fs.mkdirSync(destination);
const calls = [];
const runner = (command, args) => {
calls.push({ command, args: [...args] });
return exactUnixListing();
};
extractExactTarArchive(
'/tmp/release.tar.gz', '/tmp/extract', UNIX_ARCHIVE_NAMES,
'codebase-memory-mcp', runner,
);
await extractExactTarArchive(
archive, destination, UNIX_ARCHIVE_NAMES, 'codebase-memory-mcp', runner,
);
assert.deepEqual(calls[0].args, ['-tzf', '/tmp/release.tar.gz']);
assert.deepEqual(
calls[1].args,
['-xzf', '/tmp/release.tar.gz', '-C', '/tmp/extract', 'codebase-memory-mcp'],
);
// The listing runner sees the namespace query only; the executable is
// written by the counted `tar -xzOf` writer, never by `tar -x` into the
// tree, so the companions never touch the disk.
assert.deepEqual(calls.map((call) => call.args), [['-tzf', archive]]);
assert.equal(
fs.readFileSync(path.join(destination, 'codebase-memory-mcp'), 'utf8'),
'member:codebase-memory-mcp',
);
assert.deepEqual(fs.readdirSync(destination), ['codebase-memory-mcp']);
} finally {
fs.rmSync(root, { recursive: true, force: true });
}
});
function writeBinary(directory, tag) {
+205 -23
View File
@@ -1,5 +1,6 @@
"""Downloads codebase-memory-mcp on first run, then runs its native entry point."""
import collections
import errno
import hashlib
import json
@@ -42,6 +43,22 @@ _MAX_REDIRECTS = 5
_NETWORK_TIMEOUT_SECONDS = 120
_CANDIDATE_TIMEOUT_SECONDS = 15
_MAX_CHECKSUM_MANIFEST_BYTES = 1024 * 1024
# Release archive resource limits. Keep in sync with the Go wrapper
# (pkg/go/cmd/codebase-memory-mcp/main.go) and the npm wrapper (install.js).
_MAX_RELEASE_ARCHIVE_BYTES = 256 * 1024 * 1024
_MAX_ARCHIVE_MEMBERS = 64
_MAX_ARCHIVE_MEMBER_BYTES = 256 * 1024 * 1024
_MAX_ARCHIVE_EXPANDED_BYTES = 512 * 1024 * 1024
_ARCHIVE_COPY_CHUNK_BYTES = 64 * 1024
_ArchiveLimits = collections.namedtuple(
"_ArchiveLimits", "compressed_bytes members member_bytes expanded_bytes"
)
_DEFAULT_ARCHIVE_LIMITS = _ArchiveLimits(
compressed_bytes=_MAX_RELEASE_ARCHIVE_BYTES,
members=_MAX_ARCHIVE_MEMBERS,
member_bytes=_MAX_ARCHIVE_MEMBER_BYTES,
expanded_bytes=_MAX_ARCHIVE_EXPANDED_BYTES,
)
_REDIRECT_CODES = frozenset({301, 302, 303, 307, 308})
_RUNTIME_LOCK_NAME = ".codebase-memory-mcp-runtime.lock"
_RUNTIME_LOCK_WAIT_SECONDS = 45
@@ -113,27 +130,50 @@ def _download_https(url: str, dest: str, max_bytes: int = 0) -> None:
with response:
_validate_url_scheme(response.geturl())
declared = (response.headers.get("Content-Length") or "").strip()
# Only plain ASCII digits are a length (str.isdigit also accepts
# characters that int() rejects); anything else is left to the
# byte counter below.
if max_bytes and re.fullmatch(r"[0-9]+", declared):
if int(declared) > max_bytes:
raise RuntimeError(
f"download exceeds the {max_bytes}-byte safety limit"
)
deadline = time.monotonic() + _NETWORK_TIMEOUT_SECONDS
total = 0
with open(dest, "wb") as out:
while True:
if time.monotonic() >= deadline:
raise TimeoutError(
f"download hop timed out: {current_url}"
)
chunk = response.read(65536)
if not chunk:
return
total += len(chunk)
if max_bytes and total > max_bytes:
raise RuntimeError(
f"download exceeds the {max_bytes}-byte safety limit"
)
out.write(chunk)
try:
with open(dest, "wb") as out:
while True:
if time.monotonic() >= deadline:
raise TimeoutError(
f"download hop timed out: {current_url}"
)
chunk = response.read(65536)
if not chunk:
return
total += len(chunk)
if max_bytes and total > max_bytes:
raise RuntimeError(
f"download exceeds the {max_bytes}-byte "
f"safety limit"
)
out.write(chunk)
except BaseException:
# Never leave a partial download behind for a later step to
# mistake for the complete asset.
_remove_quietly(dest)
raise
raise RuntimeError("too many redirects")
def _remove_quietly(path) -> None:
try:
os.unlink(path)
except OSError:
pass
def _verify_candidate(path: Path) -> None:
"""Require a staged native candidate to execute successfully."""
try:
@@ -196,33 +236,157 @@ def _validate_archive_names(names, archive_names, casefold: bool = False):
)
def _validate_archive_limits(limits) -> None:
if (
limits.compressed_bytes <= 0
or limits.members <= 0
or limits.member_bytes <= 0
or limits.expanded_bytes <= 0
):
sys.exit("codebase-memory-mcp: invalid archive resource safety limits")
def _require_compressed_archive_within_limit(archive_path: str, limits) -> None:
status = os.stat(archive_path)
if not stat.S_ISREG(status.st_mode):
sys.exit("codebase-memory-mcp: release archive is not a regular file")
if status.st_size > limits.compressed_bytes:
sys.exit(
f"codebase-memory-mcp: release archive exceeds the "
f"{limits.compressed_bytes}-byte compressed safety limit"
)
def _require_member_count_within_limit(count: int, limits) -> None:
if count > limits.members:
sys.exit(
f"codebase-memory-mcp: archive exceeds the {limits.members}-member "
f"safety limit"
)
def _account_declared_archive_member(
name: str, size: int, declared_expanded: int, limits
) -> int:
"""Check one member's declared size; return the new declared total."""
if size < 0:
sys.exit(
f"codebase-memory-mcp: archive member has a negative declared "
f"size: {name!r}"
)
if size > limits.member_bytes:
sys.exit(
f"codebase-memory-mcp: archive member {name!r} exceeds the "
f"{limits.member_bytes}-byte expanded safety limit"
)
if declared_expanded > limits.expanded_bytes - size:
sys.exit(
f"codebase-memory-mcp: archive exceeds the {limits.expanded_bytes}-byte "
f"aggregate expanded safety limit"
)
return declared_expanded + size
def _copy_archive_member_within_limits(
source, output, name: str, declared_size: int, actual_expanded: int, limits
) -> int:
"""Copy a member while counting bytes; return the new actual total.
Reads stop one byte past the smaller of the member and remaining aggregate
limits, so a member that yields more than it declares can never fill the
disk. The caller removes the target on failure.
"""
remaining = limits.expanded_bytes - actual_expanded
copy_limit = min(limits.member_bytes, remaining)
copied = 0
while True:
wanted = min(_ARCHIVE_COPY_CHUNK_BYTES, copy_limit + 1 - copied)
if wanted <= 0:
break
chunk = source.read(wanted)
if not chunk:
break
copied += len(chunk)
if copied > copy_limit:
break
output.write(chunk)
if copied > limits.member_bytes:
sys.exit(
f"codebase-memory-mcp: archive member {name!r} exceeds the "
f"{limits.member_bytes}-byte actual expanded safety limit"
)
if copied > remaining:
sys.exit(
f"codebase-memory-mcp: archive exceeds the {limits.expanded_bytes}-byte "
f"aggregate actual expanded safety limit"
)
if copied != declared_size:
sys.exit(
f"codebase-memory-mcp: archive member {name!r} actual size {copied} "
f"does not match declared size {declared_size}"
)
return actual_expanded + copied
def _write_archive_member_within_limits(
source, target: str, name: str, declared_size: int, actual_expanded: int, limits
) -> int:
try:
with open(target, "xb") as output:
return _copy_archive_member_within_limits(
source, output, name, declared_size, actual_expanded, limits
)
except BaseException:
_remove_quietly(target)
raise
def _safe_extract_tar(
tf, dest: str, archive_names=(), extract_names=(), ui: bool = False
tf,
dest: str,
archive_names=(),
extract_names=(),
ui: bool = False,
limits=_DEFAULT_ARCHIVE_LIMITS,
):
"""Validate a tar namespace, then extract only its authenticated runtime set."""
_validate_archive_limits(limits)
members = tf.getmembers()
_require_member_count_within_limit(len(members), limits)
declared_expanded = 0
for member in members:
if not member.isfile():
sys.exit(
f"codebase-memory-mcp: refusing unsafe tar entry "
f"(not a regular root file: {member.name!r})"
)
declared_expanded = _account_declared_archive_member(
member.name, member.size, declared_expanded, limits
)
_validate_archive_names([member.name for member in members], archive_names)
runtime_names = list(extract_names)
by_name = {member.name: member for member in members}
dest_abs = os.path.abspath(dest)
actual_expanded = 0
for name in runtime_names:
source = tf.extractfile(by_name[name])
if source is None:
sys.exit(f"codebase-memory-mcp: could not read archive member: {name}")
target = os.path.join(dest_abs, name)
with source, open(target, "xb") as output:
shutil.copyfileobj(source, output)
with source:
actual_expanded = _write_archive_member_within_limits(
source, target, name, by_name[name].size, actual_expanded, limits
)
return tuple(runtime_names)
def _safe_extract_zip(
zf, dest: str, archive_names=(), extract_names=(), ui: bool = False
zf,
dest: str,
archive_names=(),
extract_names=(),
ui: bool = False,
limits=_DEFAULT_ARCHIVE_LIMITS,
):
"""Extract a zipfile after validating its Windows-style namespace.
@@ -230,9 +394,16 @@ def _safe_extract_zip(
members before extraction prevents archive order from selecting the binary
that a portable package-manager shim eventually executes.
"""
_validate_archive_limits(limits)
dest_abs = os.path.abspath(dest)
names = []
for info in zf.infolist():
infos = zf.infolist()
_require_member_count_within_limit(len(infos), limits)
declared_expanded = 0
for info in infos:
declared_expanded = _account_declared_archive_member(
info.filename, info.file_size, declared_expanded, limits
)
raw_name = info.filename
name = raw_name.replace("\\", "/")
segments_name = name[:-1] if name.endswith("/") else name
@@ -266,10 +437,18 @@ def _safe_extract_zip(
# Extract only the validated runtime files. This avoids relying on
# platform-specific zip path rewriting and always creates regular files.
runtime_names = list(extract_names)
actual_expanded = 0
for name in runtime_names:
target = os.path.join(dest_abs, name)
with zf.open(name) as source, open(target, "xb") as output:
shutil.copyfileobj(source, output)
with zf.open(name) as source:
actual_expanded = _write_archive_member_within_limits(
source,
target,
name,
zf.getinfo(name).file_size,
actual_expanded,
limits,
)
return tuple(runtime_names)
@@ -1179,7 +1358,7 @@ def _download(version: str) -> Path:
with tempfile.TemporaryDirectory() as tmp:
tmp_archive = os.path.join(tmp, f"cbm.{ext}")
try:
_download_https(url, tmp_archive)
_download_https(url, tmp_archive, _MAX_RELEASE_ARCHIVE_BYTES)
except (OSError, RuntimeError, urllib.error.URLError) as e:
sys.exit(
f"codebase-memory-mcp: download failed ({e})\n"
@@ -1188,6 +1367,9 @@ def _download(version: str) -> Path:
)
_verify_checksum(tmp_archive, archive, version)
_require_compressed_archive_within_limit(
tmp_archive, _DEFAULT_ARCHIVE_LIMITS
)
bin_name = (
_WINDOWS_BINARY_NAME if os_name == "windows" else "codebase-memory-mcp"
+294
View File
@@ -1,5 +1,6 @@
import errno
import hashlib
import io
import json
import os
import stat
@@ -835,3 +836,296 @@ _cli._publish_runtime_set(
False,
)
class _FakeHttpsResponse:
"""Minimal stand-in for the opener's response: headers, geturl, read."""
def __init__(self, body: bytes, headers=None):
self._body = body
self._offset = 0
self.headers = dict(headers or {})
def __enter__(self):
return self
def __exit__(self, *exc_info):
return False
def geturl(self):
return "https://example.invalid/release.tar.gz"
def read(self, size=-1):
if size is None or size < 0:
size = len(self._body) - self._offset
chunk = self._body[self._offset : self._offset + size]
self._offset += len(chunk)
return chunk
def _limits_with(**overrides):
return _cli._DEFAULT_ARCHIVE_LIMITS._replace(**overrides)
def _write_tar_gz(archive, entries):
with tarfile.open(archive, "w:gz") as tf:
for name, data in entries:
info = tarfile.TarInfo(name)
info.size = len(data)
tf.addfile(info, io.BytesIO(data))
def _write_zip(archive, entries):
with zipfile.ZipFile(archive, "w") as zf:
for name, data in entries:
zf.writestr(name, data)
def _release_entries(names):
return [(name, f"payload of {name}".encode()) for name in names]
class ArchiveLimitTests(unittest.TestCase):
URL = "https://example.invalid/release.tar.gz"
def _download(self, response, destination, max_bytes):
with mock.patch.object(_cli._HTTPS_OPENER, "open", return_value=response):
_cli._download_https(self.URL, destination, max_bytes)
def test_download_rejects_a_declared_length_over_the_limit(self):
with tempfile.TemporaryDirectory() as root:
destination = os.path.join(root, "release.tar.gz")
response = _FakeHttpsResponse(b"", {"Content-Length": "9"})
with self.assertRaises(RuntimeError) as raised:
self._download(response, destination, 8)
self.assertIn("8-byte safety limit", str(raised.exception))
self.assertFalse(os.path.exists(destination))
def test_download_rejects_a_body_over_the_limit_and_removes_the_partial_file(
self,
):
with tempfile.TemporaryDirectory() as root:
destination = os.path.join(root, "release.tar.gz")
response = _FakeHttpsResponse(b"x" * 1024)
with self.assertRaises(RuntimeError) as raised:
self._download(response, destination, 8)
self.assertIn("8-byte safety limit", str(raised.exception))
self.assertFalse(os.path.exists(destination))
def test_download_within_the_limit_stores_the_body(self):
with tempfile.TemporaryDirectory() as root:
destination = os.path.join(root, "release.tar.gz")
response = _FakeHttpsResponse(b"payload", {"Content-Length": "7"})
self._download(response, destination, 1024)
with open(destination, "rb") as stored:
self.assertEqual(stored.read(), b"payload")
def test_download_counts_bytes_when_the_declared_length_is_not_a_number(self):
# A superscript two passes str.isdigit but not int(); such values and
# plain junk must fall through to the byte counter, never crash.
for declared in ("²", "abc", "", "1e3", "-1"):
with self.subTest(declared=declared), tempfile.TemporaryDirectory() as root:
destination = os.path.join(root, "release.tar.gz")
response = _FakeHttpsResponse(b"x" * 1024, {"Content-Length": declared})
with self.assertRaises(RuntimeError) as raised:
self._download(response, destination, 8)
self.assertIn("8-byte safety limit", str(raised.exception))
self.assertFalse(os.path.exists(destination))
with tempfile.TemporaryDirectory() as root:
destination = os.path.join(root, "release.tar.gz")
response = _FakeHttpsResponse(b"payload", {"Content-Length": "²"})
self._download(response, destination, 1024)
with open(destination, "rb") as stored:
self.assertEqual(stored.read(), b"payload")
def test_unix_tar_rejects_more_members_than_the_limit(self):
with tempfile.TemporaryDirectory() as root:
archive = Path(root) / "members.tar.gz"
_write_tar_gz(archive, [("one", b"a"), ("two", b"b"), ("three", b"c")])
destination = Path(root) / "extract"
destination.mkdir()
with tarfile.open(archive) as tf, self.assertRaises(SystemExit) as raised:
_cli._safe_extract_tar(
tf,
str(destination),
("one", "two", "three"),
("one",),
limits=_limits_with(members=2),
)
self.assertIn("2-member safety limit", str(raised.exception))
self.assertFalse((destination / "one").exists())
def test_unix_tar_rejects_a_member_declared_over_the_limit(self):
with tempfile.TemporaryDirectory() as root:
archive = Path(root) / "member.tar.gz"
_write_tar_gz(archive, [("codebase-memory-mcp", b"x" * 65)])
destination = Path(root) / "extract"
destination.mkdir()
with tarfile.open(archive) as tf, self.assertRaises(SystemExit) as raised:
_cli._safe_extract_tar(
tf,
str(destination),
("codebase-memory-mcp",),
("codebase-memory-mcp",),
limits=_limits_with(member_bytes=64),
)
self.assertIn(
"'codebase-memory-mcp' exceeds the 64-byte expanded safety limit",
str(raised.exception),
)
self.assertFalse((destination / "codebase-memory-mcp").exists())
def test_unix_tar_rejects_a_declared_aggregate_over_the_limit(self):
with tempfile.TemporaryDirectory() as root:
archive = Path(root) / "aggregate.tar.gz"
_write_tar_gz(archive, [("one", b"a" * 40), ("two", b"b" * 40)])
destination = Path(root) / "extract"
destination.mkdir()
with tarfile.open(archive) as tf, self.assertRaises(SystemExit) as raised:
_cli._safe_extract_tar(
tf,
str(destination),
("one", "two"),
("one",),
limits=_limits_with(member_bytes=64, expanded_bytes=64),
)
self.assertIn(
"64-byte aggregate expanded safety limit", str(raised.exception)
)
def test_unix_tar_within_the_limits_extracts_only_the_executable(self):
with tempfile.TemporaryDirectory() as root:
archive = Path(root) / "release.tar.gz"
_write_tar_gz(archive, _release_entries(_cli._UNIX_ARCHIVE_NAMES))
destination = Path(root) / "extract"
destination.mkdir()
with tarfile.open(archive) as tf:
extracted = _cli._safe_extract_tar(
tf,
str(destination),
_cli._UNIX_ARCHIVE_NAMES,
("codebase-memory-mcp",),
)
self.assertEqual(extracted, ("codebase-memory-mcp",))
self.assertEqual(
(destination / "codebase-memory-mcp").read_bytes(),
b"payload of codebase-memory-mcp",
)
self.assertEqual(os.listdir(destination), ["codebase-memory-mcp"])
def test_archive_copy_stops_a_member_that_yields_more_than_the_limit(self):
source = io.BytesIO(b"x" * 100)
output = io.BytesIO()
with self.assertRaises(SystemExit) as raised:
_cli._copy_archive_member_within_limits(
source,
output,
"member",
100,
0,
_limits_with(member_bytes=64, expanded_bytes=1024),
)
self.assertIn(
"'member' exceeds the 64-byte actual expanded safety limit",
str(raised.exception),
)
# Reading stopped one byte past the limit; nothing beyond it was kept.
self.assertEqual(source.tell(), 65)
self.assertLessEqual(len(output.getvalue()), 64)
def test_archive_copy_rejects_a_member_that_yields_more_than_it_declares(self):
source = io.BytesIO(b"x" * 100)
with self.assertRaises(SystemExit) as raised:
_cli._copy_archive_member_within_limits(
source,
io.BytesIO(),
"member",
10,
0,
_limits_with(member_bytes=1024, expanded_bytes=1024),
)
self.assertIn(
"'member' actual size 100 does not match declared size 10",
str(raised.exception),
)
def test_archive_copy_rejects_an_aggregate_over_the_actual_limit(self):
with self.assertRaises(SystemExit) as raised:
_cli._copy_archive_member_within_limits(
io.BytesIO(b"abcd"),
io.BytesIO(),
"member",
4,
3,
_limits_with(member_bytes=10, expanded_bytes=5),
)
self.assertIn(
"5-byte aggregate actual expanded safety limit", str(raised.exception)
)
def test_archive_member_write_removes_the_partial_file_on_failure(self):
with tempfile.TemporaryDirectory() as root:
target = os.path.join(root, "codebase-memory-mcp")
with self.assertRaises(SystemExit):
_cli._write_archive_member_within_limits(
io.BytesIO(b"x" * 100),
target,
"codebase-memory-mcp",
10,
0,
_limits_with(member_bytes=1024, expanded_bytes=1024),
)
self.assertFalse(os.path.exists(target))
def test_windows_zip_rejects_more_members_than_the_limit(self):
with tempfile.TemporaryDirectory() as root:
archive = Path(root) / "members.zip"
_write_zip(archive, [("one", b"a"), ("two", b"b"), ("three", b"c")])
destination = Path(root) / "extract"
destination.mkdir()
with zipfile.ZipFile(archive) as zf, self.assertRaises(SystemExit) as raised:
_cli._safe_extract_zip(
zf,
str(destination),
("one", "two", "three"),
("one",),
limits=_limits_with(members=2),
)
self.assertIn("2-member safety limit", str(raised.exception))
def test_windows_zip_rejects_a_member_declared_over_the_limit(self):
with tempfile.TemporaryDirectory() as root:
archive = Path(root) / "member.zip"
_write_zip(archive, [(_cli._WINDOWS_BINARY_NAME, b"x" * 65)])
destination = Path(root) / "extract"
destination.mkdir()
with zipfile.ZipFile(archive) as zf, self.assertRaises(SystemExit) as raised:
_cli._safe_extract_zip(
zf,
str(destination),
(_cli._WINDOWS_BINARY_NAME,),
(_cli._WINDOWS_BINARY_NAME,),
limits=_limits_with(member_bytes=64),
)
self.assertIn("64-byte expanded safety limit", str(raised.exception))
self.assertFalse((destination / _cli._WINDOWS_BINARY_NAME).exists())
def test_windows_zip_within_the_limits_extracts_only_the_executable(self):
with tempfile.TemporaryDirectory() as root:
archive = Path(root) / "release.zip"
_write_zip(archive, _release_entries(_cli._WINDOWS_ARCHIVE_NAMES))
destination = Path(root) / "extract"
destination.mkdir()
with zipfile.ZipFile(archive) as zf:
extracted = _cli._safe_extract_zip(
zf,
str(destination),
_cli._WINDOWS_ARCHIVE_NAMES,
(_cli._WINDOWS_BINARY_NAME,),
)
self.assertEqual(extracted, (_cli._WINDOWS_BINARY_NAME,))
self.assertEqual(
(destination / _cli._WINDOWS_BINARY_NAME).read_bytes(),
b"payload of codebase-memory-mcp.exe",
)
self.assertEqual(os.listdir(destination), [_cli._WINDOWS_BINARY_NAME])
+1 -1
View File
@@ -17,7 +17,7 @@ codes) and rejects unknown flags with exit 2 + `Please consult --help.`
| leg | entry | what a run gives you |
|---|---|---|
| **test** | `test.sh` | DEFAULT = the venue leg: static contracts (Step 0a–0r) + CLEAN ASan+UBSan build + all suites via the parallel harness + prod-binary guards. `--suites a,b` = iteration mode (incremental rebuild, subset, seconds). `--tsan` = the ThreadSanitizer leg. CLANGARM64 gets CI's trap-UBSan flags automatically. |
| **test** | `test.sh` | DEFAULT = the venue leg: static contracts (Step 0a–0r) + CLEAN ASan+UBSan build + all suites via the parallel harness + prod-binary guards. `--suites a,b` = iteration mode (incremental rebuild, subset, seconds). `--tsan` = the ThreadSanitizer leg. `--contracts-only` = the static contract steps alone (PR CI's contracts job for docs-only and non-product changes). CLANGARM64 gets CI's trap-UBSan flags automatically. |
| **package wrappers** | `ci/test-package-wrappers.sh` | Go, npm, and PyPI runtime-set publication/lock suites on the current host. CI runs the same entry on Linux and Windows so both platform lock implementations gate. |
| **build** | `build.sh` | CLEAN production runtime set (native executable + authenticated integration asset; `--with-ui` adds one content-addressed UI pack). ccache via `env.sh` makes repeats fast; `CCACHE_COMPILERCHECK=content` guarantees a hit is byte-identical to a cold compile — never stale. `--version`, `STATIC=1`, `BUILD_DIR=`. |
| **lint** | `lint.sh` | clang-tidy + cppcheck + clang-format (+ no-skips policy). `--ci` = the CI gate set (no clang-tidy). Drives the same make targets as `make lint`/`lint-ci`. |
+4 -2
View File
@@ -13,8 +13,10 @@ CI and the local infrastructure — both of which the venue-parity contract
| `preflight-docker.sh` | Same idea for Colima/docker: prune runner-unlike residue, assert free space on the filesystem backing the docker data root (not the VM's `/`). Build cache + named volumes KEPT (the local analogue of actions/cache); `--deep` drops them. | `test-infrastructure/run.sh` |
| `check-glibc-compat.sh` | Run a linux binary in debian:bullseye (glibc 2.31) — the portable binary must start on old glibc. | `_smoke.yml` portable legs |
| `generate-sbom.py` | The release SPDX SBOM (vendored versions reviewable here, diffable by vendoring PRs — was inline YAML). | `release.yml` |
| `require-all-green.sh` | The aggregate gate: fail unless every needed job succeeded or legitimately skipped (was inline YAML). | `pr.yml ci-ok` |
| `verify-shard-union.sh` | Prove sharded test legs lost nothing: shard count agreement, indices 1..n, identical suite lists, union of slices == full list (was inline YAML). | `_test.yml` shard-completeness |
| `require-all-green.sh` | The aggregate gate: fail unless every needed job succeeded or legitimately skipped (was inline YAML) — and, under lane selection, every SELECTED lane ran and succeeded (skipped/cancelled/missing = red; unselected lanes may skip). | `pr.yml ci-ok` |
| `select-lanes.sh` | Decide which PR lanes a change set runs (tier, lanes, full, reasons) from its file list — the R1 rule set, fail-safe to every lane. Pinned by `tests/test_select_lanes.sh` and the September 2026 history replay (`scripts/test-impact/replay-selector.sh`). | `pr.yml changes` |
| `test-impact-shadow.sh` | The report-only test-impact shadow: fetch the latest released binary (checksum-verified) or take a given one, index this checkout into a throwaway cache, run `detect_changes` against the base and map it to suites (`scripts/test-impact/predict.py`). Never gates. | `pr.yml test-impact-shadow` |
| `verify-shard-union.sh` | Prove sharded test legs lost nothing: shard count agreement, indices 1..n, identical suite lists, union of slices == full list (was inline YAML). No manifests is green only when the lane selection holds no test leg. | `_test.yml` shard-completeness |
| `prepare-release-candidates.sh` | Copy one linker output into stripped/unstripped candidates, finalize signatures, composition-check them without execution, and record their hashes. | `_build.yml`, local artifact smoke |
| `stage-release-candidates.py` | Admit exactly eight candidate artifacts / sixteen byte-distinct binaries into the content-addressed VirusTotal scan set. | `_build.yml` |
| `select-release-candidates.py` | Apply the reviewed tuple-local VT truth table, or the explicit dry-run stripped default, and atomically copy one content-bound binary per target. | `_build.yml` |
+23 -12
View File
@@ -26,14 +26,23 @@ CI venue would never see.
.PARAMETER SkipGate
Sweep and report, but do not fail when free space is short. For inspection only.
.PARAMETER CheckOnly
Assert the free-space floor without enumerating or removing residue. Isolated
runs use fresh checkout/build/temp roots and must not sweep another run's files.
Cannot be combined with SkipGate.
#>
[CmdletBinding()]
param(
[double]$MinFreeGB = 14,
[switch]$SkipGate
[switch]$SkipGate,
[switch]$CheckOnly
)
$ErrorActionPreference = 'Stop'
if ($CheckOnly -and $SkipGate) {
throw 'CheckOnly cannot be combined with SkipGate: the disk floor is required.'
}
function Get-FreeGB {
[math]::Round((Get-PSDrive C).Free / 1GB, 2)
@@ -56,21 +65,23 @@ function Remove-ResidueDir([string]$Path) {
$before = Get-FreeGB
$removed = 0
$failed = @()
$roots = @($env:USERPROFILE, $env:TEMP) | Where-Object { $_ } | Select-Object -Unique
foreach ($root in $roots) {
if (-not (Test-Path -LiteralPath $root)) { continue }
Get-ChildItem -LiteralPath $root -Directory -Filter 'cbm-*' -ErrorAction SilentlyContinue |
ForEach-Object {
if (Remove-ResidueDir $_.FullName) {
$removed++
} else {
$failed += $_.FullName
if (-not $CheckOnly) {
$roots = @($env:USERPROFILE, $env:TEMP) | Where-Object { $_ } | Select-Object -Unique
foreach ($root in $roots) {
if (-not (Test-Path -LiteralPath $root)) { continue }
Get-ChildItem -LiteralPath $root -Directory -Filter 'cbm-*' -ErrorAction SilentlyContinue |
ForEach-Object {
if (Remove-ResidueDir $_.FullName) {
$removed++
} else {
$failed += $_.FullName
}
}
}
}
}
$after = Get-FreeGB
if ($CheckOnly) { Write-Output 'preflight-mode=check-only' }
Write-Output "residue-dirs-removed=$removed residue-dirs-failed=$($failed.Count) free-gb-before=$before free-gb-after=$after"
foreach ($f in $failed) { Write-Warning "could not sweep: $f" }
+100 -10
View File
@@ -1,31 +1,121 @@
#!/usr/bin/env bash
# require-all-green.sh — the aggregate gate step: fail unless every needed job
# succeeded (or was legitimately skipped).
# succeeded (or was legitimately skipped) and every SELECTED lane ran green.
#
# Canonical CI step (called by pr.yml ci-ok with RESULTS = toJSON(needs)).
# Lived inline in workflow YAML until 2026-07-26; the venue-parity contract
# forbids logic in workflow run-blocks. `skipped` counts as OK because gated
# jobs (e.g. pr-smoke on docs-only PRs) skip by design — a matrix rename can
# still never silently deadlock a merge, which is this gate's purpose.
# forbids logic in workflow run-blocks. `skipped` counts as OK at the needs
# level because gated jobs (e.g. pr-smoke on docs-only PRs) skip by design — a
# matrix rename can still never silently deadlock a merge, which is this
# gate's purpose.
#
# Usage: RESULTS='<json of the needs context>' scripts/ci/require-all-green.sh
# Lane selection (LANES + JOBS, both set by pr.yml): a PR runs only the lanes
# scripts/ci/select-lanes.sh selected, and there `skipped` is no longer a free
# pass — a selected lane whose jobs were skipped, cancelled, never created or
# are anything but success is a silent gate loss, so it fails. Lanes nobody
# selected may be skipped or absent (but may not fail); memwaste is
# report-only and never gates. The selection itself must be trustworthy: a
# list of known lanes that holds lint or contracts (every tier runs one).
#
# Usage: RESULTS='<json of the needs context>' \
# [LANES='<selector lanes, JSON list>' JOBS=<file>] \
# scripts/ci/require-all-green.sh
# JOBS: this run's jobs as JSON lines {"name","status","conclusion"}
# (gh api .../actions/runs/<id>/jobs?filter=latest --jq '.jobs[] | ...').
set -euo pipefail
case "${1:-}" in
-h | --help)
sed -n '2,11p' "$0" | sed 's/^# \{0,1\}//'
sed -n '2,24p' "$0" | sed 's/^# \{0,1\}//'
exit 0
;;
esac
: "${RESULTS:?set RESULTS to the toJSON(needs) context}"
UNIVERSE=""
if [ -n "${LANES+set}" ]; then
UNIVERSE=$(bash "$(dirname "$0")/select-lanes.sh" --list-lanes)
fi
printf '%s' "$RESULTS" | python3 -c "
import json, sys
import json, os, re, sys
needs = json.load(sys.stdin)
bad = {k: v['result'] for k, v in needs.items() if v['result'] not in ('success', 'skipped')}
if bad:
print('CI NOT OK:', bad)
problems = [f'needed job {k}: {v}' for k, v in sorted(bad.items())]
if 'LANES' in os.environ:
universe = set(sys.argv[1].split())
lanes = None
try:
lanes = json.loads(os.environ['LANES'])
except ValueError:
pass
if not isinstance(lanes, list) or not all(isinstance(l, str) for l in lanes):
problems.append('LANES is not a JSON list of lane names: ' + repr(os.environ['LANES']))
lanes = []
selected = set(lanes)
if selected - universe:
problems.append('unknown lane(s) selected: ' + ', '.join(sorted(selected - universe)))
if not selected & {'lint', 'contracts'}:
problems.append('selection has neither lint nor contracts -- refusing to trust it')
# job name -> lane. Matrix jobs carry their lane among the values in
# parentheses; everything else is named by its (caller /) job id.
single = {
'lint / lint': 'lint', 'lint / lint-mem': 'lint-mem',
'security / security-static': 'security-static',
'security / license-gate': 'license-gate', 'security / codeql-gate': 'codeql-gate',
'test / test-diag': 'diag', 'test / test-msan': 'msan',
'test / test-lsan-macos': 'lsan-macos', 'test / test-windows-guards': 'windows-guards',
'test / shard-completeness': 'shard-completeness', 'contracts': 'contracts',
}
matrix = ('test / test-unix (', 'test / test-windows (', 'test / test-tsan (', 'pr-smoke (')
def lane_of(name):
if name in single:
return single[name]
if name.startswith('test / test-package-wrappers'):
return 'pkg-wrappers'
if name == 'memwaste' or name.startswith('memwaste / '):
return 'memwaste'
if name.startswith(matrix) and name.endswith(')'):
values = name[name.index('(') + 1:-1].split(', ')
hits = [v for v in values if v in universe]
return hits[0] if len(hits) == 1 else None
return None
jobs_by_lane = {}
path = os.environ.get('JOBS', '')
if not path or not os.path.isfile(path):
problems.append('JOBS (the run\'s job list) is missing: cannot verify the selected lanes')
else:
for line in open(path, encoding='utf-8'):
if line.strip():
job = json.loads(line)
lane = lane_of(job.get('name', ''))
if lane:
jobs_by_lane.setdefault(lane, []).append(job)
for lane in sorted(universe - {'memwaste'}):
jobs = jobs_by_lane.get(lane, [])
if lane in selected:
if not jobs:
problems.append(f'selected lane {lane}: no job ran')
for job in jobs:
state = job.get('conclusion') if job.get('status') == 'completed' else job.get('status')
if state != 'success':
problems.append(f'selected lane {lane}: {job[\"name\"]} is {state}')
else:
for job in jobs:
if job.get('status') == 'completed' and job.get('conclusion') not in ('success', 'skipped'):
problems.append(f'unselected lane {lane}: {job[\"name\"]} is {job[\"conclusion\"]}')
if not problems:
print('selected lanes green:', ', '.join(sorted(selected)))
if problems:
print('CI NOT OK:')
for p in problems:
print(' ' + p)
sys.exit(1)
print('CI OK:', ', '.join(needs))
"
" "$UNIVERSE"
+377
View File
@@ -0,0 +1,377 @@
#!/usr/bin/env bash
# select-lanes.sh — decide which PR CI lanes a change set must run.
#
# Canonical CI step (called by pr.yml's `changes` job on the PR's file list).
# This is the R1 rule set, measured against every PR push of September 2026
# (767 pushes; scripts/test-impact/replay-selector.sh replays them, plus the 22
# real failures, as a contract): each changed path gets a category, the
# categories give a tier, the tier gives a lane set, and a few categories add
# lanes of their own. `tier` is the classification the analysis measured;
# `full` overrides it with every lane.
#
# It FAILS SAFE: full=true for tier T5, a path no rule knows, an empty file
# list, a truncated one (file_list_truncated: the pulls/files API stops at
# 3000 files), this script, any workflow, the test harness, Makefile.cbm
# (source registration and flag changes look identical in a file list) and
# vendored code. It is a pure function of the file SET (and the expected
# count): order, duplicates, blank lines and CRLF never change a byte of the
# output.
#
# Usage: scripts/ci/select-lanes.sh [--format json|github-output]
# [--expect-files N] [FILE]
# FILE (default stdin): changed paths, one per line.
# --expect-files N: the PR's changed_files; fewer distinct paths, or
# N or the list at the 3000-file API cap, means truncated -> full.
# json (default): {"full","lanes","reasons","tier"} on one line.
# github-output: tier=, full=, lanes=, reasons= lines ($GITHUB_OUTPUT).
# scripts/ci/select-lanes.sh --batch FILE
# FILE: JSON lines {"id":..., "files":[...], "expect_files"?: N};
# prints one json result per line with its "id" (the history
# replay's single process).
# scripts/ci/select-lanes.sh --list-lanes
# Every lane name, one per line (the universe ci-ok checks against).
# Exit: 0 = decided · 2 = usage error.
set -euo pipefail
usage() { sed -n '2,33p' "$0" | sed 's/^# \{0,1\}//'; }
FORMAT=json
MODE="select"
INPUT=""
EXPECT=""
while [ $# -gt 0 ]; do
case "$1" in
-h | --help)
usage
exit 0
;;
--format)
FORMAT="${2:-}"
[ $# -gt 1 ] && shift
;;
--format=*) FORMAT="${1#--format=}" ;;
--batch)
MODE="batch"
INPUT="${2:-}"
[ $# -gt 1 ] && shift
;;
--list-lanes) MODE="list" ;;
--expect-files)
# An empty value (a workflow whose count went missing) must fail
# loudly, never quietly skip the truncation check.
EXPECT="${2:-}"
[ -n "$EXPECT" ] || EXPECT="missing"
[ $# -gt 1 ] && shift
;;
-*)
echo "select-lanes.sh: unknown option '$1'. Please consult --help." >&2
exit 2
;;
*)
if [ -n "$INPUT" ]; then
echo "select-lanes.sh: more than one input file. Please consult --help." >&2
exit 2
fi
INPUT="$1"
;;
esac
shift
done
case "$FORMAT" in
json | github-output) ;;
*)
echo "select-lanes.sh: --format must be json or github-output. Please consult --help." >&2
exit 2
;;
esac
case "${EXPECT:-0}" in
*[!0-9]*)
echo "select-lanes.sh: --expect-files takes a file count. Please consult --help." >&2
exit 2
;;
esac
if [ "$MODE" = batch ] && [ -z "$INPUT" ]; then
echo "select-lanes.sh: --batch needs a file. Please consult --help." >&2
exit 2
fi
if [ -n "$INPUT" ] && [ ! -f "$INPUT" ]; then
echo "select-lanes.sh: no such file '$INPUT'. Please consult --help." >&2
exit 2
fi
# The heredoc below is the program, so stdin input is staged in a file first.
STAGED=""
if [ "$MODE" = select ] && [ -z "$INPUT" ]; then
STAGED=$(mktemp "${TMPDIR:-/tmp}/cbm-select-lanes.XXXXXX")
trap 'rm -f "$STAGED"' EXIT
cat >"$STAGED"
INPUT="$STAGED"
fi
python3 - "$MODE" "$FORMAT" "$INPUT" "$EXPECT" <<'PY'
import json
import re
import sys
mode, fmt, source = sys.argv[1:4]
expect = int(sys.argv[4]) if sys.argv[4] else None
# ── Path -> category (first matching rule wins; order is part of the rule) ──
GATE_WORKFLOWS = {"pr.yml", "_test.yml", "_lint.yml", "_security.yml",
"_smoke.yml", "_memwaste.yml", "codeql.yml", "dco.yml"}
BUILD = {"Makefile.cbm", "scripts/test.sh", "scripts/run-tests-parallel.sh",
"scripts/env.sh", "scripts/build.sh", "scripts/clean.sh",
"scripts/path-safety.sh", "scripts/test-runtime.sh", "scripts/msan.sh",
"scripts/lint.sh", ".clang-format", ".clang-tidy",
"scripts/vendored-checksums.txt"}
SMOKE = {"scripts/smoke-test.sh", "scripts/smoke-local.sh",
"scripts/smoke-fixture-server.py", "scripts/smoke-invariants.sh",
"test-infrastructure/vm/vm-smoke.sh",
"test-infrastructure/vm/windows-user-path-guard.ps1",
"scripts/test-windows.ps1"}
LINT_CONFIG = {"scripts/memory-core-baseline.txt", "scripts/lint-memory-core.py",
"scripts/hooks/pre-commit"}
NON_GATING = {"scripts/package-release.sh", "scripts/benchmark-index.sh",
"scripts/benchmark-search-graph.sh", "scripts/benchmark-streaming.py",
"scripts/soak-test.sh", ".cbmignore", ".node-version", "flake.nix"}
PKG_FILES = {"install.sh", "install.ps1", "server.json", "package.json", "manifest.json"}
DOC_SUFFIXES = (".md", ".png", ".svg", ".jpg", ".gif", ".txt")
DOC_FILES = {"LICENSE", "CODEOWNERS", ".gitignore", ".gitattributes"}
def category(path):
base = path.rsplit("/", 1)[-1]
if path == "Makefile.cbm":
return "build"
if path in LINT_CONFIG:
return "lint-cfg"
if path.startswith(("scripts/memwaste", "scripts/memlab")):
return "memwaste-tools"
if path in NON_GATING or path.startswith("scripts/test-impact/"):
return "ci-other"
if path == "scripts/test_mcp_in_process.py":
return "test-contract"
if path == "scripts/fuzz.sh":
return "fuzz"
if path == "scripts/audit-license-provenance.py":
return "ci-gate"
if path.startswith(("internal/cbm/vendored/grammars/", "tools/tree-sitter-")):
return "grammar"
if path.startswith(".github/workflows/"):
return "ci-gate" if base in GATE_WORKFLOWS else "ci-other"
if path.startswith(".github/"):
if path.endswith(".md") or ("ISSUE_TEMPLATE" in path and path.endswith((".yml", ".yaml"))):
return "docs"
return "ci-other"
if path.startswith("scripts/ci/"):
return "ci-gate"
if path in BUILD:
return "build"
if path in SMOKE:
return "smoke"
if path.startswith("tests/fixtures/"):
return "test"
if path.startswith("tests/windows/"):
return "test-win"
if path.startswith("tests/fuzz/"):
return "fuzz"
if path.startswith("tests/"):
return "test-contract" if path.endswith((".sh", ".py", ".ps1")) else "test"
if path.startswith(("docs/", "eval-results/")) or base in DOC_FILES or (
path.endswith(DOC_SUFFIXES) and not path.startswith(("src/", "internal/", "scripts/"))):
return "docs"
if path.startswith("graph-ui/"):
return "ui-frontend"
if path.startswith(("pkg/", "scripts/setup")) or base in PKG_FILES:
return "pkg"
if path.startswith(("internal/cbm/vendored/", "vendored/")):
return "vendored"
m = re.match(r"internal/cbm/lsp/([a-z]+)_", path)
if m and m.group(1) not in ("lsp", "type", "scope"):
return "lsp:" + m.group(1)
if path.startswith("internal/cbm/lsp/"):
return "lsp-core"
if path.startswith("internal/cbm/"):
return "extract"
if path == "src/main.c":
return "cli"
if path.startswith("src/foundation/"):
return "mem-core" if re.search(r"/(mem|slab|arena|alloc|mimalloc)", path) else "foundation"
if path.startswith("src/"):
return "src:" + path.split("/")[1]
if path.startswith("test-infrastructure/"):
return "local-infra"
if path.startswith("scripts/"):
return "scripts-other"
return "other"
# ── Categories -> tier (highest wins) ──
EXTRACT = {"grammar", "lsp-core", "extract"}
MID = {"pipeline", "mcp", "store", "cypher", "discover", "git", "graph_buffer",
"semantic", "simhash", "traces", "personal_memory"}
PLATFORM = {"daemon", "cli", "watcher", "ui", "foundation", "smoke", "pkg", "test-win"}
CORE = {"mem-core", "build", "vendored", "ci-gate", "other", "scripts-other"}
LOWER = {"test", "test-contract", "fuzz", "ui-frontend", "ci-other",
"local-infra", "docs", "lint-cfg", "memwaste-tools"}
def normalized(cat):
# src/<dir>/... categories are the directory name; a directory no tier
# names stays unknown (and therefore T5 + full).
if cat.startswith("src:"):
name = cat[4:]
return name if name in MID | PLATFORM else cat
return cat
def known(cat):
return cat in EXTRACT | MID | PLATFORM | CORE | LOWER or cat.startswith("lsp:")
def tier_of(cats):
if cats & CORE or not all(known(c) for c in cats):
return "T5-core"
if cats & PLATFORM:
return "T4-platform"
if cats & MID:
return "T3-mid"
if cats & EXTRACT or any(c.startswith("lsp:") for c in cats):
return "T2-extract"
if cats & {"test", "test-contract", "fuzz"}:
return "T1-tests"
if cats & {"ui-frontend", "ci-other", "local-infra", "lint-cfg", "memwaste-tools"}:
return "T0b-nonproduct"
return "T0a-docs"
# ── Tier -> lanes (R1) ──
FULL = {"codeql-gate", "diag", "license-gate", "lint", "lint-mem", "lsan-macos",
"memwaste", "msan", "pkg-wrappers", "security-static", "shard-completeness",
"smoke-mac", "smoke-ubuntu", "smoke-win", "tsan-arm", "tsan-mac", "tsan-x86",
"unix-arm64", "unix-macos-intel", "unix-macos14", "unix-x86", "windows",
"windows-guards"}
UNIVERSE = FULL | {"contracts"} # contracts: the T0a/T0b job; every test leg runs them too
LINT = {"lint", "lint-mem"}
CORE_LEGS = {"unix-x86", "unix-macos14", "windows", "shard-completeness"}
TEST_LEGS = {"unix-x86", "unix-arm64", "unix-macos14", "unix-macos-intel", "windows"}
ALWAYS = {"security-static"}
R1 = {
"T0a-docs": {"contracts"},
# The contract steps police test-infrastructure/ and lint config too.
"T0b-nonproduct": LINT | {"contracts"},
"T1-tests": LINT | CORE_LEGS,
"T2-extract": LINT | CORE_LEGS | {"codeql-gate", "tsan-x86", "smoke-ubuntu"},
"T3-mid": LINT | CORE_LEGS | {"codeql-gate", "tsan-x86", "smoke-ubuntu"},
"T4-platform": FULL - {"msan", "diag", "memwaste"},
"T5-core": FULL,
}
# Category add-ons: (categories, lanes). pkg adds nothing to a docs-only set
# because a pkg path is never docs-only (it lifts the tier to T4).
ADD_ONS = [
({"pkg"}, {"pkg-wrappers", "smoke-ubuntu", "smoke-mac", "smoke-win"}),
({"vendored", "pkg", "ci-gate", "build"}, {"license-gate"}),
({"ui-frontend"}, {"windows-guards", "smoke-ubuntu"}),
({"memwaste-tools", "mem-core"}, {"memwaste"}),
({"test-win"}, {"windows", "windows-guards"}),
# the contract steps run inside test.sh's default leg
({"ci-other", "test-contract", "fuzz"}, {"unix-x86"}),
]
HARNESS = {"scripts/test.sh", "scripts/run-tests-parallel.sh", "scripts/run-test-wave.py",
"tests/test_main.c", "tests/test_framework.h"}
def triggers(path, cat):
found = []
if path == "scripts/ci/select-lanes.sh":
found.append("the lane selector itself")
if path.startswith(".github/workflows/"):
found.append("workflow")
if path in HARNESS:
found.append("test harness")
if path == "Makefile.cbm":
found.append("Makefile.cbm (registration and flag changes are indistinguishable by name)")
if cat == "vendored":
found.append("vendored code")
if cat in ("other", "scripts-other") or not known(cat):
found.append("path matched by no rule")
return found
LIST_CAP = 3000 # the pulls/files API lists at most this many files
def truncated(paths, expect):
"""A list that may be missing files: at or over the API cap, or shorter
than the PR's changed_files (a complete list is never shorter: every
changed file is listed, renames add their old path on top)."""
if len(paths) >= LIST_CAP:
return f"{len(paths)} paths listed, at the {LIST_CAP}-file API cap"
if expect is not None and expect >= LIST_CAP:
return f"the PR changes {expect} files, over the {LIST_CAP}-file API cap"
if expect is not None and len(paths) < expect:
return f"{len(paths)} paths listed for {expect} changed files"
return None
def select(paths, expect=None):
paths = sorted({p for p in paths if p})
by_cat, by_trigger = {}, {}
for path in paths:
cat = normalized(category(path))
by_cat.setdefault(cat, []).append(path)
for trigger in triggers(path, cat):
by_trigger.setdefault(trigger, []).append(path)
cats = set(by_cat)
tier = tier_of(cats)
lanes = R1[tier] | ALWAYS
reasons = [f"category {c}: {len(p)} file(s), e.g. {p[0]}" for c, p in by_cat.items()]
for when, extra in ADD_ONS:
hit = sorted(cats & when)
if hit and tier != "T0a-docs" and not extra <= lanes:
reasons.append(f"add-on {'/'.join(hit)}: +{' +'.join(sorted(extra - lanes))}")
lanes = lanes | extra
if lanes & TEST_LEGS:
lanes = lanes | {"shard-completeness"}
for trigger, hits in by_trigger.items():
more = f" (+{len(hits) - 1} more)" if len(hits) > 1 else ""
reasons.append(f"full: {trigger}: {hits[0]}{more}")
if not paths:
reasons.append("full: empty file list (fail safe)")
if tier == "T5-core":
reasons.append("full: tier T5-core")
short = truncated(paths, expect)
if short:
reasons.append(f"full: file_list_truncated: {short} (fail safe)")
full = tier == "T5-core" or bool(by_trigger) or not paths or bool(short)
if full:
lanes = set(FULL)
return {"full": full, "lanes": sorted(lanes), "reasons": sorted(reasons), "tier": tier}
def dump(obj):
return json.dumps(obj, sort_keys=True, separators=(",", ":"))
def read_paths(path):
with open(path, encoding="utf-8") as fh: # universal newlines: CRLF == LF
return [line.strip() for line in fh]
if mode == "list":
print("\n".join(sorted(UNIVERSE)))
elif mode == "batch":
with open(source, encoding="utf-8") as fh:
for line in fh:
if line.strip():
row = json.loads(line)
print(dump({"id": row["id"], **select(row["files"], row.get("expect_files"))}))
elif fmt == "github-output":
decision = select(read_paths(source), expect)
print(f"tier={decision['tier']}")
print(f"full={'true' if decision['full'] else 'false'}")
print(f"lanes={dump(decision['lanes'])}")
print(f"reasons={dump(decision['reasons'])}")
else:
print(dump(select(read_paths(source), expect)))
PY
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env bash
# test-impact-bundle.sh — fetch the team artifact bundle of one main commit.
#
# The bundle (codebase-memory-mcp test-impact publish; src/mcp/test_impact_artifact.h)
# is published by .github/workflows/test-impact-artifact.yml as the Actions
# artifact `test-impact-<sha>`. A selection may pass --artifact-verified ONLY
# for a bundle this script fetched: it accepts nothing but a SUCCESSFUL run of
# that workflow, in THIS repository, on the main branch, whose head is exactly
# the requested commit — the provenance the receipt itself can never assert.
#
# Usage: scripts/ci/test-impact-bundle.sh --fetch SHA DIR
# Download the bundle of SHA into DIR (created). Needs GH_TOKEN with
# actions:read; $GH overrides the gh command, $GITHUB_REPOSITORY the
# repository (OWNER/NAME).
# Exit: 0 = fetched and verified · 3 = no admissible bundle for SHA ·
# 1 = the tooling broke · 2 = usage error.
set -euo pipefail
GH="${GH:-gh}"
REPO="${GITHUB_REPOSITORY:-DeusData/codebase-memory-mcp}"
WORKFLOW="test-impact-artifact.yml"
usage() { sed -n '2,19p' "$0" | sed 's/^# \{0,1\}//'; }
[ $# -eq 3 ] && [ "$1" = "--fetch" ] || { usage >&2; exit 2; }
SHA="$2"
DIR="$3"
case "$SHA" in
*[!0-9a-f]* | "") echo "test-impact-bundle: not a lowercase commit id: $SHA" >&2; exit 2 ;;
esac
[ ${#SHA} -eq 40 ] || [ ${#SHA} -eq 64 ] || { echo "test-impact-bundle: bad id length" >&2; exit 2; }
# The newest successful main-branch run of the producer for exactly this head.
run=$("$GH" api -X GET "repos/$REPO/actions/workflows/$WORKFLOW/runs" \
-f branch=main -f status=success -f head_sha="$SHA" -f per_page=20 \
--jq '[.workflow_runs[]
| select(.head_sha == "'"$SHA"'" and .head_branch == "main"
and .repository.full_name == "'"$REPO"'"
and .head_repository.full_name == "'"$REPO"'"
and (.event == "push" or .event == "schedule"
or .event == "workflow_dispatch"))]
| sort_by(.run_started_at) | last | .id // empty') || exit 1
if [ -z "$run" ]; then
echo "test-impact-bundle: no successful producer run for $SHA" >&2
exit 3
fi
mkdir -p "$DIR"
if ! "$GH" run download "$run" --repo "$REPO" --name "test-impact-$SHA" --dir "$DIR"; then
echo "test-impact-bundle: run $run has no artifact test-impact-$SHA" >&2
exit 3
fi
# The receipt must name the commit the run was for; the engine checks the rest.
if ! python3 - "$DIR/receipt.json" "$SHA" <<'PY'
import json, sys
receipt = json.load(open(sys.argv[1]))
sys.exit(0 if receipt.get("commit") == sys.argv[2] else 1)
PY
then
echo "test-impact-bundle: the receipt does not name $SHA" >&2
exit 3
fi
echo "test-impact-bundle: run $run, bundle of $SHA in $DIR"
+114
View File
@@ -0,0 +1,114 @@
#!/usr/bin/env bash
# test-impact-publish.sh — publish the team artifact bundle of the checked-out
# main commit (.github/workflows/test-impact-artifact.yml).
#
# Incremental (a push): the previous commit's bundle is the graph base and its
# coverage map the starting point; only the suites the push's own selection
# picks are re-run with coverage and merged in (scripts/test-impact/
# coverage-merge.py). The oldest observation is carried forward, so an
# incremental map ages out unless a full refresh runs.
# Full (weekly, manual, or no usable previous bundle): every suite is re-run
# and the graph is built from scratch. A weekly run ALSO builds the graph
# incrementally from the previous bundle and fails when the two graphs'
# content digests differ: an incremental index must equal a full one.
#
# Usage: scripts/ci/test-impact-publish.sh --binary PATH --runner PATH --out DIR
# --mode incremental|full [--before SHA] [--llvm-bin DIR] [--platform LABEL]
# Exit: 0 = published · 1 = failed · 2 = usage error.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
BINARY="" RUNNER="" OUT="" MODE="" BEFORE="" LLVM_BIN="" PLATFORM="linux-x86_64-clang21"
while [ $# -gt 0 ]; do
case "$1" in
--binary) BINARY="$2"; shift 2 ;;
--runner) RUNNER="$2"; shift 2 ;;
--out) OUT="$2"; shift 2 ;;
--mode) MODE="$2"; shift 2 ;;
--before) BEFORE="$2"; shift 2 ;;
--llvm-bin) LLVM_BIN="$2"; shift 2 ;;
--platform) PLATFORM="$2"; shift 2 ;;
*) sed -n '2,18p' "$0" | sed 's/^# \{0,1\}//' >&2; exit 2 ;;
esac
done
[ -n "$BINARY" ] && [ -n "$RUNNER" ] && [ -n "$OUT" ] || exit 2
case "$MODE" in incremental | full) ;; *) exit 2 ;; esac
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
# A private daemon rendezvous: the socket path must fit sun_path.
export CBM_RUNTIME_DIR="$(mktemp -d /tmp/cbm-rt.XXXXXX)"
export CBM_CACHE_DIR="$WORK/cache"
mkdir -p "$WORK/engine" && chmod 700 "$WORK/engine"
HEAD_SHA="$(git -C "$ROOT" rev-parse HEAD)"
PREVIOUS=""
if [ -n "$BEFORE" ]; then
if scripts/ci/test-impact-bundle.sh --fetch "$BEFORE" "$WORK/previous"; then
PREVIOUS="$WORK/previous"
fi
fi
if [ "$MODE" = incremental ] && [ -z "$PREVIOUS" ]; then
echo "test-impact-publish: no previous bundle for ${BEFORE:-?}: full refresh"
MODE=full
fi
map() { # out [suite...]
local out="$1"; shift
local args=()
for suite in "$@"; do args+=(--suite "$suite"); done
python3 "$ROOT/scripts/test-impact/coverage-map.py" --runner "$RUNNER" --out "$out" \
${LLVM_BIN:+--llvm-bin "$LLVM_BIN"} ${args[@]+"${args[@]}"}
}
OBSERVED=""
if [ "$MODE" = full ]; then
OBSERVED="$(date +%s)"
map "$WORK/coverage"
else
# The push's own selection decides what to re-run (whole suites).
"$BINARY" test-impact select --repo "$ROOT" --base "$BEFORE" --work "$WORK/engine" \
--artifact "$PREVIOUS" --artifact-commit "$BEFORE" --artifact-verified \
--platform "$PLATFORM" > "$WORK/selection.json"
python3 "$ROOT/scripts/test-impact/selection.py" suites "$WORK/selection.json" > "$WORK/suites.txt"
"$RUNNER" --list-suites > "$WORK/listed.txt"
if grep -qx '\*' "$WORK/suites.txt"; then
OBSERVED="$(date +%s)"
map "$WORK/coverage"
else
mapfile -t rerun < <(sort -u "$WORK/suites.txt")
map "$WORK/fresh" ${rerun[@]+"${rerun[@]}"}
python3 "$ROOT/scripts/test-impact/coverage-merge.py" --previous "$PREVIOUS/coverage" \
--fresh "$WORK/fresh" --suites "$WORK/listed.txt" --commit "$HEAD_SHA" \
--out "$WORK/coverage"
fi
fi
publish() { # out [previous]
"$BINARY" test-impact publish --repo "$ROOT" --out "$1" --work "$WORK/engine" \
--coverage "$WORK/coverage" ${OBSERVED:+--observed-at "$OBSERVED"} \
--platform "$PLATFORM" ${2:+--previous "$2"}
}
if [ "$MODE" = full ]; then
publish "$OUT"
if [ -n "$PREVIOUS" ]; then
publish "$WORK/incremental" "$PREVIOUS"
field() { python3 -c 'import json,sys;print(json.load(open(sys.argv[1]))[sys.argv[2]])' "$1" "$2"; }
# Topology (what selection reads) must be equal; edge properties and the
# corpus-statistical edges are reported, not gated (recorded defects).
full_topology="$(field "$OUT/receipt.json" graph_topology_sha256)"
incr_topology="$(field "$WORK/incremental/receipt.json" graph_topology_sha256)"
if [ "$full_topology" != "$incr_topology" ]; then
echo "test-impact-publish: incremental topology differs from the full one ($incr_topology != $full_topology)" >&2
exit 1
fi
if [ "$(field "$OUT/receipt.json" graph_content_sha256)" != \
"$(field "$WORK/incremental/receipt.json" graph_content_sha256)" ]; then
echo "::warning::incremental graph content (edge properties, similarity edges) differs from the full one; topology is equal"
fi
echo "test-impact-publish: incremental topology equals the full one ($full_topology)"
fi
else
publish "$OUT" "$PREVIOUS"
fi
echo "test-impact-publish: bundle of $HEAD_SHA in $OUT ($MODE)"
+102
View File
@@ -0,0 +1,102 @@
#!/usr/bin/env bash
# test-impact-select.sh — the PR selection with the team artifact: what
# `codebase-memory-mcp test-impact select` runs for this change.
#
# Shadow (default): REPORT-ONLY; pr.yml's test-impact-shadow job runs it
# continue-on-error and ci-ok does not need it.
# --gate DIR (pr.yml's select-tests job, only when the repository variable
# TEST_IMPACT_MODE is `gate`): also turns the answer into what the test legs
# run (scripts/test-impact/selection.py) — DIR/test-only.txt and
# DIR/optional-suites.txt for a narrowed selection — and writes
# `selection=narrowed` or `selection=all` to $GITHUB_OUTPUT. Anything but a
# well-formed narrowed answer, a lane that selects nothing included, is `all`.
#
# The base is the base side of the PR merge commit (HEAD^1 by default). Its
# bundle is fetched with scripts/ci/test-impact-bundle.sh, which only accepts
# the main-branch producer's own run for exactly that commit; only then does
# the selection pass --artifact-verified. Without a bundle the engine builds
# the graph itself and admits no coverage: every reached suite runs whole.
#
# Usage: scripts/ci/test-impact-select.sh --binary PATH --out DIR [--base REV]
# [--platform LABEL] [--gate DIR]
# Writes DIR/selection-engine.json and DIR/summary-engine.md (appended
# to $GITHUB_STEP_SUMMARY when set).
# Exit: 0 = selection written · 1 = the tooling broke · 2 = usage error.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
BINARY="" OUT="" BASE="HEAD^1" PLATFORM="linux-x86_64-clang21" GATE=""
while [ $# -gt 0 ]; do
case "$1" in
--binary) BINARY="$2"; shift 2 ;;
--out) OUT="$2"; shift 2 ;;
--base) BASE="$2"; shift 2 ;;
--platform) PLATFORM="$2"; shift 2 ;;
--gate) GATE="$2"; shift 2 ;;
*) sed -n '2,24p' "$0" | sed 's/^# \{0,1\}//' >&2; exit 2 ;;
esac
done
[ -n "$BINARY" ] && [ -n "$OUT" ] || exit 2
mkdir -p "$OUT"
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
export CBM_RUNTIME_DIR="$(mktemp -d /tmp/cbm-rt.XXXXXX)" # sun_path must fit
export CBM_CACHE_DIR="$WORK/cache"
mkdir -p "$WORK/engine" && chmod 700 "$WORK/engine"
base="$(git -C "$ROOT" rev-parse "$BASE")"
artifact=()
bundle_rc=0
"$ROOT/scripts/ci/test-impact-bundle.sh" --fetch "$base" "$WORK/bundle" || bundle_rc=$?
if [ "$bundle_rc" -eq 0 ]; then
artifact=(--artifact "$WORK/bundle" --artifact-commit "$base" --artifact-verified)
elif [ "$bundle_rc" -ne 3 ]; then
# No bundle means no coverage: more tests run, never fewer. Say so.
echo "::warning::test-impact bundle fetch failed (exit $bundle_rc): selecting without coverage"
fi
started=$(date +%s)
"$BINARY" test-impact select --repo "$ROOT" --base "$base" --work "$WORK/engine" \
--platform "$PLATFORM" ${artifact[@]+"${artifact[@]}"} > "$OUT/selection-engine.json"
elapsed=$(( $(date +%s) - started ))
python3 - "$OUT/selection-engine.json" "$elapsed" "${#artifact[@]}" > "$OUT/summary-engine.md" <<'PY'
import json, sys
answer = json.load(open(sys.argv[1]))
coverage = answer.get("receipt", {}).get("coverage", {})
lines = ["### test-impact engine", "",
f"- decision: `{answer.get('decision')}` in {sys.argv[2]} s",
f"- team artifact: {'fetched' if sys.argv[3] != '0' else 'none'}; coverage "
f"`{coverage.get('state')}` {coverage.get('rejection_reasons') or ''}"]
for lane in answer.get("lanes") or []:
suites = lane.get("suites") or []
whole = [s["suite"] for s in suites if s.get("mode") == "whole"]
narrowed = {s["suite"]: len(s.get("tests") or []) for s in suites if s.get("mode") == "tests"}
lines.append(f"- lane `{lane.get('lane')}` ({lane.get('decision')}): {len(whole)} whole, "
f"{len(narrowed)} narrowed ({sum(narrowed.values())} tests)")
print("\n".join(lines))
PY
if [ -n "$GATE" ]; then
mkdir -p "$GATE"
selection=all
filter_rc=0
python3 "$ROOT/scripts/test-impact/selection.py" filter "$OUT/selection-engine.json" \
--lane unit --out "$GATE/test-only.txt" --optional-out "$GATE/optional-suites.txt" ||
filter_rc=$?
if [ "$filter_rc" -eq 0 ]; then
selection=narrowed
echo "- PR CI runs $(wc -l < "$GATE/test-only.txt" | tr -d ' ') selection tokens" \
>> "$OUT/summary-engine.md"
else
rm -f "$GATE/test-only.txt" "$GATE/optional-suites.txt"
echo "- PR CI runs every test (selection exit $filter_rc)" >> "$OUT/summary-engine.md"
fi
if [ -n "${GITHUB_OUTPUT:-}" ]; then
echo "selection=$selection" >> "$GITHUB_OUTPUT"
fi
fi
cat "$OUT/summary-engine.md"
if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then
cat "$OUT/summary-engine.md" >> "$GITHUB_STEP_SUMMARY"
fi
+168
View File
@@ -0,0 +1,168 @@
#!/usr/bin/env bash
# test-impact-shadow.sh — the PR test-impact SHADOW: predict from the code graph
# which C test suites a change can affect, and publish the prediction. It is
# REPORT-ONLY: pr.yml runs it continue-on-error and ci-ok does not need it, so
# it can never block or pass a merge. Its predictions accumulate next to the
# lanes that actually ran and failed, which is how graph-based selection gets
# evaluated before anyone gates on it.
#
# The binary is the latest PUBLISHED release, not this checkout's build: a few
# seconds of download instead of minutes of compile, and a PR that breaks the
# indexer cannot corrupt its own prediction. pr.yml builds the checkout only
# if the download fails.
#
# Usage: scripts/ci/test-impact-shadow.sh --fetch-release DIR [--repo OWNER/REPO]
# Download the latest release's archive for this host + checksums.txt
# into DIR, verify the SHA-256, extract DIR/codebase-memory-mcp.
# Needs GH_TOKEN (read-only); $GH overrides the gh command.
# scripts/ci/test-impact-shadow.sh --binary PATH --out DIR [--base REV] [--depth N]
# Index this checkout into a throwaway cache (HOME, XDG dirs and
# CBM_CACHE_DIR under DIR/work, a private CBM_RUNTIME_DIR under /tmp:
# the daemon's socket path must fit sun_path), run
# detect_changes against REV (default HEAD^1, the base side of a PR
# merge commit) at depth N (default 15), and let
# scripts/test-impact/predict.py map it to suites with the lane
# selector's run-all triggers. Writes DIR/prediction.json and
# DIR/summary.md (appended to $GITHUB_STEP_SUMMARY when set). The
# graph is only consulted when C sources changed.
# Exit: 0 = prediction written · 1 = the tooling broke · 2 = usage error.
set -euo pipefail
usage() { sed -n '2,28p' "$0" | sed 's/^# \{0,1\}//'; }
FETCH_DIR=""
REPO="${GITHUB_REPOSITORY:-DeusData/codebase-memory-mcp}"
BINARY=""
OUT=""
BASE="HEAD^1"
DEPTH=15
while [ $# -gt 0 ]; do
case "$1" in
-h | --help)
usage
exit 0
;;
--fetch-release | --repo | --binary | --out | --base | --depth)
if [ $# -lt 2 ]; then
echo "test-impact-shadow.sh: $1 needs a value. Please consult --help." >&2
exit 2
fi
case "$1" in
--fetch-release) FETCH_DIR="$2" ;;
--repo) REPO="$2" ;;
--binary) BINARY="$2" ;;
--out) OUT="$2" ;;
--base) BASE="$2" ;;
--depth) DEPTH="$2" ;;
esac
shift
;;
*)
echo "test-impact-shadow.sh: unknown argument '$1'. Please consult --help." >&2
exit 2
;;
esac
shift
done
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
sha256_check() { # file with "<sha> <name>" lines, run in the archive's dir
if command -v sha256sum >/dev/null 2>&1; then
sha256sum -c "$1"
else
shasum -a 256 -c "$1"
fi
}
if [ -n "$FETCH_DIR" ]; then
case "$(uname -s)/$(uname -m)" in
Linux/x86_64) asset=codebase-memory-mcp-linux-amd64.tar.gz ;;
Linux/aarch64 | Linux/arm64) asset=codebase-memory-mcp-linux-arm64.tar.gz ;;
Darwin/arm64) asset=codebase-memory-mcp-darwin-arm64.tar.gz ;;
Darwin/x86_64) asset=codebase-memory-mcp-darwin-amd64.tar.gz ;;
*)
echo "test-impact-shadow.sh: no release archive for $(uname -s)/$(uname -m)" >&2
exit 1
;;
esac
read -r -a gh_cmd <<<"${GH:-gh}"
mkdir -p "$FETCH_DIR"
# No tag: the latest release.
"${gh_cmd[@]}" release download --repo "$REPO" --pattern "$asset" \
--pattern checksums.txt --dir "$FETCH_DIR" --clobber
grep " $asset\$" "$FETCH_DIR/checksums.txt" >"$FETCH_DIR/asset.sha256"
(cd "$FETCH_DIR" && sha256_check asset.sha256)
tar -xzf "$FETCH_DIR/$asset" -C "$FETCH_DIR" codebase-memory-mcp
echo "release binary: $FETCH_DIR/codebase-memory-mcp ($asset, SHA-256 verified)"
exit 0
fi
if [ -z "$BINARY" ] || [ -z "$OUT" ]; then
echo "test-impact-shadow.sh: --binary and --out are required. Please consult --help." >&2
exit 2
fi
case "$DEPTH" in
'' | *[!0-9]*)
echo "test-impact-shadow.sh: --depth must be a number. Please consult --help." >&2
exit 2
;;
esac
BINARY="$(cd "$(dirname "$BINARY")" && pwd)/$(basename "$BINARY")"
PREDICT="$ROOT/scripts/test-impact/predict.py"
mkdir -p "$OUT"
OUT="$(cd "$OUT" && pwd)"
WORK="$OUT/work"
rm -rf "$WORK"
mkdir -p "$WORK/home/.config" "$WORK/home/.cache" "$WORK/cache"
# <runtime>/cbm-daemon-<uid>/cbm-<key>.sock adds ~42 bytes to this path, and
# sun_path holds 104 (macOS) / 108: a deep $OUT or $TMPDIR does not fit.
RUNTIME=$(mktemp -d /tmp/cbm-rt.XXXXXX)
trap 'rm -rf "$RUNTIME"' EXIT
BASE_SHA=$(git -C "$ROOT" rev-parse --verify "$BASE^{commit}")
git -C "$ROOT" diff --name-only --no-renames "$BASE_SHA" HEAD >"$OUT/changed-files.txt"
bash "$ROOT/scripts/ci/select-lanes.sh" "$OUT/changed-files.txt" >"$OUT/selection.json"
# From here on nothing may touch the host's real config, cache or daemon.
export HOME="$WORK/home"
export XDG_CONFIG_HOME="$WORK/home/.config"
export XDG_CACHE_HOME="$WORK/home/.cache"
export CBM_CACHE_DIR="$WORK/cache"
export CBM_RUNTIME_DIR="$RUNTIME"
VERSION=$("$BINARY" --version 2>/dev/null | head -1 || true)
DETECT_ARGS=()
T_INDEX="-"
T_DETECT="-"
if [ "$(python3 "$PREDICT" --needs-graph --changed "$OUT/changed-files.txt")" = yes ]; then
python3 -c 'import json, sys; json.dump({"repo_path": sys.argv[1]}, open(sys.argv[2], "w"))' \
"$ROOT" "$WORK/index-args.json"
t0=$(date +%s)
"$BINARY" cli --quiet index_repository --args-file "$WORK/index-args.json" >"$WORK/index.json"
t1=$(date +%s)
python3 -c '
import json, sys
project = json.load(open(sys.argv[1]))["project"]
json.dump({"project": project, "base_branch": sys.argv[2], "depth": int(sys.argv[3]),
"limit": 5000, "changed_limit": 5000, "module_limit": 0,
"max_output_tokens": 1000000, "format": "json"}, open(sys.argv[4], "w"))
' "$WORK/index.json" "$BASE_SHA" "$DEPTH" "$WORK/detect-args.json"
"$BINARY" cli --quiet detect_changes --args-file "$WORK/detect-args.json" >"$WORK/detect.json"
t2=$(date +%s)
# The CLI started a private daemon under $RUNTIME; stop it (best effort).
"$BINARY" daemon stop >/dev/null 2>&1 || true
T_INDEX="$((t1 - t0))s"
T_DETECT="$((t2 - t1))s"
DETECT_ARGS=(--detect "$WORK/detect.json")
fi
META=$(python3 -c 'import json, sys; print(json.dumps(dict(zip(sys.argv[1::2], sys.argv[2::2]))))' \
binary "${VERSION:-unknown}" base "$BASE_SHA" "index time" "$T_INDEX" "detect time" "$T_DETECT")
python3 "$PREDICT" --root "$ROOT" --changed "$OUT/changed-files.txt" \
--selection "$OUT/selection.json" ${DETECT_ARGS[@]+"${DETECT_ARGS[@]}"} --depth "$DEPTH" \
--meta "$META" --out "$OUT/prediction.json" --summary "$OUT/summary.md"
cat "$OUT/summary.md"
if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then
cat "$OUT/summary.md" >>"$GITHUB_STEP_SUMMARY"
fi
+49 -7
View File
@@ -8,24 +8,56 @@
# For every leg it asserts: all shards agree on the shard count, indices form
# exactly 1..n, every shard saw the same full suite list, and the UNION of the
# shard slices equals that list — a rename/re-shard can never silently drop a
# suite (gate-quality loss) without failing here.
# suite (gate-quality loss) without failing here. A narrowed leg (PR CI test
# selection) records the selected list and its selection_sha256; all shards
# must agree on that too.
#
# Usage: scripts/ci/verify-shard-union.sh <manifests-dir>
# No manifests at all is a failure — unless the PR's lane selection (--lanes,
# the JSON list from scripts/ci/select-lanes.sh) holds no test leg, in which
# case no leg ran by design. Without --lanes (or with "all": dry run,
# release) every leg is expected, exactly as before lane selection.
#
# Usage: scripts/ci/verify-shard-union.sh <manifests-dir> [--lanes <json-list>|all]
set -eu
case "${1:-}" in
-h | --help)
sed -n '2,13p' "$0" | sed 's/^# \{0,1\}//'
sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//'
exit 0
;;
esac
MANIFEST_DIR="${1:?usage: verify-shard-union.sh <manifests-dir> (see --help)}"
LANES=all
if [ "${2:-}" = "--lanes" ]; then
LANES="${3:-}"
elif [ -n "${2:-}" ]; then
echo "verify-shard-union.sh: unknown argument '$2'. Please consult --help." >&2
exit 2
fi
case "$LANES" in
all | \[*\]) ;;
*)
echo "verify-shard-union.sh: --lanes must be 'all' or a JSON list. Please consult --help." >&2
exit 2
;;
esac
files=$(find "$MANIFEST_DIR" -name shard-manifest.txt | sort)
files=""
if [ -d "$MANIFEST_DIR" ]; then
files=$(find "$MANIFEST_DIR" -name shard-manifest.txt | sort)
fi
if [ -z "$files" ]; then
echo "FAIL: no shard manifests were uploaded" >&2
exit 1
# The test legs are the sharded ones: unix-* and windows (matched with its
# quotes, so the windows-guards lane never reads as the windows leg).
case "$LANES" in
all | *\"unix-* | *\"windows\"*)
echo "FAIL: no shard manifests were uploaded" >&2
exit 1
;;
esac
echo "OK: no test leg was selected ($LANES) — no manifests expected"
exit 0
fi
rc=0
for leg in $(grep -h '^leg=' $files | sort -u | sed 's/^leg=//'); do
@@ -48,9 +80,19 @@ for leg in $(grep -h '^leg=' $files | sort -u | sed 's/^leg=//'); do
rc=1
continue
fi
# A narrowed leg: every shard must have applied the same test selection
# (two selections can name the same suites and different tests).
sel_sha=$(for f in $leg_files; do
grep -h '^selection_sha256=' "$f" || echo "selection_sha256=none"
done | sort -u)
if [ "$(printf '%s\n' "$sel_sha" | wc -l)" -ne 1 ]; then
echo "FAIL: $leg shards applied different test selections" >&2
rc=1
continue
fi
union_sha=$(for f in $leg_files; do
sed -n '/^--- slice ---$/,$p' "$f" | tail -n +2
done | sort | sha256sum | awk '{print $1}')
done | sort | { sha256sum 2>/dev/null || shasum -a 256; } | awk '{print $1}')
if [ "$union_sha" != "$list_sha" ]; then
echo "FAIL: $leg union of shard slices != full suite list (GATE-QUALITY LOSS)" >&2
rc=1
+7 -8
View File
@@ -22,7 +22,7 @@ src/cli/client_adapter.c 3
src/cli/config_edit_path.c 2
src/cli/config_json_like.c 142
src/cli/config_text_edit.c 72
src/cli/config_toml_edit.c 85
src/cli/config_toml_edit.c 73
src/cli/config_yaml_edit.c 117
src/cli/hook_augment.c 31
src/cypher/cypher.c 132
@@ -44,25 +44,24 @@ src/foundation/platform.c 11
src/foundation/platform.h 5
src/foundation/private_file_lock.c 56
src/foundation/str_intern.c 7
src/foundation/subprocess.c 24
src/foundation/win_utf8.h 17
src/foundation/subprocess.c 17
src/foundation/win_utf8.h 7
src/foundation/workspace.c 3
src/foundation/yaml.c 10
src/git/git_context.c 18
src/main.c 32
src/mcp/compact_out.c 39
src/mcp/index_supervisor.c 8
src/mcp/mcp.c 784
src/mcp/mcp.c 773
src/pipeline/artifact.c 38
src/pipeline/fqn.c 23
src/pipeline/lsp_resolve.h 4
src/pipeline/lsp_surface.c 5
src/pipeline/pass_calls.c 22
src/pipeline/pass_compile_commands.c 17
src/pipeline/pass_complexity.c 15
src/pipeline/pass_configlink.c 3
src/pipeline/pass_cross_repo.c 8
src/pipeline/pass_definitions.c 14
src/pipeline/pass_definitions.c 11
src/pipeline/pass_enrichment.c 15
src/pipeline/pass_ensemble_routing.c 10
src/pipeline/pass_envscan.c 2
@@ -70,7 +69,7 @@ src/pipeline/pass_githistory.c 27
src/pipeline/pass_importance.c 4
src/pipeline/pass_infrascan.c 2
src/pipeline/pass_k8s.c 6
src/pipeline/pass_lsp_cross.c 50
src/pipeline/pass_lsp_cross.c 49
src/pipeline/pass_parallel.c 31
src/pipeline/pass_pkgmap.c 82
src/pipeline/pass_semantic.c 3
@@ -79,7 +78,7 @@ src/pipeline/pass_usages.c 4
src/pipeline/path_alias.c 42
src/pipeline/pipeline.c 134
src/pipeline/pipeline_delta.c 19
src/pipeline/pipeline_incremental.c 144
src/pipeline/pipeline_incremental.c 143
src/pipeline/registry.c 17
src/pipeline/worker_pool.c 2
src/store/store.c 358
+58 -2
View File
@@ -35,7 +35,10 @@ if [ -z "$JOBS" ]; then
fi
fi
LOGDIR="$(dirname "$RUNNER")/test-logs"
# CBM_TEST_LOG_DIR: a private log directory (tests/test_harness_selection.sh
# drives this harness before the real run and must not touch its logs). Keep
# it relative: the native Windows runner reads the selection file below it.
LOGDIR="${CBM_TEST_LOG_DIR:-$(dirname "$RUNNER")/test-logs}"
rm -rf "$LOGDIR"
mkdir -p "$LOGDIR"
@@ -129,6 +132,54 @@ if [ "$NSUITES" -lt 1 ] || grep -qvE '^[a-z0-9_]+$' "$SUITES_FILE"; then
exit 1
fi
# Test selection (smart CI; PR CI only, scripts/test-impact/selection.py).
# CBM_TEST_SELECTION_FILE lists `suite` / `suite:test` tokens. Only the suites
# it names are launched, and every launched runner judges its own tokens
# through CBM_TEST_ONLY_FILE, so a selected test the runner cannot find fails
# that suite. A selected suite this build does not register fails the run,
# unless CBM_TEST_SELECTION_OPTIONAL names it (registered only in some builds,
# e.g. the coverage runner): then it is dropped with a notice, exactly as a
# full run of this build never runs it. Everything below — sharding, the
# manifest, the union guard — then works on the selected list. Unset: the
# full list, unchanged.
SELECTION="none"
if [ -n "${CBM_TEST_SELECTION_FILE:-}" ]; then
SEL_TOKENS="$LOGDIR/selection-tokens.txt"
if ! tr -d '\r' < "$CBM_TEST_SELECTION_FILE" > "$SEL_TOKENS"; then
echo "FAIL: cannot read the test selection $CBM_TEST_SELECTION_FILE" >&2
exit 1
fi
if [ ! -s "$SEL_TOKENS" ] || grep -qvE '^[A-Za-z0-9_]+(:[A-Za-z0-9_]+)?$' "$SEL_TOKENS"; then
echo "FAIL: test selection empty or malformed: $CBM_TEST_SELECTION_FILE" >&2
exit 1
fi
cut -d: -f1 "$SEL_TOKENS" | sort -u > "$LOGDIR/selection-suites.txt"
if [ -n "${CBM_TEST_SELECTION_OPTIONAL:-}" ]; then
tr -d '\r' < "$CBM_TEST_SELECTION_OPTIONAL" | sort -u > "$LOGDIR/selection-optional.txt"
else
: > "$LOGDIR/selection-optional.txt"
fi
ABSENT=$(comm -23 "$LOGDIR/selection-suites.txt" <(sort -u "$SUITES_FILE"))
REQUIRED_ABSENT=$(comm -23 <(printf '%s\n' $ABSENT | sed '/^$/d' | sort -u) "$LOGDIR/selection-optional.txt")
if [ -n "$REQUIRED_ABSENT" ]; then
echo "FAIL: the test selection names suites this build does not register:" $REQUIRED_ABSENT >&2
exit 1
fi
[ -z "$ABSENT" ] || echo "selection: conditional suites not in this build, not run:" $ABSENT
# The runner's filter keeps only this build's suites (an unknown suite's
# token would fail every process); the suite list keeps list order.
awk 'NR == FNR { listed[$0] = 1; next } { split($0, p, ":") } p[1] in listed' \
"$SUITES_FILE" "$SEL_TOKENS" > "$LOGDIR/selection.txt"
awk 'NR == FNR { selected[$0] = 1; next } $0 in selected' \
"$LOGDIR/selection-suites.txt" "$SUITES_FILE" > "$SUITES_FILE.selected"
mv "$SUITES_FILE.selected" "$SUITES_FILE"
SELECTION="$(sort "$LOGDIR/selection.txt" | { sha256sum 2>/dev/null || shasum -a 256; } | awk '{print $1}')"
echo "selection: $(wc -l < "$SUITES_FILE" | tr -d ' ') of $NSUITES suites, $(wc -l < "$LOGDIR/selection.txt" | tr -d ' ') tokens ($SELECTION)"
NSUITES=$(wc -l < "$SUITES_FILE" | tr -d ' ')
export CBM_TEST_ONLY_FILE="$LOGDIR/selection.txt"
unset CBM_TEST_ONLY
fi
# CBM_TEST_SHARD="i/N" runs this invocation's deterministic slice of the
# suite list so CI can spread one platform's suites across N runner jobs.
# Unset (or "1/1") selects everything — the sharding path is inert unless a
@@ -246,6 +297,7 @@ NSHARD=$(wc -l < "$SHARD_EXPECT" | tr -d ' ')
echo "leg=${CBM_TEST_LEG:-local}"
echo "shard=${SHARD_INDEX}/${SHARD_TOTAL}"
echo "list_sha256=$(sort "$SUITES_FILE" | { sha256sum 2>/dev/null || shasum -a 256; } | awk '{print $1}')"
echo "selection_sha256=$SELECTION"
echo "--- slice ---"
cat "$SHARD_EXPECT"
} > "$LOGDIR/shard-manifest.txt"
@@ -344,7 +396,11 @@ for f in $(grep -v ' rc=0 ' "$RESULTS_FILE" | awk '{print $1}'); do
done
echo "────────────────────────────────────────────"
echo " $TOTAL_PASS passed, $TOTAL_FAIL failed, $TOTAL_SKIP skipped ($NSUITES suites, $JOBS jobs)"
if [ "$SELECTION" = none ]; then
echo " $TOTAL_PASS passed, $TOTAL_FAIL failed, $TOTAL_SKIP skipped ($NSUITES suites, $JOBS jobs)"
else
echo " $TOTAL_PASS passed, $TOTAL_FAIL failed, $TOTAL_SKIP skipped ($NSUITES selected suites, $JOBS jobs; selection $SELECTION)"
fi
echo "────────────────────────────────────────────"
if [ "$TOTAL_FAIL" -gt 0 ] || [ "$BAD_RC" -gt 0 ]; then
+90 -26
View File
@@ -1,5 +1,5 @@
# codebase-memory-mcp setup script (Windows)
# Default: download pre-built native Windows binary
# Default: install the pre-built native Windows binary through install.ps1
# -FromSource: build from source inside WSL (requires Go + gcc in WSL)
param(
@@ -123,7 +123,7 @@ if ($Help) {
Write-Host ""
Write-Host "Usage: .\setup-windows.ps1 [-FromSource] [-Help]"
Write-Host ""
Write-Host " Default: Download pre-built Windows binary"
Write-Host " Default: Download the pre-built Windows binary through install.ps1"
Write-Host " -FromSource: Build from source inside WSL (requires Go 1.23+ and gcc in WSL)"
Write-Host ""
exit 0
@@ -225,7 +225,7 @@ if ($FromSource) {
exit 1
}
# Configure — WSL binary needs wsl.exe wrapper
# Configure -- WSL binary needs wsl.exe wrapper
$mcpConfig = [ordered]@{
type = "stdio"
command = "wsl.exe"
@@ -243,41 +243,105 @@ if ($FromSource) {
Write-Host " wsl.exe -- rm -rf ~/.cache/codebase-memory-mcp/"
} else {
# --- Download pre-built native Windows binary ---
Write-Host "Fetching latest release..." -ForegroundColor White
# --- Download + install through install.ps1 ---
#
# install.ps1 is the one implementation of "fetch a release and install
# it": it downloads checksums.txt next to the archive, verifies the
# archive's SHA-256 against it, validates the zip layout and only then
# runs the binary's own `install`. This script used to carry a second
# copy of that download, which did not keep up with the installer. It
# now fetches install.ps1 from the same origin and branch it is itself
# served from and hands over to it, so there is exactly one install path.
#
# CBM_DOWNLOAD_URL (the installers' download-base override, for local
# testing) also moves the installer fetch: install.ps1 is then taken from
# "$env:CBM_DOWNLOAD_URL/install.ps1".
$installerUrl = "https://raw.githubusercontent.com/$Repo/main/install.ps1"
if ($env:CBM_DOWNLOAD_URL) {
$installerUrl = $env:CBM_DOWNLOAD_URL.TrimEnd('/') + "/install.ps1"
}
$releaseUrl = "https://api.github.com/repos/$Repo/releases/latest"
$release = Invoke-RestMethod -Uri $releaseUrl -Headers @{ "User-Agent" = "codebase-memory-mcp-setup" }
$tag = $release.tag_name
if (-not $tag) {
Write-Fail "Could not determine latest release."
Write-Host " Check: https://github.com/$Repo/releases"
# Same transport rule as install.ps1: HTTPS, or plain HTTP for a loopback
# authority only (the local test fixture). No redirects are followed: both
# origins serve the installer directly.
try { $installerUri = [Uri]$installerUrl } catch { $installerUri = $null }
$loopbackHttp = (
$installerUri -and $installerUri.IsAbsoluteUri -and
$installerUri.Scheme -eq "http" -and $installerUri.IsLoopback -and
[string]::IsNullOrEmpty($installerUri.UserInfo)
)
if (-not $installerUri -or -not $installerUri.IsAbsoluteUri -or
($installerUri.Scheme -ne "https" -and -not $loopbackHttp) -or
-not [string]::IsNullOrEmpty($installerUri.UserInfo)) {
Write-Fail "Refusing non-HTTPS installer URL: $installerUrl"
exit 1
}
Write-Ok "Latest release: $tag"
$asset = "codebase-memory-mcp-windows-amd64.zip"
$downloadUrl = "https://github.com/$Repo/releases/download/$tag/$asset"
# TLS 1.2+ for the fetch (older Windows PowerShell defaults to TLS 1.0,
# which GitHub rejects). TLS 1.3 only where schannel can negotiate it; see
# the note in install.ps1.
$protocols = [Net.SecurityProtocolType]::Tls12
if ([Environment]::OSVersion.Version.Build -ge 20348 -and
([enum]::GetNames([Net.SecurityProtocolType]) -contains 'Tls13')) {
$protocols = $protocols -bor [Net.SecurityProtocolType]::Tls13
}
[Net.ServicePointManager]::SecurityProtocol = $protocols
Write-Host "Downloading $asset..." -ForegroundColor White
# Create install directory
if (-not (Test-Path $InstallDir)) {
New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
# A fresh staging directory of its own with an owner-only DACL, the
# way install.ps1 stages its own download. The ACL is best effort: a
# filesystem that cannot carry one must not fail the install.
$stageDir = Join-Path ([System.IO.Path]::GetTempPath()) "cbm-setup-$(Get-Random)"
New-Item -ItemType Directory -Path $stageDir -Force | Out-Null
try {
$stageAcl = New-Object System.Security.AccessControl.DirectorySecurity
$stageAcl.SetAccessRuleProtection($true, $false)
$stageOwner = ([System.Security.Principal.WindowsIdentity]::GetCurrent()).User
$stageAcl.SetOwner($stageOwner)
$stageAcl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule(
$stageOwner, 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow')))
Set-Acl -Path $stageDir -AclObject $stageAcl -ErrorAction Stop
} catch {
Write-Host " note: could not harden the staging directory ACL: $($_.Exception.Message)"
}
$tmpZip = Join-Path $env:TEMP $asset
Invoke-WebRequest -Uri $downloadUrl -OutFile $tmpZip -UseBasicParsing
$installer = Join-Path $stageDir "install.ps1"
$failure = $null
try {
Write-Host "Fetching install.ps1..." -ForegroundColor White
Invoke-WebRequest -Uri $installerUrl -OutFile $installer -UseBasicParsing -MaximumRedirection 0
if (-not (Test-Path -LiteralPath $installer -PathType Leaf) -or
(Get-Item -LiteralPath $installer).Length -eq 0) {
throw "fetched an empty install.ps1 from $installerUrl"
}
Write-Ok "install.ps1 fetched"
# Extract
Expand-Archive -Path $tmpZip -DestinationPath $InstallDir -Force
Remove-Item $tmpZip -Force
Write-Host ""
Write-Host "Installing through install.ps1..." -ForegroundColor White
# The installer configures nothing (--skip-config): agent configuration
# stays this script's interactive step below. It runs in a child host
# of the same PowerShell so its exit code is unambiguous and its global
# settings do not leak into this session. The pre-seeded exit code
# keeps a child that fails to start from reading as success.
$hostExe = (Get-Process -Id $PID).Path
$global:LASTEXITCODE = 1
& $hostExe -NoProfile -ExecutionPolicy Bypass -File $installer "--dir=$InstallDir" --skip-config
if ($LASTEXITCODE -ne 0) {
throw "install.ps1 exited with $LASTEXITCODE"
}
} catch {
$failure = "$_"
} finally {
Remove-Item -Recurse -Force $stageDir -ErrorAction SilentlyContinue
}
if ($failure) {
Write-Fail "Installation through install.ps1 failed: $failure"
exit 1
}
$binaryPath = Join-Path $InstallDir "$BinaryName.exe"
if (-not (Test-Path $binaryPath)) {
Write-Fail "Binary not found at $binaryPath after extraction"
Write-Fail "Binary not found at $binaryPath after installation"
exit 1
}
Write-Ok "Installed to $binaryPath"
+61 -65
View File
@@ -2,14 +2,14 @@
set -euo pipefail
# codebase-memory-mcp setup script (macOS + Linux)
# Default: download pre-built binary from GitHub Release
# Default: install the latest pre-built binary through install.sh
# --from-source: build from source (requires Go + C compiler)
REPO="DeusData/codebase-memory-mcp"
INSTALL_DIR="$HOME/.local/bin"
BINARY_NAME="codebase-memory-mcp"
SOURCE_DIR="$HOME/.local/share/codebase-memory-mcp"
CLEANUP_DIR="" # set by download_binary for EXIT trap
CLEANUP_DIR="" # set by install_release for EXIT trap
# --- Colors ---
@@ -43,7 +43,7 @@ for arg in "$@"; do
--help|-h)
echo "Usage: $0 [--from-source]"
echo ""
echo " Default: Download pre-built binary from GitHub Release"
echo " Default: Download the pre-built binary through install.sh"
echo " --from-source: Clone and build from source (requires Go 1.23+ and a C compiler)"
exit 0
;;
@@ -51,36 +51,6 @@ for arg in "$@"; do
esac
done
# --- Platform detection ---
detect_platform() {
local os arch
os=$(uname -s)
arch=$(uname -m)
case "$os" in
Darwin) os="darwin" ;;
Linux) os="linux" ;;
*) die "Unsupported OS: $os. Use WSL2 on Windows." ;;
esac
case "$arch" in
arm64|aarch64) arch="arm64" ;;
x86_64|amd64)
# On macOS, uname -m returns x86_64 under Rosetta even on Apple Silicon.
# Check the actual hardware to pick the right binary.
if [ "$os" = "darwin" ] && sysctl -n hw.optional.arm64 2>/dev/null | grep -q '1'; then
arch="arm64"
else
arch="amd64"
fi
;;
*) die "Unsupported architecture: $arch" ;;
esac
echo "${os}-${arch}"
}
# --- Prerequisite checks ---
check_download_tool() {
@@ -133,46 +103,74 @@ check_git() {
ok "Git found"
}
# --- Download binary ---
# --- Download + install through install.sh ---
#
# install.sh is the one implementation of "fetch a release and install it": it
# downloads checksums.txt next to the archive, verifies the archive's SHA-256
# against it, checks the archive layout, and only then runs the binary's own
# `install`. This script used to carry a second copy of that download,
# which did not keep up with the installer. It now fetches install.sh from the
# same origin and branch it is itself served from and hands over to it, so
# there is exactly one install path.
#
# CBM_DOWNLOAD_URL (the installers' download-base override, for local testing)
# also moves the installer fetch: install.sh is then taken from
# "$CBM_DOWNLOAD_URL/install.sh".
fetch() {
local url="$1" tool="$2"
if [ "$tool" = "curl" ]; then
curl -fsSL "$url"
INSTALLER_URL="https://raw.githubusercontent.com/${REPO}/main/install.sh"
if [ -n "${CBM_DOWNLOAD_URL:-}" ]; then
INSTALLER_URL="${CBM_DOWNLOAD_URL%/}/install.sh"
fi
# Same transport rule as install.sh: HTTPS everywhere; plain HTTP only for an
# exact loopback authority (the local test fixture), with redirects disabled
# there so a fixture cannot bounce the fetch to the network.
is_loopback_http_url() {
[[ "$1" =~ ^http://(localhost|127\.0\.0\.1|\[::1\])(:[0-9]+)?([/?\#].*)?$ ]]
}
fetch_installer() {
local url="$1" destination="$2" tool="$3"
if is_loopback_http_url "$url"; then
if [ "$tool" = "curl" ]; then
curl -fsS --noproxy '*' --proto '=http' -o "$destination" "$url"
else
wget -q --no-proxy --max-redirect=0 -O "$destination" "$url"
fi
elif [[ "$url" == https://* ]]; then
if [ "$tool" = "curl" ]; then
curl -fsSL --max-redirs 5 --proto '=https' --proto-redir '=https' \
-o "$destination" "$url"
else
wget -q --https-only --max-redirect=5 -O "$destination" "$url"
fi
else
wget -qO- "$url"
die "Refusing non-HTTPS installer URL: $url"
fi
}
download_binary() {
local platform="$1" tool="$2"
install_release() {
local tool="$1"
echo ""
echo "${BOLD}Fetching latest release...${RESET}"
local tag
tag=$(fetch "https://api.github.com/repos/${REPO}/releases/latest" "$tool" | grep '"tag_name"' | head -1 | sed 's/.*"tag_name": *"//;s/".*//')
if [ -z "$tag" ]; then
die "Could not determine latest release. Check https://github.com/${REPO}/releases"
fi
ok "Latest release: $tag"
local asset="codebase-memory-mcp-${platform}.tar.gz"
local url="https://github.com/${REPO}/releases/download/${tag}/${asset}"
echo "${BOLD}Downloading ${asset}...${RESET}"
echo "${BOLD}Fetching install.sh...${RESET}"
CLEANUP_DIR=$(mktemp -d)
trap 'rm -rf "$CLEANUP_DIR"' EXIT
local tmpdir="$CLEANUP_DIR"
local installer="$CLEANUP_DIR/install.sh"
fetch "$url" "$tool" > "${tmpdir}/${asset}"
tar -xzf "${tmpdir}/${asset}" -C "$tmpdir"
fetch_installer "$INSTALLER_URL" "$installer" "$tool" ||
die "Could not fetch install.sh from ${INSTALLER_URL}"
[ -s "$installer" ] || die "Fetched an empty install.sh from ${INSTALLER_URL}"
ok "install.sh fetched"
mkdir -p "$INSTALL_DIR"
mv "${tmpdir}/${BINARY_NAME}" "${INSTALL_DIR}/${BINARY_NAME}"
chmod +x "${INSTALL_DIR}/${BINARY_NAME}"
ok "Installed to ${INSTALL_DIR}/${BINARY_NAME}"
echo ""
echo "${BOLD}Installing through install.sh...${RESET}"
# The installer configures nothing (--skip-config): agent configuration
# stays this script's interactive step below. stdin is detached so that,
# when this script itself arrives through `curl | bash`, nothing the
# installer runs can read the rest of this script as its input.
bash "$installer" --dir "$INSTALL_DIR" --skip-config </dev/null ||
die "install.sh failed (see the messages above)"
}
# --- Build from source ---
@@ -297,11 +295,9 @@ echo ""
if [ "$FROM_SOURCE" = true ]; then
build_from_source
else
platform=$(detect_platform)
ok "Platform: ${platform}"
tool=$(check_download_tool)
ok "Download tool: ${tool}"
download_binary "$platform" "$tool"
install_release "$tool"
fi
# Verify binary
+70 -20
View File
@@ -116,6 +116,16 @@ copy_smoke_binary() {
cp "$BINARY" "$destination"
}
# A fixture home that belongs to a Claude Code user. An empty ~/.claude is not
# evidence of one (#1180: other tools create that directory, and install then
# configured a client the user never had); the user config Claude Code itself
# writes is. Same seed as test_mark_claude_code_installed in tests/test_cli.c.
mark_claude_code_installed() {
local home="$1"
mkdir -p "$home/.claude"
echo '{}' > "$home/.claude.json"
}
# Retire this run's private account daemon (if one is running) and wait until
# it reports not-running. Install/uninstall flows leave an ephemeral daemon
# draining asynchronously whose mapped generation backing and open logs
@@ -2126,27 +2136,33 @@ sys.exit(0 if ok else 1)
fi
echo "OK 8w-ii: Kiro MCP + steering + isolated exact-tool graph agent"
# 8x: Hermes Agent YAML MCP mapping
# 8x: Hermes Agent YAML MCP mapping. Native Windows Hermes keeps its home in
# %LOCALAPPDATA%\hermes (#1180); the stub-only FAKE_HOME has no legacy ~/.hermes.
if [[ "$BINARY" == *.exe ]]; then
HERMES_HOME_DIR="$FAKE_HOME/AppData/Local/hermes"
else
HERMES_HOME_DIR="$FAKE_HOME/.hermes"
fi
HERMES_CMD=$(sed -n '/^ codebase-memory-mcp:/{n;s/^ *command: *//p;}' \
"$FAKE_HOME/.hermes/config.yaml" 2>/dev/null | head -1)
if ! grep -q '^mcp_servers:' "$FAKE_HOME/.hermes/config.yaml" 2>/dev/null ||
! grep -q '^ codebase-memory-mcp:' "$FAKE_HOME/.hermes/config.yaml" 2>/dev/null ||
"$HERMES_HOME_DIR/config.yaml" 2>/dev/null | head -1)
if ! grep -q '^mcp_servers:' "$HERMES_HOME_DIR/config.yaml" 2>/dev/null ||
! grep -q '^ codebase-memory-mcp:' "$HERMES_HOME_DIR/config.yaml" 2>/dev/null ||
! quoted_path_value_matches "$HERMES_CMD" "$SELF_PATH"; then
echo "FAIL 8x: Hermes MCP mapping missing or malformed"
exit 1
fi
echo "OK 8x: Hermes Agent MCP"
if ! grep -q '^name: codebase-memory$' "$FAKE_HOME/.hermes/skills/codebase-memory/SKILL.md" 2>/dev/null ||
! grep -q 'delegate_task' "$FAKE_HOME/.hermes/skills/codebase-memory/SKILL.md" 2>/dev/null ||
! grep -q '`context`' "$FAKE_HOME/.hermes/skills/codebase-memory/SKILL.md" 2>/dev/null; then
if ! grep -q '^name: codebase-memory$' "$HERMES_HOME_DIR/skills/codebase-memory/SKILL.md" 2>/dev/null ||
! grep -q 'delegate_task' "$HERMES_HOME_DIR/skills/codebase-memory/SKILL.md" 2>/dev/null ||
! grep -q '`context`' "$HERMES_HOME_DIR/skills/codebase-memory/SKILL.md" 2>/dev/null; then
echo "FAIL 8x-i: Hermes delegation skill missing"
exit 1
fi
echo "OK 8x-i: Hermes durable delegation skill"
if ! grep -q '^hooks:' "$FAKE_HOME/.hermes/config.yaml" 2>/dev/null ||
! grep -q '^ pre_llm_call:' "$FAKE_HOME/.hermes/config.yaml" 2>/dev/null ||
! grep -q 'hook-augment' "$FAKE_HOME/.hermes/config.yaml" 2>/dev/null ||
! grep -q -- '--dialect hermes' "$FAKE_HOME/.hermes/config.yaml" 2>/dev/null; then
if ! grep -q '^hooks:' "$HERMES_HOME_DIR/config.yaml" 2>/dev/null ||
! grep -q '^ pre_llm_call:' "$HERMES_HOME_DIR/config.yaml" 2>/dev/null ||
! grep -q 'hook-augment' "$HERMES_HOME_DIR/config.yaml" 2>/dev/null ||
! grep -q -- '--dialect hermes' "$HERMES_HOME_DIR/config.yaml" 2>/dev/null; then
echo "FAIL 8x-ii: Hermes pre_llm_call context hook missing"
exit 1
fi
@@ -3148,8 +3164,8 @@ fi
echo "OK 9l: JSON agents, lifecycle hooks, and Kilo cleaned; foreign settings preserved"
# 9m: YAML/TOML new agents are cleaned.
if grep -q '^ codebase-memory-mcp:' "$FAKE_HOME/.hermes/config.yaml" 2>/dev/null ||
grep -q '^ pre_llm_call:' "$FAKE_HOME/.hermes/config.yaml" 2>/dev/null ||
if grep -q '^ codebase-memory-mcp:' "$HERMES_HOME_DIR/config.yaml" 2>/dev/null ||
grep -q '^ pre_llm_call:' "$HERMES_HOME_DIR/config.yaml" 2>/dev/null ||
grep -q '^ codebase-memory-mcp:' "$GOOSE_CFG" 2>/dev/null ||
grep -q 'codebase-memory-mcp' "$FAKE_HOME/.grok/config.toml" 2>/dev/null ||
grep -q '^name = "codebase-memory-mcp"' "$FAKE_HOME/.vibe/config.toml" 2>/dev/null; then
@@ -3203,7 +3219,7 @@ echo "OK 9m-i: durable instruction blocks removed"
# 9n: Skills removed (consolidated skill dir)
if [ -d "$FAKE_HOME/.claude/skills/codebase-memory" ] ||
[ -d "$FAKE_HOME/.hermes/skills/codebase-memory" ] ||
[ -d "$HERMES_HOME_DIR/skills/codebase-memory" ] ||
[ -d "$FAKE_HOME/.agents/skills/codebase-memory" ] ||
[ -d "$FAKE_HOME/.qoder/skills/codebase-memory" ] ||
[ -d "$CUSTOM_KIMI_HOME/skills/codebase-memory" ] ||
@@ -3281,9 +3297,40 @@ echo "OK 9b-1: install with minimal agents exits cleanly"
retire_account_daemon "9b-1-cleanup"
smoke_rmtree "$EMPTY_HOME"
# 9b-1b (#1180): an empty ~/.claude directory is not a Claude Code install.
# The empty home above is this venue's control: where it showed no Claude Code,
# the same home plus a bare ~/.claude must not be configured for it either.
#
# SKIP_WHITELIST: a venue with a claude executable (a developer machine) detects
# Claude Code in every home, so there is nothing for the bare directory to
# change. Narrowing PATH for this one install does not make such a venue
# claude-free: with HOME as the scanned home the product also probes fixed
# locations (/usr/local/bin, /opt/homebrew/bin). A venue without one, such as
# the Linux test image, runs the check.
if grep -q 'Detected agents:.* Claude-Code' <<<"$INSTALL_OUT"; then
echo "SKIP 9b-1b: a claude executable is installed in this venue (whitelisted above)"
else
BARE_HOME=$(smoke_mktemp_dir)
mkdir -p "$BARE_HOME/.claude" "$BARE_HOME/.local/bin"
BARE_RC=0
BARE_OUT=$(HOME="$BARE_HOME" LOCALAPPDATA="$BARE_HOME/AppData/Local" "$BINARY" install -y 2>&1) || BARE_RC=$?
if [ "$BARE_RC" -ge 128 ] ||
! grep -q 'Detected agents:' <<<"$BARE_OUT" ||
grep -q 'Detected agents:.* Claude-Code' <<<"$BARE_OUT" ||
[ -e "$BARE_HOME/.claude.json" ] ||
[ -n "$(ls -A "$BARE_HOME/.claude" 2>/dev/null)" ]; then
echo "FAIL 9b-1b: a bare ~/.claude directory was configured as a Claude Code install"
exit 1
fi
echo "OK 9b-1b: a bare ~/.claude directory is not configured as Claude Code"
retire_account_daemon "9b-1b-cleanup"
smoke_rmtree "$BARE_HOME"
fi
# 9b-2: Install twice (idempotent)
IDEM_HOME=$(smoke_mktemp_dir)
mkdir -p "$IDEM_HOME/.claude" "$IDEM_HOME/.local/bin"
mkdir -p "$IDEM_HOME/.local/bin"
mark_claude_code_installed "$IDEM_HOME"
copy_smoke_binary "$IDEM_HOME/.local/bin/codebase-memory-mcp"
run_no_crash 9b-2 env HOME="$IDEM_HOME" LOCALAPPDATA="$IDEM_HOME/AppData/Local" "$BINARY" install -y
IDEM_INSTALLER="$BINARY"
@@ -3307,7 +3354,8 @@ smoke_rmtree "$IDEM_HOME"
# 9b-3: Uninstall without prior install
CLEAN_HOME=$(smoke_mktemp_dir)
mkdir -p "$CLEAN_HOME/.claude" "$CLEAN_HOME/.local/bin"
mkdir -p "$CLEAN_HOME/.local/bin"
mark_claude_code_installed "$CLEAN_HOME"
UNINSTALL_RC=0
UNINSTALL_OUT=$(HOME="$CLEAN_HOME" "$BINARY" uninstall -y -n 2>&1) || UNINSTALL_RC=$?
if [ "$UNINSTALL_RC" -ge 128 ]; then
@@ -3331,7 +3379,8 @@ smoke_rmtree "$CORRUPT_HOME"
# 9b-8: Double uninstall
DBL_HOME=$(smoke_mktemp_dir)
mkdir -p "$DBL_HOME/.claude" "$DBL_HOME/.local/bin"
mkdir -p "$DBL_HOME/.local/bin"
mark_claude_code_installed "$DBL_HOME"
copy_smoke_binary "$DBL_HOME/.local/bin/codebase-memory-mcp"
run_no_crash 9b-8-install env HOME="$DBL_HOME" "$BINARY" install -y
DBL_UNINSTALLER="$BINARY"
@@ -3828,7 +3877,7 @@ if [ "$DL_OS" != "windows" ] && [ -f "$REPO_ROOT/install.sh" ]; then
echo "--- Phase 13: install.sh E2E ---"
INSTALL_TEST_HOME=$(smoke_mktemp_dir)
INSTALL_TEST_DIR=$(smoke_mktemp_dir)
mkdir -p "$INSTALL_TEST_HOME/.claude"
mark_claude_code_installed "$INSTALL_TEST_HOME"
mkdir -p "$INSTALL_TEST_HOME/.local/bin"
# 13a: run install.sh with local URL + isolated HOME
@@ -3865,7 +3914,8 @@ if [ "$DL_OS" != "windows" ] && [ -f "$REPO_ROOT/install.sh" ]; then
echo "OK 13d: no signing needed ($DL_OS)"
fi
# 13e: agent configs created (at least Claude Code since we made ~/.claude)
# 13e: agent configs created (at least Claude Code: the fixture home is a
# Claude Code user's)
if [ -f "$INSTALL_TEST_HOME/.claude.json" ] && grep -q 'codebase-memory-mcp' "$INSTALL_TEST_HOME/.claude.json" 2>/dev/null; then
echo "OK 13e: agent configs created by install.sh"
else
@@ -3892,7 +3942,7 @@ elif [ -f "$REPO_ROOT/install.ps1" ] && command -v powershell.exe &>/dev/null; t
echo "--- Phase 13: install.ps1 E2E (Windows) ---"
PS1_TEST_HOME=$(smoke_mktemp_dir)
PS1_TEST_DIR=$(smoke_mktemp_dir)
mkdir -p "$PS1_TEST_HOME/.claude"
mark_claude_code_installed "$PS1_TEST_HOME"
# Convert MSYS paths to Windows paths for PowerShell
if command -v cygpath &>/dev/null; then
+351
View File
@@ -0,0 +1,351 @@
#!/usr/bin/env python3
"""coverage-map.py — build the per-test coverage map of the C test runner: for
every test, the set of functions it executed, children included. The test-impact
engine uses it to narrow the suites whose tests cross a process boundary, where
the call graph alone cannot say which test reaches which function.
Input: the instrumented runner (make -f Makefile.cbm test-runner-cov). Run with
CBM_TEST_COVERAGE_DIR it leaves, per suite, raw profiles named after the test
(tests/test_main.c, "Per-test coverage profiles"):
<test>.parent.profraw the runner inside the test
<test>.<pid>.profraw one per child process of the test
<test>.<pid>.forked an unaccounted forked child
_setup.<n>.profraw the runner between tests
_setup.child.<pid>.profraw children started between tests
_setup.child.<pid>.forked unaccounted children started between tests
Output directory:
functions.tsv <id> TAB <source file> TAB <name>
The file is where the function body stands, relative to the
repository root, read from the coverage mapping of the
runner itself (llvm-cov export). The profile only names the
translation unit: a function of a unity build or a static
inline of a header would otherwise carry the wrong file,
and the engine joins the map to the graph by file and name.
tests.tsv <suite>:<test> TAB <complete|incomplete> TAB <reason> TAB <ids>
ids ascending, space separated. <suite>:* is what ran BETWEEN
the tests of a suite (its setup); a change there concerns
every test of the suite.
meta.json commit, toolchain, and per suite: tests, incomplete tests,
exit code, wall seconds
A test is `incomplete` when its coverage cannot be trusted to be whole: a
forked child left its marker, a profile is unreadable, the parent profile is
missing, or the suite did not exit 0. An incomplete test must always be
selected; the map never claims less than it knows.
Raw profiles are about 7 MiB each, so every suite is reduced and deleted before
the next one runs.
Usage:
coverage-map.py --runner build/cov/test-runner --out DIR
[--suite NAME ...] [--llvm-bin DIR] [--root DIR]
[--keep-raw] [--timeout SECONDS]
Exit status: 0 map written; 1 a suite failed or timed out (map still written,
its tests incomplete); 2 usage or toolchain error.
"""
import argparse
import json
import os
import re
import shutil
import subprocess
import sys
import time
PROFILE = re.compile(r"^(?P<stem>.+?)\.(?P<who>parent|\d+)\.profraw$")
MARKER = re.compile(r"^(?P<stem>.+?)\.(?P<pid>\d+)\.forked$")
SETUP = re.compile(r"^_setup\.(?:\d+|child\.\d+)\.profraw$")
# `llvm-profdata show --all-functions` prints one block per function, opened by
# the name at two spaces of indent and a trailing colon.
FUNCTION_LINE = re.compile(r"^ (?P<name>\S.*):$")
SETUP_TEST = "*"
def fail(message):
print(f"coverage-map: {message}", file=sys.stderr)
sys.exit(2)
def executed_functions(profdata, profile):
"""Names of the functions `profile` counted at least once, or None when the
profile cannot be read."""
proc = subprocess.run([profdata, "show", "--all-functions", "--value-cutoff=1", profile],
capture_output=True, text=True)
if proc.returncode != 0:
return None
names = set()
for line in proc.stdout.splitlines():
match = FUNCTION_LINE.match(line)
if match:
names.add(match.group("name"))
return names
def source_table(llvm_cov, profdata_tool, runner, profile, root, work):
"""{profile name: (source file, function name)} for every function compiled
into the runner, from its coverage mapping. `profile` is any raw profile of
that runner: llvm-cov wants one, its counts are not used."""
indexed = os.path.join(work, "table.profdata")
merged = subprocess.run([profdata_tool, "merge", "-sparse", "-o", indexed, profile],
capture_output=True, text=True)
if merged.returncode != 0:
fail(f"llvm-profdata merge failed: {merged.stderr.strip()[-300:]}")
exported = subprocess.run([llvm_cov, "export", "-format=text", "-skip-expansions",
"-skip-branches", f"-instr-profile={indexed}", runner],
capture_output=True, text=True)
os.remove(indexed)
if exported.returncode != 0:
fail(f"llvm-cov export failed: {exported.stderr.strip()[-300:]}")
base = os.path.realpath(root) + os.sep
table = {}
for record in json.loads(exported.stdout)["data"][0]["functions"]:
files = record.get("filenames") or []
if not files:
continue
path = os.path.realpath(files[0])
if path.startswith(base):
path = path[len(base):]
table[record["name"]] = (path.replace(os.sep, "/"), record["name"].rpartition(":")[2])
return table
class Functions:
"""Stable ids for (source file, function name), in order of first
appearance. Several profile names can be one function: a static inline of a
header is recorded once per file that includes it."""
def __init__(self, table):
self.table = table
self.ids = {}
def key(self, name):
if name in self.table:
return self.table[name]
# Not in the coverage mapping: keep what the profile says, the
# translation unit for a function with internal linkage.
source, _, short = name.rpartition(":")
return (source, short)
def ids_of(self, names):
out = set()
for name in names:
key = self.key(name)
if key not in self.ids:
self.ids[key] = len(self.ids)
out.add(self.ids[key])
return sorted(out)
def rows(self):
for (source, short), ident in sorted(self.ids.items(), key=lambda item: item[1]):
yield ident, source, short
def reduce_suite(suite, suite_dir, profdata, suite_ok):
"""Turn one suite's raw profiles into {test: (status, reason, names)}."""
tests = {}
markers = {}
setup = set()
setup_unreadable = False
setup_markers = []
entries = sorted(os.listdir(suite_dir)) if os.path.isdir(suite_dir) else []
for entry in entries:
path = os.path.join(suite_dir, entry)
marker = MARKER.match(entry)
if marker:
if marker.group("stem") == "_setup.child":
setup_markers.append(marker.group("pid"))
else:
markers.setdefault(marker.group("stem"), []).append(marker.group("pid"))
continue
if SETUP.match(entry):
names = executed_functions(profdata, path)
if names is None:
setup_unreadable = True
else:
setup |= names
continue
profile = PROFILE.match(entry)
if not profile:
continue
test = tests.setdefault(profile.group("stem"),
{"names": set(), "parent": False, "unreadable": []})
names = executed_functions(profdata, path)
if names is None:
test["unreadable"].append(entry)
continue
test["names"] |= names
test["parent"] = test["parent"] or profile.group("who") == "parent"
result = {}
for stem, pids in markers.items():
tests.setdefault(stem, {"names": set(), "parent": False, "unreadable": []})
for stem, test in sorted(tests.items()):
reasons = []
if stem in markers:
reasons.append("unaccounted forked child: " + ",".join(sorted(markers[stem])))
if test["unreadable"]:
reasons.append("unreadable profile: " + ",".join(sorted(test["unreadable"])))
if not test["parent"]:
reasons.append("no parent profile")
if not suite_ok:
reasons.append("suite did not exit 0")
result[stem] = ("incomplete" if reasons else "complete", "; ".join(reasons), test["names"])
setup_reasons = []
if setup_markers:
setup_reasons.append("unaccounted forked child: " + ",".join(sorted(setup_markers)))
if setup_unreadable:
setup_reasons.append("unreadable setup profile")
if not suite_ok:
setup_reasons.append("suite did not exit 0")
result[SETUP_TEST] = ("incomplete" if setup_reasons else "complete",
"; ".join(setup_reasons), setup)
return result
def tool_version(tool):
proc = subprocess.run([tool, "--version"], capture_output=True, text=True)
lines = [line.strip() for line in proc.stdout.splitlines() if line.strip()]
return lines[0] if lines else "unknown"
def git_commit(root):
proc = subprocess.run(["git", "-C", root, "rev-parse", "HEAD"], capture_output=True, text=True)
return proc.stdout.strip() if proc.returncode == 0 else "unknown"
def add_v2_arguments(parser):
parser.add_argument("--format", type=int, choices=(1, 2), default=1,
help="wire format (2 is experimental and unadmitted)")
parser.add_argument("--registry", help="format2 image-bound expected registry JSON")
for flag, default, help_text in (
("max-output-bytes", 32 * 1024 * 1024, "combined stdout/stderr bytes per command"),
("max-total-output-bytes", 512 * 1024 * 1024, "aggregate command stdout/stderr bytes"),
("max-profile-bytes", 64 * 1024 * 1024, "bytes per raw input profile"),
("max-total-profile-bytes", 512 * 1024 * 1024, "aggregate raw and separately converted bytes"),
("max-profile-files", 4096, "raw entries including directories, markers and listing profile"),
("conversion-timeout", 60, "seconds per tool command"),
("reduction-timeout", 600, "seconds per strict parse or final pure reduction"),
("max-map-bytes", 64 * 1024 * 1024, "combined producer wire bytes"),
):
parser.add_argument("--" + flag, type=int, default=default,
help="format2: %s (default: %s)" % (help_text, default))
parser.epilog = ((parser.epilog or "") +
" Format2 always retains bounded raw and diagnostic evidence; "
"--keep-raw only changes format1. Format2 supports the observed "
"LLVM 22.1.8 frontend text dialect and POSIX process groups.")
def main():
parser = argparse.ArgumentParser(description="Build the per-test coverage map.")
parser.add_argument("--runner", required=True, help="instrumented test runner")
parser.add_argument("--out", required=True, help="output directory")
parser.add_argument("--suite", action="append", default=[], help="suite to run (repeatable)")
parser.add_argument("--llvm-bin", default=os.environ.get("COV_LLVM_BIN", ""),
help="directory holding llvm-profdata and llvm-cov of the compiler that "
"built the runner")
parser.add_argument("--root", default="",
help="repository root the source files are named relative to "
"(default: the repository this script is in)")
parser.add_argument("--keep-raw", action="store_true", help="keep the raw profiles")
parser.add_argument("--timeout", type=int, default=3600, help="seconds per suite")
add_v2_arguments(parser)
args = parser.parse_args()
if args.format == 2:
from coverage_live_v2 import collect
return collect(args)
runner = os.path.abspath(args.runner)
if not os.access(runner, os.X_OK):
fail(f"runner is not executable: {runner}")
profdata = os.path.join(args.llvm_bin, "llvm-profdata") if args.llvm_bin else "llvm-profdata"
llvm_cov = os.path.join(args.llvm_bin, "llvm-cov") if args.llvm_bin else "llvm-cov"
for tool in (profdata, llvm_cov):
if not shutil.which(tool):
fail(f"not found: {tool}")
root = args.root or os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
out = os.path.abspath(args.out)
raw = os.path.join(out, "raw")
os.makedirs(raw, exist_ok=True)
# Listing the suites is also the cheapest run of the runner: the profile it
# leaves is the one llvm-cov needs to print the coverage mapping.
listing_profile = os.path.join(out, "listing.profraw")
listed = subprocess.run([runner, "--list-suites"], capture_output=True, text=True,
env=dict(os.environ, LLVM_PROFILE_FILE=listing_profile))
if listed.returncode != 0:
fail("the runner could not list its suites")
if not os.path.exists(listing_profile):
fail("the runner wrote no profile: is it the instrumented build (make test-runner-cov)?")
table = source_table(llvm_cov, profdata, runner, listing_profile, root, out)
os.remove(listing_profile)
suites = args.suite or [line.strip() for line in listed.stdout.splitlines() if line.strip()]
if not suites:
fail("no suites to run")
functions = Functions(table)
test_rows = []
suite_meta = []
failed = False
env = dict(os.environ)
# A plain name: a runner built for continuous profiles would otherwise start
# in that mode and ignore every per-test name (tests/test_main.c).
env["LLVM_PROFILE_FILE"] = os.devnull
env["CBM_TEST_COVERAGE_DIR"] = raw
for suite in suites:
suite_dir = os.path.join(raw, suite)
shutil.rmtree(suite_dir, ignore_errors=True)
started = time.monotonic()
try:
run = subprocess.run([runner, suite], env=env, stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL, timeout=args.timeout)
code = run.returncode
except subprocess.TimeoutExpired:
code = -1
wall = time.monotonic() - started
suite_ok = code == 0
failed = failed or not suite_ok
reduced = reduce_suite(suite, suite_dir, profdata, suite_ok)
incomplete = 0
for test, (status, reason, names) in sorted(reduced.items()):
if test != SETUP_TEST and status != "complete":
incomplete += 1
ids = " ".join(str(i) for i in functions.ids_of(names))
test_rows.append(f"{suite}:{test}\t{status}\t{reason}\t{ids}")
suite_meta.append({"suite": suite, "tests": len(reduced) - 1, "incomplete": incomplete,
"exit": code, "wall_s": round(wall, 2)})
print(f"{suite}: {len(reduced) - 1} tests, {incomplete} incomplete, exit {code}, "
f"{wall:.1f} s", flush=True)
if not args.keep_raw:
shutil.rmtree(suite_dir, ignore_errors=True)
if not args.keep_raw:
shutil.rmtree(raw, ignore_errors=True)
with open(os.path.join(out, "functions.tsv"), "w", encoding="utf-8", newline="\n") as handle:
for ident, source, short in functions.rows():
handle.write(f"{ident}\t{source}\t{short}\n")
with open(os.path.join(out, "tests.tsv"), "w", encoding="utf-8", newline="\n") as handle:
handle.write("\n".join(test_rows) + "\n")
meta = {
"format": 1,
"commit": git_commit(root),
"llvm_profdata": tool_version(profdata),
"platform": sys.platform,
"functions": len(functions.ids),
"functions_compiled": len(set(table.values())),
"tests": sum(entry["tests"] for entry in suite_meta),
"incomplete": sum(entry["incomplete"] for entry in suite_meta),
"suites": suite_meta,
}
with open(os.path.join(out, "meta.json"), "w", encoding="utf-8", newline="\n") as handle:
json.dump(meta, handle, indent=1)
handle.write("\n")
print(f"map: {meta['tests']} tests, {meta['incomplete']} incomplete, "
f"{meta['functions']} functions -> {out}")
return 1 if failed else 0
if __name__ == "__main__":
sys.exit(main())
+136
View File
@@ -0,0 +1,136 @@
#!/usr/bin/env python3
"""coverage-merge.py — the incremental coverage map of a main-branch push.
Merges FRESH (coverage-map.py output for the suites the push re-ran, made with
the new commit's instrumented runner, so its functions.tsv is the new image's
complete function table) into PREVIOUS (the previous published map):
* a suite FRESH ran keeps FRESH's rows exactly;
* every other suite the new runner still lists is carried from PREVIOUS,
each row's function ids remapped by (source file, name) onto FRESH's
table; a function the new image no longer has drops out of the row, and a
row's status (an incomplete row stays incomplete) is never improved;
* a PREVIOUS suite the new runner no longer lists is dropped.
The merged meta.json names --commit and recounts every suite, so the C reader
(cbm_coverage_map_metadata_matches) checks it like a full map. When the map was
observed is not decided here: the producer carries the oldest observation of
the previous bundle forward, so an incremental map ages out unless a full
refresh runs (the receipt's 7-day bound).
Usage: coverage-merge.py --previous DIR --fresh DIR --suites FILE --commit SHA --out DIR
Exit: 0 merged · 1 inconsistent inputs · 2 usage.
"""
from __future__ import annotations
import argparse
import json
import os
import sys
SETUP = "*"
def fail(message: str) -> None:
print(f"coverage-merge: {message}", file=sys.stderr)
sys.exit(1)
def read_functions(path: str) -> tuple[dict[int, tuple[str, str]], list[str]]:
by_id: dict[int, tuple[str, str]] = {}
lines = []
with open(path, encoding="utf-8") as handle:
for line in handle:
line = line.rstrip("\n")
if not line:
continue
fields = line.split("\t")
if len(fields) != 3 or not fields[0].isdigit():
fail(f"malformed function row in {path}: {line!r}")
by_id[int(fields[0])] = (fields[1], fields[2])
lines.append(line)
return by_id, lines
def read_tests(path: str) -> list[tuple[str, str, str, str, list[int]]]:
rows = []
with open(path, encoding="utf-8") as handle:
for line in handle:
line = line.rstrip("\n")
if not line:
continue
fields = line.split("\t")
if len(fields) != 4 or ":" not in fields[0]:
fail(f"malformed test row in {path}: {line!r}")
suite, test = fields[0].split(":", 1)
ids = [int(token) for token in fields[3].split()] if fields[3] else []
rows.append((suite, test, fields[1], fields[2], ids))
return rows
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
for flag in ("previous", "fresh", "suites", "commit", "out"):
parser.add_argument("--" + flag, required=True)
args = parser.parse_args()
_, fresh_lines = read_functions(os.path.join(args.fresh, "functions.tsv"))
fresh_by_id, _ = read_functions(os.path.join(args.fresh, "functions.tsv"))
previous_by_id, _ = read_functions(os.path.join(args.previous, "functions.tsv"))
fresh_ids = {key: ident for ident, key in fresh_by_id.items()}
with open(os.path.join(args.fresh, "meta.json"), encoding="utf-8") as handle:
fresh_meta = json.load(handle)
with open(os.path.join(args.previous, "meta.json"), encoding="utf-8") as handle:
previous_meta = json.load(handle)
with open(args.suites, encoding="utf-8") as handle:
listed = [line.strip() for line in handle if line.strip()]
fresh_rows = read_tests(os.path.join(args.fresh, "tests.tsv"))
previous_rows = read_tests(os.path.join(args.previous, "tests.tsv"))
rerun = {row[0] for row in fresh_rows}
if not rerun <= set(listed):
fail("a re-run suite is not listed by the new runner")
merged = [row for row in fresh_rows]
for suite, test, status, reason, ids in previous_rows:
if suite in rerun or suite not in listed:
continue
mapped = sorted({fresh_ids[previous_by_id[i]] for i in ids
if i in previous_by_id and previous_by_id[i] in fresh_ids})
merged.append((suite, test, status, reason, mapped))
merged.sort(key=lambda row: (row[0], row[1]))
suite_meta = {entry["suite"]: entry for entry in previous_meta.get("suites", [])}
for entry in fresh_meta.get("suites", []):
suite_meta[entry["suite"]] = entry
counts: dict[str, list[int]] = {}
for suite, test, status, _, _ in merged:
tally = counts.setdefault(suite, [0, 0])
if test != SETUP:
tally[0] += 1
tally[1] += status != "complete"
suites = []
for suite in sorted(counts):
entry = dict(suite_meta.get(suite, {"suite": suite, "exit": 0}))
entry["tests"], entry["incomplete"] = counts[suite]
suites.append(entry)
os.makedirs(args.out, exist_ok=True)
with open(os.path.join(args.out, "functions.tsv"), "w", encoding="utf-8", newline="\n") as handle:
handle.write("".join(line + "\n" for line in fresh_lines))
with open(os.path.join(args.out, "tests.tsv"), "w", encoding="utf-8", newline="\n") as handle:
for suite, test, status, reason, ids in merged:
handle.write(f"{suite}:{test}\t{status}\t{reason}\t{' '.join(map(str, ids))}\n")
meta = dict(fresh_meta)
meta.update({"commit": args.commit, "tests": sum(c[0] for c in counts.values()),
"incomplete": sum(c[1] for c in counts.values()), "suites": suites})
with open(os.path.join(args.out, "meta.json"), "w", encoding="utf-8", newline="\n") as handle:
json.dump(meta, handle, indent=1)
handle.write("\n")
print(f"merged: {len(rerun)} suite(s) re-run, "
f"{len(counts) - len(rerun & set(counts))} carried, {len(merged)} rows")
return 0
if __name__ == "__main__":
sys.exit(main())
+649
View File
@@ -0,0 +1,649 @@
"""Experimental, bounded POSIX native transport for unadmitted profile-map v2.
Only actual observations are transported. Registry/image hashes are custody
facts, not evidence that a registry, interval, runtime or image set is complete.
No RowEvidence is created here, so this bundle cannot authorize negative
coverage evidence or test narrowing. Raw evidence is retained, including on
failure. Process groups cannot prove closure of children that leave the group.
"""
import errno
import hashlib
import json
import os
import re
import selectors
import signal
import stat
import subprocess
import sys
import time
from profile_map_v2 import (
ExpectedSuite, ObservationRole, ProfileMapError, ProfileMapLimits,
ProfileMapLimitExceededError, ProfileMapCancelledError,
ProfileObservation, SuiteOutcome, build_profile_map_v2,
)
from profile_text_parser import (
ParseLimits, ProfileTextError, LimitExceededError, ParseCancelledError, parse_profile_text,
)
REGISTRY_CAP = 2 * 1024 * 1024
ROW_CAP = 65536
RECORD_CAP = 4000000
COUNTER_CAP = 32000000
NAME_CAP = 128 * 1024 * 1024
GRACE = 2.0
PROFILE = re.compile(r"^(.+?)\.(parent|[0-9]+)\.profraw$")
SETUP = re.compile(r"^_setup\.(?:[0-9]+|child\.[0-9]+)\.profraw$")
MARKER = re.compile(r"^(.+?)\.([0-9]+)\.forked$")
class CollectionError(ValueError):
def __init__(self, kind, message):
super().__init__(message)
self.kind = kind
def _error(kind, message):
raise CollectionError(kind, message)
def _directory(path):
"""Open each absolute component without following symlinks; caller closes."""
path = os.path.abspath(path)
fd = os.open(os.sep, os.O_RDONLY | os.O_DIRECTORY)
try:
for part in path.split(os.sep):
if not part:
continue
next_fd = os.open(part, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW,
dir_fd=fd)
os.close(fd)
fd = next_fd
return fd
except BaseException:
os.close(fd)
raise
def _regular(path):
parent, name = os.path.split(os.path.abspath(path))
directory = _directory(parent)
try:
fd = os.open(name, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK,
dir_fd=directory)
finally:
os.close(directory)
if not stat.S_ISREG(os.fstat(fd).st_mode):
os.close(fd)
_error("input", "input is not a regular file")
return fd
def _signature(info):
return (info.st_dev, info.st_ino, info.st_size, info.st_mtime_ns, info.st_ctime_ns)
def _read(path, limit):
fd = _regular(path)
try:
before = os.fstat(fd)
if before.st_size > limit:
_error("resource_limit", "file byte limit exceeded")
data = bytearray()
while True:
part = os.read(fd, min(65536, limit + 1 - len(data)))
if not part:
break
data.extend(part)
if len(data) > limit:
_error("resource_limit", "file byte limit exceeded")
if _signature(before) != _signature(os.fstat(fd)):
_error("identity", "input changed during capture")
return bytes(data)
finally:
os.close(fd)
def _identity(path):
fd = _regular(path)
try:
before = os.fstat(fd)
if before.st_size > 1024 * 1024 * 1024:
_error("resource_limit", "executable exceeds 1GiB identity limit")
digest = hashlib.sha256()
size = 0
while True:
part = os.read(fd, 65536)
if not part:
break
size += len(part)
if size > 1024 * 1024 * 1024:
_error("resource_limit", "executable grew beyond identity limit")
digest.update(part)
if _signature(before) != _signature(os.fstat(fd)) or size != before.st_size:
_error("identity", "executable changed while hashing")
if not os.access(path, os.X_OK):
_error("input", "executable is not executable")
return {"path": path, "sha256": digest.hexdigest(), "bytes": size}
finally:
os.close(fd)
def _pairs(values):
result = {}
for key, value in values:
if key in result:
_error("input", "duplicate registry key")
result[key] = value
return result
def _identifier(value, suite=False):
if type(value) is not str or not value:
_error("input", "registry identifier must be a nonempty string")
try:
data = value.encode("utf-8", "strict")
except UnicodeError:
_error("input", "registry identifier is not valid UTF-8")
if any(char in data for char in (0, 9, 10, 13)) or (suite and b":" in data):
_error("input", "registry identifier violates producer grammar")
if not suite and value == "*":
_error("input", "setup test name is reserved")
# Native routing uses each identifier as one filesystem component. The
# pure producer accepts a wider byte grammar; those routes are unsupported.
if value in (".", "..") or "/" in value or "\\" in value:
_error("unsupported", "registry identifier cannot be a native path component")
if not suite and value in ("_setup", "_setup.child"):
_error("unsupported", "registry test collides with native setup routing")
return data
def _registry(path, image):
raw = _read(path, REGISTRY_CAP)
try:
value = json.loads(raw.decode("utf-8", "strict"), object_pairs_hook=_pairs,
parse_constant=lambda _: _error("input", "nonfinite JSON number"))
except (UnicodeError, json.JSONDecodeError, RecursionError):
_error("input", "invalid registry JSON")
if type(value) is not dict or set(value) != {"format", "image_sha256", "suites"}:
_error("input", "registry keys do not match v0 schema")
if value["format"] != "cbm.coverage.registry.v0":
_error("input", "unsupported registry format")
declared = value["image_sha256"]
if type(declared) is not str or not re.fullmatch(r"[0-9a-f]{64}", declared):
_error("input", "registry image digest must be lowercase SHA256")
if declared != image["sha256"]:
_error("identity", "registry does not match runner image")
suites = value["suites"]
if type(suites) is not list or not suites:
_error("input", "registry suites must be nonempty")
result = {}
rows = 0
for item in suites:
if type(item) is not dict or set(item) != {"name", "tests"}:
_error("input", "registry suite keys do not match schema")
name = item["name"]
_identifier(name, suite=True)
if name in result:
_error("input", "duplicate registry suite")
tests = item["tests"]
if type(tests) is not list or not tests:
_error("input", "registry tests must be nonempty")
seen = set()
for test in tests:
_identifier(test)
if test in seen:
_error("input", "duplicate registry test")
seen.add(test)
rows += 1 + len(tests)
if rows > ROW_CAP:
_error("resource_limit", "registry row limit exceeded")
result[name] = tuple(tests)
return result, hashlib.sha256(raw).hexdigest(), raw
class Evidence:
def __init__(self, path, args):
self.path = os.path.abspath(path)
self.args = args
self.records = []
self.uncertainty = []
self.converted_bytes = 0
self.captured_bytes = 0
self.allowed_suites = set()
self.directory = None
parent, name = os.path.split(self.path)
if not name:
_error("input", "output must name a fresh directory")
fd = _directory(parent)
try:
os.mkdir(name, 0o700, dir_fd=fd)
self.directory = os.open(name, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW,
dir_fd=fd)
finally:
os.close(fd)
try:
self.directory_identity = os.fstat(self.directory)
for name in ("evidence", "raw"):
os.mkdir(name, 0o700, dir_fd=self.directory)
except BaseException:
self.close()
raise
def close(self):
if self.directory is not None:
os.close(self.directory)
self.directory = None
def check(self):
fd = _directory(self.path)
try:
actual = os.fstat(fd)
if (actual.st_dev, actual.st_ino) != (self.directory_identity.st_dev,
self.directory_identity.st_ino):
_error("identity", "owned output directory was replaced")
finally:
os.close(fd)
def write(self, relative, data):
self.check()
parent, name = os.path.split(relative)
fd = _directory(os.path.join(self.path, parent))
try:
target = os.open(name, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW,
0o600, dir_fd=fd)
try:
with os.fdopen(target, "wb") as stream:
stream.write(data)
stream.flush()
os.fsync(stream.fileno())
except BaseException:
# Partial evidence remains; no marker is created on this path.
raise
finally:
os.close(fd)
return os.path.join(self.path, relative)
def json(self, relative, value):
data = (json.dumps(value, sort_keys=True, indent=2,
ensure_ascii=True) + "\n").encode("ascii")
if len(data) > 64 * 1024 * 1024:
_error("resource_limit", "metadata exceeds 64MiB bound")
return self.write(relative, data)
def publish(self, meta):
# A partial JSON file is never a commit marker. The pending complete
# file remains evidence, and hard-link creation cannot overwrite a
# preexisting or symlink destination.
self.json("meta.pending.json", meta)
for name in ("profiles", "tests"):
data = _read(os.path.join(self.path, name + ".tsv"), self.args.max_map_bytes)
if hashlib.sha256(data).hexdigest() != meta[name + "_sha256"]:
_error("identity", "wire changed before publication")
self.check()
os.fsync(self.directory)
os.link("meta.pending.json", "meta.json", src_dir_fd=self.directory,
dst_dir_fd=self.directory, follow_symlinks=False)
def inventory(self):
"""Bound scans while children run; never follow raw tree symlinks."""
self.check()
root = os.path.join(self.path, "raw")
pending = [root]
files, total, count = [], 0, 1 # listing counts as one profile slot
try:
info = os.stat("listing.profraw", dir_fd=self.directory, follow_symlinks=False)
except FileNotFoundError:
info = None
if info is not None:
if not stat.S_ISREG(info.st_mode):
_error("input", "listing profile is not a regular file")
if info.st_size > self.args.max_profile_bytes:
_error("resource_limit", "listing profile byte limit exceeded")
total = info.st_size
if total > self.args.max_total_profile_bytes:
_error("resource_limit", "aggregate raw byte limit exceeded")
while pending:
directory = pending.pop()
fd = _directory(directory)
try:
with os.scandir(fd) as entries:
for entry in entries:
count += 1
if count > self.args.max_profile_files:
_error("resource_limit", "raw entry count limit exceeded")
info = entry.stat(follow_symlinks=False)
path = os.path.join(directory, entry.name)
if stat.S_ISDIR(info.st_mode) and directory == root:
if entry.name not in self.allowed_suites:
self.uncertainty.append({"path": path, "reason": "unknown_suite_destination"})
_error("input", "raw suite directory is outside requested scope")
pending.append(path)
elif stat.S_ISREG(info.st_mode):
if info.st_size > self.args.max_profile_bytes:
_error("resource_limit", "raw file byte limit exceeded")
total += info.st_size
if total > self.args.max_total_profile_bytes:
_error("resource_limit", "aggregate raw byte limit exceeded")
files.append(path)
else:
self.uncertainty.append({"path": path, "reason": "unsupported_raw_entry"})
_error("input", "unsupported raw tree entry")
finally:
os.close(fd)
return sorted(files)
def _group_exists(pid):
try:
os.killpg(pid, 0)
return True
except ProcessLookupError:
return False
def _quiesce(proc):
sent = []
for sig in (signal.SIGTERM, signal.SIGKILL):
if proc.poll() is not None and not _group_exists(proc.pid):
return True, sent
try:
os.killpg(proc.pid, sig)
sent.append(signal.Signals(sig).name)
except ProcessLookupError:
pass
until = time.monotonic() + GRACE
while time.monotonic() < until:
if proc.poll() is not None and not _group_exists(proc.pid):
return True, sent
time.sleep(0.02)
return proc.poll() is not None and not _group_exists(proc.pid), sent
def _run(evidence, label, argv, environment, timeout, cwd, watch_raw=False):
selector = selectors.DefaultSelector()
streams = {"stdout": bytearray(), "stderr": bytearray()}
started = time.monotonic()
proc = None
failure = None
quiescent, cleanup = False, []
try:
proc = subprocess.Popen(argv, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, env=environment, cwd=cwd,
start_new_session=True, bufsize=0)
for name, pipe in (("stdout", proc.stdout), ("stderr", proc.stderr)):
os.set_blocking(pipe.fileno(), False)
selector.register(pipe, selectors.EVENT_READ, name)
while True:
if watch_raw:
evidence.inventory()
left = timeout - (time.monotonic() - started)
if left <= 0:
failure = "timeout"
break
for key, _ in selector.select(min(left, 0.05)):
part = os.read(key.fd, 65536)
if not part:
selector.unregister(key.fileobj)
continue
used = sum(len(data) for data in streams.values())
allowed = min(evidence.args.max_output_bytes - used,
evidence.args.max_total_output_bytes - evidence.captured_bytes - used)
streams[key.data].extend(part[:max(0, allowed)])
if len(part) > allowed:
failure = "resource_limit"
break
if failure:
break
if proc.poll() is not None:
if _group_exists(proc.pid):
failure = "quiescence"
break
if not selector.get_map():
break
except CollectionError as error:
failure = error.kind
except (OSError, ValueError):
failure = "process"
finally:
if proc is not None:
quiescent, cleanup = _quiesce(proc)
if not quiescent:
failure = "quiescence"
if cleanup and failure is None:
failure = "quiescence"
proc.stdout.close()
proc.stderr.close()
selector.close()
record = {"label": label, "argv": argv, "returncode": proc.returncode if proc else None,
"failure": failure, "quiescent": quiescent, "cleanup": cleanup,
"wall_s": round(time.monotonic() - started, 6), "timeout_s": timeout}
evidence.captured_bytes += sum(len(data) for data in streams.values())
for name, data in streams.items():
path = evidence.write("evidence/" + label + "." + name, bytes(data))
record[name] = {"path": path, "bytes": len(data),
"sha256": hashlib.sha256(data).hexdigest(),
"capture": "complete" if failure is None else "bounded_prefix"}
evidence.json("evidence/" + label + ".process.json", record)
evidence.records.append(record)
return record, bytes(streams["stdout"])
def _required(record):
if record["failure"] or record["returncode"] != 0 or not record["quiescent"]:
_error(record["failure"] or "tool", "required native command failed: " + record["label"])
def _environment():
result = dict(os.environ)
for name in ("CBM_TEST_COVERAGE_DIR", "CBM_TEST_ONLY", "CBM_TEST_ONLY_FILE"):
result.pop(name, None)
return result
def _convert(evidence, tool, path, label, environment, root, unavailable=False):
try:
raw = _read(path, evidence.args.max_profile_bytes)
except OSError as error:
if unavailable and error.errno in (errno.EACCES, errno.EPERM, errno.ENOENT):
evidence.uncertainty.append({"path": path, "reason": "profile_unreadable"})
return None
raise
snapshot = evidence.write("evidence/" + label + ".profraw", raw)
raw_digest = hashlib.sha256(raw).hexdigest()
record, text = _run(evidence, label, [tool, "merge", "--instr", "--text", "--failure-mode=any",
"--num-threads=1", "--output=-", snapshot],
environment, evidence.args.conversion_timeout, root)
record["input"] = {"path": path, "snapshot": snapshot, "sha256": raw_digest,
"bytes": len(raw)}
_required(record)
if hashlib.sha256(_read(snapshot, evidence.args.max_profile_bytes)).hexdigest() != raw_digest:
_error("identity", "conversion input changed")
evidence.converted_bytes += len(text)
if evidence.converted_bytes > evidence.args.max_total_profile_bytes:
_error("resource_limit", "aggregate converted byte limit exceeded")
# Reject malformed transport, even though the pure producer can represent
# unavailable/malformed observations conservatively. Unknown identities and
# counter-shape incompatibilities are rejected by the producer's full join.
deadline = time.monotonic() + evidence.args.reduction_timeout
parse_profile_text(text, ParseLimits(evidence.args.max_output_bytes, RECORD_CAP,
COUNTER_CAP, NAME_CAP),
cancel=lambda _: time.monotonic() >= deadline)
return text
def _scope(registry, requested, available):
selected = requested or list(registry)
if len(set(selected)) != len(selected):
_error("input", "duplicate requested suite")
for name in selected:
if name not in registry or name not in available:
_error("input", "requested suite is absent from registry or native list")
return selected
def _observations(evidence, tool, registry, selected, environment, root):
observations = []
for index, path in enumerate(evidence.inventory()):
relative = os.path.relpath(path, os.path.join(evidence.path, "raw"))
parts = relative.split(os.sep)
if len(parts) != 2 or parts[0] not in selected:
evidence.uncertainty.append({"path": path, "reason": "unknown_profile_destination"})
_error("input", "raw profile destination is outside requested scope")
suite, name = parts
marker = MARKER.fullmatch(name)
setup = SETUP.fullmatch(name)
profile = PROFILE.fullmatch(name)
if setup:
test = "*"
role = ObservationRole.CHILD if name.startswith("_setup.child.") else ObservationRole.SETUP
elif marker:
test = "*" if marker.group(1) == "_setup.child" else marker.group(1)
role = ObservationRole.CHILD
elif profile:
test = profile.group(1)
role = ObservationRole.PARENT if profile.group(2) == "parent" else ObservationRole.CHILD
else:
evidence.uncertainty.append({"path": path, "reason": "unaccounted_raw_entry"})
_error("input", "unaccounted raw profile entry")
if test != "*" and test not in registry[suite]:
evidence.uncertainty.append({"path": path, "reason": "unknown_test_identity"})
_error("input", "raw test identifier is outside expected registry")
if marker:
evidence.uncertainty.append({"path": path, "reason": "unaccounted_fork"})
text = None
else:
text = _convert(evidence, tool, path, "profile-%06d" % index,
environment, root, unavailable=True)
observations.append(ProfileObservation(suite.encode("utf-8"), test.encode("utf-8"),
name.encode("utf-8"), role, text))
return tuple(observations)
def collect(args):
evidence = None
try:
if os.name != "posix" or not all(hasattr(os, name) for name in
("O_NOFOLLOW", "O_DIRECTORY", "killpg", "set_blocking")):
_error("unsupported", "format2 requires POSIX owned process-group custody")
for name in ("timeout", "max_output_bytes", "max_total_output_bytes", "max_profile_bytes", "max_total_profile_bytes",
"max_profile_files", "conversion_timeout", "reduction_timeout", "max_map_bytes"):
value = getattr(args, name)
if type(value) is not int or value <= 0 or value > sys.maxsize // 2:
_error("input", "resource limits must be positive bounded integers")
if not args.registry or not args.llvm_bin:
_error("input", "format2 requires --registry and explicit --llvm-bin")
runner = os.path.realpath(os.path.abspath(args.runner))
tool = os.path.realpath(os.path.join(os.path.abspath(args.llvm_bin), "llvm-profdata"))
root = os.path.abspath(args.root or os.path.join(os.path.dirname(__file__), "..", ".."))
root_fd = _directory(root)
os.close(root_fd)
image = _identity(runner)
tool_identity = _identity(tool)
registry_path = os.path.abspath(args.registry)
registry, registry_digest, registry_bytes = _registry(registry_path, image)
# Reject explicit scope errors before any runner/tool action.
_scope(registry, args.suite, set(registry))
evidence = Evidence(args.out, args)
registry_snapshot = evidence.write("evidence/registry.json", registry_bytes)
environment = _environment()
environment["LLVM_PROFILE_FILE"] = os.devnull
version_record, version = _run(evidence, "tool-version", [tool, "--version"], environment,
args.conversion_timeout, root)
_required(version_record)
if not re.search(rb"LLVM version 22\.1\.8(?:\s|$)", version):
_error("unsupported", "only observed LLVM 22.1.8 proftext transport is supported")
listing_path = os.path.join(evidence.path, "listing.profraw")
listing_env = dict(environment, LLVM_PROFILE_FILE=listing_path)
listed, listing = _run(evidence, "list-suites", [runner, "--list-suites"], listing_env,
args.timeout, root, watch_raw=True)
_required(listed)
try:
available = listing.decode("utf-8", "strict").splitlines()
except UnicodeError:
_error("input", "native suite list is not UTF-8")
if not available or any(not name for name in available) or len(set(available)) != len(available):
_error("input", "native suite list is empty or ambiguous")
for name in available:
_identifier(name, suite=True)
selected = _scope(registry, args.suite, set(available))
evidence.allowed_suites = set(selected)
enumeration = _convert(evidence, tool, listing_path, "enumeration", environment, root)
evidence.inventory()
suite_env = dict(environment, CBM_TEST_COVERAGE_DIR=os.path.join(evidence.path, "raw"))
outcomes = []
expected = []
failed = False
for index, suite in enumerate(selected):
record, _ = _run(evidence, "suite-%06d" % index, [runner, suite], suite_env,
args.timeout, root, watch_raw=True)
if record["failure"] not in (None, "timeout") or not record["quiescent"]:
_error(record["failure"] or "quiescence", "native suite capture failed")
outcome = (SuiteOutcome.TIMED_OUT if record["failure"] == "timeout" else
SuiteOutcome.PASSED if record["returncode"] == 0 else SuiteOutcome.FAILED)
failed = failed or outcome is not SuiteOutcome.PASSED
expected.append(ExpectedSuite(suite.encode("utf-8"),
tuple(test.encode("utf-8") for test in registry[suite]), outcome))
outcomes.append({"suite": suite, "tests": list(registry[suite]),
"outcome": outcome.value, "exit": record["returncode"], "process": record})
observations = _observations(evidence, tool, registry, selected, environment, root)
if _identity(runner) != image or _identity(tool) != tool_identity:
_error("identity", "runner or conversion tool changed during collection")
# No affirmative runtime/interval/image/child assertions are supplied.
limits = ProfileMapLimits(args.max_total_profile_bytes + REGISTRY_CAP * 2, ROW_CAP,
args.max_profile_files, RECORD_CAP, RECORD_CAP, COUNTER_CAP,
NAME_CAP, COUNTER_CAP, args.max_map_bytes)
deadline = time.monotonic() + args.reduction_timeout
produced = build_profile_map_v2(bytes.fromhex(image["sha256"]), enumeration,
tuple(expected), observations, (), limits,
cancel=lambda _: time.monotonic() >= deadline)
profiles_path = evidence.write("profiles.tsv", produced.profiles_tsv)
tests_path = evidence.write("tests.tsv", produced.tests_tsv)
meta = {"format": 2, "admission": "unverified", "image_sha256": image["sha256"],
"registry_sha256": registry_digest, "profiles_sha256": produced.profiles_sha256.hex(),
"tests_sha256": produced.tests_sha256.hex(), "registry": {
"path": registry_path, "snapshot": registry_snapshot, "sha256": registry_digest,
"format": "cbm.coverage.registry.v0", "completeness": "unverified"},
"scope": selected,
"image": image, "llvm_profdata": dict(tool_identity, version=version.decode("utf-8", "replace")),
"profiles": {"path": profiles_path, "sha256": produced.profiles_sha256.hex()},
"tests": {"path": tests_path, "sha256": produced.tests_sha256.hex()},
"suites": outcomes, "processes": evidence.records, "uncertainty": evidence.uncertainty,
"evidence_root": os.path.join(evidence.path, "evidence"),
"raw_root": os.path.join(evidence.path, "raw"),
"row_evidence": "absent", "platform": sys.platform,
"raw_retained": True, "captured_bytes": evidence.captured_bytes,
"converted_bytes": evidence.converted_bytes,
"custody": {"method": "POSIX session and process group",
"descendant_closure": "unproved",
"compiler_runtime_compatibility": "unverified"},
"limits": {name: getattr(args, name) for name in (
"timeout", "max_output_bytes", "max_total_output_bytes", "max_profile_bytes",
"max_total_profile_bytes", "max_profile_files", "conversion_timeout",
"reduction_timeout", "max_map_bytes")},
"selection_environment": {"CBM_SKIP_PERF": environment.get("CBM_SKIP_PERF")}}
# Consumer acceptance requires this final marker AND both wire digests.
evidence.publish(meta)
return 1 if failed else 0
except (CollectionError, ProfileMapError, ProfileTextError, OSError, UnicodeError,
ValueError, MemoryError) as error:
kind = getattr(error, "kind", "input_or_conversion")
if isinstance(error, (ProfileMapLimitExceededError, LimitExceededError, MemoryError)):
kind = "resource_limit"
if isinstance(error, (ProfileMapCancelledError, ParseCancelledError)):
kind = "reduction_timeout"
print("coverage-map: format2 %s: %s" % (kind, str(error)), file=sys.stderr)
if evidence is not None:
try:
evidence.json("failure.json", {"format": 2, "error": kind,
"message": str(error), "processes": evidence.records,
"uncertainty": evidence.uncertainty})
except (OSError, ValueError, MemoryError):
pass
return 2
finally:
if evidence is not None:
evidence.close()
@@ -0,0 +1,326 @@
# Distinct changed-file sets of the September 2026 PR pushes (git diff
# --name-only merge-base..head per push), referenced by 2026-09-pushes.tsv.
# Columns (TAB): fileset, paths (space-separated; no path contains a space).
f001 README.md
f002 internal/cbm/vendored/grammars/swift/scanner.c scripts/vendored-checksums.txt tests/test_extraction.c
f003 internal/cbm/lsp/go_lsp.c src/pipeline/pass_lsp_cross.c tests/test_go_lsp.c tests/test_parallel.c
f004 src/foundation/mem.c src/foundation/mem.h tests/test_mem.c
f005 Makefile.cbm internal/cbm/cbm.c internal/cbm/cbm.h src/mcp/mcp.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pipeline_internal.h src/store/store.c tests/test_index_resilience.c tests/test_mcp.c tests/test_parse_coverage.c tests/test_pipeline.c
f006 .github/workflows/pr.yml Makefile.cbm internal/cbm/cbm.c internal/cbm/cbm.h scripts/ci/README.md scripts/ci/coverage-gate-allowlist.txt scripts/ci/parse-partial-baseline.txt scripts/ci/self-index-coverage-gate.sh src/mcp/mcp.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pipeline_internal.h src/store/store.c tests/test_index_resilience.c tests/test_mcp.c tests/test_parse_coverage.c tests/test_pipeline.c tests/test_venue_parity_contract.sh
f007 README.md scripts/smoke-test.sh src/cli/cli.c tests/test_cli.c
f008 src/ui/http_server.c tests/test_httpd.c
f009 src/daemon/application.c src/daemon/application.h src/daemon/frontend.c src/daemon/frontend.h src/daemon/runtime.c src/daemon/runtime.h src/main.c tests/test_daemon_frontend.c tests/test_daemon_runtime.c
f010 internal/cbm/lsp/c_lsp.c internal/cbm/lsp/type_registry.c internal/cbm/lsp/type_registry.h tests/test_c_lsp.c
f011 src/pipeline/fqn.c src/pipeline/pass_pkgmap.c tests/test_edge_imports.c tests/test_fqn.c
f012 internal/cbm/extract_calls.c tests/test_extraction.c tests/test_pipeline.c
f013 src/mcp/mcp.c tests/test_mcp.c
f014 src/cypher/cypher.c tests/test_cypher.c
f015 src/mcp/mcp.c src/store/store.c src/store/store.h src/ui/layout3d.c src/ui/layout3d.h tests/test_store_arch.c tests/test_ui.c
f016 Makefile.cbm README.md docs/index.html docs/llms.txt scripts/smoke-invariants.sh src/cli/cli.c src/cli/cli.h src/foundation/platform.c src/foundation/platform.h src/mcp/mcp.c src/personal_memory/memory.c src/personal_memory/memory.h tests/test_cli.c tests/test_mcp.c
f017 internal/cbm/extract_defs.c tests/test_extraction.c
f018 internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/swift/scanner.c scripts/vendored-checksums.txt tests/test_extraction.c
f019 internal/cbm/extract_calls.c internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/swift/scanner.c scripts/vendored-checksums.txt tests/test_extraction.c tests/test_pipeline.c
f020 src/daemon/application.c tests/test_daemon_application.c tests/windows/test_non_ascii_path.py
f021 internal/cbm/lsp/ts_lsp.c tests/repro/repro_lsp_ts.c
f022 src/graph_buffer/graph_buffer.c src/mcp/mcp.c tests/test_graph_buffer.c tests/test_mcp.c
f023 internal/cbm/cbm.h internal/cbm/extract_calls.c src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
f024 .github/workflows/pr.yml Makefile.cbm internal/cbm/cbm.c internal/cbm/cbm.h scripts/ci/README.md scripts/ci/coverage-gate-allowlist.txt scripts/ci/parse-partial-baseline.txt scripts/ci/self-index-coverage-gate.sh scripts/test.sh src/mcp/mcp.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pipeline_internal.h src/store/store.c tests/test_coverage_gate_contract.sh tests/test_index_resilience.c tests/test_mcp.c tests/test_parse_coverage.c tests/test_pipeline.c tests/test_venue_parity_contract.sh
f025 Makefile.cbm README.md docs/CONFIGURATION.md docs/INDEX_RESOURCE_LIMITS.md scripts/test-runtime.sh src/cli/cli.c src/cli/cli.h src/daemon/application.c src/discover/discover.c src/discover/discover.h src/foundation/index_policy.c src/foundation/index_policy.h src/mcp/mcp.c src/mcp/mcp_internal.h src/pipeline/pipeline.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h tests/test_daemon_application.c tests/test_discover.c tests/test_index_policy.c tests/test_main.c tests/test_pipeline.c tests/test_worker_error_response.sh tests/test_worker_watchdog.sh
f026 Makefile.cbm src/cli/cli.c src/pipeline/pass_doclinks.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h tests/test_doclinks.c tests/test_edge_structural.c tests/test_lang_contract.c tests/test_main.c
f027 src/mcp/mcp.c src/mcp/mcp_internal.h tests/test_mcp.c
f028 internal/cbm/extract_defs.c src/discover/language.c src/pipeline/pass_route_nodes.c tests/test_extraction.c tests/test_language.c
f029 README.md docs/CONFIGURATION.md scripts/smoke-test.sh src/cli/cli.c src/cli/cli.h src/main.c tests/test_cli.c
f030 src/main.c tests/test_parent_watchdog.sh
f031 internal/cbm/extract_imports.c tests/test_extraction.c
f032 internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_unified.c internal/cbm/helpers.h src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
f033 internal/cbm/extract_calls.c src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
f034 internal/cbm/cbm.c internal/cbm/preprocessor.cpp internal/cbm/preprocessor.h tests/test_extraction.c
f035 internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/extract_unified.c internal/cbm/helpers.c internal/cbm/lsp/type_registry.c src/pipeline/lsp_resolve.h src/pipeline/registry.c tests/test_extraction.c tests/test_pipeline.c
f036 internal/cbm/cbm.h internal/cbm/extract_semantic.c internal/cbm/extract_usages.c src/pipeline/pass_parallel.c src/pipeline/pass_usages.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_pipeline.c tests/test_registry.c
f037 internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_defs.c src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
f038 internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/extract_unified.c internal/cbm/extract_unified.h src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
f039 src/cli/cli.c src/ui/http_server.c tests/test_cli.c
f040 internal/cbm/extract_channels.c tests/test_extraction.c tests/test_pipeline.c
f041 internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_unified.c internal/cbm/helpers.h internal/cbm/lsp/go_lsp.c src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_extraction.c tests/test_parallel.c tests/test_pipeline.c tests/test_registry.c
f042 internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_unified.c internal/cbm/helpers.c internal/cbm/helpers.h internal/cbm/lsp/go_lsp.c src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_extraction.c tests/test_parallel.c tests/test_pipeline.c tests/test_registry.c
f043 internal/cbm/extract_defs.c tests/test_edge_types_probe.c
f044 internal/cbm/extract_calls.c tests/test_extraction.c
f045 internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_channels.c internal/cbm/extract_defs.c internal/cbm/extract_env_accesses.c internal/cbm/extract_imports.c internal/cbm/extract_node_stack.h internal/cbm/extract_semantic.c internal/cbm/extract_type_assigns.c internal/cbm/extract_type_refs.c tests/test_extraction.c tests/test_mem.c
f046 src/cli/client_adapter.c tests/test_agent_clients.c
f047 graph-ui/src/hooks/useProjects.test.tsx graph-ui/src/hooks/useProjects.ts
f048 internal/cbm/cbm.c tests/test_parse_coverage.c
f049 graph-ui/src/App.test.tsx graph-ui/src/App.tsx src/ui/http_server.c tests/test_httpd.c
f050 src/daemon/application.c src/foundation/workspace.c src/foundation/workspace.h src/mcp/mcp.c src/ui/http_server.c tests/test_workspace.c
f051 README.md docs/CONFIGURATION.md src/cli/cli.c src/cli/cli.h src/daemon/ipc.c src/foundation/compat_fs.c src/foundation/compat_fs.h src/main.c tests/test_daemon_ipc.c
f052 src/traces/traces.c src/traces/traces.h tests/test_traces.c
f053 src/pipeline/registry.c tests/test_pipeline.c
f054 src/cli/cli.h src/cli/hook_augment.c src/foundation/platform.c src/foundation/platform.h src/mcp/index_supervisor.c src/mcp/mcp.c src/mcp/mcp.h tests/test_cli.c tests/test_platform.c
f055 tests/test_cli.c
f056 src/daemon/application.c src/mcp/mcp.c src/pipeline/pipeline.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c tests/test_daemon_application.c tests/test_mcp.c tests/test_pipeline.c
f057 internal/cbm/extract_defs.c src/discover/language.c tests/test_extraction.c tests/test_language.c
f058 src/cli/hook_augment.c src/daemon/runtime.c src/daemon/runtime.h src/foundation/sha256.c src/foundation/sha256.h src/main.c tests/test_cli.c tests/test_daemon_runtime.c
f059 tests/windows/test_cli_non_ascii_arg.py tests/windows/test_daemon_stability.py tests/windows/test_hook_augment.py
f060 src/cli/cli.c
f061 .github/workflows/_test.yml
f062 scripts/smoke-test.sh
f063 src/foundation/subprocess.c src/foundation/subprocess.h src/mcp/index_supervisor.c tests/test_main.c tests/test_subprocess.c
f064 graph-ui/src/components/EdgeLines.test.ts graph-ui/src/components/EdgeLines.tsx graph-ui/src/components/GraphTab.edgeSampling.test.tsx graph-ui/src/components/GraphTab.test.ts graph-ui/src/components/GraphTab.tsx graph-ui/src/components/NodeCloud.test.ts graph-ui/src/components/NodeCloud.tsx graph-ui/src/components/NodeDetailPanel.test.tsx graph-ui/src/components/NodeDetailPanel.tsx graph-ui/src/components/NodeLabels.tsx graph-ui/src/hooks/useGraphData.test.ts graph-ui/src/hooks/useGraphData.ts graph-ui/src/lib/edgeBudget.test.ts graph-ui/src/lib/edgeBudget.ts graph-ui/src/lib/graphIndex.test.ts graph-ui/src/lib/graphIndex.ts graph-ui/src/lib/topK.test.ts graph-ui/src/lib/topK.ts src/store/store.c src/store/store.h src/ui/http_server.c src/ui/layout3d.c src/ui/layout3d.h src/ui/layout3d_internal.h tests/test_httpd.c tests/test_store_edges.c tests/test_store_search.c tests/test_ui.c
f065 graph-ui/package-lock.json
f066 src/foundation/lock_registry.c src/foundation/lock_registry_internal.h tests/test_lock_registry.c
f067 src/daemon/version_cohort.c src/daemon/version_cohort.h tests/test_version_cohort.c
f068 internal/cbm/extract_defs.c tests/test_extraction_inheritance.c
f069 internal/cbm/extract_channels.c tests/test_extraction.c
f070 test-infrastructure/Dockerfile.msan
f071 src/git/git_context.c src/ui/http_server.c tests/test_daemon_application.c tests/test_git_context.c tests/test_httpd.c
f072 scripts/hooks/pre-commit
f073 tests/test_daemon_runtime.c
f074 src/cli/agent_profiles.c src/cli/client_adapter.c tests/test_agent_clients.c tests/test_agent_profiles.c
f075 internal/cbm/sqlite_writer.c src/graph_buffer/graph_buffer.c tests/test_graph_buffer.c
f076 scripts/run-test-wave.py tests/test_parallel_harness_contract.sh
f077 internal/cbm/extract_imports.c internal/cbm/lang_specs.c tests/test_edge_structural.c tests/test_extraction.c tests/test_grammar_labels.c
f078 internal/cbm/sqlite_writer.c src/graph_buffer/graph_buffer.c tests/test_graph_buffer.c tests/test_sqlite_writer.c
f079 src/foundation/dump_verify.c src/foundation/dump_verify.h tests/test_dump_verify.c
f080 Makefile.cbm scripts/ci/check-binary-composition.sh tests/test_mcpb_bundle_contract.sh tests/test_release_candidate_derivation_contract.sh
f081 scripts/smoke-test.sh src/cli/cli.c tests/test_cli.c tests/windows/test_hook_augment.py
f082 src/main.c tests/windows/test_daemon_lifecycle.py
f083 README.md docs/CONFIGURATION.md graph-ui/src/components/NodeDetailPanel.test.tsx graph-ui/src/components/NodeDetailPanel.tsx graph-ui/src/components/StatsTab.test.tsx graph-ui/src/hooks/useProjects.test.tsx graph-ui/src/hooks/useProjects.ts scripts/smoke-test.sh src/cli/cli.c src/cli/cli.h src/cli/client_adapter.c src/cli/hook_augment.c src/cli/progress_sink.c src/cli/progress_sink.h src/cypher/cypher.c src/cypher/cypher.h src/foundation/compat_fs.c src/foundation/compat_fs.h src/foundation/log.c src/foundation/log.h src/main.c src/mcp/compact_out.c src/mcp/compact_out.h src/mcp/mcp.c src/mcp/mcp.h src/mcp/mcp_internal.h src/pipeline/pipeline.c src/store/store.c src/store/store.h src/ui/http_server.c tests/scale_contract.sh tests/test_cli.c tests/test_cypher.c tests/test_daemon_runtime.c tests/test_diagnostics.c tests/test_httpd.c tests/test_incremental.c tests/test_index_resilience.c tests/test_index_supervisor.c tests/test_log.c tests/test_main.c tests/test_mcp.c tests/test_pipeline.c tests/test_store_pragmas.c tests/windows/test_daemon_lifecycle.py tests/windows/test_daemon_stability.py
f084 README.md docs/CONFIGURATION.md graph-ui/src/components/NodeDetailPanel.test.tsx graph-ui/src/components/NodeDetailPanel.tsx graph-ui/src/components/StatsTab.test.tsx graph-ui/src/hooks/useProjects.test.tsx graph-ui/src/hooks/useProjects.ts scripts/smoke-test.sh src/cli/cli.c src/cli/cli.h src/cli/client_adapter.c src/cli/hook_augment.c src/cli/progress_sink.c src/cli/progress_sink.h src/cypher/cypher.c src/cypher/cypher.h src/foundation/compat_fs.c src/foundation/compat_fs.h src/foundation/log.c src/foundation/log.h src/main.c src/mcp/compact_out.c src/mcp/compact_out.h src/mcp/mcp.c src/mcp/mcp.h src/mcp/mcp_internal.h src/pipeline/pipeline.c src/store/store.c src/store/store.h src/ui/http_server.c tests/scale_contract.sh tests/test_cli.c tests/test_cypher.c tests/test_daemon_runtime.c tests/test_diagnostics.c tests/test_httpd.c tests/test_incremental.c tests/test_index_resilience.c tests/test_index_supervisor.c tests/test_log.c tests/test_main.c tests/test_mcp.c tests/test_pipeline.c tests/test_store_pragmas.c tests/windows/test_daemon_lifecycle.py tests/windows/test_daemon_stability.py tests/windows/test_hook_augment.py
f085 src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_pipeline.c tests/test_registry.c
f086 src/cli/config_yaml_edit.c tests/test_config_yaml_edit.c
f087 src/foundation/compat_fs.c tests/test_cli.c tests/test_platform.c
f088 .github/workflows/release.yml
f089 .github/workflows/pages.yml
f090 src/store/store.c tests/test_store_nodes.c
f091 src/discover/discover.c tests/test_discover.c
f092 Makefile.cbm internal/cbm/extract_defs.c internal/cbm/helpers.c internal/cbm/helpers.h tests/repro/repro_issue1692.c tests/repro/repro_main.c
f093 src/mcp/mcp.c src/store/store.c tests/test_mcp.c tests/test_store_nodes.c
f094 tests/test_mcp.c
f095 src/mcp/mcp.c src/store/store.c src/store/store.h tests/test_mcp.c
f096 scripts/msan.sh scripts/test.sh tests/test_main.c
f097 src/daemon/ipc.c src/daemon/ipc.h tests/test_cli.c tests/test_daemon_ipc.c
f098 src/daemon/application.c src/mcp/mcp.c tests/test_daemon.c
f099 Makefile.cbm README.md graph-ui/index.html graph-ui/src/App.tsx graph-ui/src/components/ChangesTab.tsx graph-ui/src/components/CommandK.tsx graph-ui/src/components/ControlTab.tsx graph-ui/src/components/DashboardTab.tsx graph-ui/src/components/DisplaySettingsMenu.tsx graph-ui/src/components/ErrorBoundary.tsx graph-ui/src/components/FilterPanel.tsx graph-ui/src/components/FlowsTab.observed.test.tsx graph-ui/src/components/FlowsTab.tsx graph-ui/src/components/FlowsTab.zh.test.tsx graph-ui/src/components/GraphLoader.tsx graph-ui/src/components/GraphScene.tsx graph-ui/src/components/GraphTab.deadcode.test.tsx graph-ui/src/components/GraphTab.filters.test.tsx graph-ui/src/components/GraphTab.regions.test.tsx graph-ui/src/components/GraphTab.selection.test.tsx graph-ui/src/components/GraphTab.tsx graph-ui/src/components/HaloLayer.tsx graph-ui/src/components/MetricChip.tsx graph-ui/src/components/Minimap.tsx graph-ui/src/components/MissedCallout.tsx graph-ui/src/components/ModulesTab.tsx graph-ui/src/components/NodeDetailPanel.showmore.test.tsx graph-ui/src/components/NodeDetailPanel.tsx graph-ui/src/components/NodeTooltip.tsx graph-ui/src/components/OverviewTab.tsx graph-ui/src/components/ProjectCard.tsx graph-ui/src/components/ProjectSwitcher.tsx graph-ui/src/components/PromptBasket.tsx graph-ui/src/components/RegionPanel.tsx graph-ui/src/components/RegionTooltip.tsx graph-ui/src/components/Sidebar.tsx graph-ui/src/components/StatsTab.tsx graph-ui/src/components/SymbolTab.tsx graph-ui/src/components/TriggerTree.tsx graph-ui/src/components/WikiHoverCard.test.tsx graph-ui/src/components/WikiHoverCard.tsx graph-ui/src/components/WikiPanel.test.tsx graph-ui/src/components/WikiPanel.tsx graph-ui/src/components/WikiPanel.zh.test.tsx graph-ui/src/components/wikiTier.ts graph-ui/src/hooks/useGraphData.ts graph-ui/src/hooks/useProjects.test.tsx graph-ui/src/hooks/useProjects.ts graph-ui/src/lib/atlas.test.ts graph-ui/src/lib/atlas.ts graph-ui/src/lib/complexity.test.ts graph-ui/src/lib/complexity.ts graph-ui/src/lib/composer.ts graph-ui/src/lib/density.test.ts graph-ui/src/lib/density.ts graph-ui/src/lib/findings.test.ts graph-ui/src/lib/findings.ts graph-ui/src/lib/firstread.test.ts graph-ui/src/lib/firstread.ts graph-ui/src/lib/flowgroup.test.ts graph-ui/src/lib/flowgroup.ts graph-ui/src/lib/i18n.ts graph-ui/src/lib/impact.test.ts graph-ui/src/lib/impact.ts graph-ui/src/lib/observed.test.ts graph-ui/src/lib/observed.ts graph-ui/src/lib/questions.test.ts graph-ui/src/lib/questions.ts graph-ui/src/lib/rationale.test.ts graph-ui/src/lib/rationale.ts graph-ui/src/lib/regions.test.ts graph-ui/src/lib/regions.ts graph-ui/src/lib/treemap.ts graph-ui/src/lib/types.ts graph-ui/src/lib/who.test.ts graph-ui/src/lib/who.ts graph-ui/src/lib/why.test.ts graph-ui/src/lib/why.ts graph-ui/src/lib/whyfetch.ts graph-ui/src/styles/globals.css graph-ui/src/test/setup.ts graph-ui/src/wiki/entries.test.ts graph-ui/src/wiki/entries.ts graph-ui/tsconfig.tsbuildinfo graph-ui/vite.config.ts scripts/run-test-wave.py scripts/security-ui.sh src/main.c src/mcp/mcp.c src/pipeline/pipeline.h src/pipeline/pipeline_internal.h src/store/store.c src/store/store.h src/ui/atlas.h src/ui/atlas_api.c src/ui/atlas_flows.c src/ui/atlas_handout.c src/ui/atlas_metrics.c src/ui/atlas_why.c src/ui/http_server.c src/ui/layout3d.c src/ui/layout3d.h src/ui/layout_internal.h src/ui/layout_regions.c src/watcher/watcher.c src/watcher/watcher.h test-infrastructure/docker-compose.yml test-infrastructure/ladder.sh test-infrastructure/run.sh tests/test_cypher.c tests/test_daemon_runtime.c tests/test_httpd.c tests/test_incremental.c tests/test_mcp.c tests/test_py_lsp_scale.c tests/test_ui.c tests/test_watcher.c
f100 README.md docs/CONFIGURATION.md scripts/test_mcp_in_process.py src/main.c
f101 tests/test_pipeline.c
f102 src/daemon/application.c src/daemon/application_internal.h tests/test_daemon.c tests/test_daemon_application.c
f103 src/discover/discover.c src/discover/discover.h src/discover/language.c tests/test_language.c
f104 src/watcher/watcher.c src/watcher/watcher.h tests/test_watcher.c
f105 scripts/test.sh src/main.c tests/test_worker_session_scope.sh
f106 src/pipeline/pass_complexity.c tests/fixtures/complexity_pass_cycle_order/p00_a.py tests/fixtures/complexity_pass_cycle_order/p00_b.py tests/fixtures/complexity_pass_cycle_order/p01_a.py tests/fixtures/complexity_pass_cycle_order/p01_b.py tests/fixtures/complexity_pass_cycle_order/p02_a.py tests/fixtures/complexity_pass_cycle_order/p02_b.py tests/fixtures/complexity_pass_cycle_order/p03_a.py tests/fixtures/complexity_pass_cycle_order/p03_b.py tests/fixtures/complexity_pass_cycle_order/p04_a.py tests/fixtures/complexity_pass_cycle_order/p04_b.py tests/fixtures/complexity_pass_cycle_order/p05_a.py tests/fixtures/complexity_pass_cycle_order/p05_b.py tests/fixtures/complexity_pass_cycle_order/p06_a.py tests/fixtures/complexity_pass_cycle_order/p06_b.py tests/fixtures/complexity_pass_cycle_order/p07_a.py tests/fixtures/complexity_pass_cycle_order/p07_b.py tests/fixtures/complexity_pass_cycle_order/p08_a.py tests/fixtures/complexity_pass_cycle_order/p08_b.py tests/fixtures/complexity_pass_cycle_order/p09_a.py tests/fixtures/complexity_pass_cycle_order/p09_b.py tests/fixtures/complexity_pass_cycle_order/p10_a.py tests/fixtures/complexity_pass_cycle_order/p10_b.py tests/fixtures/complexity_pass_cycle_order/p11_a.py tests/fixtures/complexity_pass_cycle_order/p11_b.py tests/fixtures/complexity_pass_cycle_order/p12_a.py tests/fixtures/complexity_pass_cycle_order/p12_b.py tests/fixtures/complexity_pass_cycle_order/p13_a.py tests/fixtures/complexity_pass_cycle_order/p13_b.py tests/fixtures/complexity_pass_cycle_order/p14_a.py tests/fixtures/complexity_pass_cycle_order/p14_b.py tests/fixtures/complexity_pass_cycle_order/p15_a.py tests/fixtures/complexity_pass_cycle_order/p15_b.py tests/fixtures/complexity_pass_cycle_order/p16_a.py tests/fixtures/complexity_pass_cycle_order/p16_b.py tests/fixtures/complexity_pass_cycle_order/p17_a.py tests/fixtures/complexity_pass_cycle_order/p17_b.py tests/fixtures/complexity_pass_cycle_order/p18_a.py tests/fixtures/complexity_pass_cycle_order/p18_b.py tests/fixtures/complexity_pass_cycle_order/p19_a.py tests/fixtures/complexity_pass_cycle_order/p19_b.py tests/fixtures/complexity_pass_cycle_order/p20_a.py tests/fixtures/complexity_pass_cycle_order/p20_b.py tests/fixtures/complexity_pass_cycle_order/p21_a.py tests/fixtures/complexity_pass_cycle_order/p21_b.py tests/fixtures/complexity_pass_cycle_order/p22_a.py tests/fixtures/complexity_pass_cycle_order/p22_b.py tests/fixtures/complexity_pass_cycle_order/p23_a.py tests/fixtures/complexity_pass_cycle_order/p23_b.py tests/fixtures/complexity_pass_cycle_order/t00_x.py tests/fixtures/complexity_pass_cycle_order/t00_y.py tests/fixtures/complexity_pass_cycle_order/t00_z.py tests/fixtures/complexity_pass_cycle_order/t01_x.py tests/fixtures/complexity_pass_cycle_order/t01_y.py tests/fixtures/complexity_pass_cycle_order/t01_z.py tests/fixtures/complexity_pass_cycle_order/t02_x.py tests/fixtures/complexity_pass_cycle_order/t02_y.py tests/fixtures/complexity_pass_cycle_order/t02_z.py tests/fixtures/complexity_pass_cycle_order/t03_x.py tests/fixtures/complexity_pass_cycle_order/t03_y.py tests/fixtures/complexity_pass_cycle_order/t03_z.py tests/fixtures/complexity_pass_cycle_order/t04_x.py tests/fixtures/complexity_pass_cycle_order/t04_y.py tests/fixtures/complexity_pass_cycle_order/t04_z.py tests/fixtures/complexity_pass_cycle_order/t05_x.py tests/fixtures/complexity_pass_cycle_order/t05_y.py tests/fixtures/complexity_pass_cycle_order/t05_z.py tests/test_pipeline.c
f107 docs/benchmarks/bounded-two-pass-results.json docs/benchmarks/bounded-two-pass.md scripts/benchmark-streaming.py src/pipeline/lsp_surface.c src/pipeline/lsp_surface.h src/pipeline/pass_parallel.c src/pipeline/pipeline.c src/pipeline/pipeline_internal.h tests/test_pipeline.c
f108 src/pipeline/pass_semantic_edges.c tests/test_pipeline.c
f109 .github/workflows/pr.yml internal/cbm/cbm.c scripts/ci/README.md scripts/ci/coverage-gate-allowlist.txt scripts/ci/parse-partial-baseline.txt scripts/ci/self-index-coverage-gate.sh scripts/test.sh tests/test_coverage_gate_contract.sh tests/test_parse_coverage.c tests/test_venue_parity_contract.sh
f110 Makefile.cbm README.md docs/CONFIGURATION.md docs/INDEX_RESOURCE_LIMITS.md scripts/test-runtime.sh src/cli/cli.c src/cli/cli.h src/daemon/application.c src/discover/discover.c src/discover/discover.h src/foundation/index_policy.c src/foundation/index_policy.h src/foundation/subprocess.c src/foundation/subprocess.h src/mcp/index_supervisor.c src/mcp/index_supervisor.h src/mcp/mcp.c src/mcp/mcp.h src/mcp/mcp_internal.h src/pipeline/pipeline.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h tests/test_daemon_application.c tests/test_discover.c tests/test_index_policy.c tests/test_index_supervisor.c tests/test_main.c tests/test_mcp.c tests/test_pipeline.c tests/test_subprocess.c tests/test_worker_error_response.sh tests/test_worker_watchdog.sh
f111 Makefile.cbm README.md docs/CONFIGURATION.md docs/INDEX_RESOURCE_LIMITS.md scripts/test-runtime.sh src/cli/cli.c src/cli/cli.h src/daemon/application.c src/daemon/bootstrap.c src/discover/discover.c src/discover/discover.h src/foundation/compat_fs.c src/foundation/compat_fs.h src/foundation/index_policy.c src/foundation/index_policy.h src/foundation/subprocess.c src/foundation/subprocess.h src/main.c src/mcp/index_supervisor.c src/mcp/index_supervisor.h src/mcp/mcp.c src/mcp/mcp.h src/mcp/mcp_internal.h src/pipeline/pipeline.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h tests/test_daemon_application.c tests/test_discover.c tests/test_index_policy.c tests/test_index_supervisor.c tests/test_main.c tests/test_mcp.c tests/test_pipeline.c tests/test_platform.c tests/test_subprocess.c tests/test_worker_error_response.sh tests/test_worker_watchdog.sh
f112 Makefile.cbm README.md docs/CONFIGURATION.md docs/INDEX_RESOURCE_LIMITS.md scripts/test-runtime.sh src/cli/cli.c src/cli/cli.h src/daemon/application.c src/daemon/bootstrap.c src/discover/discover.c src/discover/discover.h src/foundation/compat_fs.c src/foundation/compat_fs.h src/foundation/index_policy.c src/foundation/index_policy.h src/foundation/subprocess.c src/foundation/subprocess.h src/git/git_context.c src/git/git_context.h src/main.c src/mcp/index_supervisor.c src/mcp/index_supervisor.h src/mcp/mcp.c src/mcp/mcp.h src/mcp/mcp_internal.h src/pipeline/pipeline.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h tests/test_daemon_application.c tests/test_discover.c tests/test_git_context.c tests/test_index_policy.c tests/test_index_supervisor.c tests/test_main.c tests/test_mcp.c tests/test_pipeline.c tests/test_platform.c tests/test_subprocess.c tests/test_worker_error_response.sh tests/test_worker_watchdog.sh
f113 src/pipeline/pipeline_incremental.c tests/test_pipeline.c
f114 README.md THIRD_PARTY.md docs/index.html docs/llms.txt internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/grammar_vba.c internal/cbm/helpers.c internal/cbm/lang_specs.c internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/vba/LICENSE internal/cbm/vendored/grammars/vba/parser.c internal/cbm/vendored/grammars/vba/tree_sitter/alloc.h internal/cbm/vendored/grammars/vba/tree_sitter/array.h internal/cbm/vendored/grammars/vba/tree_sitter/parser.h pkg/chocolatey/codebase-memory-mcp.nuspec pkg/npm/README.md scripts/new-languages.json scripts/package-release.sh scripts/vendored-checksums.txt server.json src/discover/discover.h src/discover/language.c src/discover/userconfig.c tests/repro/repro_call_argument_matrix_b.c tests/repro/repro_call_node_manifest.c tests/repro/repro_invariant_breadth.c tests/repro/repro_language_registry.c tests/test_extraction.c tests/test_grammar_labels.c tests/test_grammar_regression.c tests/test_lang_contract.c tests/test_language.c
f115 README.md THIRD_PARTY.md docs/index.html docs/llms.txt internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/extract_unified.c internal/cbm/extract_unified.h internal/cbm/grammar_vba.c internal/cbm/helpers.c internal/cbm/helpers.h internal/cbm/lang_specs.c internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/vba/LICENSE internal/cbm/vendored/grammars/vba/parser.c internal/cbm/vendored/grammars/vba/tree_sitter/alloc.h internal/cbm/vendored/grammars/vba/tree_sitter/array.h internal/cbm/vendored/grammars/vba/tree_sitter/parser.h pkg/chocolatey/codebase-memory-mcp.nuspec pkg/npm/README.md scripts/new-languages.json scripts/package-release.sh scripts/vendored-checksums.txt server.json src/discover/discover.h src/discover/language.c src/discover/userconfig.c tests/repro/repro_call_argument_matrix_b.c tests/repro/repro_call_node_manifest.c tests/repro/repro_invariant_breadth.c tests/repro/repro_language_registry.c tests/test_extraction.c tests/test_grammar_labels.c tests/test_grammar_regression.c tests/test_lang_contract.c tests/test_language.c
f116 internal/cbm/lang_specs.c
f117 internal/cbm/extract_node_stack.h internal/cbm/lsp/ts_lsp.c tests/test_ts_lsp.c
f118 .cbmignore src/cli/cli.c src/cli/cli.h src/main.c src/mcp/mcp.c
f119 .cbmignore src/cli/cli.c src/cli/cli.h src/main.c src/mcp/mcp.c tests/test_cli.c
f120 src/git/git_context.c src/git/git_context.h src/mcp/mcp.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/store/store.c src/store/store.h tests/test_git_context.c tests/test_mcp.c tests/test_store_nodes.c
f121 src/watcher/watcher.c tests/test_watcher.c
f122 scripts/run-tests-parallel.sh src/git/git_context.c src/git/git_context.h src/mcp/mcp.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/store/store.c src/store/store.h tests/test_git_context.c tests/test_mcp.c tests/test_store_nodes.c
f123 scripts/test-windows.ps1 tests/windows/test_mcp_stdio.py
f124 internal/cbm/extract_defs.c tests/test_grammar_labels.c tests/test_grammar_regression.c
f125 src/cli/cli.c tests/test_cli.c
f126 src/cli/cli.c src/cli/config_json_like.c src/cli/hook_augment.c src/daemon/application.c src/foundation/compat_fs.c src/foundation/compat_fs.h src/main.c src/mcp/index_supervisor.c src/mcp/mcp.c src/pipeline/artifact.c src/pipeline/pipeline.c src/ui/config.c tests/test_artifact.c tests/test_cli.c tests/test_platform.c
f127 tests/test_daemon_frontend.c
f128 src/daemon/ipc.c src/daemon/ipc_internal.h tests/test_daemon_ipc.c
f129 .github/dependabot.yml .github/workflows/_build.yml .github/workflows/_lint.yml .github/workflows/_test.yml .github/workflows/release.yml .node-version CONTRIBUTING.md scripts/ci/lint-workflows.sh scripts/ci/test-frontend.sh tests/test_venue_parity_contract.sh
f130 src/foundation/compat_fs.c src/foundation/compat_fs.h src/pipeline/pipeline.c src/pipeline/pipeline_internal.h tests/test_pipeline.c
f131 src/daemon/application.c src/mcp/mcp.c src/pipeline/pass_parallel.c src/pipeline/pipeline.h src/pipeline/pipeline_internal.h tests/test_daemon_application.c tests/test_incremental.c tests/test_mcp.c tests/test_pipeline.c
f132 src/daemon/bootstrap.c src/daemon/bootstrap.h src/daemon/host.c src/daemon/ipc.c src/daemon/ipc.h src/daemon/ipc_internal.h src/foundation/platform.c src/foundation/platform.h tests/test_daemon_bootstrap.c tests/test_daemon_ipc.c
f133 Makefile.cbm src/cli/cli.c src/cli/config_edit_path.c src/cli/config_edit_path.h src/cli/config_json_like.c src/cli/config_text_edit.c src/cli/config_toml_edit.c src/cli/config_yaml_edit.c tests/test_cli.c tests/test_config_json_like.c tests/test_config_text_edit.c tests/test_config_toml_edit.c tests/test_config_yaml_edit.c
f134 src/cli/cli.c src/mcp/index_supervisor.c src/mcp/index_supervisor.h tests/test_cli.c
f135 src/cli/cli.c src/cli/config_json_like.c src/cli/config_json_like.h src/mcp/mcp.c tests/test_cli.c tests/test_config_json_like.c tests/test_mcp.c
f136 src/cli/cli.c tests/test_cli.c tests/test_smoke_fixture_contract.sh
f137 .github/workflows/_lint.yml .github/workflows/_test.yml
f138 src/daemon/ipc.c src/daemon/ipc.h tests/test_daemon_ipc.c
f139 src/cli/activation_transaction.c src/daemon/ipc.c src/daemon/ipc.h tests/test_daemon_ipc.c
f140 src/cli/cli.h src/main.c tests/test_cli.c
f141 internal/cbm/extract_defs.c tests/test_edge_types_probe.c tests/test_extraction.c
f142 internal/cbm/lsp/php_lsp.c
f143 internal/cbm/lsp/php_lsp.c src/pipeline/registry.c
f144 src/cli/cli.c src/cli/cli.h src/main.c src/mcp/mcp.c tests/test_cli.c
f145 README.md docs/UPSTREAM-SYNC.md docs/lsp-uplift/PERL-CROSS-FILE-INHERITANCE.md docs/lsp-uplift/PERL-DUCK-TYPING-AND-CROSS-LANG-AUDIT.md docs/lsp-uplift/PLAN.md docs/lsp-uplift/RESOLVER-PLAYBOOK.md docs/lsp-uplift/SOP.md internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/extract_unified.c internal/cbm/helpers.c internal/cbm/lang_specs.c internal/cbm/lsp/generated/perl_stdlib_data.c internal/cbm/lsp/generated/rust_crates_seed.c internal/cbm/lsp/go_lsp.c internal/cbm/lsp/go_lsp.h internal/cbm/lsp/go_stdlib_modern.c internal/cbm/lsp/java_lsp.c internal/cbm/lsp/perl_lsp.c internal/cbm/lsp/perl_lsp.h internal/cbm/lsp/py_builtins.c internal/cbm/lsp/py_lsp.c internal/cbm/lsp/py_stdlib_compat.c internal/cbm/lsp/rust_cargo.c internal/cbm/lsp/rust_cargo.h internal/cbm/lsp/rust_lsp.c internal/cbm/lsp/rust_lsp.h internal/cbm/lsp/type_registry.h internal/cbm/lsp_all.c internal/cbm/service_patterns.c internal/cbm/service_patterns.h src/discover/language.c src/pipeline/lsp_surface.c src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_lsp_cross.c src/pipeline/pass_lsp_cross.h src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pass_route_nodes.c src/pipeline/pass_semantic.c src/pipeline/pass_tests.c src/pipeline/pipeline.c src/pipeline/registry.c tests/test_extraction.c tests/test_go_lsp.c tests/test_java_lsp.c tests/test_language.c tests/test_perl_lsp.c tests/test_pipeline.c tests/test_py_lsp.c tests/test_registry.c tests/test_route_canon.c tests/test_rust_lsp.c
f146 src/foundation/subprocess.c src/foundation/subprocess.h src/mcp/index_supervisor.c src/mcp/index_supervisor.h tests/test_index_supervisor.c tests/test_main.c tests/test_subprocess.c
f147 src/daemon/daemon.c src/daemon/daemon.h src/daemon/host.c src/daemon/runtime.c src/daemon/runtime.h tests/test_daemon_runtime.c
f148 internal/cbm/cbm.c src/foundation/compat.c src/foundation/compat.h tests/test_edge_types_probe.c
f149 scripts/test-windows.ps1 tests/windows/mcp_stdio.py tests/windows/test_mcp_stdio.py
f150 .github/workflows/codeql.yml
f151 .github/workflows/scorecard.yml
f152 src/cli/cli.c src/foundation/sha256.c src/foundation/sha256.h src/mcp/index_supervisor.c src/mcp/index_supervisor.h tests/test_cli.c
f153 install.ps1 scripts/test.sh src/cli/activation_transaction.c src/cli/cli.c tests/test_activation_diagnostic_contract.sh tests/test_windows_bundle_contract.sh
f154 scripts/run-test-wave.py src/cli/cli.c src/foundation/sha256.c src/foundation/sha256.h src/mcp/index_supervisor.c src/mcp/index_supervisor.h tests/test_cli.c
f155 .github/workflows/_security.yml graph-ui/package-lock.json graph-ui/package.json scripts/test.sh tests/test_security_gate_fail_closed.sh
f156 graph-ui/package-lock.json graph-ui/package.json
f157 .github/workflows/_security.yml .github/workflows/pr.yml .github/workflows/release.yml graph-ui/package-lock.json graph-ui/package.json scripts/test.sh tests/test_security_gate_fail_closed.sh
f158 tests/test_fqn.c
f159 .github/workflows/dry-run.yml tests/test_security_gate_fail_closed.sh
f160 tests/test_daemon_ipc.c tests/test_main.c
f161 src/daemon/ipc.c tests/test_daemon_ipc.c tests/test_main.c
f162 .github/workflows/_test.yml src/daemon/ipc.c src/daemon/ipc.h tests/test_daemon_ipc.c tests/test_main.c
f163 scripts/soak-test.sh scripts/test.sh tests/test_soak_runtime_isolation_contract.sh
f164 scripts/memlab.sh scripts/test.sh tests/test_memlab_runtime_isolation_contract.sh
f165 scripts/benchmark-index.sh scripts/benchmark-search-graph.sh scripts/test.sh tests/test_benchmark_runtime_isolation_contract.sh
f166 scripts/smoke-test.sh scripts/test.sh tests/test_smoke_runtime_isolation_contract.sh
f167 Makefile.cbm scripts/lint-memory-core.py scripts/memory-core-baseline.txt src/foundation/mem.c src/foundation/mem.h src/foundation/mem_core.c src/foundation/mem_core.h src/foundation/platform.h src/foundation/system_info.c src/pipeline/pipeline.c tests/test_mem.c
f168 src/cli/activation_transaction.c
f169 Makefile.cbm internal/cbm/arena.h internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/extract_unified.c internal/cbm/lsp/c_lsp.c internal/cbm/lsp/c_lsp.h internal/cbm/lsp/cs_lsp.c internal/cbm/lsp/go_lsp.c internal/cbm/lsp/py_lsp.c internal/cbm/lsp/py_lsp.h internal/cbm/lsp/type_registry.c internal/cbm/lsp/type_registry.h internal/cbm/result_compact.c internal/cbm/result_spill.c internal/cbm/result_spill.h scripts/lint-memory-core.py scripts/memory-core-baseline.txt src/foundation/arena.c src/foundation/arena.h src/foundation/dyn_array.h src/foundation/hash_table.c src/foundation/hash_table.h src/foundation/mem.c src/foundation/mem.h src/foundation/mem_core.c src/foundation/mem_core.h src/foundation/platform.h src/foundation/slab_alloc.c src/foundation/system_info.c src/graph_buffer/graph_buffer.c src/graph_buffer/graph_buffer.h src/mcp/mcp.c src/pipeline/lsp_surface.c src/pipeline/lsp_surface.h src/pipeline/pass_complexity.c src/pipeline/pass_importance.c src/pipeline/pass_lsp_cross.c src/pipeline/pass_lsp_cross.h src/pipeline/pass_parallel.c src/pipeline/pass_semantic.c src/pipeline/pass_semantic_edges.c src/pipeline/pass_similarity.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/pipeline/worker_pool.c src/semantic/semantic.c src/semantic/semantic.h src/simhash/minhash.c tests/test_arena.c tests/test_c_lsp.c tests/test_extraction.c tests/test_mcp.c tests/test_mem.c tests/test_parallel.c tests/test_pipeline.c
f170 docs/CONFIGURATION.md src/main.c tests/test_daemon_open_readiness.py
f171 src/daemon/ipc.c tests/test_daemon_ipc.c
f172 src/cli/hook_augment.c src/daemon/runtime.c src/daemon/runtime.h src/main.c tests/test_cli.c tests/test_daemon_runtime.c
f173 Makefile.cbm internal/cbm/arena.h internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/extract_unified.c internal/cbm/lsp/c_lsp.c internal/cbm/lsp/c_lsp.h internal/cbm/lsp/cs_lsp.c internal/cbm/lsp/go_lsp.c internal/cbm/lsp/py_lsp.c internal/cbm/lsp/py_lsp.h internal/cbm/lsp/type_registry.c internal/cbm/lsp/type_registry.h internal/cbm/result_compact.c internal/cbm/result_spill.c internal/cbm/result_spill.h scripts/lint-memory-core.py scripts/memory-core-baseline.txt src/foundation/arena.c src/foundation/arena.h src/foundation/diagnostics.c src/foundation/dyn_array.h src/foundation/hash_table.c src/foundation/hash_table.h src/foundation/mem.c src/foundation/mem.h src/foundation/mem_core.c src/foundation/mem_core.h src/foundation/platform.h src/foundation/slab_alloc.c src/foundation/system_info.c src/graph_buffer/graph_buffer.c src/graph_buffer/graph_buffer.h src/main.c src/mcp/mcp.c src/pipeline/lsp_surface.c src/pipeline/lsp_surface.h src/pipeline/pass_complexity.c src/pipeline/pass_importance.c src/pipeline/pass_lsp_cross.c src/pipeline/pass_lsp_cross.h src/pipeline/pass_parallel.c src/pipeline/pass_semantic.c src/pipeline/pass_semantic_edges.c src/pipeline/pass_similarity.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/pipeline/worker_pool.c src/semantic/semantic.c src/semantic/semantic.h src/simhash/minhash.c tests/test_arena.c tests/test_c_lsp.c tests/test_extraction.c tests/test_graph_buffer.c tests/test_mcp.c tests/test_mem.c tests/test_parallel.c tests/test_pipeline.c
f174 README.md scripts/smoke-test.sh src/cli/cli.c src/cli/cli.h tests/test_cli.c
f175 src/foundation/mem.c src/foundation/mem.h
f176 flake.nix pkg/chocolatey/codebase-memory-mcp.nuspec pkg/chocolatey/tools/chocolateyInstall.ps1 pkg/go/cmd/codebase-memory-mcp/main.go pkg/npm/package.json pkg/pypi/pyproject.toml pkg/pypi/src/codebase_memory_mcp/_cli.py server.json
f177 graph-ui/src/components/FilterPanel.tsx graph-ui/src/components/GraphTab.deadcode.test.tsx graph-ui/src/lib/colors.ts
f178 CONTRIBUTING.md README.md docs/index.html pkg/npm/README.md
f179 internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/service_patterns.c internal/cbm/service_patterns.h src/cli/cli.c src/cypher/cypher.c src/foundation/str_util.c src/foundation/str_util.h src/mcp/mcp.c src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_cli.c tests/test_cypher.c tests/test_extraction.c tests/test_infrascan.c tests/test_mcp.c tests/test_parse_coverage.c tests/test_pipeline.c tests/test_registry.c tests/test_str_util.c
f180 internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/service_patterns.c internal/cbm/service_patterns.h scripts/memory-core-baseline.txt src/cli/cli.c src/cypher/cypher.c src/foundation/str_util.c src/foundation/str_util.h src/mcp/mcp.c src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_cli.c tests/test_cypher.c tests/test_extraction.c tests/test_infrascan.c tests/test_mcp.c tests/test_parse_coverage.c tests/test_pipeline.c tests/test_registry.c tests/test_str_util.c
f181 src/discover/language.c tests/test_language.c
f182 .github/workflows/_memwaste.yml .github/workflows/dry-run.yml .github/workflows/nightly-soak.yml .github/workflows/pr.yml .github/workflows/release.yml Makefile.cbm internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/extract_node_stack.h internal/cbm/lsp/go_lsp.c internal/cbm/result_compact.c internal/cbm/sqlite_writer.c scripts/fuzz.sh scripts/memlab-drive.py scripts/memlab-report.py scripts/memlab.sh scripts/memwaste-padding.py scripts/memwaste-ratchet.py scripts/memwaste-report.py scripts/memwaste-scaling.py scripts/memwaste.sh scripts/sanitizer-coverage-map.py scripts/sanitizer-map.json src/foundation/arena.c src/foundation/arena.h src/foundation/compat_thread.c src/foundation/dyn_array.h src/foundation/hash_table.c src/foundation/mem.c src/foundation/mem.h src/foundation/mem_core.c src/foundation/mem_events.c src/foundation/mem_events.h src/foundation/mem_override_darwin.c src/foundation/mem_override_libc.c src/foundation/mem_override_posix.c src/foundation/mem_override_win.c src/foundation/mem_profile.c src/foundation/mem_profile.h src/main.c src/pipeline/lsp_surface.c src/pipeline/pass_configlink.c src/pipeline/pass_k8s.c src/pipeline/pass_lsp_cross.c src/pipeline/pass_lsp_cross.h src/pipeline/pass_parallel.c src/pipeline/pass_semantic.c src/pipeline/pass_semantic_edges.c src/pipeline/pass_similarity.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/registry.c src/pipeline/worker_pool.c src/semantic/semantic.c src/semantic/semantic.h src/simhash/minhash.c src/simhash/minhash.h tests/fuzz/cbm_fuzz.c tests/fuzz/corpus/config/json-empty.seed tests/fuzz/corpus/config/json.seed tests/fuzz/corpus/config/toml.seed tests/fuzz/corpus/config/yaml.seed tests/fuzz/corpus/cypher/agg.seed tests/fuzz/corpus/cypher/hops.seed tests/fuzz/corpus/cypher/match.seed tests/fuzz/corpus/cypher/optional.seed tests/fuzz/corpus/cypher/union.seed tests/fuzz/corpus/cypher/where.seed tests/fuzz/corpus/extract/c.seed tests/fuzz/corpus/extract/cpp.seed tests/fuzz/corpus/extract/csharp.seed tests/fuzz/corpus/extract/go.seed tests/fuzz/corpus/extract/java.seed tests/fuzz/corpus/extract/javascript.seed tests/fuzz/corpus/extract/makefile.seed tests/fuzz/corpus/extract/php.seed tests/fuzz/corpus/extract/python.seed tests/fuzz/corpus/extract/ruby.seed tests/fuzz/corpus/extract/rust.seed tests/fuzz/corpus/extract/sql.seed tests/fuzz/corpus/extract/typescript.seed tests/fuzz/corpus/extract/yaml.seed tests/test_arena.c tests/test_extraction.c tests/test_hash_table.c tests/test_main.c tests/test_mem.c tests/test_mem_events.c tests/test_registry.c tests/test_semantic.c tests/test_shell_line_endings.sh tests/test_simhash.c tests/test_venue_parity_contract.sh
f183 src/cli/cli.h src/main.c tests/test_cli.c tests/test_daemon_bootstrap.c
f184 src/cli/cli.h src/main.c tests/test_cli.c tests/test_daemon_bootstrap.c tests/test_mcp.c
f185 src/ui/layout3d.c tests/test_ui.c
f186 Makefile.cbm README.md src/daemon/application.c src/daemon/bootstrap.c src/foundation/mem_core.c src/foundation/mem_core.h src/main.c src/mcp/cache.c src/mcp/cache.h src/mcp/mcp.c tests/test_cache.c tests/test_cache_cli.py tests/test_daemon_application.c tests/test_daemon_bootstrap.c tests/test_main.c tests/test_mcp.c
f187 .github/workflows/_memwaste.yml .github/workflows/dry-run.yml .github/workflows/nightly-soak.yml .github/workflows/pr.yml .github/workflows/release.yml Makefile.cbm internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/extract_node_stack.h internal/cbm/lsp/go_lsp.c internal/cbm/result_compact.c internal/cbm/result_spill.c internal/cbm/result_spill.h internal/cbm/sqlite_writer.c scripts/fuzz.sh scripts/memlab-drive.py scripts/memlab-report.py scripts/memlab.sh scripts/memory-core-baseline.txt scripts/memwaste-padding.py scripts/memwaste-ratchet.py scripts/memwaste-report.py scripts/memwaste-scaling.py scripts/memwaste.sh scripts/sanitizer-coverage-map.py scripts/sanitizer-map.json scripts/smoke-test.sh scripts/test.sh src/foundation/arena.c src/foundation/arena.h src/foundation/compat_thread.c src/foundation/dyn_array.h src/foundation/hash_table.c src/foundation/mem.c src/foundation/mem.h src/foundation/mem_core.c src/foundation/mem_events.c src/foundation/mem_events.h src/foundation/mem_override_darwin.c src/foundation/mem_override_libc.c src/foundation/mem_override_posix.c src/foundation/mem_override_win.c src/foundation/mem_profile.c src/foundation/mem_profile.h src/foundation/platform.h src/foundation/system_info.c src/graph_buffer/graph_buffer.c src/main.c src/pipeline/lsp_surface.c src/pipeline/pass_configlink.c src/pipeline/pass_k8s.c src/pipeline/pass_lsp_cross.c src/pipeline/pass_lsp_cross.h src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pass_route_nodes.c src/pipeline/pass_semantic.c src/pipeline/pass_semantic_edges.c src/pipeline/pass_similarity.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/pipeline/registry.c src/pipeline/worker_pool.c src/semantic/semantic.c src/semantic/semantic.h src/simhash/minhash.c src/simhash/minhash.h tests/fuzz/cbm_fuzz.c tests/fuzz/corpus/config/json-empty.seed tests/fuzz/corpus/config/json.seed tests/fuzz/corpus/config/toml.seed tests/fuzz/corpus/config/yaml.seed tests/fuzz/corpus/cypher/agg.seed tests/fuzz/corpus/cypher/hops.seed tests/fuzz/corpus/cypher/match.seed tests/fuzz/corpus/cypher/optional.seed tests/fuzz/corpus/cypher/union.seed tests/fuzz/corpus/cypher/where.seed tests/fuzz/corpus/extract/c.seed tests/fuzz/corpus/extract/cpp.seed tests/fuzz/corpus/extract/csharp.seed tests/fuzz/corpus/extract/go.seed tests/fuzz/corpus/extract/java.seed tests/fuzz/corpus/extract/javascript.seed tests/fuzz/corpus/extract/makefile.seed tests/fuzz/corpus/extract/php.seed tests/fuzz/corpus/extract/python.seed tests/fuzz/corpus/extract/ruby.seed tests/fuzz/corpus/extract/rust.seed tests/fuzz/corpus/extract/sql.seed tests/fuzz/corpus/extract/typescript.seed tests/fuzz/corpus/extract/yaml.seed tests/test_arena.c tests/test_extraction.c tests/test_hash_table.c tests/test_main.c tests/test_mem.c tests/test_mem_events.c tests/test_parallel.c tests/test_pipeline.c tests/test_registry.c tests/test_semantic.c tests/test_shell_line_endings.sh tests/test_simhash.c tests/test_thread_stack_tls_contract.sh tests/test_venue_parity_contract.sh
f188 .github/workflows/_memwaste.yml .github/workflows/dry-run.yml .github/workflows/nightly-soak.yml .github/workflows/pr.yml .github/workflows/release.yml Makefile.cbm internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/extract_node_stack.h internal/cbm/extract_unified.c internal/cbm/extract_usages.c internal/cbm/lsp/go_lsp.c internal/cbm/result_compact.c internal/cbm/result_spill.c internal/cbm/result_spill.h internal/cbm/sqlite_writer.c internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/properties/scanner.c scripts/fuzz.sh scripts/memlab-drive.py scripts/memlab-report.py scripts/memlab.sh scripts/memory-core-baseline.txt scripts/memwaste-padding.py scripts/memwaste-ratchet.py scripts/memwaste-report.py scripts/memwaste-scaling.py scripts/memwaste.sh scripts/sanitizer-coverage-map.py scripts/sanitizer-map.json scripts/smoke-test.sh scripts/test.sh scripts/vendored-checksums.txt src/foundation/arena.c src/foundation/arena.h src/foundation/compat_thread.c src/foundation/dyn_array.h src/foundation/hash_table.c src/foundation/mem.c src/foundation/mem.h src/foundation/mem_core.c src/foundation/mem_events.c src/foundation/mem_events.h src/foundation/mem_override_darwin.c src/foundation/mem_override_libc.c src/foundation/mem_override_posix.c src/foundation/mem_override_win.c src/foundation/mem_profile.c src/foundation/mem_profile.h src/foundation/platform.h src/foundation/system_info.c src/graph_buffer/graph_buffer.c src/main.c src/pipeline/lsp_surface.c src/pipeline/pass_configlink.c src/pipeline/pass_k8s.c src/pipeline/pass_lsp_cross.c src/pipeline/pass_lsp_cross.h src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pass_route_nodes.c src/pipeline/pass_semantic.c src/pipeline/pass_semantic_edges.c src/pipeline/pass_similarity.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/pipeline/registry.c src/pipeline/worker_pool.c src/semantic/semantic.c src/semantic/semantic.h src/simhash/minhash.c src/simhash/minhash.h test-infrastructure/vm/vm-run-tests.sh tests/fuzz/cbm_fuzz.c tests/fuzz/corpus/config/json-empty.seed tests/fuzz/corpus/config/json.seed tests/fuzz/corpus/config/toml.seed tests/fuzz/corpus/config/yaml.seed tests/fuzz/corpus/cypher/agg.seed tests/fuzz/corpus/cypher/hops.seed tests/fuzz/corpus/cypher/match.seed tests/fuzz/corpus/cypher/optional.seed tests/fuzz/corpus/cypher/union.seed tests/fuzz/corpus/cypher/where.seed tests/fuzz/corpus/extract/c.seed tests/fuzz/corpus/extract/cpp.seed tests/fuzz/corpus/extract/csharp.seed tests/fuzz/corpus/extract/go.seed tests/fuzz/corpus/extract/java.seed tests/fuzz/corpus/extract/javascript.seed tests/fuzz/corpus/extract/makefile.seed tests/fuzz/corpus/extract/php.seed tests/fuzz/corpus/extract/python.seed tests/fuzz/corpus/extract/ruby.seed tests/fuzz/corpus/extract/rust.seed tests/fuzz/corpus/extract/sql.seed tests/fuzz/corpus/extract/typescript.seed tests/fuzz/corpus/extract/yaml.seed tests/test_arena.c tests/test_extraction.c tests/test_hash_table.c tests/test_main.c tests/test_mem.c tests/test_mem_events.c tests/test_parallel.c tests/test_pipeline.c tests/test_registry.c tests/test_semantic.c tests/test_shell_line_endings.sh tests/test_simhash.c tests/test_thread_stack_tls_contract.sh tests/test_venue_parity_contract.sh tests/test_vm_verdict_contract.sh
f189 internal/cbm/extract_calls.c internal/cbm/lsp/ts_lsp.c tests/test_extraction.c tests/test_ts_lsp.c
f190 .github/workflows/pr.yml Makefile.cbm README.md src/cli/cache.c src/cli/cache.h src/daemon/bootstrap.c src/daemon/version_cohort.c src/daemon/version_cohort.h src/foundation/mem_core.c src/foundation/mem_core.h src/main.c tests/test_cache.c tests/test_cache_cli.py tests/test_daemon_bootstrap.c tests/test_main.c tests/test_mcp.c tests/test_version_cohort.c
f191 .github/workflows/pr.yml Makefile.cbm README.md pkg/pypi/tests/test_cli.py src/cli/cache.c src/cli/cache.h src/daemon/bootstrap.c src/daemon/version_cohort.c src/daemon/version_cohort.h src/foundation/mem_core.c src/foundation/mem_core.h src/main.c tests/test_cache.c tests/test_cache_cli.py tests/test_daemon_bootstrap.c tests/test_main.c tests/test_mcp.c tests/test_version_cohort.c
f192 install.sh tests/test-install-android-guard.sh
f193 src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pipeline.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/pipeline/registry.c tests/test_pipeline.c tests/test_registry.c
f194 internal/cbm/vendored/grammars/sql/parser.c internal/cbm/vendored/grammars/tsx/parser.c scripts/vendored-checksums.txt tests/test_parse_coverage.c
f195 internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/sql/parser.c internal/cbm/vendored/grammars/tsx/parser.c scripts/vendored-checksums.txt tests/test_parse_coverage.c
f196 src/mcp/mcp.c src/pipeline/pipeline.c src/pipeline/pipeline.h tests/test_pipeline.c
f197 .github/workflows/_lint.yml .github/workflows/_test.yml .github/workflows/release.yml
f198 internal/cbm/lang_specs.c tests/test_extraction.c
f199 install.ps1
f200 internal/cbm/lsp/py_lsp.c internal/cbm/lsp/scope.c internal/cbm/lsp/scope.h tests/test_scope.c
f201 src/cypher/cypher.c src/foundation/recursion_whitelist.h tests/test_cypher.c
f202 internal/cbm/service_patterns.c tests/test_infrascan.c tests/test_pipeline.c
f203 scripts/memory-core-baseline.txt src/mcp/mcp.c
f204 internal/cbm/service_patterns.c tests/test_pipeline.c
f205 src/foundation/compat_thread.c
f206 CONTRIBUTING.md README.md
f207 README.md docs/WINDOWS_TROUBLESHOOTING.md
f208 scripts/memory-core-baseline.txt src/mcp/mcp.c tests/test_worker_session_scope.sh
f209 .github/workflows/smoke.yml scripts/smoke-invariants.sh
f210 internal/cbm/cbm.c internal/cbm/preprocessor.cpp internal/cbm/preprocessor.h src/mcp/mcp.c tests/test_extraction.c
f211 README.md docs/CONFIGURATION.md docs/INDEX_RESOURCE_LIMITS.md scripts/test-runtime.sh src/cli/cli.c src/daemon/application.c src/foundation/index_policy.c src/foundation/index_policy.h src/foundation/subprocess.c src/foundation/subprocess.h src/mcp/index_supervisor.c src/mcp/index_supervisor.h src/mcp/mcp.c src/mcp/mcp.h src/mcp/mcp_internal.h src/pipeline/pipeline.c tests/test_daemon_application.c tests/test_index_policy.c tests/test_index_supervisor.c tests/test_mcp.c tests/test_subprocess.c
f212 docs/EVALUATION_PLAN.md scripts/benchmark-index.sh scripts/benchmark-search-graph.sh scripts/test.sh tests/test_benchmark_runtime_isolation_contract.sh
f213 Makefile.cbm src/foundation/compat.c src/foundation/compat.h src/foundation/limits.h src/foundation/mem_override_win.c src/foundation/source_encoding.c src/foundation/source_encoding.h src/mcp/index_supervisor.c src/mcp/mcp.c src/pipeline/pass_calls.c src/pipeline/pass_compile_commands.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_semantic.c src/pipeline/pass_usages.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h tests/test_pipeline.c tests/test_platform.c
f214 README.md docs/CONFIGURATION.md docs/INDEX_RESOURCE_LIMITS.md scripts/test-runtime.sh src/cli/cli.c src/daemon/application.c src/foundation/index_policy.c src/foundation/index_policy.h src/foundation/subprocess.c src/foundation/subprocess.h src/mcp/index_supervisor.c src/mcp/index_supervisor.h src/mcp/mcp.c src/mcp/mcp.h src/mcp/mcp_internal.h src/pipeline/pipeline.c tests/test_daemon_application.c tests/test_index_policy.c tests/test_index_supervisor.c tests/test_mcp.c tests/test_subprocess.c tests/test_worker_session_scope.sh
f215 .github/workflows/pr.yml
f216 CONTRIBUTING.md
f217 README.md docs/CONFIGURATION.md docs/INDEX_RESOURCE_LIMITS.md scripts/test-runtime.sh src/cli/cli.c src/daemon/application.c src/daemon/application.h src/foundation/index_policy.c src/foundation/index_policy.h src/foundation/subprocess.c src/foundation/subprocess.h src/mcp/index_supervisor.c src/mcp/index_supervisor.h src/mcp/mcp.c src/mcp/mcp.h src/mcp/mcp_internal.h src/pipeline/pipeline.c tests/test_daemon_application.c tests/test_index_policy.c tests/test_index_supervisor.c tests/test_mcp.c tests/test_subprocess.c tests/test_worker_session_scope.sh
f218 tests/test_daemon_bootstrap.c
f219 internal/cbm/service_patterns.c tests/test_infrascan.c
f220 Makefile.cbm src/cli/cli.c src/pipeline/pass_doclinks.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h tests/test_doclinks.c tests/test_edge_structural.c tests/test_lang_contract.c tests/test_main.c tests/test_pipeline.c
f221 Makefile.cbm README.md src/cli/cli.c src/pipeline/pass_doclinks.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h tests/test_doclinks.c tests/test_edge_structural.c tests/test_lang_contract.c tests/test_main.c tests/test_pipeline.c
f222 src/daemon/application.c src/mcp/mcp.c src/pipeline/lsp_surface.c src/pipeline/pipeline.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h tests/test_daemon_application.c tests/test_mcp.c tests/test_pipeline.c
f223 README.md docs/CONFIGURATION.md docs/INDEX_RESOURCE_LIMITS.md scripts/test-runtime.sh src/cli/cli.c src/daemon/application.c src/daemon/application.h src/foundation/index_policy.c src/foundation/index_policy.h src/foundation/subprocess.c src/foundation/subprocess.h src/mcp/index_supervisor.c src/mcp/index_supervisor.h src/mcp/mcp.c src/mcp/mcp.h src/mcp/mcp_internal.h src/pipeline/pipeline.c tests/test_daemon_application.c tests/test_daemon_bootstrap.c tests/test_index_policy.c tests/test_index_supervisor.c tests/test_mcp.c tests/test_subprocess.c tests/test_worker_session_scope.sh
f224 internal/cbm/lsp/ts_lsp.c internal/cbm/lsp/ts_lsp.h tests/test_stack_overflow.c
f225 src/foundation/mem_override_win.c
f226 src/foundation/compat.c src/mcp/index_supervisor.c src/mcp/mcp.c tests/test_platform.c
f227 src/pipeline/lsp_surface.c tests/test_pipeline.c
f228 src/daemon/host.c src/daemon/ipc.c src/daemon/ipc.h src/daemon/runtime.c src/daemon/runtime.h src/daemon/version_cohort.c src/daemon/version_cohort.h src/foundation/private_file_lock.c src/foundation/private_file_lock_internal.h tests/test_daemon_ipc.c tests/test_private_file_lock.c tests/test_version_cohort.c
f229 src/foundation/compat.h tests/test_platform.c
f230 README.md docs/CONFIGURATION.md src/cli/cli.c src/cli/cli.h src/cli/hook_augment.c src/daemon/application.c src/git/git_context.c src/git/git_context.h src/mcp/mcp.c src/mcp/mcp_internal.h tests/test_cli.c tests/test_git_context.c tests/test_mcp.c
f231 internal/cbm/extract_defs.c src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_call_reference_contract.c tests/test_registry.c
f232 internal/cbm/extract_calls.c internal/cbm/extract_unified.c internal/cbm/extract_unified.h tests/repro/repro_call_scope_usages.c tests/test_convergence_probe.c
f233 pkg/scoop/codebase-memory-mcp.json
f234 src/mcp/mcp.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/store/store.c src/store/store.h tests/test_mcp.c tests/test_pipeline.c
f235 internal/cbm/extract_defs.c internal/cbm/helpers.c internal/cbm/helpers.h tests/test_extraction.c
f236 src/pipeline/registry.c tests/test_pipeline.c tests/test_registry.c
f237 internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_unified.c internal/cbm/extract_usages.c internal/cbm/helpers.c internal/cbm/helpers.h internal/cbm/result_compact.c src/foundation/constants.h src/foundation/recursion_whitelist.h src/mcp/mcp.c src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_usages.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c src/pipeline/registry.c src/store/store.c src/store/store.h tests/repro/repro_call_argument_matrix_a.c tests/repro/repro_call_node_behaviors.c tests/test_extraction.c tests/test_mcp.c tests/test_pipeline.c tests/test_registry.c tests/test_store_nodes.c
f238 src/foundation/compat_fs.c src/foundation/compat_fs.h src/mcp/mcp.c src/pipeline/pipeline.c tests/test_pipeline.c
f239 src/mcp/mcp.c src/pipeline/pass_cross_repo.c src/pipeline/pass_cross_repo.h tests/test_cli.c tests/test_cross_repo.c
f240 src/pipeline/pass_cross_repo.c tests/test_cross_repo.c
f241 internal/cbm/extract_defs.c internal/cbm/lang_specs.c tests/test_extraction.c
f242 docs/CONFIGURATION.md src/daemon/ipc.c src/daemon/ipc.h tests/test_daemon_ipc.c
f243 docs/CONFIGURATION.md src/daemon/ipc.c src/daemon/ipc.h src/main.c tests/test_daemon_ipc.c
f244 src/cli/config_toml_edit.c src/cli/config_toml_edit.h tests/test_cli.c tests/test_config_toml_edit.c
f245 docs/cbmignore.md src/discover/discover.c tests/test_discover.c
f246 Makefile.cbm internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/sql_values.c internal/cbm/sql_values.h tests/test_extraction.c tests/test_parse_coverage.c tests/test_pipeline.c
f247 THIRD_PARTY.md internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/javascript/parser.c internal/cbm/vendored/grammars/tsx/parser.c internal/cbm/vendored/grammars/tsx/tree_sitter/array.h internal/cbm/vendored/grammars/tsx/tree_sitter/parser.h scripts/audit-license-provenance.py scripts/vendored-checksums.txt tests/test_parse_coverage.c tools/tree-sitter-javascript/grammar.js tools/tree-sitter-javascript/package.json tools/tree-sitter-javascript/patches/jsx-lone-ampersand.patch tools/tree-sitter-javascript/src/grammar.json tools/tree-sitter-javascript/src/node-types.json tools/tree-sitter-javascript/src/parser.c tools/tree-sitter-javascript/src/scanner.c tools/tree-sitter-javascript/src/tree_sitter/alloc.h tools/tree-sitter-javascript/src/tree_sitter/array.h tools/tree-sitter-javascript/src/tree_sitter/parser.h tools/tree-sitter-javascript/test/corpus/jsx_ampersand.txt tools/tree-sitter-javascript/tree-sitter.json tools/tree-sitter-tsx/define-grammar.js tools/tree-sitter-tsx/grammar.js tools/tree-sitter-tsx/javascript-grammar.js tools/tree-sitter-tsx/package.json tools/tree-sitter-tsx/src/_common_scanner.h tools/tree-sitter-tsx/src/grammar.json tools/tree-sitter-tsx/src/node-types.json tools/tree-sitter-tsx/src/parser.c tools/tree-sitter-tsx/src/scanner.c tools/tree-sitter-tsx/src/tree_sitter/alloc.h tools/tree-sitter-tsx/src/tree_sitter/array.h tools/tree-sitter-tsx/src/tree_sitter/parser.h tools/tree-sitter-tsx/test/corpus/jsx_ampersand.txt tools/tree-sitter-tsx/tree-sitter.json
f248 internal/cbm/lsp/c_lsp.c internal/cbm/lsp/ts_lsp.c internal/cbm/lsp/ts_lsp.h tests/test_c_lsp.c tests/test_ts_lsp.c
f249 internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/c_sharp/parser.c internal/cbm/vendored/grammars/c_sharp/scanner.c internal/cbm/vendored/grammars/c_sharp/tree_sitter/alloc.h internal/cbm/vendored/grammars/c_sharp/tree_sitter/array.h internal/cbm/vendored/grammars/c_sharp/tree_sitter/parser.h scripts/vendored-checksums.txt tests/test_parse_coverage.c
f250 internal/cbm/sqlite_writer.c tests/test_sqlite_writer.c
f251 src/mcp/mcp.c src/pipeline/fqn.c src/pipeline/pipeline.h tests/test_mcp.c
f252 internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/result_compact.c src/pipeline/lsp_surface.c src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pipeline.c src/pipeline/pipeline_delta.c src/pipeline/pipeline_internal.h tests/test_extraction.c tests/test_pipeline.c
f253 README.md docs/CONFIGURATION.md src/cli/cli.c src/cli/cli.h src/daemon/host.c src/discover/discover.c src/discover/discover.h src/watcher/watcher.c src/watcher/watcher.h tests/test_cli.c tests/test_watcher.c
f254 src/daemon/runtime.c src/daemon/runtime.h tests/test_daemon_runtime.c
f255 internal/cbm/cbm.c internal/cbm/cbm.h tests/test_parse_coverage.c
f256 scripts/test.sh tests/repro/repro_main.c tests/test_git_env_isolation_contract.sh tests/test_helpers.h tests/test_main.c
f257 src/cli/config_toml_edit.c tests/test_config_toml_edit.c
f258 internal/cbm/lsp/generated/rust_stdlib_data.c src/pipeline/lsp_resolve.h src/pipeline/pass_calls.c src/pipeline/pass_parallel.c tests/test_parallel.c tests/test_pipeline.c
f259 internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/lsp/c_lsp.c internal/cbm/lsp/cs_lsp.c internal/cbm/lsp/go_lsp.c internal/cbm/lsp/java_lsp.c internal/cbm/lsp/kotlin_lsp.c internal/cbm/lsp/php_lsp.c internal/cbm/lsp/py_lsp.c internal/cbm/lsp/rust_lsp.c internal/cbm/lsp/ts_lsp.c tests/test_parse_coverage.c
f260 Makefile.cbm src/foundation/compat_fs.c src/foundation/compat_fs.h src/foundation/git_env.c src/foundation/git_env.h src/foundation/subprocess.c src/foundation/subprocess.h src/git/git_context.c src/mcp/mcp.c src/pipeline/artifact.c src/pipeline/pass_githistory.c src/watcher/watcher.c tests/test_git_context.c tests/test_subprocess.c
f261 Makefile.cbm internal/cbm/callable_sig.c internal/cbm/callable_sig.h internal/cbm/cbm.h internal/cbm/lsp/go_lsp.h internal/cbm/lsp/type_registry.h internal/cbm/result_compact.c scripts/memory-core-baseline.txt src/mcp/mcp.c src/pipeline/lsp_resolve.h src/pipeline/pass_ensemble_routing.c src/pipeline/pass_lsp_cross.c src/pipeline/pass_semantic.c src/pipeline/registry.c src/store/store.c src/store/store.h tests/test_callable_sig.c tests/test_main.c tests/test_store_nodes.c
f262 src/pipeline/pass_cross_repo.c src/store/store.c src/store/store.h tests/test_cross_repo.c
f263 src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline.h src/pipeline/pipeline_internal.h src/pipeline/registry.c tests/test_edge_imports.c tests/test_registry.c
f264 internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/lsp/cs_lsp.c internal/cbm/lsp/type_registry.h internal/cbm/result_compact.c src/pipeline/pass_lsp_cross.c src/pipeline/pipeline.c tests/test_parallel.c
f265 README.md docs/CONFIGURATION.md docs/index.html src/daemon/application.c src/mcp/mcp.c src/mcp/mcp.h src/watcher/watcher.c src/watcher/watcher.h tests/test_daemon_application.c tests/test_watcher.c
f266 src/daemon/host.c src/daemon/host_internal.h src/daemon/ipc.c src/daemon/ipc.h src/daemon/runtime.c src/daemon/runtime.h src/daemon/version_cohort.c src/daemon/version_cohort.h src/foundation/private_file_lock.c src/foundation/private_file_lock_internal.h tests/test_daemon_ipc.c tests/test_daemon_runtime.c tests/test_private_file_lock.c tests/test_version_cohort.c
f267 src/mcp/mcp.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/store/store.c tests/test_pipeline.c
f268 README.md docs/CONFIGURATION.md
f269 internal/cbm/extract_usages.c internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/rescript/scanner.c src/discover/discover.c src/discover/discover.h src/discover/language.c tests/test_complexity.c tests/test_extraction.c tests/test_language.c
f270 internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/result_compact.c scripts/memory-core-baseline.txt src/pipeline/lsp_surface.c src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pipeline.c src/pipeline/pipeline_delta.c src/pipeline/pipeline_internal.h tests/test_extraction.c tests/test_pipeline.c
f271 THIRD_PARTY.md internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/pascal/parser.c internal/cbm/vendored/grammars/pascal/tree_sitter/parser.h scripts/audit-license-provenance.py scripts/vendored-checksums.txt tests/test_parse_coverage.c tools/tree-sitter-pascal/grammar.js tools/tree-sitter-pascal/package.json tools/tree-sitter-pascal/patches/unicode-identifiers.patch tools/tree-sitter-pascal/src/grammar.json tools/tree-sitter-pascal/src/node-types.json tools/tree-sitter-pascal/src/parser.c tools/tree-sitter-pascal/src/tree_sitter/alloc.h tools/tree-sitter-pascal/src/tree_sitter/array.h tools/tree-sitter-pascal/src/tree_sitter/parser.h tools/tree-sitter-pascal/test/corpus/unicode_identifiers.txt tools/tree-sitter-pascal/tree-sitter.json
f272 README.md src/daemon/application.c src/daemon/frontend.c src/daemon/frontend.h src/mcp/mcp.c src/mcp/mcp.h src/ui/http_server.c tests/test_daemon_application.c tests/test_daemon_frontend.c tests/test_mcp.c
f273 internal/cbm/cbm.h src/pipeline/pipeline_internal.h
f274 pkg/scoop/codebase-memory-mcp.json tests/test_version_metadata_contract.sh
f275 README.md scripts/memory-core-baseline.txt src/cli/cli.c src/mcp/mcp.c src/pipeline/artifact.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/store/store.c src/store/store.h tests/test_artifact.c tests/test_mcp.c tests/test_pipeline.c
f276 internal/cbm/extract_imports.c src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline_internal.h tests/test_edge_imports.c tests/test_extraction.c tests/test_pipeline.c
f277 internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/extract_unified.c internal/cbm/helpers.c src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline_internal.h src/pipeline/registry.c tests/test_edge_imports.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
f278 src/foundation/compat.c
f279 internal/cbm/cbm.c src/cli/cli.c src/foundation/compat.h tests/test_extraction.c tests/test_platform.c
f280 src/mcp/mcp.c src/pipeline/pipeline.h src/pipeline/pipeline_internal.h tests/test_mcp.c
f281 internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/extract_unified.c internal/cbm/helpers.c src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline.h src/pipeline/pipeline_internal.h src/pipeline/registry.c tests/test_edge_imports.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
f282 THIRD_PARTY.md internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_usages.c internal/cbm/helpers.c internal/cbm/helpers.h internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/objectscript_routine/objectscript_common.h internal/cbm/vendored/grammars/objectscript_routine/parser.c internal/cbm/vendored/grammars/objectscript_routine/scanner.c internal/cbm/vendored/grammars/objectscript_routine/tree_sitter/array.h internal/cbm/vendored/grammars/objectscript_udl/objectscript_common.h internal/cbm/vendored/grammars/objectscript_udl/parser.c internal/cbm/vendored/grammars/objectscript_udl/scanner.c internal/cbm/vendored/grammars/objectscript_udl/tree_sitter/array.h scripts/vendored-checksums.txt tests/test_extraction.c
f283 internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_unified.c internal/cbm/extract_usages.c internal/cbm/helpers.c internal/cbm/helpers.h tests/test_extraction.c
f284 src/daemon/application.c src/foundation/log.c src/foundation/log.h src/mcp/mcp.c src/ui/http_server.c tests/test_log.c tests/test_mcp.c
f285 internal/cbm/extract_defs.c scripts/memory-core-baseline.txt tests/test_extraction.c
f286 README.md docs/CONFIGURATION.md scripts/test.sh src/main.c tests/test_in_process_mcp.py tests/test_in_process_mcp.sh tests/test_runtime_isolation_contract.sh
f287 internal/cbm/extract_imports.c scripts/memory-core-baseline.txt src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline_internal.h tests/test_edge_imports.c tests/test_extraction.c tests/test_pipeline.c
f288 internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/extract_unified.c internal/cbm/helpers.c scripts/memory-core-baseline.txt src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline_internal.h src/pipeline/registry.c tests/test_edge_imports.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
f289 internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/extract_unified.c internal/cbm/helpers.c scripts/memory-core-baseline.txt src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline.h src/pipeline/pipeline_internal.h src/pipeline/registry.c tests/test_edge_imports.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
f290 src/cli/client_adapter.c src/cli/client_adapter.h tests/test_agent_clients.c
f291 internal/cbm/extract_usages.c internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/rescript/scanner.c scripts/vendored-checksums.txt src/discover/discover.c src/discover/discover.h src/discover/language.c tests/test_complexity.c tests/test_extraction.c tests/test_language.c
f292 Makefile.cbm internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/lsp/php_lsp.c internal/cbm/php_inline_html.c tests/test_extraction.c tests/test_pipeline.c
f293 Makefile.cbm internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/lsp/php_lsp.c internal/cbm/php_inline_html.c tests/test_extraction.c tests/test_php_lsp.c tests/test_pipeline.c
f294 src/daemon/bootstrap.h src/daemon/runtime.c src/daemon/runtime.h src/main.c tests/test_daemon_version.c
f295 scripts/memory-core-baseline.txt src/cli/agent_profiles.c src/cli/agent_profiles.h src/cli/cli.c tests/test_cli.c
f296 src/foundation/mem.c src/foundation/mem.h src/pipeline/pass_parallel.c tests/test_parallel.c
f297 internal/cbm/extract_defs.c internal/cbm/helpers.c internal/cbm/lang_specs.c tests/test_extraction.c
f298 src/mcp/mcp.c
f299 README.md THIRD_PARTY.md docs/index.html docs/llms.txt internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/extract_usages.c internal/cbm/grammar_st.c internal/cbm/lang_specs.c internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/st/LICENSE internal/cbm/vendored/grammars/st/parser.c internal/cbm/vendored/grammars/st/scanner.c internal/cbm/vendored/grammars/st/tree_sitter/alloc.h internal/cbm/vendored/grammars/st/tree_sitter/array.h internal/cbm/vendored/grammars/st/tree_sitter/parser.h pkg/chocolatey/codebase-memory-mcp.nuspec pkg/npm/README.md scripts/new-languages.json scripts/package-release.sh scripts/vendored-checksums.txt server.json src/discover/language.c src/discover/userconfig.c src/mcp/mcp.c tests/repro/repro_call_argument_matrix_b.c tests/repro/repro_call_node_manifest.c tests/repro/repro_language_registry.c tests/test_extraction.c tests/test_grammar_labels.c tests/test_grammar_regression.c tests/test_language.c
f300 README.md scripts/memory-core-baseline.txt src/cli/cli.c src/mcp/mcp.c src/pipeline/artifact.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/store/store.c src/store/store.h tests/test_artifact.c tests/test_mcp.c tests/test_pipeline.c tests/test_store_arch.c
f301 internal/cbm/cbm.h src/mcp/mcp.c src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/store/store.c src/store/store.h tests/test_mcp.c tests/test_pipeline.c
f302 THIRD_PARTY.md internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_usages.c internal/cbm/helpers.c internal/cbm/helpers.h internal/cbm/lang_specs.c internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/objectscript_routine/objectscript_common.h internal/cbm/vendored/grammars/objectscript_routine/parser.c internal/cbm/vendored/grammars/objectscript_routine/scanner.c internal/cbm/vendored/grammars/objectscript_routine/tree_sitter/array.h internal/cbm/vendored/grammars/objectscript_udl/objectscript_common.h internal/cbm/vendored/grammars/objectscript_udl/parser.c internal/cbm/vendored/grammars/objectscript_udl/scanner.c internal/cbm/vendored/grammars/objectscript_udl/tree_sitter/array.h scripts/vendored-checksums.txt tests/repro/repro_call_node_behaviors.c tests/repro/repro_call_node_manifest.c tests/test_extraction.c
f303 internal/cbm/lsp/ts_lsp.c tests/repro/repro_issue514.c tests/test_ts_lsp.c
f304 src/cli/cli.c src/cli/cli.h src/foundation/str_util.c src/foundation/str_util.h src/pipeline/pass_cross_repo.c tests/test_cli.c
f305 internal/cbm/result_spill.c internal/cbm/result_spill.h tests/test_extraction.c tests/test_main.c
f306 internal/cbm/lsp/ts_lsp.c tests/test_pipeline.c tests/test_ts_lsp.c
f307 tests/test_watcher.c
f308 tests/test_daemon_ipc.c
f309 src/daemon/bootstrap.c src/daemon/bootstrap.h tests/repro/repro_main.c tests/test_daemon_bootstrap.c tests/test_helpers.h tests/test_main.c
f310 src/cli/cli.c src/cli/cli.h tests/test_cli.c
f311 internal/cbm/extract_imports.c scripts/memory-core-baseline.txt src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h tests/test_edge_imports.c tests/test_extraction.c tests/test_pipeline.c
f312 src/pipeline/pass_definitions.c tests/test_parallel.c
f313 internal/cbm/extract_usages.c tests/repro/repro_lexical_binding_precision.c tests/test_extraction.c
f314 internal/cbm/lsp/py_lsp.c tests/test_py_lsp.c tests/test_py_lsp_scale.c
f315 src/discover/discover.c src/discover/discover.h src/discover/language.c tests/test_discover.c tests/test_language.c
f316 internal/cbm/lsp/py_lsp.c internal/cbm/lsp/py_lsp.h tests/test_py_lsp.c
f317 internal/cbm/extract_defs.c src/store/store.h tests/test_extraction.c
f318 scripts/memory-core-baseline.txt src/cli/activation_transaction.c src/cli/activation_transaction.h tests/test_activation_transaction.c
f319 internal/cbm/lsp/py_lsp.c src/pipeline/lsp_resolve.h tests/repro/repro_lsp_go_py.c tests/test_edge_structural.c tests/test_lsp_resolution_probe.c tests/test_py_lsp.c
f320 internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/extract_unified.c internal/cbm/helpers.c scripts/memory-core-baseline.txt src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/pipeline/registry.c tests/test_edge_imports.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
f321 internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/extract_unified.c internal/cbm/helpers.c scripts/memory-core-baseline.txt src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/pipeline/registry.c tests/test_edge_imports.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
f322 src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_tests.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/pipeline/registry.c tests/test_pipeline.c tests/test_registry.c
f323 src/pipeline/pass_calls.c src/pipeline/pass_lsp_cross.c src/pipeline/pass_parallel.c src/pipeline/pass_semantic.c src/pipeline/pass_usages.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_pipeline.c
1 # Distinct changed-file sets of the September 2026 PR pushes (git diff
2 # --name-only merge-base..head per push), referenced by 2026-09-pushes.tsv.
3 # Columns (TAB): fileset, paths (space-separated; no path contains a space).
4 f001 README.md
5 f002 internal/cbm/vendored/grammars/swift/scanner.c scripts/vendored-checksums.txt tests/test_extraction.c
6 f003 internal/cbm/lsp/go_lsp.c src/pipeline/pass_lsp_cross.c tests/test_go_lsp.c tests/test_parallel.c
7 f004 src/foundation/mem.c src/foundation/mem.h tests/test_mem.c
8 f005 Makefile.cbm internal/cbm/cbm.c internal/cbm/cbm.h src/mcp/mcp.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pipeline_internal.h src/store/store.c tests/test_index_resilience.c tests/test_mcp.c tests/test_parse_coverage.c tests/test_pipeline.c
9 f006 .github/workflows/pr.yml Makefile.cbm internal/cbm/cbm.c internal/cbm/cbm.h scripts/ci/README.md scripts/ci/coverage-gate-allowlist.txt scripts/ci/parse-partial-baseline.txt scripts/ci/self-index-coverage-gate.sh src/mcp/mcp.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pipeline_internal.h src/store/store.c tests/test_index_resilience.c tests/test_mcp.c tests/test_parse_coverage.c tests/test_pipeline.c tests/test_venue_parity_contract.sh
10 f007 README.md scripts/smoke-test.sh src/cli/cli.c tests/test_cli.c
11 f008 src/ui/http_server.c tests/test_httpd.c
12 f009 src/daemon/application.c src/daemon/application.h src/daemon/frontend.c src/daemon/frontend.h src/daemon/runtime.c src/daemon/runtime.h src/main.c tests/test_daemon_frontend.c tests/test_daemon_runtime.c
13 f010 internal/cbm/lsp/c_lsp.c internal/cbm/lsp/type_registry.c internal/cbm/lsp/type_registry.h tests/test_c_lsp.c
14 f011 src/pipeline/fqn.c src/pipeline/pass_pkgmap.c tests/test_edge_imports.c tests/test_fqn.c
15 f012 internal/cbm/extract_calls.c tests/test_extraction.c tests/test_pipeline.c
16 f013 src/mcp/mcp.c tests/test_mcp.c
17 f014 src/cypher/cypher.c tests/test_cypher.c
18 f015 src/mcp/mcp.c src/store/store.c src/store/store.h src/ui/layout3d.c src/ui/layout3d.h tests/test_store_arch.c tests/test_ui.c
19 f016 Makefile.cbm README.md docs/index.html docs/llms.txt scripts/smoke-invariants.sh src/cli/cli.c src/cli/cli.h src/foundation/platform.c src/foundation/platform.h src/mcp/mcp.c src/personal_memory/memory.c src/personal_memory/memory.h tests/test_cli.c tests/test_mcp.c
20 f017 internal/cbm/extract_defs.c tests/test_extraction.c
21 f018 internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/swift/scanner.c scripts/vendored-checksums.txt tests/test_extraction.c
22 f019 internal/cbm/extract_calls.c internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/swift/scanner.c scripts/vendored-checksums.txt tests/test_extraction.c tests/test_pipeline.c
23 f020 src/daemon/application.c tests/test_daemon_application.c tests/windows/test_non_ascii_path.py
24 f021 internal/cbm/lsp/ts_lsp.c tests/repro/repro_lsp_ts.c
25 f022 src/graph_buffer/graph_buffer.c src/mcp/mcp.c tests/test_graph_buffer.c tests/test_mcp.c
26 f023 internal/cbm/cbm.h internal/cbm/extract_calls.c src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
27 f024 .github/workflows/pr.yml Makefile.cbm internal/cbm/cbm.c internal/cbm/cbm.h scripts/ci/README.md scripts/ci/coverage-gate-allowlist.txt scripts/ci/parse-partial-baseline.txt scripts/ci/self-index-coverage-gate.sh scripts/test.sh src/mcp/mcp.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pipeline_internal.h src/store/store.c tests/test_coverage_gate_contract.sh tests/test_index_resilience.c tests/test_mcp.c tests/test_parse_coverage.c tests/test_pipeline.c tests/test_venue_parity_contract.sh
28 f025 Makefile.cbm README.md docs/CONFIGURATION.md docs/INDEX_RESOURCE_LIMITS.md scripts/test-runtime.sh src/cli/cli.c src/cli/cli.h src/daemon/application.c src/discover/discover.c src/discover/discover.h src/foundation/index_policy.c src/foundation/index_policy.h src/mcp/mcp.c src/mcp/mcp_internal.h src/pipeline/pipeline.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h tests/test_daemon_application.c tests/test_discover.c tests/test_index_policy.c tests/test_main.c tests/test_pipeline.c tests/test_worker_error_response.sh tests/test_worker_watchdog.sh
29 f026 Makefile.cbm src/cli/cli.c src/pipeline/pass_doclinks.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h tests/test_doclinks.c tests/test_edge_structural.c tests/test_lang_contract.c tests/test_main.c
30 f027 src/mcp/mcp.c src/mcp/mcp_internal.h tests/test_mcp.c
31 f028 internal/cbm/extract_defs.c src/discover/language.c src/pipeline/pass_route_nodes.c tests/test_extraction.c tests/test_language.c
32 f029 README.md docs/CONFIGURATION.md scripts/smoke-test.sh src/cli/cli.c src/cli/cli.h src/main.c tests/test_cli.c
33 f030 src/main.c tests/test_parent_watchdog.sh
34 f031 internal/cbm/extract_imports.c tests/test_extraction.c
35 f032 internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_unified.c internal/cbm/helpers.h src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
36 f033 internal/cbm/extract_calls.c src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
37 f034 internal/cbm/cbm.c internal/cbm/preprocessor.cpp internal/cbm/preprocessor.h tests/test_extraction.c
38 f035 internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/extract_unified.c internal/cbm/helpers.c internal/cbm/lsp/type_registry.c src/pipeline/lsp_resolve.h src/pipeline/registry.c tests/test_extraction.c tests/test_pipeline.c
39 f036 internal/cbm/cbm.h internal/cbm/extract_semantic.c internal/cbm/extract_usages.c src/pipeline/pass_parallel.c src/pipeline/pass_usages.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_pipeline.c tests/test_registry.c
40 f037 internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_defs.c src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
41 f038 internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/extract_unified.c internal/cbm/extract_unified.h src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
42 f039 src/cli/cli.c src/ui/http_server.c tests/test_cli.c
43 f040 internal/cbm/extract_channels.c tests/test_extraction.c tests/test_pipeline.c
44 f041 internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_unified.c internal/cbm/helpers.h internal/cbm/lsp/go_lsp.c src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_extraction.c tests/test_parallel.c tests/test_pipeline.c tests/test_registry.c
45 f042 internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_unified.c internal/cbm/helpers.c internal/cbm/helpers.h internal/cbm/lsp/go_lsp.c src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_extraction.c tests/test_parallel.c tests/test_pipeline.c tests/test_registry.c
46 f043 internal/cbm/extract_defs.c tests/test_edge_types_probe.c
47 f044 internal/cbm/extract_calls.c tests/test_extraction.c
48 f045 internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_channels.c internal/cbm/extract_defs.c internal/cbm/extract_env_accesses.c internal/cbm/extract_imports.c internal/cbm/extract_node_stack.h internal/cbm/extract_semantic.c internal/cbm/extract_type_assigns.c internal/cbm/extract_type_refs.c tests/test_extraction.c tests/test_mem.c
49 f046 src/cli/client_adapter.c tests/test_agent_clients.c
50 f047 graph-ui/src/hooks/useProjects.test.tsx graph-ui/src/hooks/useProjects.ts
51 f048 internal/cbm/cbm.c tests/test_parse_coverage.c
52 f049 graph-ui/src/App.test.tsx graph-ui/src/App.tsx src/ui/http_server.c tests/test_httpd.c
53 f050 src/daemon/application.c src/foundation/workspace.c src/foundation/workspace.h src/mcp/mcp.c src/ui/http_server.c tests/test_workspace.c
54 f051 README.md docs/CONFIGURATION.md src/cli/cli.c src/cli/cli.h src/daemon/ipc.c src/foundation/compat_fs.c src/foundation/compat_fs.h src/main.c tests/test_daemon_ipc.c
55 f052 src/traces/traces.c src/traces/traces.h tests/test_traces.c
56 f053 src/pipeline/registry.c tests/test_pipeline.c
57 f054 src/cli/cli.h src/cli/hook_augment.c src/foundation/platform.c src/foundation/platform.h src/mcp/index_supervisor.c src/mcp/mcp.c src/mcp/mcp.h tests/test_cli.c tests/test_platform.c
58 f055 tests/test_cli.c
59 f056 src/daemon/application.c src/mcp/mcp.c src/pipeline/pipeline.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c tests/test_daemon_application.c tests/test_mcp.c tests/test_pipeline.c
60 f057 internal/cbm/extract_defs.c src/discover/language.c tests/test_extraction.c tests/test_language.c
61 f058 src/cli/hook_augment.c src/daemon/runtime.c src/daemon/runtime.h src/foundation/sha256.c src/foundation/sha256.h src/main.c tests/test_cli.c tests/test_daemon_runtime.c
62 f059 tests/windows/test_cli_non_ascii_arg.py tests/windows/test_daemon_stability.py tests/windows/test_hook_augment.py
63 f060 src/cli/cli.c
64 f061 .github/workflows/_test.yml
65 f062 scripts/smoke-test.sh
66 f063 src/foundation/subprocess.c src/foundation/subprocess.h src/mcp/index_supervisor.c tests/test_main.c tests/test_subprocess.c
67 f064 graph-ui/src/components/EdgeLines.test.ts graph-ui/src/components/EdgeLines.tsx graph-ui/src/components/GraphTab.edgeSampling.test.tsx graph-ui/src/components/GraphTab.test.ts graph-ui/src/components/GraphTab.tsx graph-ui/src/components/NodeCloud.test.ts graph-ui/src/components/NodeCloud.tsx graph-ui/src/components/NodeDetailPanel.test.tsx graph-ui/src/components/NodeDetailPanel.tsx graph-ui/src/components/NodeLabels.tsx graph-ui/src/hooks/useGraphData.test.ts graph-ui/src/hooks/useGraphData.ts graph-ui/src/lib/edgeBudget.test.ts graph-ui/src/lib/edgeBudget.ts graph-ui/src/lib/graphIndex.test.ts graph-ui/src/lib/graphIndex.ts graph-ui/src/lib/topK.test.ts graph-ui/src/lib/topK.ts src/store/store.c src/store/store.h src/ui/http_server.c src/ui/layout3d.c src/ui/layout3d.h src/ui/layout3d_internal.h tests/test_httpd.c tests/test_store_edges.c tests/test_store_search.c tests/test_ui.c
68 f065 graph-ui/package-lock.json
69 f066 src/foundation/lock_registry.c src/foundation/lock_registry_internal.h tests/test_lock_registry.c
70 f067 src/daemon/version_cohort.c src/daemon/version_cohort.h tests/test_version_cohort.c
71 f068 internal/cbm/extract_defs.c tests/test_extraction_inheritance.c
72 f069 internal/cbm/extract_channels.c tests/test_extraction.c
73 f070 test-infrastructure/Dockerfile.msan
74 f071 src/git/git_context.c src/ui/http_server.c tests/test_daemon_application.c tests/test_git_context.c tests/test_httpd.c
75 f072 scripts/hooks/pre-commit
76 f073 tests/test_daemon_runtime.c
77 f074 src/cli/agent_profiles.c src/cli/client_adapter.c tests/test_agent_clients.c tests/test_agent_profiles.c
78 f075 internal/cbm/sqlite_writer.c src/graph_buffer/graph_buffer.c tests/test_graph_buffer.c
79 f076 scripts/run-test-wave.py tests/test_parallel_harness_contract.sh
80 f077 internal/cbm/extract_imports.c internal/cbm/lang_specs.c tests/test_edge_structural.c tests/test_extraction.c tests/test_grammar_labels.c
81 f078 internal/cbm/sqlite_writer.c src/graph_buffer/graph_buffer.c tests/test_graph_buffer.c tests/test_sqlite_writer.c
82 f079 src/foundation/dump_verify.c src/foundation/dump_verify.h tests/test_dump_verify.c
83 f080 Makefile.cbm scripts/ci/check-binary-composition.sh tests/test_mcpb_bundle_contract.sh tests/test_release_candidate_derivation_contract.sh
84 f081 scripts/smoke-test.sh src/cli/cli.c tests/test_cli.c tests/windows/test_hook_augment.py
85 f082 src/main.c tests/windows/test_daemon_lifecycle.py
86 f083 README.md docs/CONFIGURATION.md graph-ui/src/components/NodeDetailPanel.test.tsx graph-ui/src/components/NodeDetailPanel.tsx graph-ui/src/components/StatsTab.test.tsx graph-ui/src/hooks/useProjects.test.tsx graph-ui/src/hooks/useProjects.ts scripts/smoke-test.sh src/cli/cli.c src/cli/cli.h src/cli/client_adapter.c src/cli/hook_augment.c src/cli/progress_sink.c src/cli/progress_sink.h src/cypher/cypher.c src/cypher/cypher.h src/foundation/compat_fs.c src/foundation/compat_fs.h src/foundation/log.c src/foundation/log.h src/main.c src/mcp/compact_out.c src/mcp/compact_out.h src/mcp/mcp.c src/mcp/mcp.h src/mcp/mcp_internal.h src/pipeline/pipeline.c src/store/store.c src/store/store.h src/ui/http_server.c tests/scale_contract.sh tests/test_cli.c tests/test_cypher.c tests/test_daemon_runtime.c tests/test_diagnostics.c tests/test_httpd.c tests/test_incremental.c tests/test_index_resilience.c tests/test_index_supervisor.c tests/test_log.c tests/test_main.c tests/test_mcp.c tests/test_pipeline.c tests/test_store_pragmas.c tests/windows/test_daemon_lifecycle.py tests/windows/test_daemon_stability.py
87 f084 README.md docs/CONFIGURATION.md graph-ui/src/components/NodeDetailPanel.test.tsx graph-ui/src/components/NodeDetailPanel.tsx graph-ui/src/components/StatsTab.test.tsx graph-ui/src/hooks/useProjects.test.tsx graph-ui/src/hooks/useProjects.ts scripts/smoke-test.sh src/cli/cli.c src/cli/cli.h src/cli/client_adapter.c src/cli/hook_augment.c src/cli/progress_sink.c src/cli/progress_sink.h src/cypher/cypher.c src/cypher/cypher.h src/foundation/compat_fs.c src/foundation/compat_fs.h src/foundation/log.c src/foundation/log.h src/main.c src/mcp/compact_out.c src/mcp/compact_out.h src/mcp/mcp.c src/mcp/mcp.h src/mcp/mcp_internal.h src/pipeline/pipeline.c src/store/store.c src/store/store.h src/ui/http_server.c tests/scale_contract.sh tests/test_cli.c tests/test_cypher.c tests/test_daemon_runtime.c tests/test_diagnostics.c tests/test_httpd.c tests/test_incremental.c tests/test_index_resilience.c tests/test_index_supervisor.c tests/test_log.c tests/test_main.c tests/test_mcp.c tests/test_pipeline.c tests/test_store_pragmas.c tests/windows/test_daemon_lifecycle.py tests/windows/test_daemon_stability.py tests/windows/test_hook_augment.py
88 f085 src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_pipeline.c tests/test_registry.c
89 f086 src/cli/config_yaml_edit.c tests/test_config_yaml_edit.c
90 f087 src/foundation/compat_fs.c tests/test_cli.c tests/test_platform.c
91 f088 .github/workflows/release.yml
92 f089 .github/workflows/pages.yml
93 f090 src/store/store.c tests/test_store_nodes.c
94 f091 src/discover/discover.c tests/test_discover.c
95 f092 Makefile.cbm internal/cbm/extract_defs.c internal/cbm/helpers.c internal/cbm/helpers.h tests/repro/repro_issue1692.c tests/repro/repro_main.c
96 f093 src/mcp/mcp.c src/store/store.c tests/test_mcp.c tests/test_store_nodes.c
97 f094 tests/test_mcp.c
98 f095 src/mcp/mcp.c src/store/store.c src/store/store.h tests/test_mcp.c
99 f096 scripts/msan.sh scripts/test.sh tests/test_main.c
100 f097 src/daemon/ipc.c src/daemon/ipc.h tests/test_cli.c tests/test_daemon_ipc.c
101 f098 src/daemon/application.c src/mcp/mcp.c tests/test_daemon.c
102 f099 Makefile.cbm README.md graph-ui/index.html graph-ui/src/App.tsx graph-ui/src/components/ChangesTab.tsx graph-ui/src/components/CommandK.tsx graph-ui/src/components/ControlTab.tsx graph-ui/src/components/DashboardTab.tsx graph-ui/src/components/DisplaySettingsMenu.tsx graph-ui/src/components/ErrorBoundary.tsx graph-ui/src/components/FilterPanel.tsx graph-ui/src/components/FlowsTab.observed.test.tsx graph-ui/src/components/FlowsTab.tsx graph-ui/src/components/FlowsTab.zh.test.tsx graph-ui/src/components/GraphLoader.tsx graph-ui/src/components/GraphScene.tsx graph-ui/src/components/GraphTab.deadcode.test.tsx graph-ui/src/components/GraphTab.filters.test.tsx graph-ui/src/components/GraphTab.regions.test.tsx graph-ui/src/components/GraphTab.selection.test.tsx graph-ui/src/components/GraphTab.tsx graph-ui/src/components/HaloLayer.tsx graph-ui/src/components/MetricChip.tsx graph-ui/src/components/Minimap.tsx graph-ui/src/components/MissedCallout.tsx graph-ui/src/components/ModulesTab.tsx graph-ui/src/components/NodeDetailPanel.showmore.test.tsx graph-ui/src/components/NodeDetailPanel.tsx graph-ui/src/components/NodeTooltip.tsx graph-ui/src/components/OverviewTab.tsx graph-ui/src/components/ProjectCard.tsx graph-ui/src/components/ProjectSwitcher.tsx graph-ui/src/components/PromptBasket.tsx graph-ui/src/components/RegionPanel.tsx graph-ui/src/components/RegionTooltip.tsx graph-ui/src/components/Sidebar.tsx graph-ui/src/components/StatsTab.tsx graph-ui/src/components/SymbolTab.tsx graph-ui/src/components/TriggerTree.tsx graph-ui/src/components/WikiHoverCard.test.tsx graph-ui/src/components/WikiHoverCard.tsx graph-ui/src/components/WikiPanel.test.tsx graph-ui/src/components/WikiPanel.tsx graph-ui/src/components/WikiPanel.zh.test.tsx graph-ui/src/components/wikiTier.ts graph-ui/src/hooks/useGraphData.ts graph-ui/src/hooks/useProjects.test.tsx graph-ui/src/hooks/useProjects.ts graph-ui/src/lib/atlas.test.ts graph-ui/src/lib/atlas.ts graph-ui/src/lib/complexity.test.ts graph-ui/src/lib/complexity.ts graph-ui/src/lib/composer.ts graph-ui/src/lib/density.test.ts graph-ui/src/lib/density.ts graph-ui/src/lib/findings.test.ts graph-ui/src/lib/findings.ts graph-ui/src/lib/firstread.test.ts graph-ui/src/lib/firstread.ts graph-ui/src/lib/flowgroup.test.ts graph-ui/src/lib/flowgroup.ts graph-ui/src/lib/i18n.ts graph-ui/src/lib/impact.test.ts graph-ui/src/lib/impact.ts graph-ui/src/lib/observed.test.ts graph-ui/src/lib/observed.ts graph-ui/src/lib/questions.test.ts graph-ui/src/lib/questions.ts graph-ui/src/lib/rationale.test.ts graph-ui/src/lib/rationale.ts graph-ui/src/lib/regions.test.ts graph-ui/src/lib/regions.ts graph-ui/src/lib/treemap.ts graph-ui/src/lib/types.ts graph-ui/src/lib/who.test.ts graph-ui/src/lib/who.ts graph-ui/src/lib/why.test.ts graph-ui/src/lib/why.ts graph-ui/src/lib/whyfetch.ts graph-ui/src/styles/globals.css graph-ui/src/test/setup.ts graph-ui/src/wiki/entries.test.ts graph-ui/src/wiki/entries.ts graph-ui/tsconfig.tsbuildinfo graph-ui/vite.config.ts scripts/run-test-wave.py scripts/security-ui.sh src/main.c src/mcp/mcp.c src/pipeline/pipeline.h src/pipeline/pipeline_internal.h src/store/store.c src/store/store.h src/ui/atlas.h src/ui/atlas_api.c src/ui/atlas_flows.c src/ui/atlas_handout.c src/ui/atlas_metrics.c src/ui/atlas_why.c src/ui/http_server.c src/ui/layout3d.c src/ui/layout3d.h src/ui/layout_internal.h src/ui/layout_regions.c src/watcher/watcher.c src/watcher/watcher.h test-infrastructure/docker-compose.yml test-infrastructure/ladder.sh test-infrastructure/run.sh tests/test_cypher.c tests/test_daemon_runtime.c tests/test_httpd.c tests/test_incremental.c tests/test_mcp.c tests/test_py_lsp_scale.c tests/test_ui.c tests/test_watcher.c
103 f100 README.md docs/CONFIGURATION.md scripts/test_mcp_in_process.py src/main.c
104 f101 tests/test_pipeline.c
105 f102 src/daemon/application.c src/daemon/application_internal.h tests/test_daemon.c tests/test_daemon_application.c
106 f103 src/discover/discover.c src/discover/discover.h src/discover/language.c tests/test_language.c
107 f104 src/watcher/watcher.c src/watcher/watcher.h tests/test_watcher.c
108 f105 scripts/test.sh src/main.c tests/test_worker_session_scope.sh
109 f106 src/pipeline/pass_complexity.c tests/fixtures/complexity_pass_cycle_order/p00_a.py tests/fixtures/complexity_pass_cycle_order/p00_b.py tests/fixtures/complexity_pass_cycle_order/p01_a.py tests/fixtures/complexity_pass_cycle_order/p01_b.py tests/fixtures/complexity_pass_cycle_order/p02_a.py tests/fixtures/complexity_pass_cycle_order/p02_b.py tests/fixtures/complexity_pass_cycle_order/p03_a.py tests/fixtures/complexity_pass_cycle_order/p03_b.py tests/fixtures/complexity_pass_cycle_order/p04_a.py tests/fixtures/complexity_pass_cycle_order/p04_b.py tests/fixtures/complexity_pass_cycle_order/p05_a.py tests/fixtures/complexity_pass_cycle_order/p05_b.py tests/fixtures/complexity_pass_cycle_order/p06_a.py tests/fixtures/complexity_pass_cycle_order/p06_b.py tests/fixtures/complexity_pass_cycle_order/p07_a.py tests/fixtures/complexity_pass_cycle_order/p07_b.py tests/fixtures/complexity_pass_cycle_order/p08_a.py tests/fixtures/complexity_pass_cycle_order/p08_b.py tests/fixtures/complexity_pass_cycle_order/p09_a.py tests/fixtures/complexity_pass_cycle_order/p09_b.py tests/fixtures/complexity_pass_cycle_order/p10_a.py tests/fixtures/complexity_pass_cycle_order/p10_b.py tests/fixtures/complexity_pass_cycle_order/p11_a.py tests/fixtures/complexity_pass_cycle_order/p11_b.py tests/fixtures/complexity_pass_cycle_order/p12_a.py tests/fixtures/complexity_pass_cycle_order/p12_b.py tests/fixtures/complexity_pass_cycle_order/p13_a.py tests/fixtures/complexity_pass_cycle_order/p13_b.py tests/fixtures/complexity_pass_cycle_order/p14_a.py tests/fixtures/complexity_pass_cycle_order/p14_b.py tests/fixtures/complexity_pass_cycle_order/p15_a.py tests/fixtures/complexity_pass_cycle_order/p15_b.py tests/fixtures/complexity_pass_cycle_order/p16_a.py tests/fixtures/complexity_pass_cycle_order/p16_b.py tests/fixtures/complexity_pass_cycle_order/p17_a.py tests/fixtures/complexity_pass_cycle_order/p17_b.py tests/fixtures/complexity_pass_cycle_order/p18_a.py tests/fixtures/complexity_pass_cycle_order/p18_b.py tests/fixtures/complexity_pass_cycle_order/p19_a.py tests/fixtures/complexity_pass_cycle_order/p19_b.py tests/fixtures/complexity_pass_cycle_order/p20_a.py tests/fixtures/complexity_pass_cycle_order/p20_b.py tests/fixtures/complexity_pass_cycle_order/p21_a.py tests/fixtures/complexity_pass_cycle_order/p21_b.py tests/fixtures/complexity_pass_cycle_order/p22_a.py tests/fixtures/complexity_pass_cycle_order/p22_b.py tests/fixtures/complexity_pass_cycle_order/p23_a.py tests/fixtures/complexity_pass_cycle_order/p23_b.py tests/fixtures/complexity_pass_cycle_order/t00_x.py tests/fixtures/complexity_pass_cycle_order/t00_y.py tests/fixtures/complexity_pass_cycle_order/t00_z.py tests/fixtures/complexity_pass_cycle_order/t01_x.py tests/fixtures/complexity_pass_cycle_order/t01_y.py tests/fixtures/complexity_pass_cycle_order/t01_z.py tests/fixtures/complexity_pass_cycle_order/t02_x.py tests/fixtures/complexity_pass_cycle_order/t02_y.py tests/fixtures/complexity_pass_cycle_order/t02_z.py tests/fixtures/complexity_pass_cycle_order/t03_x.py tests/fixtures/complexity_pass_cycle_order/t03_y.py tests/fixtures/complexity_pass_cycle_order/t03_z.py tests/fixtures/complexity_pass_cycle_order/t04_x.py tests/fixtures/complexity_pass_cycle_order/t04_y.py tests/fixtures/complexity_pass_cycle_order/t04_z.py tests/fixtures/complexity_pass_cycle_order/t05_x.py tests/fixtures/complexity_pass_cycle_order/t05_y.py tests/fixtures/complexity_pass_cycle_order/t05_z.py tests/test_pipeline.c
110 f107 docs/benchmarks/bounded-two-pass-results.json docs/benchmarks/bounded-two-pass.md scripts/benchmark-streaming.py src/pipeline/lsp_surface.c src/pipeline/lsp_surface.h src/pipeline/pass_parallel.c src/pipeline/pipeline.c src/pipeline/pipeline_internal.h tests/test_pipeline.c
111 f108 src/pipeline/pass_semantic_edges.c tests/test_pipeline.c
112 f109 .github/workflows/pr.yml internal/cbm/cbm.c scripts/ci/README.md scripts/ci/coverage-gate-allowlist.txt scripts/ci/parse-partial-baseline.txt scripts/ci/self-index-coverage-gate.sh scripts/test.sh tests/test_coverage_gate_contract.sh tests/test_parse_coverage.c tests/test_venue_parity_contract.sh
113 f110 Makefile.cbm README.md docs/CONFIGURATION.md docs/INDEX_RESOURCE_LIMITS.md scripts/test-runtime.sh src/cli/cli.c src/cli/cli.h src/daemon/application.c src/discover/discover.c src/discover/discover.h src/foundation/index_policy.c src/foundation/index_policy.h src/foundation/subprocess.c src/foundation/subprocess.h src/mcp/index_supervisor.c src/mcp/index_supervisor.h src/mcp/mcp.c src/mcp/mcp.h src/mcp/mcp_internal.h src/pipeline/pipeline.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h tests/test_daemon_application.c tests/test_discover.c tests/test_index_policy.c tests/test_index_supervisor.c tests/test_main.c tests/test_mcp.c tests/test_pipeline.c tests/test_subprocess.c tests/test_worker_error_response.sh tests/test_worker_watchdog.sh
114 f111 Makefile.cbm README.md docs/CONFIGURATION.md docs/INDEX_RESOURCE_LIMITS.md scripts/test-runtime.sh src/cli/cli.c src/cli/cli.h src/daemon/application.c src/daemon/bootstrap.c src/discover/discover.c src/discover/discover.h src/foundation/compat_fs.c src/foundation/compat_fs.h src/foundation/index_policy.c src/foundation/index_policy.h src/foundation/subprocess.c src/foundation/subprocess.h src/main.c src/mcp/index_supervisor.c src/mcp/index_supervisor.h src/mcp/mcp.c src/mcp/mcp.h src/mcp/mcp_internal.h src/pipeline/pipeline.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h tests/test_daemon_application.c tests/test_discover.c tests/test_index_policy.c tests/test_index_supervisor.c tests/test_main.c tests/test_mcp.c tests/test_pipeline.c tests/test_platform.c tests/test_subprocess.c tests/test_worker_error_response.sh tests/test_worker_watchdog.sh
115 f112 Makefile.cbm README.md docs/CONFIGURATION.md docs/INDEX_RESOURCE_LIMITS.md scripts/test-runtime.sh src/cli/cli.c src/cli/cli.h src/daemon/application.c src/daemon/bootstrap.c src/discover/discover.c src/discover/discover.h src/foundation/compat_fs.c src/foundation/compat_fs.h src/foundation/index_policy.c src/foundation/index_policy.h src/foundation/subprocess.c src/foundation/subprocess.h src/git/git_context.c src/git/git_context.h src/main.c src/mcp/index_supervisor.c src/mcp/index_supervisor.h src/mcp/mcp.c src/mcp/mcp.h src/mcp/mcp_internal.h src/pipeline/pipeline.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h tests/test_daemon_application.c tests/test_discover.c tests/test_git_context.c tests/test_index_policy.c tests/test_index_supervisor.c tests/test_main.c tests/test_mcp.c tests/test_pipeline.c tests/test_platform.c tests/test_subprocess.c tests/test_worker_error_response.sh tests/test_worker_watchdog.sh
116 f113 src/pipeline/pipeline_incremental.c tests/test_pipeline.c
117 f114 README.md THIRD_PARTY.md docs/index.html docs/llms.txt internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/grammar_vba.c internal/cbm/helpers.c internal/cbm/lang_specs.c internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/vba/LICENSE internal/cbm/vendored/grammars/vba/parser.c internal/cbm/vendored/grammars/vba/tree_sitter/alloc.h internal/cbm/vendored/grammars/vba/tree_sitter/array.h internal/cbm/vendored/grammars/vba/tree_sitter/parser.h pkg/chocolatey/codebase-memory-mcp.nuspec pkg/npm/README.md scripts/new-languages.json scripts/package-release.sh scripts/vendored-checksums.txt server.json src/discover/discover.h src/discover/language.c src/discover/userconfig.c tests/repro/repro_call_argument_matrix_b.c tests/repro/repro_call_node_manifest.c tests/repro/repro_invariant_breadth.c tests/repro/repro_language_registry.c tests/test_extraction.c tests/test_grammar_labels.c tests/test_grammar_regression.c tests/test_lang_contract.c tests/test_language.c
118 f115 README.md THIRD_PARTY.md docs/index.html docs/llms.txt internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/extract_unified.c internal/cbm/extract_unified.h internal/cbm/grammar_vba.c internal/cbm/helpers.c internal/cbm/helpers.h internal/cbm/lang_specs.c internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/vba/LICENSE internal/cbm/vendored/grammars/vba/parser.c internal/cbm/vendored/grammars/vba/tree_sitter/alloc.h internal/cbm/vendored/grammars/vba/tree_sitter/array.h internal/cbm/vendored/grammars/vba/tree_sitter/parser.h pkg/chocolatey/codebase-memory-mcp.nuspec pkg/npm/README.md scripts/new-languages.json scripts/package-release.sh scripts/vendored-checksums.txt server.json src/discover/discover.h src/discover/language.c src/discover/userconfig.c tests/repro/repro_call_argument_matrix_b.c tests/repro/repro_call_node_manifest.c tests/repro/repro_invariant_breadth.c tests/repro/repro_language_registry.c tests/test_extraction.c tests/test_grammar_labels.c tests/test_grammar_regression.c tests/test_lang_contract.c tests/test_language.c
119 f116 internal/cbm/lang_specs.c
120 f117 internal/cbm/extract_node_stack.h internal/cbm/lsp/ts_lsp.c tests/test_ts_lsp.c
121 f118 .cbmignore src/cli/cli.c src/cli/cli.h src/main.c src/mcp/mcp.c
122 f119 .cbmignore src/cli/cli.c src/cli/cli.h src/main.c src/mcp/mcp.c tests/test_cli.c
123 f120 src/git/git_context.c src/git/git_context.h src/mcp/mcp.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/store/store.c src/store/store.h tests/test_git_context.c tests/test_mcp.c tests/test_store_nodes.c
124 f121 src/watcher/watcher.c tests/test_watcher.c
125 f122 scripts/run-tests-parallel.sh src/git/git_context.c src/git/git_context.h src/mcp/mcp.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/store/store.c src/store/store.h tests/test_git_context.c tests/test_mcp.c tests/test_store_nodes.c
126 f123 scripts/test-windows.ps1 tests/windows/test_mcp_stdio.py
127 f124 internal/cbm/extract_defs.c tests/test_grammar_labels.c tests/test_grammar_regression.c
128 f125 src/cli/cli.c tests/test_cli.c
129 f126 src/cli/cli.c src/cli/config_json_like.c src/cli/hook_augment.c src/daemon/application.c src/foundation/compat_fs.c src/foundation/compat_fs.h src/main.c src/mcp/index_supervisor.c src/mcp/mcp.c src/pipeline/artifact.c src/pipeline/pipeline.c src/ui/config.c tests/test_artifact.c tests/test_cli.c tests/test_platform.c
130 f127 tests/test_daemon_frontend.c
131 f128 src/daemon/ipc.c src/daemon/ipc_internal.h tests/test_daemon_ipc.c
132 f129 .github/dependabot.yml .github/workflows/_build.yml .github/workflows/_lint.yml .github/workflows/_test.yml .github/workflows/release.yml .node-version CONTRIBUTING.md scripts/ci/lint-workflows.sh scripts/ci/test-frontend.sh tests/test_venue_parity_contract.sh
133 f130 src/foundation/compat_fs.c src/foundation/compat_fs.h src/pipeline/pipeline.c src/pipeline/pipeline_internal.h tests/test_pipeline.c
134 f131 src/daemon/application.c src/mcp/mcp.c src/pipeline/pass_parallel.c src/pipeline/pipeline.h src/pipeline/pipeline_internal.h tests/test_daemon_application.c tests/test_incremental.c tests/test_mcp.c tests/test_pipeline.c
135 f132 src/daemon/bootstrap.c src/daemon/bootstrap.h src/daemon/host.c src/daemon/ipc.c src/daemon/ipc.h src/daemon/ipc_internal.h src/foundation/platform.c src/foundation/platform.h tests/test_daemon_bootstrap.c tests/test_daemon_ipc.c
136 f133 Makefile.cbm src/cli/cli.c src/cli/config_edit_path.c src/cli/config_edit_path.h src/cli/config_json_like.c src/cli/config_text_edit.c src/cli/config_toml_edit.c src/cli/config_yaml_edit.c tests/test_cli.c tests/test_config_json_like.c tests/test_config_text_edit.c tests/test_config_toml_edit.c tests/test_config_yaml_edit.c
137 f134 src/cli/cli.c src/mcp/index_supervisor.c src/mcp/index_supervisor.h tests/test_cli.c
138 f135 src/cli/cli.c src/cli/config_json_like.c src/cli/config_json_like.h src/mcp/mcp.c tests/test_cli.c tests/test_config_json_like.c tests/test_mcp.c
139 f136 src/cli/cli.c tests/test_cli.c tests/test_smoke_fixture_contract.sh
140 f137 .github/workflows/_lint.yml .github/workflows/_test.yml
141 f138 src/daemon/ipc.c src/daemon/ipc.h tests/test_daemon_ipc.c
142 f139 src/cli/activation_transaction.c src/daemon/ipc.c src/daemon/ipc.h tests/test_daemon_ipc.c
143 f140 src/cli/cli.h src/main.c tests/test_cli.c
144 f141 internal/cbm/extract_defs.c tests/test_edge_types_probe.c tests/test_extraction.c
145 f142 internal/cbm/lsp/php_lsp.c
146 f143 internal/cbm/lsp/php_lsp.c src/pipeline/registry.c
147 f144 src/cli/cli.c src/cli/cli.h src/main.c src/mcp/mcp.c tests/test_cli.c
148 f145 README.md docs/UPSTREAM-SYNC.md docs/lsp-uplift/PERL-CROSS-FILE-INHERITANCE.md docs/lsp-uplift/PERL-DUCK-TYPING-AND-CROSS-LANG-AUDIT.md docs/lsp-uplift/PLAN.md docs/lsp-uplift/RESOLVER-PLAYBOOK.md docs/lsp-uplift/SOP.md internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/extract_unified.c internal/cbm/helpers.c internal/cbm/lang_specs.c internal/cbm/lsp/generated/perl_stdlib_data.c internal/cbm/lsp/generated/rust_crates_seed.c internal/cbm/lsp/go_lsp.c internal/cbm/lsp/go_lsp.h internal/cbm/lsp/go_stdlib_modern.c internal/cbm/lsp/java_lsp.c internal/cbm/lsp/perl_lsp.c internal/cbm/lsp/perl_lsp.h internal/cbm/lsp/py_builtins.c internal/cbm/lsp/py_lsp.c internal/cbm/lsp/py_stdlib_compat.c internal/cbm/lsp/rust_cargo.c internal/cbm/lsp/rust_cargo.h internal/cbm/lsp/rust_lsp.c internal/cbm/lsp/rust_lsp.h internal/cbm/lsp/type_registry.h internal/cbm/lsp_all.c internal/cbm/service_patterns.c internal/cbm/service_patterns.h src/discover/language.c src/pipeline/lsp_surface.c src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_lsp_cross.c src/pipeline/pass_lsp_cross.h src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pass_route_nodes.c src/pipeline/pass_semantic.c src/pipeline/pass_tests.c src/pipeline/pipeline.c src/pipeline/registry.c tests/test_extraction.c tests/test_go_lsp.c tests/test_java_lsp.c tests/test_language.c tests/test_perl_lsp.c tests/test_pipeline.c tests/test_py_lsp.c tests/test_registry.c tests/test_route_canon.c tests/test_rust_lsp.c
149 f146 src/foundation/subprocess.c src/foundation/subprocess.h src/mcp/index_supervisor.c src/mcp/index_supervisor.h tests/test_index_supervisor.c tests/test_main.c tests/test_subprocess.c
150 f147 src/daemon/daemon.c src/daemon/daemon.h src/daemon/host.c src/daemon/runtime.c src/daemon/runtime.h tests/test_daemon_runtime.c
151 f148 internal/cbm/cbm.c src/foundation/compat.c src/foundation/compat.h tests/test_edge_types_probe.c
152 f149 scripts/test-windows.ps1 tests/windows/mcp_stdio.py tests/windows/test_mcp_stdio.py
153 f150 .github/workflows/codeql.yml
154 f151 .github/workflows/scorecard.yml
155 f152 src/cli/cli.c src/foundation/sha256.c src/foundation/sha256.h src/mcp/index_supervisor.c src/mcp/index_supervisor.h tests/test_cli.c
156 f153 install.ps1 scripts/test.sh src/cli/activation_transaction.c src/cli/cli.c tests/test_activation_diagnostic_contract.sh tests/test_windows_bundle_contract.sh
157 f154 scripts/run-test-wave.py src/cli/cli.c src/foundation/sha256.c src/foundation/sha256.h src/mcp/index_supervisor.c src/mcp/index_supervisor.h tests/test_cli.c
158 f155 .github/workflows/_security.yml graph-ui/package-lock.json graph-ui/package.json scripts/test.sh tests/test_security_gate_fail_closed.sh
159 f156 graph-ui/package-lock.json graph-ui/package.json
160 f157 .github/workflows/_security.yml .github/workflows/pr.yml .github/workflows/release.yml graph-ui/package-lock.json graph-ui/package.json scripts/test.sh tests/test_security_gate_fail_closed.sh
161 f158 tests/test_fqn.c
162 f159 .github/workflows/dry-run.yml tests/test_security_gate_fail_closed.sh
163 f160 tests/test_daemon_ipc.c tests/test_main.c
164 f161 src/daemon/ipc.c tests/test_daemon_ipc.c tests/test_main.c
165 f162 .github/workflows/_test.yml src/daemon/ipc.c src/daemon/ipc.h tests/test_daemon_ipc.c tests/test_main.c
166 f163 scripts/soak-test.sh scripts/test.sh tests/test_soak_runtime_isolation_contract.sh
167 f164 scripts/memlab.sh scripts/test.sh tests/test_memlab_runtime_isolation_contract.sh
168 f165 scripts/benchmark-index.sh scripts/benchmark-search-graph.sh scripts/test.sh tests/test_benchmark_runtime_isolation_contract.sh
169 f166 scripts/smoke-test.sh scripts/test.sh tests/test_smoke_runtime_isolation_contract.sh
170 f167 Makefile.cbm scripts/lint-memory-core.py scripts/memory-core-baseline.txt src/foundation/mem.c src/foundation/mem.h src/foundation/mem_core.c src/foundation/mem_core.h src/foundation/platform.h src/foundation/system_info.c src/pipeline/pipeline.c tests/test_mem.c
171 f168 src/cli/activation_transaction.c
172 f169 Makefile.cbm internal/cbm/arena.h internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/extract_unified.c internal/cbm/lsp/c_lsp.c internal/cbm/lsp/c_lsp.h internal/cbm/lsp/cs_lsp.c internal/cbm/lsp/go_lsp.c internal/cbm/lsp/py_lsp.c internal/cbm/lsp/py_lsp.h internal/cbm/lsp/type_registry.c internal/cbm/lsp/type_registry.h internal/cbm/result_compact.c internal/cbm/result_spill.c internal/cbm/result_spill.h scripts/lint-memory-core.py scripts/memory-core-baseline.txt src/foundation/arena.c src/foundation/arena.h src/foundation/dyn_array.h src/foundation/hash_table.c src/foundation/hash_table.h src/foundation/mem.c src/foundation/mem.h src/foundation/mem_core.c src/foundation/mem_core.h src/foundation/platform.h src/foundation/slab_alloc.c src/foundation/system_info.c src/graph_buffer/graph_buffer.c src/graph_buffer/graph_buffer.h src/mcp/mcp.c src/pipeline/lsp_surface.c src/pipeline/lsp_surface.h src/pipeline/pass_complexity.c src/pipeline/pass_importance.c src/pipeline/pass_lsp_cross.c src/pipeline/pass_lsp_cross.h src/pipeline/pass_parallel.c src/pipeline/pass_semantic.c src/pipeline/pass_semantic_edges.c src/pipeline/pass_similarity.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/pipeline/worker_pool.c src/semantic/semantic.c src/semantic/semantic.h src/simhash/minhash.c tests/test_arena.c tests/test_c_lsp.c tests/test_extraction.c tests/test_mcp.c tests/test_mem.c tests/test_parallel.c tests/test_pipeline.c
173 f170 docs/CONFIGURATION.md src/main.c tests/test_daemon_open_readiness.py
174 f171 src/daemon/ipc.c tests/test_daemon_ipc.c
175 f172 src/cli/hook_augment.c src/daemon/runtime.c src/daemon/runtime.h src/main.c tests/test_cli.c tests/test_daemon_runtime.c
176 f173 Makefile.cbm internal/cbm/arena.h internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/extract_unified.c internal/cbm/lsp/c_lsp.c internal/cbm/lsp/c_lsp.h internal/cbm/lsp/cs_lsp.c internal/cbm/lsp/go_lsp.c internal/cbm/lsp/py_lsp.c internal/cbm/lsp/py_lsp.h internal/cbm/lsp/type_registry.c internal/cbm/lsp/type_registry.h internal/cbm/result_compact.c internal/cbm/result_spill.c internal/cbm/result_spill.h scripts/lint-memory-core.py scripts/memory-core-baseline.txt src/foundation/arena.c src/foundation/arena.h src/foundation/diagnostics.c src/foundation/dyn_array.h src/foundation/hash_table.c src/foundation/hash_table.h src/foundation/mem.c src/foundation/mem.h src/foundation/mem_core.c src/foundation/mem_core.h src/foundation/platform.h src/foundation/slab_alloc.c src/foundation/system_info.c src/graph_buffer/graph_buffer.c src/graph_buffer/graph_buffer.h src/main.c src/mcp/mcp.c src/pipeline/lsp_surface.c src/pipeline/lsp_surface.h src/pipeline/pass_complexity.c src/pipeline/pass_importance.c src/pipeline/pass_lsp_cross.c src/pipeline/pass_lsp_cross.h src/pipeline/pass_parallel.c src/pipeline/pass_semantic.c src/pipeline/pass_semantic_edges.c src/pipeline/pass_similarity.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/pipeline/worker_pool.c src/semantic/semantic.c src/semantic/semantic.h src/simhash/minhash.c tests/test_arena.c tests/test_c_lsp.c tests/test_extraction.c tests/test_graph_buffer.c tests/test_mcp.c tests/test_mem.c tests/test_parallel.c tests/test_pipeline.c
177 f174 README.md scripts/smoke-test.sh src/cli/cli.c src/cli/cli.h tests/test_cli.c
178 f175 src/foundation/mem.c src/foundation/mem.h
179 f176 flake.nix pkg/chocolatey/codebase-memory-mcp.nuspec pkg/chocolatey/tools/chocolateyInstall.ps1 pkg/go/cmd/codebase-memory-mcp/main.go pkg/npm/package.json pkg/pypi/pyproject.toml pkg/pypi/src/codebase_memory_mcp/_cli.py server.json
180 f177 graph-ui/src/components/FilterPanel.tsx graph-ui/src/components/GraphTab.deadcode.test.tsx graph-ui/src/lib/colors.ts
181 f178 CONTRIBUTING.md README.md docs/index.html pkg/npm/README.md
182 f179 internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/service_patterns.c internal/cbm/service_patterns.h src/cli/cli.c src/cypher/cypher.c src/foundation/str_util.c src/foundation/str_util.h src/mcp/mcp.c src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_cli.c tests/test_cypher.c tests/test_extraction.c tests/test_infrascan.c tests/test_mcp.c tests/test_parse_coverage.c tests/test_pipeline.c tests/test_registry.c tests/test_str_util.c
183 f180 internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/service_patterns.c internal/cbm/service_patterns.h scripts/memory-core-baseline.txt src/cli/cli.c src/cypher/cypher.c src/foundation/str_util.c src/foundation/str_util.h src/mcp/mcp.c src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_cli.c tests/test_cypher.c tests/test_extraction.c tests/test_infrascan.c tests/test_mcp.c tests/test_parse_coverage.c tests/test_pipeline.c tests/test_registry.c tests/test_str_util.c
184 f181 src/discover/language.c tests/test_language.c
185 f182 .github/workflows/_memwaste.yml .github/workflows/dry-run.yml .github/workflows/nightly-soak.yml .github/workflows/pr.yml .github/workflows/release.yml Makefile.cbm internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/extract_node_stack.h internal/cbm/lsp/go_lsp.c internal/cbm/result_compact.c internal/cbm/sqlite_writer.c scripts/fuzz.sh scripts/memlab-drive.py scripts/memlab-report.py scripts/memlab.sh scripts/memwaste-padding.py scripts/memwaste-ratchet.py scripts/memwaste-report.py scripts/memwaste-scaling.py scripts/memwaste.sh scripts/sanitizer-coverage-map.py scripts/sanitizer-map.json src/foundation/arena.c src/foundation/arena.h src/foundation/compat_thread.c src/foundation/dyn_array.h src/foundation/hash_table.c src/foundation/mem.c src/foundation/mem.h src/foundation/mem_core.c src/foundation/mem_events.c src/foundation/mem_events.h src/foundation/mem_override_darwin.c src/foundation/mem_override_libc.c src/foundation/mem_override_posix.c src/foundation/mem_override_win.c src/foundation/mem_profile.c src/foundation/mem_profile.h src/main.c src/pipeline/lsp_surface.c src/pipeline/pass_configlink.c src/pipeline/pass_k8s.c src/pipeline/pass_lsp_cross.c src/pipeline/pass_lsp_cross.h src/pipeline/pass_parallel.c src/pipeline/pass_semantic.c src/pipeline/pass_semantic_edges.c src/pipeline/pass_similarity.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/registry.c src/pipeline/worker_pool.c src/semantic/semantic.c src/semantic/semantic.h src/simhash/minhash.c src/simhash/minhash.h tests/fuzz/cbm_fuzz.c tests/fuzz/corpus/config/json-empty.seed tests/fuzz/corpus/config/json.seed tests/fuzz/corpus/config/toml.seed tests/fuzz/corpus/config/yaml.seed tests/fuzz/corpus/cypher/agg.seed tests/fuzz/corpus/cypher/hops.seed tests/fuzz/corpus/cypher/match.seed tests/fuzz/corpus/cypher/optional.seed tests/fuzz/corpus/cypher/union.seed tests/fuzz/corpus/cypher/where.seed tests/fuzz/corpus/extract/c.seed tests/fuzz/corpus/extract/cpp.seed tests/fuzz/corpus/extract/csharp.seed tests/fuzz/corpus/extract/go.seed tests/fuzz/corpus/extract/java.seed tests/fuzz/corpus/extract/javascript.seed tests/fuzz/corpus/extract/makefile.seed tests/fuzz/corpus/extract/php.seed tests/fuzz/corpus/extract/python.seed tests/fuzz/corpus/extract/ruby.seed tests/fuzz/corpus/extract/rust.seed tests/fuzz/corpus/extract/sql.seed tests/fuzz/corpus/extract/typescript.seed tests/fuzz/corpus/extract/yaml.seed tests/test_arena.c tests/test_extraction.c tests/test_hash_table.c tests/test_main.c tests/test_mem.c tests/test_mem_events.c tests/test_registry.c tests/test_semantic.c tests/test_shell_line_endings.sh tests/test_simhash.c tests/test_venue_parity_contract.sh
186 f183 src/cli/cli.h src/main.c tests/test_cli.c tests/test_daemon_bootstrap.c
187 f184 src/cli/cli.h src/main.c tests/test_cli.c tests/test_daemon_bootstrap.c tests/test_mcp.c
188 f185 src/ui/layout3d.c tests/test_ui.c
189 f186 Makefile.cbm README.md src/daemon/application.c src/daemon/bootstrap.c src/foundation/mem_core.c src/foundation/mem_core.h src/main.c src/mcp/cache.c src/mcp/cache.h src/mcp/mcp.c tests/test_cache.c tests/test_cache_cli.py tests/test_daemon_application.c tests/test_daemon_bootstrap.c tests/test_main.c tests/test_mcp.c
190 f187 .github/workflows/_memwaste.yml .github/workflows/dry-run.yml .github/workflows/nightly-soak.yml .github/workflows/pr.yml .github/workflows/release.yml Makefile.cbm internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/extract_node_stack.h internal/cbm/lsp/go_lsp.c internal/cbm/result_compact.c internal/cbm/result_spill.c internal/cbm/result_spill.h internal/cbm/sqlite_writer.c scripts/fuzz.sh scripts/memlab-drive.py scripts/memlab-report.py scripts/memlab.sh scripts/memory-core-baseline.txt scripts/memwaste-padding.py scripts/memwaste-ratchet.py scripts/memwaste-report.py scripts/memwaste-scaling.py scripts/memwaste.sh scripts/sanitizer-coverage-map.py scripts/sanitizer-map.json scripts/smoke-test.sh scripts/test.sh src/foundation/arena.c src/foundation/arena.h src/foundation/compat_thread.c src/foundation/dyn_array.h src/foundation/hash_table.c src/foundation/mem.c src/foundation/mem.h src/foundation/mem_core.c src/foundation/mem_events.c src/foundation/mem_events.h src/foundation/mem_override_darwin.c src/foundation/mem_override_libc.c src/foundation/mem_override_posix.c src/foundation/mem_override_win.c src/foundation/mem_profile.c src/foundation/mem_profile.h src/foundation/platform.h src/foundation/system_info.c src/graph_buffer/graph_buffer.c src/main.c src/pipeline/lsp_surface.c src/pipeline/pass_configlink.c src/pipeline/pass_k8s.c src/pipeline/pass_lsp_cross.c src/pipeline/pass_lsp_cross.h src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pass_route_nodes.c src/pipeline/pass_semantic.c src/pipeline/pass_semantic_edges.c src/pipeline/pass_similarity.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/pipeline/registry.c src/pipeline/worker_pool.c src/semantic/semantic.c src/semantic/semantic.h src/simhash/minhash.c src/simhash/minhash.h tests/fuzz/cbm_fuzz.c tests/fuzz/corpus/config/json-empty.seed tests/fuzz/corpus/config/json.seed tests/fuzz/corpus/config/toml.seed tests/fuzz/corpus/config/yaml.seed tests/fuzz/corpus/cypher/agg.seed tests/fuzz/corpus/cypher/hops.seed tests/fuzz/corpus/cypher/match.seed tests/fuzz/corpus/cypher/optional.seed tests/fuzz/corpus/cypher/union.seed tests/fuzz/corpus/cypher/where.seed tests/fuzz/corpus/extract/c.seed tests/fuzz/corpus/extract/cpp.seed tests/fuzz/corpus/extract/csharp.seed tests/fuzz/corpus/extract/go.seed tests/fuzz/corpus/extract/java.seed tests/fuzz/corpus/extract/javascript.seed tests/fuzz/corpus/extract/makefile.seed tests/fuzz/corpus/extract/php.seed tests/fuzz/corpus/extract/python.seed tests/fuzz/corpus/extract/ruby.seed tests/fuzz/corpus/extract/rust.seed tests/fuzz/corpus/extract/sql.seed tests/fuzz/corpus/extract/typescript.seed tests/fuzz/corpus/extract/yaml.seed tests/test_arena.c tests/test_extraction.c tests/test_hash_table.c tests/test_main.c tests/test_mem.c tests/test_mem_events.c tests/test_parallel.c tests/test_pipeline.c tests/test_registry.c tests/test_semantic.c tests/test_shell_line_endings.sh tests/test_simhash.c tests/test_thread_stack_tls_contract.sh tests/test_venue_parity_contract.sh
191 f188 .github/workflows/_memwaste.yml .github/workflows/dry-run.yml .github/workflows/nightly-soak.yml .github/workflows/pr.yml .github/workflows/release.yml Makefile.cbm internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/extract_node_stack.h internal/cbm/extract_unified.c internal/cbm/extract_usages.c internal/cbm/lsp/go_lsp.c internal/cbm/result_compact.c internal/cbm/result_spill.c internal/cbm/result_spill.h internal/cbm/sqlite_writer.c internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/properties/scanner.c scripts/fuzz.sh scripts/memlab-drive.py scripts/memlab-report.py scripts/memlab.sh scripts/memory-core-baseline.txt scripts/memwaste-padding.py scripts/memwaste-ratchet.py scripts/memwaste-report.py scripts/memwaste-scaling.py scripts/memwaste.sh scripts/sanitizer-coverage-map.py scripts/sanitizer-map.json scripts/smoke-test.sh scripts/test.sh scripts/vendored-checksums.txt src/foundation/arena.c src/foundation/arena.h src/foundation/compat_thread.c src/foundation/dyn_array.h src/foundation/hash_table.c src/foundation/mem.c src/foundation/mem.h src/foundation/mem_core.c src/foundation/mem_events.c src/foundation/mem_events.h src/foundation/mem_override_darwin.c src/foundation/mem_override_libc.c src/foundation/mem_override_posix.c src/foundation/mem_override_win.c src/foundation/mem_profile.c src/foundation/mem_profile.h src/foundation/platform.h src/foundation/system_info.c src/graph_buffer/graph_buffer.c src/main.c src/pipeline/lsp_surface.c src/pipeline/pass_configlink.c src/pipeline/pass_k8s.c src/pipeline/pass_lsp_cross.c src/pipeline/pass_lsp_cross.h src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pass_route_nodes.c src/pipeline/pass_semantic.c src/pipeline/pass_semantic_edges.c src/pipeline/pass_similarity.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/pipeline/registry.c src/pipeline/worker_pool.c src/semantic/semantic.c src/semantic/semantic.h src/simhash/minhash.c src/simhash/minhash.h test-infrastructure/vm/vm-run-tests.sh tests/fuzz/cbm_fuzz.c tests/fuzz/corpus/config/json-empty.seed tests/fuzz/corpus/config/json.seed tests/fuzz/corpus/config/toml.seed tests/fuzz/corpus/config/yaml.seed tests/fuzz/corpus/cypher/agg.seed tests/fuzz/corpus/cypher/hops.seed tests/fuzz/corpus/cypher/match.seed tests/fuzz/corpus/cypher/optional.seed tests/fuzz/corpus/cypher/union.seed tests/fuzz/corpus/cypher/where.seed tests/fuzz/corpus/extract/c.seed tests/fuzz/corpus/extract/cpp.seed tests/fuzz/corpus/extract/csharp.seed tests/fuzz/corpus/extract/go.seed tests/fuzz/corpus/extract/java.seed tests/fuzz/corpus/extract/javascript.seed tests/fuzz/corpus/extract/makefile.seed tests/fuzz/corpus/extract/php.seed tests/fuzz/corpus/extract/python.seed tests/fuzz/corpus/extract/ruby.seed tests/fuzz/corpus/extract/rust.seed tests/fuzz/corpus/extract/sql.seed tests/fuzz/corpus/extract/typescript.seed tests/fuzz/corpus/extract/yaml.seed tests/test_arena.c tests/test_extraction.c tests/test_hash_table.c tests/test_main.c tests/test_mem.c tests/test_mem_events.c tests/test_parallel.c tests/test_pipeline.c tests/test_registry.c tests/test_semantic.c tests/test_shell_line_endings.sh tests/test_simhash.c tests/test_thread_stack_tls_contract.sh tests/test_venue_parity_contract.sh tests/test_vm_verdict_contract.sh
192 f189 internal/cbm/extract_calls.c internal/cbm/lsp/ts_lsp.c tests/test_extraction.c tests/test_ts_lsp.c
193 f190 .github/workflows/pr.yml Makefile.cbm README.md src/cli/cache.c src/cli/cache.h src/daemon/bootstrap.c src/daemon/version_cohort.c src/daemon/version_cohort.h src/foundation/mem_core.c src/foundation/mem_core.h src/main.c tests/test_cache.c tests/test_cache_cli.py tests/test_daemon_bootstrap.c tests/test_main.c tests/test_mcp.c tests/test_version_cohort.c
194 f191 .github/workflows/pr.yml Makefile.cbm README.md pkg/pypi/tests/test_cli.py src/cli/cache.c src/cli/cache.h src/daemon/bootstrap.c src/daemon/version_cohort.c src/daemon/version_cohort.h src/foundation/mem_core.c src/foundation/mem_core.h src/main.c tests/test_cache.c tests/test_cache_cli.py tests/test_daemon_bootstrap.c tests/test_main.c tests/test_mcp.c tests/test_version_cohort.c
195 f192 install.sh tests/test-install-android-guard.sh
196 f193 src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pipeline.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/pipeline/registry.c tests/test_pipeline.c tests/test_registry.c
197 f194 internal/cbm/vendored/grammars/sql/parser.c internal/cbm/vendored/grammars/tsx/parser.c scripts/vendored-checksums.txt tests/test_parse_coverage.c
198 f195 internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/sql/parser.c internal/cbm/vendored/grammars/tsx/parser.c scripts/vendored-checksums.txt tests/test_parse_coverage.c
199 f196 src/mcp/mcp.c src/pipeline/pipeline.c src/pipeline/pipeline.h tests/test_pipeline.c
200 f197 .github/workflows/_lint.yml .github/workflows/_test.yml .github/workflows/release.yml
201 f198 internal/cbm/lang_specs.c tests/test_extraction.c
202 f199 install.ps1
203 f200 internal/cbm/lsp/py_lsp.c internal/cbm/lsp/scope.c internal/cbm/lsp/scope.h tests/test_scope.c
204 f201 src/cypher/cypher.c src/foundation/recursion_whitelist.h tests/test_cypher.c
205 f202 internal/cbm/service_patterns.c tests/test_infrascan.c tests/test_pipeline.c
206 f203 scripts/memory-core-baseline.txt src/mcp/mcp.c
207 f204 internal/cbm/service_patterns.c tests/test_pipeline.c
208 f205 src/foundation/compat_thread.c
209 f206 CONTRIBUTING.md README.md
210 f207 README.md docs/WINDOWS_TROUBLESHOOTING.md
211 f208 scripts/memory-core-baseline.txt src/mcp/mcp.c tests/test_worker_session_scope.sh
212 f209 .github/workflows/smoke.yml scripts/smoke-invariants.sh
213 f210 internal/cbm/cbm.c internal/cbm/preprocessor.cpp internal/cbm/preprocessor.h src/mcp/mcp.c tests/test_extraction.c
214 f211 README.md docs/CONFIGURATION.md docs/INDEX_RESOURCE_LIMITS.md scripts/test-runtime.sh src/cli/cli.c src/daemon/application.c src/foundation/index_policy.c src/foundation/index_policy.h src/foundation/subprocess.c src/foundation/subprocess.h src/mcp/index_supervisor.c src/mcp/index_supervisor.h src/mcp/mcp.c src/mcp/mcp.h src/mcp/mcp_internal.h src/pipeline/pipeline.c tests/test_daemon_application.c tests/test_index_policy.c tests/test_index_supervisor.c tests/test_mcp.c tests/test_subprocess.c
215 f212 docs/EVALUATION_PLAN.md scripts/benchmark-index.sh scripts/benchmark-search-graph.sh scripts/test.sh tests/test_benchmark_runtime_isolation_contract.sh
216 f213 Makefile.cbm src/foundation/compat.c src/foundation/compat.h src/foundation/limits.h src/foundation/mem_override_win.c src/foundation/source_encoding.c src/foundation/source_encoding.h src/mcp/index_supervisor.c src/mcp/mcp.c src/pipeline/pass_calls.c src/pipeline/pass_compile_commands.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_semantic.c src/pipeline/pass_usages.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h tests/test_pipeline.c tests/test_platform.c
217 f214 README.md docs/CONFIGURATION.md docs/INDEX_RESOURCE_LIMITS.md scripts/test-runtime.sh src/cli/cli.c src/daemon/application.c src/foundation/index_policy.c src/foundation/index_policy.h src/foundation/subprocess.c src/foundation/subprocess.h src/mcp/index_supervisor.c src/mcp/index_supervisor.h src/mcp/mcp.c src/mcp/mcp.h src/mcp/mcp_internal.h src/pipeline/pipeline.c tests/test_daemon_application.c tests/test_index_policy.c tests/test_index_supervisor.c tests/test_mcp.c tests/test_subprocess.c tests/test_worker_session_scope.sh
218 f215 .github/workflows/pr.yml
219 f216 CONTRIBUTING.md
220 f217 README.md docs/CONFIGURATION.md docs/INDEX_RESOURCE_LIMITS.md scripts/test-runtime.sh src/cli/cli.c src/daemon/application.c src/daemon/application.h src/foundation/index_policy.c src/foundation/index_policy.h src/foundation/subprocess.c src/foundation/subprocess.h src/mcp/index_supervisor.c src/mcp/index_supervisor.h src/mcp/mcp.c src/mcp/mcp.h src/mcp/mcp_internal.h src/pipeline/pipeline.c tests/test_daemon_application.c tests/test_index_policy.c tests/test_index_supervisor.c tests/test_mcp.c tests/test_subprocess.c tests/test_worker_session_scope.sh
221 f218 tests/test_daemon_bootstrap.c
222 f219 internal/cbm/service_patterns.c tests/test_infrascan.c
223 f220 Makefile.cbm src/cli/cli.c src/pipeline/pass_doclinks.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h tests/test_doclinks.c tests/test_edge_structural.c tests/test_lang_contract.c tests/test_main.c tests/test_pipeline.c
224 f221 Makefile.cbm README.md src/cli/cli.c src/pipeline/pass_doclinks.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h tests/test_doclinks.c tests/test_edge_structural.c tests/test_lang_contract.c tests/test_main.c tests/test_pipeline.c
225 f222 src/daemon/application.c src/mcp/mcp.c src/pipeline/lsp_surface.c src/pipeline/pipeline.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h tests/test_daemon_application.c tests/test_mcp.c tests/test_pipeline.c
226 f223 README.md docs/CONFIGURATION.md docs/INDEX_RESOURCE_LIMITS.md scripts/test-runtime.sh src/cli/cli.c src/daemon/application.c src/daemon/application.h src/foundation/index_policy.c src/foundation/index_policy.h src/foundation/subprocess.c src/foundation/subprocess.h src/mcp/index_supervisor.c src/mcp/index_supervisor.h src/mcp/mcp.c src/mcp/mcp.h src/mcp/mcp_internal.h src/pipeline/pipeline.c tests/test_daemon_application.c tests/test_daemon_bootstrap.c tests/test_index_policy.c tests/test_index_supervisor.c tests/test_mcp.c tests/test_subprocess.c tests/test_worker_session_scope.sh
227 f224 internal/cbm/lsp/ts_lsp.c internal/cbm/lsp/ts_lsp.h tests/test_stack_overflow.c
228 f225 src/foundation/mem_override_win.c
229 f226 src/foundation/compat.c src/mcp/index_supervisor.c src/mcp/mcp.c tests/test_platform.c
230 f227 src/pipeline/lsp_surface.c tests/test_pipeline.c
231 f228 src/daemon/host.c src/daemon/ipc.c src/daemon/ipc.h src/daemon/runtime.c src/daemon/runtime.h src/daemon/version_cohort.c src/daemon/version_cohort.h src/foundation/private_file_lock.c src/foundation/private_file_lock_internal.h tests/test_daemon_ipc.c tests/test_private_file_lock.c tests/test_version_cohort.c
232 f229 src/foundation/compat.h tests/test_platform.c
233 f230 README.md docs/CONFIGURATION.md src/cli/cli.c src/cli/cli.h src/cli/hook_augment.c src/daemon/application.c src/git/git_context.c src/git/git_context.h src/mcp/mcp.c src/mcp/mcp_internal.h tests/test_cli.c tests/test_git_context.c tests/test_mcp.c
234 f231 internal/cbm/extract_defs.c src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_call_reference_contract.c tests/test_registry.c
235 f232 internal/cbm/extract_calls.c internal/cbm/extract_unified.c internal/cbm/extract_unified.h tests/repro/repro_call_scope_usages.c tests/test_convergence_probe.c
236 f233 pkg/scoop/codebase-memory-mcp.json
237 f234 src/mcp/mcp.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/store/store.c src/store/store.h tests/test_mcp.c tests/test_pipeline.c
238 f235 internal/cbm/extract_defs.c internal/cbm/helpers.c internal/cbm/helpers.h tests/test_extraction.c
239 f236 src/pipeline/registry.c tests/test_pipeline.c tests/test_registry.c
240 f237 internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_unified.c internal/cbm/extract_usages.c internal/cbm/helpers.c internal/cbm/helpers.h internal/cbm/result_compact.c src/foundation/constants.h src/foundation/recursion_whitelist.h src/mcp/mcp.c src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_usages.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c src/pipeline/registry.c src/store/store.c src/store/store.h tests/repro/repro_call_argument_matrix_a.c tests/repro/repro_call_node_behaviors.c tests/test_extraction.c tests/test_mcp.c tests/test_pipeline.c tests/test_registry.c tests/test_store_nodes.c
241 f238 src/foundation/compat_fs.c src/foundation/compat_fs.h src/mcp/mcp.c src/pipeline/pipeline.c tests/test_pipeline.c
242 f239 src/mcp/mcp.c src/pipeline/pass_cross_repo.c src/pipeline/pass_cross_repo.h tests/test_cli.c tests/test_cross_repo.c
243 f240 src/pipeline/pass_cross_repo.c tests/test_cross_repo.c
244 f241 internal/cbm/extract_defs.c internal/cbm/lang_specs.c tests/test_extraction.c
245 f242 docs/CONFIGURATION.md src/daemon/ipc.c src/daemon/ipc.h tests/test_daemon_ipc.c
246 f243 docs/CONFIGURATION.md src/daemon/ipc.c src/daemon/ipc.h src/main.c tests/test_daemon_ipc.c
247 f244 src/cli/config_toml_edit.c src/cli/config_toml_edit.h tests/test_cli.c tests/test_config_toml_edit.c
248 f245 docs/cbmignore.md src/discover/discover.c tests/test_discover.c
249 f246 Makefile.cbm internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/sql_values.c internal/cbm/sql_values.h tests/test_extraction.c tests/test_parse_coverage.c tests/test_pipeline.c
250 f247 THIRD_PARTY.md internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/javascript/parser.c internal/cbm/vendored/grammars/tsx/parser.c internal/cbm/vendored/grammars/tsx/tree_sitter/array.h internal/cbm/vendored/grammars/tsx/tree_sitter/parser.h scripts/audit-license-provenance.py scripts/vendored-checksums.txt tests/test_parse_coverage.c tools/tree-sitter-javascript/grammar.js tools/tree-sitter-javascript/package.json tools/tree-sitter-javascript/patches/jsx-lone-ampersand.patch tools/tree-sitter-javascript/src/grammar.json tools/tree-sitter-javascript/src/node-types.json tools/tree-sitter-javascript/src/parser.c tools/tree-sitter-javascript/src/scanner.c tools/tree-sitter-javascript/src/tree_sitter/alloc.h tools/tree-sitter-javascript/src/tree_sitter/array.h tools/tree-sitter-javascript/src/tree_sitter/parser.h tools/tree-sitter-javascript/test/corpus/jsx_ampersand.txt tools/tree-sitter-javascript/tree-sitter.json tools/tree-sitter-tsx/define-grammar.js tools/tree-sitter-tsx/grammar.js tools/tree-sitter-tsx/javascript-grammar.js tools/tree-sitter-tsx/package.json tools/tree-sitter-tsx/src/_common_scanner.h tools/tree-sitter-tsx/src/grammar.json tools/tree-sitter-tsx/src/node-types.json tools/tree-sitter-tsx/src/parser.c tools/tree-sitter-tsx/src/scanner.c tools/tree-sitter-tsx/src/tree_sitter/alloc.h tools/tree-sitter-tsx/src/tree_sitter/array.h tools/tree-sitter-tsx/src/tree_sitter/parser.h tools/tree-sitter-tsx/test/corpus/jsx_ampersand.txt tools/tree-sitter-tsx/tree-sitter.json
251 f248 internal/cbm/lsp/c_lsp.c internal/cbm/lsp/ts_lsp.c internal/cbm/lsp/ts_lsp.h tests/test_c_lsp.c tests/test_ts_lsp.c
252 f249 internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/c_sharp/parser.c internal/cbm/vendored/grammars/c_sharp/scanner.c internal/cbm/vendored/grammars/c_sharp/tree_sitter/alloc.h internal/cbm/vendored/grammars/c_sharp/tree_sitter/array.h internal/cbm/vendored/grammars/c_sharp/tree_sitter/parser.h scripts/vendored-checksums.txt tests/test_parse_coverage.c
253 f250 internal/cbm/sqlite_writer.c tests/test_sqlite_writer.c
254 f251 src/mcp/mcp.c src/pipeline/fqn.c src/pipeline/pipeline.h tests/test_mcp.c
255 f252 internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/result_compact.c src/pipeline/lsp_surface.c src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pipeline.c src/pipeline/pipeline_delta.c src/pipeline/pipeline_internal.h tests/test_extraction.c tests/test_pipeline.c
256 f253 README.md docs/CONFIGURATION.md src/cli/cli.c src/cli/cli.h src/daemon/host.c src/discover/discover.c src/discover/discover.h src/watcher/watcher.c src/watcher/watcher.h tests/test_cli.c tests/test_watcher.c
257 f254 src/daemon/runtime.c src/daemon/runtime.h tests/test_daemon_runtime.c
258 f255 internal/cbm/cbm.c internal/cbm/cbm.h tests/test_parse_coverage.c
259 f256 scripts/test.sh tests/repro/repro_main.c tests/test_git_env_isolation_contract.sh tests/test_helpers.h tests/test_main.c
260 f257 src/cli/config_toml_edit.c tests/test_config_toml_edit.c
261 f258 internal/cbm/lsp/generated/rust_stdlib_data.c src/pipeline/lsp_resolve.h src/pipeline/pass_calls.c src/pipeline/pass_parallel.c tests/test_parallel.c tests/test_pipeline.c
262 f259 internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/lsp/c_lsp.c internal/cbm/lsp/cs_lsp.c internal/cbm/lsp/go_lsp.c internal/cbm/lsp/java_lsp.c internal/cbm/lsp/kotlin_lsp.c internal/cbm/lsp/php_lsp.c internal/cbm/lsp/py_lsp.c internal/cbm/lsp/rust_lsp.c internal/cbm/lsp/ts_lsp.c tests/test_parse_coverage.c
263 f260 Makefile.cbm src/foundation/compat_fs.c src/foundation/compat_fs.h src/foundation/git_env.c src/foundation/git_env.h src/foundation/subprocess.c src/foundation/subprocess.h src/git/git_context.c src/mcp/mcp.c src/pipeline/artifact.c src/pipeline/pass_githistory.c src/watcher/watcher.c tests/test_git_context.c tests/test_subprocess.c
264 f261 Makefile.cbm internal/cbm/callable_sig.c internal/cbm/callable_sig.h internal/cbm/cbm.h internal/cbm/lsp/go_lsp.h internal/cbm/lsp/type_registry.h internal/cbm/result_compact.c scripts/memory-core-baseline.txt src/mcp/mcp.c src/pipeline/lsp_resolve.h src/pipeline/pass_ensemble_routing.c src/pipeline/pass_lsp_cross.c src/pipeline/pass_semantic.c src/pipeline/registry.c src/store/store.c src/store/store.h tests/test_callable_sig.c tests/test_main.c tests/test_store_nodes.c
265 f262 src/pipeline/pass_cross_repo.c src/store/store.c src/store/store.h tests/test_cross_repo.c
266 f263 src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline.h src/pipeline/pipeline_internal.h src/pipeline/registry.c tests/test_edge_imports.c tests/test_registry.c
267 f264 internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/lsp/cs_lsp.c internal/cbm/lsp/type_registry.h internal/cbm/result_compact.c src/pipeline/pass_lsp_cross.c src/pipeline/pipeline.c tests/test_parallel.c
268 f265 README.md docs/CONFIGURATION.md docs/index.html src/daemon/application.c src/mcp/mcp.c src/mcp/mcp.h src/watcher/watcher.c src/watcher/watcher.h tests/test_daemon_application.c tests/test_watcher.c
269 f266 src/daemon/host.c src/daemon/host_internal.h src/daemon/ipc.c src/daemon/ipc.h src/daemon/runtime.c src/daemon/runtime.h src/daemon/version_cohort.c src/daemon/version_cohort.h src/foundation/private_file_lock.c src/foundation/private_file_lock_internal.h tests/test_daemon_ipc.c tests/test_daemon_runtime.c tests/test_private_file_lock.c tests/test_version_cohort.c
270 f267 src/mcp/mcp.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/store/store.c tests/test_pipeline.c
271 f268 README.md docs/CONFIGURATION.md
272 f269 internal/cbm/extract_usages.c internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/rescript/scanner.c src/discover/discover.c src/discover/discover.h src/discover/language.c tests/test_complexity.c tests/test_extraction.c tests/test_language.c
273 f270 internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/result_compact.c scripts/memory-core-baseline.txt src/pipeline/lsp_surface.c src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pipeline.c src/pipeline/pipeline_delta.c src/pipeline/pipeline_internal.h tests/test_extraction.c tests/test_pipeline.c
274 f271 THIRD_PARTY.md internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/pascal/parser.c internal/cbm/vendored/grammars/pascal/tree_sitter/parser.h scripts/audit-license-provenance.py scripts/vendored-checksums.txt tests/test_parse_coverage.c tools/tree-sitter-pascal/grammar.js tools/tree-sitter-pascal/package.json tools/tree-sitter-pascal/patches/unicode-identifiers.patch tools/tree-sitter-pascal/src/grammar.json tools/tree-sitter-pascal/src/node-types.json tools/tree-sitter-pascal/src/parser.c tools/tree-sitter-pascal/src/tree_sitter/alloc.h tools/tree-sitter-pascal/src/tree_sitter/array.h tools/tree-sitter-pascal/src/tree_sitter/parser.h tools/tree-sitter-pascal/test/corpus/unicode_identifiers.txt tools/tree-sitter-pascal/tree-sitter.json
275 f272 README.md src/daemon/application.c src/daemon/frontend.c src/daemon/frontend.h src/mcp/mcp.c src/mcp/mcp.h src/ui/http_server.c tests/test_daemon_application.c tests/test_daemon_frontend.c tests/test_mcp.c
276 f273 internal/cbm/cbm.h src/pipeline/pipeline_internal.h
277 f274 pkg/scoop/codebase-memory-mcp.json tests/test_version_metadata_contract.sh
278 f275 README.md scripts/memory-core-baseline.txt src/cli/cli.c src/mcp/mcp.c src/pipeline/artifact.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/store/store.c src/store/store.h tests/test_artifact.c tests/test_mcp.c tests/test_pipeline.c
279 f276 internal/cbm/extract_imports.c src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline_internal.h tests/test_edge_imports.c tests/test_extraction.c tests/test_pipeline.c
280 f277 internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/extract_unified.c internal/cbm/helpers.c src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline_internal.h src/pipeline/registry.c tests/test_edge_imports.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
281 f278 src/foundation/compat.c
282 f279 internal/cbm/cbm.c src/cli/cli.c src/foundation/compat.h tests/test_extraction.c tests/test_platform.c
283 f280 src/mcp/mcp.c src/pipeline/pipeline.h src/pipeline/pipeline_internal.h tests/test_mcp.c
284 f281 internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/extract_unified.c internal/cbm/helpers.c src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline.h src/pipeline/pipeline_internal.h src/pipeline/registry.c tests/test_edge_imports.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
285 f282 THIRD_PARTY.md internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_usages.c internal/cbm/helpers.c internal/cbm/helpers.h internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/objectscript_routine/objectscript_common.h internal/cbm/vendored/grammars/objectscript_routine/parser.c internal/cbm/vendored/grammars/objectscript_routine/scanner.c internal/cbm/vendored/grammars/objectscript_routine/tree_sitter/array.h internal/cbm/vendored/grammars/objectscript_udl/objectscript_common.h internal/cbm/vendored/grammars/objectscript_udl/parser.c internal/cbm/vendored/grammars/objectscript_udl/scanner.c internal/cbm/vendored/grammars/objectscript_udl/tree_sitter/array.h scripts/vendored-checksums.txt tests/test_extraction.c
286 f283 internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_unified.c internal/cbm/extract_usages.c internal/cbm/helpers.c internal/cbm/helpers.h tests/test_extraction.c
287 f284 src/daemon/application.c src/foundation/log.c src/foundation/log.h src/mcp/mcp.c src/ui/http_server.c tests/test_log.c tests/test_mcp.c
288 f285 internal/cbm/extract_defs.c scripts/memory-core-baseline.txt tests/test_extraction.c
289 f286 README.md docs/CONFIGURATION.md scripts/test.sh src/main.c tests/test_in_process_mcp.py tests/test_in_process_mcp.sh tests/test_runtime_isolation_contract.sh
290 f287 internal/cbm/extract_imports.c scripts/memory-core-baseline.txt src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline_internal.h tests/test_edge_imports.c tests/test_extraction.c tests/test_pipeline.c
291 f288 internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/extract_unified.c internal/cbm/helpers.c scripts/memory-core-baseline.txt src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline_internal.h src/pipeline/registry.c tests/test_edge_imports.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
292 f289 internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/extract_unified.c internal/cbm/helpers.c scripts/memory-core-baseline.txt src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline.h src/pipeline/pipeline_internal.h src/pipeline/registry.c tests/test_edge_imports.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
293 f290 src/cli/client_adapter.c src/cli/client_adapter.h tests/test_agent_clients.c
294 f291 internal/cbm/extract_usages.c internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/rescript/scanner.c scripts/vendored-checksums.txt src/discover/discover.c src/discover/discover.h src/discover/language.c tests/test_complexity.c tests/test_extraction.c tests/test_language.c
295 f292 Makefile.cbm internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/lsp/php_lsp.c internal/cbm/php_inline_html.c tests/test_extraction.c tests/test_pipeline.c
296 f293 Makefile.cbm internal/cbm/cbm.c internal/cbm/cbm.h internal/cbm/lsp/php_lsp.c internal/cbm/php_inline_html.c tests/test_extraction.c tests/test_php_lsp.c tests/test_pipeline.c
297 f294 src/daemon/bootstrap.h src/daemon/runtime.c src/daemon/runtime.h src/main.c tests/test_daemon_version.c
298 f295 scripts/memory-core-baseline.txt src/cli/agent_profiles.c src/cli/agent_profiles.h src/cli/cli.c tests/test_cli.c
299 f296 src/foundation/mem.c src/foundation/mem.h src/pipeline/pass_parallel.c tests/test_parallel.c
300 f297 internal/cbm/extract_defs.c internal/cbm/helpers.c internal/cbm/lang_specs.c tests/test_extraction.c
301 f298 src/mcp/mcp.c
302 f299 README.md THIRD_PARTY.md docs/index.html docs/llms.txt internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/extract_usages.c internal/cbm/grammar_st.c internal/cbm/lang_specs.c internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/st/LICENSE internal/cbm/vendored/grammars/st/parser.c internal/cbm/vendored/grammars/st/scanner.c internal/cbm/vendored/grammars/st/tree_sitter/alloc.h internal/cbm/vendored/grammars/st/tree_sitter/array.h internal/cbm/vendored/grammars/st/tree_sitter/parser.h pkg/chocolatey/codebase-memory-mcp.nuspec pkg/npm/README.md scripts/new-languages.json scripts/package-release.sh scripts/vendored-checksums.txt server.json src/discover/language.c src/discover/userconfig.c src/mcp/mcp.c tests/repro/repro_call_argument_matrix_b.c tests/repro/repro_call_node_manifest.c tests/repro/repro_language_registry.c tests/test_extraction.c tests/test_grammar_labels.c tests/test_grammar_regression.c tests/test_language.c
303 f300 README.md scripts/memory-core-baseline.txt src/cli/cli.c src/mcp/mcp.c src/pipeline/artifact.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/store/store.c src/store/store.h tests/test_artifact.c tests/test_mcp.c tests/test_pipeline.c tests/test_store_arch.c
304 f301 internal/cbm/cbm.h src/mcp/mcp.c src/pipeline/pass_calls.c src/pipeline/pass_parallel.c src/pipeline/pipeline.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/store/store.c src/store/store.h tests/test_mcp.c tests/test_pipeline.c
305 f302 THIRD_PARTY.md internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_usages.c internal/cbm/helpers.c internal/cbm/helpers.h internal/cbm/lang_specs.c internal/cbm/vendored/grammars/MANIFEST.md internal/cbm/vendored/grammars/objectscript_routine/objectscript_common.h internal/cbm/vendored/grammars/objectscript_routine/parser.c internal/cbm/vendored/grammars/objectscript_routine/scanner.c internal/cbm/vendored/grammars/objectscript_routine/tree_sitter/array.h internal/cbm/vendored/grammars/objectscript_udl/objectscript_common.h internal/cbm/vendored/grammars/objectscript_udl/parser.c internal/cbm/vendored/grammars/objectscript_udl/scanner.c internal/cbm/vendored/grammars/objectscript_udl/tree_sitter/array.h scripts/vendored-checksums.txt tests/repro/repro_call_node_behaviors.c tests/repro/repro_call_node_manifest.c tests/test_extraction.c
306 f303 internal/cbm/lsp/ts_lsp.c tests/repro/repro_issue514.c tests/test_ts_lsp.c
307 f304 src/cli/cli.c src/cli/cli.h src/foundation/str_util.c src/foundation/str_util.h src/pipeline/pass_cross_repo.c tests/test_cli.c
308 f305 internal/cbm/result_spill.c internal/cbm/result_spill.h tests/test_extraction.c tests/test_main.c
309 f306 internal/cbm/lsp/ts_lsp.c tests/test_pipeline.c tests/test_ts_lsp.c
310 f307 tests/test_watcher.c
311 f308 tests/test_daemon_ipc.c
312 f309 src/daemon/bootstrap.c src/daemon/bootstrap.h tests/repro/repro_main.c tests/test_daemon_bootstrap.c tests/test_helpers.h tests/test_main.c
313 f310 src/cli/cli.c src/cli/cli.h tests/test_cli.c
314 f311 internal/cbm/extract_imports.c scripts/memory-core-baseline.txt src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h tests/test_edge_imports.c tests/test_extraction.c tests/test_pipeline.c
315 f312 src/pipeline/pass_definitions.c tests/test_parallel.c
316 f313 internal/cbm/extract_usages.c tests/repro/repro_lexical_binding_precision.c tests/test_extraction.c
317 f314 internal/cbm/lsp/py_lsp.c tests/test_py_lsp.c tests/test_py_lsp_scale.c
318 f315 src/discover/discover.c src/discover/discover.h src/discover/language.c tests/test_discover.c tests/test_language.c
319 f316 internal/cbm/lsp/py_lsp.c internal/cbm/lsp/py_lsp.h tests/test_py_lsp.c
320 f317 internal/cbm/extract_defs.c src/store/store.h tests/test_extraction.c
321 f318 scripts/memory-core-baseline.txt src/cli/activation_transaction.c src/cli/activation_transaction.h tests/test_activation_transaction.c
322 f319 internal/cbm/lsp/py_lsp.c src/pipeline/lsp_resolve.h tests/repro/repro_lsp_go_py.c tests/test_edge_structural.c tests/test_lsp_resolution_probe.c tests/test_py_lsp.c
323 f320 internal/cbm/cbm.h internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/extract_unified.c internal/cbm/helpers.c scripts/memory-core-baseline.txt src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/pipeline/registry.c tests/test_edge_imports.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
324 f321 internal/cbm/cbm.h internal/cbm/extract_calls.c internal/cbm/extract_defs.c internal/cbm/extract_imports.c internal/cbm/extract_unified.c internal/cbm/helpers.c scripts/memory-core-baseline.txt src/pipeline/pass_calls.c src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_pkgmap.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/pipeline/registry.c tests/test_edge_imports.c tests/test_extraction.c tests/test_pipeline.c tests/test_registry.c
325 f322 src/pipeline/pass_definitions.c src/pipeline/pass_parallel.c src/pipeline/pass_tests.c src/pipeline/pipeline.h src/pipeline/pipeline_incremental.c src/pipeline/pipeline_internal.h src/pipeline/registry.c tests/test_pipeline.c tests/test_registry.c
326 f323 src/pipeline/pass_calls.c src/pipeline/pass_lsp_cross.c src/pipeline/pass_parallel.c src/pipeline/pass_semantic.c src/pipeline/pass_usages.c src/pipeline/pipeline.h src/pipeline/registry.c tests/test_pipeline.c
@@ -0,0 +1,776 @@
# Every PR push of September 2026 (one pr.yml run each) with the tier the
# lane analysis assigned it (sel_classify.py) and the tier the selector must
# produce. They differ only where a deviation is documented in `note`:
# makefile-by-name the analysis classified Makefile.cbm by its DIFF
# (source registration / test flags -> no tier of its
# own); a file list cannot show that, so the selector
# treats every Makefile.cbm change as a build-flag
# change: T5-core, full. Deliberately conservative.
# Columns (TAB): run_id, pr, head_sha, fileset, analysis_tier, expected_tier, note.
33464275799 1697 22f90fe883f641c2c460a210a1de3a8df74c4285 f001 T0a-docs T0a-docs
33464725171 1986 9d91891cb5dc6b9d67a0d4919d44d6346799af23 f002 T5-core T5-core
33484752078 1939 e11d5900cd8735fd2ff5ca5308bc028fbe5ed2a5 f003 T3-mid T3-mid
33492041913 1657 7f158e950f874bea6d9530eba16f44b0032f5cac f004 T5-core T5-core
33512870122 1941 35efca813cb2e280fd98168e037cc2a098e365c3 f005 T3-mid T5-core makefile-by-name
33512876012 1971 a5af586d9275249ff9ab8fd7504c9a1d12f7ef85 f005 T3-mid T5-core makefile-by-name
33512878163 1968 9e954104ff5c23bbbb05eccce024b74e6e0a1a6a f006 T5-core T5-core
33525282952 1739 2a2e817bbc11c64755e9d9e3c2e3bb951cf29083 f007 T4-platform T4-platform
33525334366 1817 e7bf16ac616d3d04733caa075a59902fa204d664 f008 T4-platform T4-platform
33529411666 1784 fad564e1e1ad683657ae604bec2c5f0852f19151 f009 T4-platform T4-platform
33530652927 1992 abd82a108b459d70a923d7b4a43651fbeb835912 f001 T0a-docs T0a-docs
33530742647 1697 22f90fe883f641c2c460a210a1de3a8df74c4285 f001 T0a-docs T0a-docs
33533600441 1993 da61c81af243e3277e3089fb474ee5c08fe5c148 f010 T2-extract T2-extract
33533605337 1697 4b844b7d9e782ff26284248c072578215cda3d2f f001 T0a-docs T0a-docs
33541770974 1697 957b27fca50d2c3b1ac86d771733f39612bae841 f001 T0a-docs T0a-docs
33542424474 1811 fb2e38f5bbd45a2504d9daee09fc4c57a8d7db7a f011 T3-mid T3-mid
33542805417 1697 4f3412b78a5fa64ad01cb9ffedea603d101262ec f001 T0a-docs T0a-docs
33542807266 1896 50eaeebeaa2edb2ce14cabbf0d128e47f47b9c5b f012 T2-extract T2-extract
33542815580 1703 359f7360ddd39dc155637076e76a4e855c27611f f013 T3-mid T3-mid
33543233252 1811 b149247a0fd78d912ec906f44c2cf30bae7e5209 f011 T3-mid T3-mid
33549536985 1918 738174f74a2fbc2ad53ee9f4b2aed1eea2214d8e f014 T3-mid T3-mid
33550775043 1996 5e328814fc99ffe588daae4f0f1e8cf5066e084b f015 T4-platform T4-platform
33556880174 808 c19373cd4eba3dc836ccd9b12569cceb9aeab970 f016 T4-platform T5-core makefile-by-name
33557912833 808 909d01dd80de2c2d2b3518b60e33901dd4f284d0 f016 T4-platform T5-core makefile-by-name
33558106767 808 4b33971df0796fe841df867ca202f28a59a5824f f016 T4-platform T5-core makefile-by-name
33558842421 1697 40e5e373ab27bba3129405f346aa5937f9c93850 f001 T0a-docs T0a-docs
33558848235 1895 230f42454d76e103209e3baa54f6b7bf563cd2bd f017 T2-extract T2-extract
33560684540 1998 45b97287c75a0d2f49a28ac900c2dc00d518e594 f014 T3-mid T3-mid
33564199875 1986 b2ac3c58a99c534d4882674b5651deb61296be42 f018 T5-core T5-core
33564348882 1697 40e5e373ab27bba3129405f346aa5937f9c93850 f001 T0a-docs T0a-docs
33564426767 1976 82e1de8cef4121db80c53f4e144530dcf5dcb747 f019 T5-core T5-core
33565344188 1876 9d4ac2b98b9c19633ce2cbf8d9c6bf9edc57bba6 f020 T4-platform T4-platform
33569142503 808 89f1edcb50ecdec1e0db64002f1d8eed07164d46 f016 T4-platform T5-core makefile-by-name
33569301401 1999 2641ec858dff11307d42e60e8e3dccc627d02f0e f021 T2-extract T2-extract
33571644044 1875 25a7aacc87250bff752ff61cd33e9236be5d1310 f014 T3-mid T3-mid
33571903536 1877 62b44519c68b403ea44d9a85ba8ee74cddee66e6 f022 T3-mid T3-mid
33572811960 1912 63f79365e3217d2ef7a3615567bb96627f3fd6b3 f023 T3-mid T3-mid
33574864607 1968 a3e8fa3d08691f050f16d75879fd8a84b73ad76a f024 T5-core T5-core
33576560737 1986 e8204092333ee1c38cd681a81abb36f125288894 f018 T5-core T5-core
33577534823 1999 a739a2d81b43eb6938d014e005b306ab27e02396 f021 T2-extract T2-extract
33578326689 2002 976f312ea24acb8b1840ee5f766f3e2588af333e f014 T3-mid T3-mid
33578871318 1723 b769ad6324f866a580d731f5579a9d076cc45cf0 f025 T5-core T5-core
33580573384 1832 844a36b78aa10f16338d24a1a819ca9a31525129 f026 T4-platform T5-core makefile-by-name
33586571817 1836 2f37f61e8848f31f308bce8623f66dc2fdb50619 f027 T3-mid T3-mid
33588509339 1824 fd73c347fbcfd7f66c210577b4d713fc6ce7780b f028 T3-mid T3-mid
33590570368 1862 0e8fc16abce48346d07af78acf3293a805085e23 f029 T4-platform T4-platform
33593858493 1874 c0173a3362878e3cb9e7bf9b65c7ab79b46e73ef f030 T4-platform T4-platform
33593864739 1871 4772f4715172173559ba3f9130ddd90df4416ed7 f013 T3-mid T3-mid
33596260489 1779 98e386e58b753bcf63f67aeeacbfcb94961d035c f013 T3-mid T3-mid
33599715238 1739 4c1b23477b8e0de27feee74eac11c21a1fd64151 f007 T4-platform T4-platform
33600443796 808 53a97f0f74e34a55076ea3d1f1dc0be562b1c7b9 f016 T4-platform T5-core makefile-by-name
33600512620 808 003f8f4c2efbc22fe72e2dcab7fec5cfd1b42cd0 f016 T4-platform T5-core makefile-by-name
33605426923 1931 5cb90f64adaf9c5a38b591971aa7c09e3d9e958c f031 T2-extract T2-extract
33605453120 1936 27d4d34f7973823530922ec5485ce19a64589e22 f032 T3-mid T3-mid
33605453184 1907 651ae3c2af1a22ec61a498b39350785aee480973 f033 T3-mid T3-mid
33605453306 1913 a6b1a69a7c9a9ba67ac16c88e9c11de3524f87cc f032 T3-mid T3-mid
33605454090 1915 fcbd12ee1ba8580c1c83ce7ba797613ff5af00e7 f032 T3-mid T3-mid
33608282620 2005 256b3d3722247f451a2213023dc36a1ebd5827af f034 T2-extract T2-extract
33610039868 1811 cd8a5ed0d7c5674072f5d422b12302aa6424fda3 f011 T3-mid T3-mid
33610440955 1946 4bfb074b2892ec181e6b16dee3ee984af6dda201 f035 T3-mid T3-mid
33612193345 2006 349aecba9528427fa635975e246f5b8959b14f30 f036 T3-mid T3-mid
33612686565 2002 3d9a6cf5cb7fd39c6b5fb4dfa43011aa70f736ea f014 T3-mid T3-mid
33614840408 1947 ec0ca565a6571cad6e1c90914400b8dd4c2098e9 f037 T3-mid T3-mid
33615574338 1912 97517a4652f376cb6bf096255e235edf456f86b1 f038 T3-mid T3-mid
33615892072 1777 7fedaba331d8fbd241582f3ceeccd70af0ade149 f039 T4-platform T4-platform
33616176807 1949 91a3c6a5173de50fb47c4ebc5a8ba448598f7d5a f040 T2-extract T2-extract
33617264651 1913 dbba7573016e17f20fe5afaafe5cc2cddd98744e f041 T3-mid T3-mid
33617265496 1936 ac46c17fc37f78ae518145e73d51b46ca3013fac f042 T3-mid T3-mid
33617265877 1915 adff62e7f20dd439129d17c5dc028c7069af92f2 f042 T3-mid T3-mid
33620018950 2008 c36b4fbc446f9085704a6073b81ec743fd4180fc f043 T2-extract T2-extract
33620040078 2009 592894a4387a50e1d128ff6a2695ff48f8cc32a5 f044 T2-extract T2-extract
33641828637 2013 40f2722d6166c1f97fa1667c1e63e8c97e6f05f0 f045 T2-extract T2-extract
33653455785 1809 88269ac2ee70ab88f3ee8d84bd509399e3a8f3d6 f046 T4-platform T4-platform
33654490848 1809 b026a6907807f7a411e7da4de45d0cedc2e93dc9 f046 T4-platform T4-platform
33656319321 1819 64ea496a7b3623f5f8b28343aba99535b282d86a f047 T0b-nonproduct T0b-nonproduct
33658603433 1773 572725e61a00b9deda7407971dc9290477170515 f048 T2-extract T2-extract
33658798739 1811 3f08fac5139a4acd3f393e9ca78538c1ba8b94d7 f011 T3-mid T3-mid
33663175876 2016 0e3c9a3d52fbdd234db180894e94f434efce3505 f049 T4-platform T4-platform
33663200103 2017 1e92269d7562d89a816271c4d99904ee53a94606 f050 T4-platform T4-platform
33669707175 2016 e0785b353af1af3812d5d14cad55c839efc5749d f049 T4-platform T4-platform
33683309620 1649 ff8714662728f55a5ff368462fd8daedc80b89a6 f051 T4-platform T4-platform
33687771558 1832 69a9c9005a6d81889cc7fe5e0355e610d2a08e6a f026 T4-platform T5-core makefile-by-name
33688645901 1723 83ca40ced11f51e479d4d868ed167b47cfe993d9 f025 T5-core T5-core
33700059940 1896 c4e902849f8062e37a0f0ddaed1c6cc042780cb3 f012 T2-extract T2-extract
33700061299 1976 7b528b3247257fdc8d503a4af617007bbe3f8454 f012 T2-extract T2-extract
33702992083 1874 4529db7eb56c5793227bdce34723827030796a8b f030 T4-platform T4-platform
33702997094 1871 2283bcb9eb85b17863786608a0c41b0937521b29 f013 T3-mid T3-mid
33703360466 1998 41e649281bb9b27ead5d6ec223a0f0c78924c8f8 f014 T3-mid T3-mid
33705255783 1918 426e415f5427f2fb2e9c6f918cc7cc9abd9c109b f014 T3-mid T3-mid
33705617151 1881 57ef0a6d8662e9854dc5711922f801c3da381071 f052 T3-mid T3-mid
33706085211 1897 2c76563a137876b110cec15b9b6fa08d35069f73 f053 T3-mid T3-mid
33706763915 1880 6b801eec68d6465aad2020de438e629cead99f5c f054 T4-platform T4-platform
33708048488 2019 ad04339030bbfea4e77139bcfa9a87356c1e0e5b f055 T1-tests T1-tests
33727721488 2022 c846ed7b37331696543e71a70a8e5c8dd301467d f056 T4-platform T4-platform
33731062466 1874 c38d19139ad9657ab563951ccbf83dcbd1bb2435 f030 T4-platform T4-platform
33731071275 1871 adb49b3a28826e6eef871b1777b63668199c0463 f013 T3-mid T3-mid
33732861010 1811 da09b73bc0c03f3909a0153d75b8479220c31e2c f011 T3-mid T3-mid
33739760521 1811 e79a7d73b45644d86ab3ca3c7af84aa04aebe75f f011 T3-mid T3-mid
33740406928 2024 1770d68a2c934501b889e625dc94ef933e3a1815 f057 T3-mid T3-mid
33745916176 1811 bbecc9b9e92af203a03433e8cb6968e1e8d21cb2 f011 T3-mid T3-mid
33751987784 1996 7c4b5fc8a7c9ed2f1cf3b675bb821dbbccf2c1bb f015 T4-platform T4-platform
33752032210 1811 b64e2170009159a5111b1339312512f4e66b1b57 f011 T3-mid T3-mid
33754008677 1874 956489dcf88d0fd571efc493e6a93bb1d96760c7 f030 T4-platform T4-platform
33757191193 1767 06576511ce84b54b2177a75ae82c3af7dc897160 f058 T4-platform T4-platform
33758229230 2026 73f4a432e4ac1d575635a490d96e0f5ea588344d f059 T4-platform T4-platform
33763283492 2027 0ac290e2e243148b9e84bb682cf1101fe38bc6e3 f060 T4-platform T4-platform
33765101593 2028 1d38be3331576f50ce555d99717974060a6a16a8 f061 T5-core T5-core
33765957247 2029 be66a2d2c6f1538a4a831eb2c91622997be5b5ec f062 T4-platform T4-platform
33771464451 1809 e65378e6a5faf49abd7a0987994d7c98a9f834ad f046 T4-platform T4-platform
33775413774 1809 d402623c20e1dbff62e560a865fd4955798dfbfe f046 T4-platform T4-platform
33779258079 2030 3507d9555d73aa746cde23139588081c6c02d2e2 f063 T4-platform T4-platform
33799475629 1819 b25b1f65b402ba5395da846f05a306746055594f f047 T0b-nonproduct T0b-nonproduct
33799483900 1918 74700067c48b967edc4ef723b658a0fbb04f291c f014 T3-mid T3-mid
33799487158 1880 e63292a8d4036fc2618ecbee973bd6cccaaaffc4 f054 T4-platform T4-platform
33799492841 1881 4d4af28672729a5c1326974684728c9ee150926a f052 T3-mid T3-mid
33799495512 1897 429141261daefc04fc94f35300f7dbd899ed28f2 f053 T3-mid T3-mid
33799501343 1767 8a74d814e5149be17f2fc70f9a59d0c166604273 f058 T4-platform T4-platform
33799505794 2024 380b1c5af1d5f3e94248752266031b3035080793 f057 T3-mid T3-mid
33799508762 1811 f47cd47afe9e7e144f3c6d49df37c5b53d3d1cdb f011 T3-mid T3-mid
33809990745 2040 12f5e2c3636b694cbcc76b197815a983349fff5a f064 T4-platform T4-platform
33816056229 2041 588cd5543a617c73646be61de6926b9ac7004064 f055 T1-tests T1-tests
33816348076 2042 9933548586760fd81f2cf76673ec42c338ff62eb f065 T0b-nonproduct T0b-nonproduct
33818108315 2043 7109e5e7b013f43230f735f93043ae608be5d98f f066 T4-platform T4-platform
33831172851 2005 ec28e66434bc03ec33dcd05563b3bfd7f58efe86 f034 T2-extract T2-extract
33849949280 2047 9104feb67f78959e64ddff78d236e0dea475a434 f067 T4-platform T4-platform
33850387435 1976 aa3627d3bfb38627d700043a599622bb0e80ef2f f012 T2-extract T2-extract
33850532415 1709 2efb7a6323f992d5c407cc85e37b6d167c184ca1 f068 T2-extract T2-extract
33850538524 1721 dc703dad4a706a0f2c8130a869922677dba09ae3 f044 T2-extract T2-extract
33850542851 1730 90ba8990764674b2eeaca6fa372c654a63a04f51 f069 T2-extract T2-extract
33864581134 2048 f404dfbc7ae47f9912b18a0554a5e5d91f65be1a f070 T0b-nonproduct T0b-nonproduct
33868849245 1816 2ea73059f894df564e21be409908c2b5d3d53b37 f071 T4-platform T4-platform
33879400189 1996 8a1f2503c33bdd9a4d87782493af5640a505594f f015 T4-platform T4-platform
33879404796 2041 e74f041b3387e89fc3d3bc727cae772516099e54 f055 T1-tests T1-tests
33879411345 1809 b435bd87b6d0e6a8da43c2964829927fff331f5c f046 T4-platform T4-platform
33879415252 2002 c6ff57df0c216dc0fc86442f111607d8b3bd7ef8 f014 T3-mid T3-mid
33879421262 1741 fbda6bacf78b47e4c498dfb44ae653f9d10a4add f072 T0b-nonproduct T0b-nonproduct
33879427336 1742 e63bc6f06a03e93fad9e962c9b9aaf4b0309306e f073 T1-tests T1-tests
33879430001 1976 6f7a4af4a3a1716a5aeb5cae60a112e2231195a5 f012 T2-extract T2-extract
33879554682 2043 7e47043acce9bdfd87e7becc7fe7a348b02753ec f066 T4-platform T4-platform
33883643915 2042 6b0c44afcfeae65eed82e2dbbd58f00b043f21f6 f065 T0b-nonproduct T0b-nonproduct
33883715988 1649 f195c6aa81f425882626cca1186116ec64a0143e f051 T4-platform T4-platform
33884431041 1649 89186a26bcc37ae51f943b785270aa66716c832c f051 T4-platform T4-platform
33885636468 1933 8d46d2586255fc85e35f00ff8b814424b140e623 f074 T4-platform T4-platform
33887517545 2051 e2195dcb8f6fba33b72da60c1f3fffc83cdcd714 f075 T3-mid T3-mid
33891234168 1874 8069c7cd4c73ff7ea4513d38d7dfd08c350472a4 f030 T4-platform T4-platform
33892259683 2055 36bee9039848829c7d2271c44a46515cdaa8d870 f076 T5-core T5-core
33892321505 2056 24f54c0eb64c516941abee2ad0d7de758b34ac86 f077 T2-extract T2-extract
33892540012 2055 132e8fc32309e843ccd97138c8f032e317333673 f076 T5-core T5-core
33910645341 2051 1067e519c04f6273dd6e2b95b16bd0a9db4c3056 f078 T3-mid T3-mid
33913569180 1851 20c7dd3dbbba55770b82a99f6fcec6120f362873 f079 T4-platform T4-platform
33914255147 1914 7b92d2a6218936767b112081b9aa4e79d5b81dd9 f080 T5-core T5-core
33917205956 1849 39c74bdd5c8b5dc3bcd43f962195add0ba794150 f081 T4-platform T4-platform
33918309198 1796 ad479b61e9f23c01606f42c7d44ca174f104a2c8 f082 T4-platform T4-platform
33921848127 1597 335279ee403922c0b84c3972c3fc4782ffe878bc f083 T4-platform T4-platform
33923008470 1597 0094133cca7d92e587c4d876ccdb37140515fa7c f084 T4-platform T4-platform
33923354326 1766 797a11a15eff6154428aeb268c0b55edb5647a7a f085 T3-mid T3-mid
33924300711 2059 e0f37d7cb305cd2ff1d709c3ba1198cf950e83df f086 T4-platform T4-platform
33924381537 1597 3cd0043d0a3dc282c6a36ae0a262ebe02a4cec0f f084 T4-platform T4-platform
33932138466 2060 3a8b8e82120872a56ca4d0157260569d90abe85c f087 T4-platform T4-platform
33934626037 1784 4cf2acf2b7483ffa0fe3918f1a38e6d72d08b743 f009 T4-platform T4-platform
33935475331 1597 b900338a71c7395ade83c4e371ca277f2cb5498b f084 T4-platform T4-platform
33940288019 2062 9b4a3bc91e2f4d50e00deccf236c6b0db177f4eb f065 T0b-nonproduct T0b-nonproduct
33941972045 1597 10d05b38a63ff1efa62bfe2d419b01c72bcb2bc9 f084 T4-platform T4-platform
33944160492 1597 a15d99e39fd9ea04722f064ed02dc726b13d2943 f084 T4-platform T4-platform
33947308282 1874 9ae4ba7648101603eada6e7cd54c6cdc284617f6 f030 T4-platform T4-platform
33947974349 2063 f8132b42ac9ee1fde464866b5f3b08939528f267 f088 T0b-nonproduct T0b-nonproduct
33948072527 2064 3979f12a749232e1f934949f58f68c83a8b66014 f089 T0b-nonproduct T0b-nonproduct
33949446878 1597 9427f435158662074d6576053a6675a403696543 f084 T4-platform T4-platform
33951152502 2065 7dfd2c064911a7f2b246b7edd610fab4f993240c f090 T3-mid T3-mid
33951945765 2066 4b73a518d720bbc8773970402ca96f5661726b78 f013 T3-mid T3-mid
33963617997 1874 41f52a0f3b948468c468ddcca36489eb5f42fff3 f030 T4-platform T4-platform
33970672289 2063 3b173288feafebac3f0845f53e481f69adcd70b1 f088 T0b-nonproduct T0b-nonproduct
33971609329 1784 b9da6dca03126ef0c8cacccb3321dd5d69ccf34d f009 T4-platform T4-platform
33979110285 1822 7fbaf02904363d38c88f9b749b4523a614feecd5 f091 T3-mid T3-mid
33979848957 1759 683d4bc312625a3d26819635673529ab02a0d0e1 f092 T2-extract T5-core makefile-by-name
33980369725 2056 4f82a1e56f0eef435fed0eac5c8dd06b5b30c2fc f077 T2-extract T2-extract
33980647419 2065 3e1a22897ccea3404b95126112009e8b1a719a39 f093 T3-mid T3-mid
33980897876 2065 73c509f4246b0f97ca772b6d9a3159181a7fa6b6 f093 T3-mid T3-mid
33980899537 2066 c4bec4a6b38ed66ccd97df06490e051d3d9d81cd f094 T1-tests T1-tests
33982832791 2067 b7e18c8940d2bb58d85339d27da0754e5aba92e5 f095 T3-mid T3-mid
33984123524 1716 439659b333895a49174dca1fe4343f356616210c f096 T5-core T5-core
33985769258 1894 b49c7a736c0789069e5c2d07a301143e382af53c f097 T4-platform T4-platform
33986188532 1739 b51411753e9423f1c67f810ecf54f9fce903cefc f007 T4-platform T4-platform
33986691207 2069 3b9052c955a9aafbb3baff874a93bba19afeaace f076 T5-core T5-core
33990039141 1941 b04f54506ab42eddcbd89232a0d47d8355edd596 f005 T3-mid T5-core makefile-by-name
33990592318 1971 f89f01e9456c8241a28fb909d930ec8d88b23239 f005 T3-mid T5-core makefile-by-name
33993950950 2059 3007dbf8426764a92ace4da111836b86bec6b508 f086 T4-platform T4-platform
34001348423 2056 e384f88f90d1f13ac95220f15de72e888a654c98 f077 T2-extract T2-extract
34001350759 1822 eb3bd818b597b9c1f317906b350cbf323f2a4995 f091 T3-mid T3-mid
34001352917 1716 26682d58c7595c4166d7b7d274885115f2dac324 f096 T5-core T5-core
34001355464 1759 b35f73123302d8d101323c3621e6fe755d4970c6 f092 T2-extract T5-core makefile-by-name
34001357884 2069 f8951524252b71be8d8d3ea1f86d03eb97d6e9d5 f076 T5-core T5-core
34001359042 2066 7da8bf15838c42f8e3f366fc2188ca63f2b61942 f094 T1-tests T1-tests
34001473433 1739 9a5e3f36a9580f76211300faa8b7ed5942169b47 f007 T4-platform T4-platform
34002279390 2070 c1b9c451c7f124936e635835ca4dd0cf8bf7d5b3 f098 T4-platform T4-platform
34003689551 1860 a44732b6f8ee6409cd12b135f14867f1b5ecab33 f099 T5-core T5-core
34010341126 1998 04ba2fa368db12a80091315bfa649626a36145e1 f014 T3-mid T3-mid
34011344933 1968 17b6dfe68b65ae7fae797487c69efdf110b0cbae f024 T5-core T5-core
34012479012 2065 c72fe3222a5f3caac2e87636252931f3cfce2619 f093 T3-mid T3-mid
34012788868 2041 874a972fb7c4eb06e739dce0a390fffb85d31f83 f055 T1-tests T1-tests
34012788874 1976 eb80a4ecff04d8bfacef20e1aa2ee29445a782ff f012 T2-extract T2-extract
34014013202 1874 a3500040e69af8284e3cac1398e00ae3d00a0524 f030 T4-platform T4-platform
34014527172 2065 60449f895dbaedb4e74c4f155a8e214d78486e12 f093 T3-mid T3-mid
34018899805 2072 b0f9398d59c0c8a5bb03b80eba84d30e630160ec f100 T4-platform T4-platform
34019712929 2072 06644428b6d0a6295414d23d2c0613f29a73e1d7 f100 T4-platform T4-platform
34019861168 2072 bae6977e91daf703b812136c70556cefe4e3c283 f100 T4-platform T4-platform
34035459918 1968 f3e1253859c299d194c608d91d5c89065ca0c9ff f024 T5-core T5-core
34037162475 2073 5abd77f5d9580d75f8600914b46495be21e9a4a5 f101 T1-tests T1-tests
34037415003 1971 9540d64c52aecf89ce8b0524b8fea69c2aafb0e9 f005 T3-mid T5-core makefile-by-name
34037489952 2056 9cb2e9dd1c9fd4e6b3cb9f939ec46264b05c75ab f077 T2-extract T2-extract
34037491375 1716 8ab6bb6edd69a4ec64df07c3910a5c035b42c937 f096 T5-core T5-core
34037493874 1739 53f55ebe72870695c15ace98101df5af6eeef38e f007 T4-platform T4-platform
34037496632 1759 bb21edbbbba442364b88598af97478a97e62243a f092 T2-extract T5-core makefile-by-name
34037543448 2041 99fa463023fe63520d5a23c29f777b9d91d553b7 f055 T1-tests T1-tests
34038315061 1941 ddb6533cd9c16e8e2be12aa791521420fb427881 f005 T3-mid T5-core makefile-by-name
34038319912 2059 306c02d44e9a464faf68640f183fa6e82fea0907 f086 T4-platform T4-platform
34038901925 1971 ede0e3b111054c165b0237124084894b45e0e052 f005 T3-mid T5-core makefile-by-name
34039040074 2070 9846c3f1711d557c78426913efaff340e37feb4e f102 T4-platform T4-platform
34044235267 2074 6db22aadb6e5a92b9b193608e2ac3f420e461ec3 f103 T3-mid T3-mid
34044659567 1998 c8ea1cad0dd34e6f6f8c542014a1a22885f67ee3 f014 T3-mid T3-mid
34044858364 2075 086422020f5fd9396b46384b10413cdca6994c96 f104 T4-platform T4-platform
34044911261 2065 d550dcbe73558bb773211a0ff9463daff91721fd f093 T3-mid T3-mid
34045088264 2076 a08b9ecbbda54793275cbd4e93fecf0ad4ff1c0a f105 T5-core T5-core
34063281067 1739 1d35def402f586b4c18bba41de33c881eda8cfa2 f007 T4-platform T4-platform
34064340654 2079 e410c86f270a013d79639577d82009698b207e8b f106 T3-mid T3-mid
34065487385 1998 98899ba5d62c9a66d038b24f3857b137d03d44b0 f014 T3-mid T3-mid
34066407580 1716 e3c0010267e783b4270e0ac7c8ee6332b61aeabb f096 T5-core T5-core
34066408930 2076 1b89228d90b32ddd46f494df8755f496f6aba030 f105 T5-core T5-core
34066410797 1759 fbe9eae11c237459da50a06ebd83f709d655d491 f092 T2-extract T5-core makefile-by-name
34066413612 2065 8eb63ecfe79ea1f33fd911b8913a8b704ecde9d8 f093 T3-mid T3-mid
34066415650 1739 76d0382f8f958238119c8e36bbdd204838d15db5 f007 T4-platform T4-platform
34066415871 2079 dab58c0701d8806ddac2783949f1a478225f80bb f106 T3-mid T3-mid
34066418484 1998 a143b0882d0457f2f56b2d734128f914325d555f f014 T3-mid T3-mid
34068996256 2082 16a739d32314faeee5539386a92afd2363dc1879 f107 T3-mid T3-mid
34069613782 2083 77a0c7d94156a43f19047330c7356be4ee12cba5 f108 T3-mid T3-mid
34071276309 1874 4ef5672c53777f9dd316510dacfa3b88e2166fe2 f030 T4-platform T4-platform
34074515665 1894 088fedb2d25b869621fd93db05b2f3aa69e30e65 f097 T4-platform T4-platform
34074587051 1894 4d3d06ac2aba832ba774cad396a3f445b290f0b4 f097 T4-platform T4-platform
34081576339 1971 e5c17de6217ec5938d18d8c51e2c0d7ead116a19 f048 T2-extract T2-extract
34081831353 1968 2b1dafd83d6df8b0ad8a064e1b23bc27ecf4de7d f109 T5-core T5-core
34085524473 2084 4e0b7e1d168744438f0602735c4fee3970f3caf1 f008 T4-platform T4-platform
34086907163 2082 d8dbf01c3f90257d98c7e23a21c5c6f7f64d3a4d f107 T3-mid T3-mid
34090735558 1723 c8e7f7e3cc26f1faf8eaff5889d6ff0abd75ae9e f025 T5-core T5-core
34090831729 1724 70b411d5cf392e1ad6528e9f78d391304368ee30 f110 T5-core T5-core
34090999743 1725 8152277aebdd974badfbe0ffce13dfd231a23732 f111 T5-core T5-core
34091031555 1725 980934b2a2cf4c1dd32163894b500e0c96587bc6 f111 T5-core T5-core
34091168476 1726 fa11ee4193bb7537e18865fb649d1e73e8be1db9 f111 T5-core T5-core
34091245271 1727 f9a7e1fece6264d1677ed5dbbc2894553f7ecebf f112 T5-core T5-core
34091261353 1728 4602cb100fb0dd404d49531158204fa3f00d9ac2 f112 T5-core T5-core
34091378540 2072 c72cb8bb950019b5451325e52940da956250f0d0 f100 T4-platform T4-platform
34091504731 2072 bc945a4d6c6716ee22d314b6b53a7d50ed007081 f100 T4-platform T4-platform
34091593211 1725 ca6a5d5bc408d59af6ae9ca1edab0d169664092c f111 T5-core T5-core
34091642820 1726 f870ce4cd80c0882568ba19438a3cb6ebb2e43a4 f111 T5-core T5-core
34091649819 1727 8d4c416579db0148869f3f5c7ffed3e0e90c4417 f112 T5-core T5-core
34091656935 1728 37e213860e8f8390f54880bfff717345d2da5689 f112 T5-core T5-core
34091819685 2022 84fffefa48cbb60ca1533d2433921787c8ea1031 f056 T4-platform T4-platform
34092213233 2072 838fb7090f3a08920363e9ee793824b4d6202bef f100 T4-platform T4-platform
34098390879 2085 9f57edfa576072398728eda2c6fc2585d2ecb063 f113 T3-mid T3-mid
34109049475 2086 b9d550b6f3e1c08ce6cf59140601205d609dcd52 f114 T5-core T5-core
34109055503 2087 45b28f0b11d3e74e28e492c6c234a0f7ed0b30e4 f115 T5-core T5-core
34113927999 2086 2082b2d2890ceb007e04b9ba77129d14a8f4df52 f114 T5-core T5-core
34113933101 2087 9a7445e93de55eaca53855057ce6ce1a6f234564 f115 T5-core T5-core
34115081932 2073 8e29e1568f442ed9422e0fcd24d2594ae0ba7893 f101 T1-tests T1-tests
34118689934 2089 dc34e63a0a0aa29cb70456b692733124aba65350 f046 T4-platform T4-platform
34126118251 2090 6b56d78e1bbb5715c9d0c715453244187bd299bc f116 T2-extract T2-extract
34132427862 2073 aec9bfe3751d8c1f2eb2cb48d8fe584b9d3fd973 f101 T1-tests T1-tests
34134175243 1725 8d57fa45ae548e23b11083072447a62ff053da86 f111 T5-core T5-core
34134183283 1726 c263f48d0307540b0837726ba5a3c2cd9d2ea012 f111 T5-core T5-core
34134191503 1727 94119b3803e276a4cc45f4b92ddaf3d1377e3889 f112 T5-core T5-core
34134201351 1728 f588bc374ab91697ccb4467f5df9686ada5cb62c f112 T5-core T5-core
34138662959 1784 85482e3c599d454d76ccef5e862a5dc6c38a898e f009 T4-platform T4-platform
34139187715 2091 b974adf6ed9b82d8669d1a65b1757816ce0d12df f117 T2-extract T2-extract
34139262637 1784 a21562666317f752f9921109a0c70427f3febe04 f009 T4-platform T4-platform
34141564461 2092 d1abca20c091b9623187c5cfc61f251e7bd7796e f118 T4-platform T4-platform
34143404498 2091 ec56ea8f60f0058bd1cd0fffb38cc0153b99f895 f117 T2-extract T2-extract
34143899077 2092 1f08f82adcd979ba2f0efb38a1ccfed13901f76c f118 T4-platform T4-platform
34146676636 2092 4f8290aae42ad3f2ca4767089057e74756311743 f119 T4-platform T4-platform
34159409001 1561 b939a2d6ee76e482288a0dbbf712cd94d0d57164 f120 T3-mid T3-mid
34168455959 2094 7219a24cc692500cdeb5cc192adbd47ac8bb6ee1 f086 T4-platform T4-platform
34168458726 2095 d3eb7e9823b43a8e2e5917d2d234367c1f88436d f091 T3-mid T3-mid
34168461878 2096 9e1faa7327f8192101f16d8d6d81020937475166 f121 T4-platform T4-platform
34180076668 1561 6875483ffe40017f79884872a325e2ff6b37befb f122 T5-core T5-core
34180195791 1561 a7941a4d2094aa6bbb0683e3a17371a0463de209 f122 T5-core T5-core
34186497464 2097 d653052508998144e39357a37f72a12448c769f1 f123 T4-platform T4-platform
34187802611 2098 5f9ac15818e3bbda9e031db54867dfab2b6c531e f124 T2-extract T2-extract
34187888044 2099 95f252447de4ae08707b199f02f296f28f8dcb21 f125 T4-platform T4-platform
34188664789 2100 2a47a6556cc14dd5ac036b3c2f3225c9925cdeec f013 T3-mid T3-mid
34190172584 2060 8622d6a0ed4935aedaa336d216d8b6b269732de5 f126 T4-platform T4-platform
34192737413 2098 f6c310c08e6dad38b92e953c85c7c5876f6df839 f124 T2-extract T2-extract
34194384003 2097 d9dec5dd3b334a514113670b42732ca9ea4a15de f123 T4-platform T4-platform
34194639858 2100 669f6a3741ef7b875aa5eaeda317acf1900bcd33 f013 T3-mid T3-mid
34195390569 2101 0c1d6f68dbf3f2c8215ae9c52003d785ac874a22 f127 T1-tests T1-tests
34195424010 2060 3c854ae217c4c5d2f48d8aaeb136541bb0d8b52a f126 T4-platform T4-platform
34196235240 2103 9f876f70ed13636a8f9a25ba043e8f682c6688f6 f128 T4-platform T4-platform
34236364592 2105 22e0335d94552dc1a9d9ef0ee8221e7665608209 f013 T3-mid T3-mid
34240237866 1784 18b2f8670c64fd83221617c5e7970fb95af17454 f009 T4-platform T4-platform
34244310689 2105 dcb10fe902905926eaf9325bb03254dde9cea6ab f013 T3-mid T3-mid
34249699686 1823 b09ef3df3289e74bae129df4df29886fefc75484 f076 T5-core T5-core
34249700997 1823 b09ef3df3289e74bae129df4df29886fefc75484 f076 T5-core T5-core
34253442941 2109 e65fffc5b1ac269c16d4371fc8c1ccbd102e267a f129 T5-core T5-core
34255410324 2109 840de91e384d2ea36dc2360ee12ace26d25b680f f129 T5-core T5-core
34257609138 2111 7b4eb4a00ac600060be4836ee4e1bbfa7bbedf24 f130 T4-platform T4-platform
34257629348 2112 21591d51168d26f931f64d62edc36013cd1f6249 f131 T4-platform T4-platform
34257632907 2113 00060cec854dbd38d7864aeb95a2d8ba1baa8ab2 f132 T4-platform T4-platform
34260896139 2110 d40ed1a6bbc968bbddf90abd3b38e74a131a1b5a f133 T4-platform T5-core makefile-by-name
34261770203 2113 0567dc70a097f36a953368978d6728b0a412b229 f132 T4-platform T4-platform
34261855843 1768 e8dcb28ba719e4272f605f3b961422bb1f3c3fad f055 T1-tests T1-tests
34263433901 2110 7bf366f63b3fd5211cec19e483ee2ed27145ca14 f133 T4-platform T5-core makefile-by-name
34263688699 2115 013e1f7ec7d4b981a1cd28bf35d5767c195f8447 f134 T4-platform T4-platform
34264299704 2116 3a4160b4610f83ea26daf69f1ffe7cb898bf48ad f135 T4-platform T4-platform
34273160491 2119 8135be28a764fad4ee55abe466762bbda7ebab74 f136 T4-platform T4-platform
34274955966 2116 7e73b48ef155a0e14a6e058be56287232e3976ea f135 T4-platform T4-platform
34275249072 2111 91e31211df9dd61459617d857d46b71699e34df8 f130 T4-platform T4-platform
34278140735 2110 a3c24a71b4c28e805b912f9f928b6b0b151f9495 f133 T4-platform T5-core makefile-by-name
34282381014 2113 ecfb9642e153ffc656d0bc2344b17e9d454ff916 f132 T4-platform T4-platform
34288397854 2115 92cb3b9a4bebbc79bd283e447104669ca068ea64 f134 T4-platform T4-platform
34315414268 2116 e45d705105a63f2fd9b35ab9e37ffbc640be8231 f135 T4-platform T4-platform
34315782814 2124 8134712dc43b159fd863244ce53ba23641f20f8c f070 T0b-nonproduct T0b-nonproduct
34327374288 1723 21ae02154a198265d7dbed13af4280aa2b1e5690 f025 T5-core T5-core
34327382260 1724 87da4422c34feb4df8ff369dab8d770c33d142eb f110 T5-core T5-core
34327387577 1725 d82f760040412d51d424f3c287d277e89d4fb199 f111 T5-core T5-core
34327494146 1726 9380b22a24b3ec85482fd209475f5b1641b5f699 f111 T5-core T5-core
34327502315 1727 579336cbdaa6493f47ed4de10f64387b6f7f16b8 f112 T5-core T5-core
34327526954 1728 9d5cfa53098003cb68bf1812a94ca51f5e046056 f112 T5-core T5-core
34379806132 1823 5e5e433d94880c283400215ba79094225a8b33df f076 T5-core T5-core
34392601650 2116 9ef7da8cbfba8de5526ccd05e7196f5fa73af8f1 f135 T4-platform T4-platform
34392735468 2110 91c63be5d4e41d44e0746ef533af4a0b1967d6b3 f133 T4-platform T5-core makefile-by-name
34392998828 2136 557cb0102863e66cbd5cc622509a29a4964d1831 f137 T5-core T5-core
34393093741 2137 4fa11d1ab0b5807a7eaed645a6e3675f0663def8 f125 T4-platform T4-platform
34408696449 2138 8116672a7e3a02aecb5cb0e6fd9e685cd5066fe1 f138 T4-platform T4-platform
34413434571 2139 df579830f3941e5ebaef73dc87d183aee358fbcb f139 T4-platform T4-platform
34414749652 2139 618849512d01a3d14fcb704532911f9d02c23436 f139 T4-platform T4-platform
34415791973 2139 62bafb76b380d3af1cd1d6228b4afea0082c2a7b f139 T4-platform T4-platform
34433093443 2140 a1d11e79087bb1284d6126b02b22c3ce7e72ab07 f140 T4-platform T4-platform
34446903088 2089 8f3c15044b8200a15ee1895c5127f4bf117da493 f046 T4-platform T4-platform
34456518090 2160 f7b56b900d4726221de2882d5a5d076b29f6758f f125 T4-platform T4-platform
34456521294 2161 c8e244d8ff1d51e57c9d757a8e6bad80dc67f107 f091 T3-mid T3-mid
34460157213 2159 8d353a573dcc4be7ae69a74685eeb819a5626b5f f013 T3-mid T3-mid
34460157936 2158 b5f119c3f27bc3601bc048b16bbfaf57af3908f2 f141 T2-extract T2-extract
34464851279 2161 6790e1ac0fc134360cda6544befc1af2e29b9605 f091 T3-mid T3-mid
34464853698 2160 b3af44832790fbc6dd6a7e09d18362f55149a4da f125 T4-platform T4-platform
34465154636 2164 70926a2956ce986bfacbf6e7ebfd398fd91ff266 f142 T2-extract T2-extract
34465653349 2164 9115f5154383bd091e125c956aa4f241ebac1c7b f143 T3-mid T3-mid
34470429949 2159 7e111f877b85b79efdd371475d7f71e61174f95e f013 T3-mid T3-mid
34484066063 2092 b2bb18cfc79e129f50b5da940413e5735bce09b1 f144 T4-platform T4-platform
34491583983 2092 b317506b5a62a3383e763011e5b148a5afd92a89 f144 T4-platform T4-platform
34491670375 2092 b037b488be4620a5e92424e7e390ee625cd7e5af f144 T4-platform T4-platform
34524166740 2170 5fc107a0a61419faf0a2495ab923aba25a37ddb5 f053 T3-mid T3-mid
34528400072 1823 ac75e587425c60154dbcaa7392c0522764c93647 f076 T5-core T5-core
34551561701 2171 4842e11b2d9f5e6c2ced39d12dc487bcc7d915be f145 T3-mid T3-mid
34595459266 2030 e65bc6b71e32d20aeafb42c00b6097a0e2ded2c0 f146 T4-platform T4-platform
34595482014 2030 eeff3f19ff97d60af4f34298aa38a652fe0705b2 f146 T4-platform T4-platform
34636198205 2179 6091db7104bcaa0b61566272290e04ce448851a7 f147 T4-platform T4-platform
34636557089 2116 f6a29270b72a5373e4d949ed3f4078a40830c57d f135 T4-platform T4-platform
34640188970 2180 527016258e0da33ef883966f618609f4cf58abf8 f148 T4-platform T4-platform
34649361704 2139 53700ee1c72f1a5c2551a83c74d20278e4ac664b f139 T4-platform T4-platform
34649395431 2137 ca36316c6b34a6081de24d69699665a20b16de0e f125 T4-platform T4-platform
34649399412 2113 ca86fdb057b3d7f6d31e179ccbe64629fb1b0921 f132 T4-platform T4-platform
34649404928 2110 33534653f10c052f2a5c7f1953756aeb10499be9 f133 T4-platform T5-core makefile-by-name
34649406520 2115 78a6d19f1eaaa73054639ab0f14c372c53cd1bb3 f134 T4-platform T4-platform
34649409769 2111 951b9ab022df391410528fe98769d091c203fc60 f130 T4-platform T4-platform
34651276769 2181 c8184ed94887a6b5e130726885b44a07c63a47b7 f091 T3-mid T3-mid
34656706072 2182 14bacf8919b64bea19b537b74c894f0f82783bdf f149 T4-platform T4-platform
34676336284 2185 eb5e34cfc141454e849daf413f648e73f74c455a f150 T5-core T5-core
34676377540 2186 f6281874bba9a78ac7543564ff8f76b5262daeef f151 T0b-nonproduct T0b-nonproduct
34676429093 2187 cea35fcc0520a0ba95139bd0210052f09b9cfa20 f150 T5-core T5-core
34696991657 2115 9a460b389ce3002ac164605c3627bb86aa3b9be3 f152 T4-platform T4-platform
34700514102 2188 734906379f235c07cce960ce6a57746ea3be48bb f153 T5-core T5-core
34703996478 2115 fa99ff64eb6579fb15f3aae1aff7da349cb2999a f154 T5-core T5-core
34708249832 2189 22cdf7d03db87bba19a76a072dd517dd88a54d40 f155 T5-core T5-core
34708361984 2190 8c2692c1ee32284c813c05746e833dea5dfedfe0 f156 T0b-nonproduct T0b-nonproduct
34710253103 2189 1ad52f5fb1b3d96255756de01ce30912b1899e09 f157 T5-core T5-core
34712092850 2191 27cacee1c2d3022fa0fe915b03ca4310042b7514 f124 T2-extract T2-extract
34713347879 2190 044fef7957d48a097b48f4ac3c505aa67de8164f f156 T0b-nonproduct T0b-nonproduct
34713405483 2192 9073fe4469bd222ca01bd48249b52bbf7ed5710d f158 T1-tests T1-tests
34720983404 2076 2b80b31306b3dbb9ac8bc0ad50372f724f1dd243 f105 T5-core T5-core
34725488475 2194 09c0e88a6484c5fb696d7fc7b5a5e3f32b8cba68 f159 T1-tests T1-tests
34729075944 1561 e0b9642010cbf003bef9b8b29ba4ebb02b38171c f120 T3-mid T3-mid
34729170802 1561 789c60c86bc75b342c52ddedcaf174d12ccdfa73 f120 T3-mid T3-mid
34745069386 2195 469c3dd996f23cdb629f1bbd6a751bc0f29a99f2 f160 T1-tests T1-tests
34746469011 2195 f9038b84eb3bf82c2dff4cc4ea7f1ec1d8d13f01 f161 T4-platform T4-platform
34747474867 2195 d21e6cc762a1816d8c274174c9bba01601b888ee f162 T5-core T5-core
34747919526 2195 085017ab2172c2f3d4cd3e27251033ecbc5b814b f162 T5-core T5-core
34748079069 2195 ac8f5b8a8137f15e95478be71ee6742e928ba5bc f162 T5-core T5-core
34751940360 2073 232764a12295a59f05619a2bd032c8f92af6f17d f101 T1-tests T1-tests
34752354822 1716 8d4fed61db63559a0961f11f821db21c26e7d24f f096 T5-core T5-core
34752961647 2196 81a8a03c820cb8bccafd0ebac355eede21b5e8bf f163 T5-core T5-core
34752965817 2197 823e720982e1bce047a9243bd9b24d9688dadb72 f164 T5-core T5-core
34752971903 2198 b5b69af7f8d0d1083974e993f0e9b92a435fa253 f165 T5-core T5-core
34753122921 2199 d14bc7051c8a32b2678bf10d5ee711637d606edf f166 T5-core T5-core
34758237901 2201 1167972031d427be6773b86ec6dc9903317360d7 f013 T3-mid T3-mid
34764451388 2202 7fedfe85c63446d3695b75d463a9b8767655730f f167 T5-core T5-core
34769627346 2203 848ce778766d73ab4eb09ba8c98001da8f587fe0 f168 T4-platform T4-platform
34769883104 2203 00411ce7bc41995783bfb16aada673515585446d f168 T4-platform T4-platform
34769958569 2203 d64c9f2afe6b2add648ee1e3eead48acda025579 f168 T4-platform T4-platform
34797944694 2203 581f0c74006beb3edd28babbebeda7e48b3fa1d6 f168 T4-platform T4-platform
34805101784 2202 7b77fd4834afe355cc4c7326ac3a55f0502f7264 f169 T5-core T5-core
34822774278 2205 e0a6133293556d87de6c2ca43de674e3a643e339 f170 T4-platform T4-platform
34823711563 2158 334e0b0849241430d26c3fdb6ffc8ec7a676965c f141 T2-extract T2-extract
34823714967 2159 63cdde702d9eb366b58006986e1498a18ae7f7d8 f013 T3-mid T3-mid
34823719320 2160 c1dd44151fee8cfd815c693392a7c1c72def98fd f125 T4-platform T4-platform
34823723515 2161 80f8bda5670c8ac1956e2a8fac7221c40c12da47 f091 T3-mid T3-mid
34833703378 2203 3b7f2559213ee80f5cfe0e2329c418211e09749e f168 T4-platform T4-platform
34853838753 2207 45c94be627a3088c275ff4f6acdc1f118515849a f171 T4-platform T4-platform
34858382857 1767 036562df35486924c62cdb11a8c9518e479959d1 f172 T4-platform T4-platform
34876200704 2202 9724d903046626df0640ced218259cb58070e4e8 f173 T5-core T5-core
34884655464 2202 9e69cb7d9b7ad4b40fa5f8cbbb653dc70e108130 f173 T5-core T5-core
34886874258 2202 d89ccabef064661794269c0de726021736d72b51 f173 T5-core T5-core
34891515627 2202 d70b79417978a9f3ab0d1aa849879dbf049793cf f173 T5-core T5-core
34898586377 1823 d4ae05a03dae831972a9dae7d2a2dd2ffce39e35 f076 T5-core T5-core
34943783091 1781 7c88e64eb954e9400273d3ebb9f6f88f1aebc165 f174 T4-platform T4-platform
34986091885 1823 955e4c9c58504b3c8e7d3e42866d16fadbb6ca7d f076 T5-core T5-core
34993109423 2212 172787de9c18352bb29545da2426d9d61b260fc3 f175 T5-core T5-core
35010526411 2213 d401186ca09e3992bc0e804af3945fe1cf81d6d2 f123 T4-platform T4-platform
35010533052 2214 1f1f8334ca25502c3ed1d4e91e8c6144d07bb7e9 f013 T3-mid T3-mid
35010542675 2216 729c3acc0fedae0ff77629281caf6dba1ac487fb f013 T3-mid T3-mid
35010543243 2215 783547e4fd79bb6b38f0741b6118a719e4109059 f127 T1-tests T1-tests
35010546755 2217 193c60d66db7eb36c58a7d0803d3936f5dae1d70 f149 T4-platform T4-platform
35010550773 2218 0a1d229ba89c5a7ad6c63c1587f5a938cc890270 f124 T2-extract T2-extract
35010559332 2219 a335422f63be3b5711ad40742bbaba40b2600f25 f158 T1-tests T1-tests
35016014963 1907 50ee111714baa9d3bfe9bdba3b81e27cd1bdfe38 f033 T3-mid T3-mid
35018285825 1913 da7619474d5ad27ff5073cf505b0b6cd5d0db36e f041 T3-mid T3-mid
35037109751 2220 62fd80b842f00bd72e655d23c6afbfe12e199b6b f176 T4-platform T4-platform
35037798343 2221 22ce867efd88dff9c219f789967ed2612329ee6d f001 T0a-docs T0a-docs
35047093950 2072 e5076d215e3a31ee7f7528d9e5ceda83fd8e8a8a f100 T4-platform T4-platform
35048046352 2072 8199efa4710893eb77d7c98d2730683a6c51327f f100 T4-platform T4-platform
35072023020 2223 17a326a58b591e786790e79ceb3be6dc14ee3988 f177 T0b-nonproduct T0b-nonproduct
35113639467 2223 43620d39d27554548fc2d613ed707efc8676748c f177 T0b-nonproduct T0b-nonproduct
35129477140 2225 58c1c0caa7061ae868305dbe59a6c5e67004849d f178 T0a-docs T0a-docs
35142303658 2227 dce2c5be9710c364eaa3a654d417ffcc392b62a4 f179 T4-platform T4-platform
35145042859 2227 3d848acaa7d22885d5040c957b3aecdcc4376107 f180 T4-platform T4-platform
35186676917 2140 b74c03558045bf29b7ef95c8aca803c15d3c7594 f140 T4-platform T4-platform
35272232918 2230 e0becdeb094dd5e898997a2add826a9f84befbd7 f181 T3-mid T3-mid
35273896570 2231 7054e4ff8af76f4fc1fe89407874e9910d0e412d f017 T2-extract T2-extract
35295711667 2233 f0a86ef2c2bc352a479162cca79d1aa64598f09e f182 T5-core T5-core
35303378012 2140 b52df4b0ce5c38667277716cd97ede964684802c f183 T4-platform T4-platform
35303488221 2140 ca3461170fefc959fa7688fae4e212d6e9f2f99a f183 T4-platform T4-platform
35312734228 2140 d85fe1d513ac97c8044b1065bbd0b7b9ebc87ec9 f184 T4-platform T4-platform
35314622268 2234 9b4d697a57a03f242164db956843a5313168005d f185 T4-platform T4-platform
35330927778 2236 949492b50c7dd81271f0180d8425d32316becbcf f186 T5-core T5-core
35397596933 2233 1d58385b513a414ead34544346e86549dda3c4b1 f187 T5-core T5-core
35443109227 2203 e176ae552b99f0fd08a2dde8bdf52025a3e20059 f168 T4-platform T4-platform
35446976110 2233 8c1a9d61c27292bf7a52f10fb54186d4e24de28f f188 T5-core T5-core
35448941856 2233 9db5217bdbb779212c5d066ed1a84f7ca6fc0282 f188 T5-core T5-core
35467285032 2233 9db483402ea2fd4e0a5ff3bdd2b1fc50a7518ec9 f188 T5-core T5-core
35470450948 808 bf2c54e0ef45d0d9767d7945a160f630f890f27d f016 T4-platform T5-core makefile-by-name
35470489882 808 80d277834a233f2e7a2df68b8c08a1f233cf3dfa f016 T4-platform T5-core makefile-by-name
35472952969 808 5515ca4ec6b20b62ed0530489785e4f131fd5159 f016 T4-platform T5-core makefile-by-name
35473919864 2225 c067c771b9f93d08a3e614e68aee7d710d2f256c f178 T0a-docs T0a-docs
35475706415 2241 6166934defd9bc5b687ab9c8de3779e71a844395 f014 T3-mid T3-mid
35475754479 2242 e8a46c16e233bff111092dff18985cca94890bcc f189 T2-extract T2-extract
35481840120 2236 aac550b732040acfe6ab6c0bfe5d0cbef5d9f9e9 f186 T5-core T5-core
35483670414 2243 243b405afeb3998707ea4d556f3178c8e8419cab f150 T5-core T5-core
35483768695 2236 d2dd3b347bbdcc695f911e80b1b7f7a32d790e5a f190 T5-core T5-core
35486887056 1999 aa4da14d23dc28df58b9881dc66d89c0d1c0d788 f021 T2-extract T2-extract
35493947102 2236 2caafc0c41bd083092afeb692824e718a8e1e007 f191 T5-core T5-core
35495099219 808 83667268d9c435c2634994efef45b09daa98904a f016 T4-platform T5-core makefile-by-name
35498544795 2227 656fe99f9934654bbab927d29add13f7822e1bc8 f180 T4-platform T4-platform
35499978643 2244 56de845327dce03cdd1269b8aa73c6a1ecdb5cfa f125 T4-platform T4-platform
35500380927 2245 ed78ff056e89da814ff5ae05da6fb32b7e10c0db f192 T4-platform T4-platform
35501823298 2089 e171bd48b5e5845ec6f00db65ff300caac4f7304 f046 T4-platform T4-platform
35502589515 2140 618fbca0c39da7157c7f01a26071ef3a4d4a5b00 f140 T4-platform T4-platform
35502728596 1766 81820262e61f6e7f11920a5c4596b4297e793cf8 f193 T3-mid T3-mid
35502852336 2246 fed0c1b4e3563f1567b3ff85652ec51229014a83 f194 T5-core T5-core
35502954849 2246 2eeb15f434d9520e0050fe1f5b6c4c8316a4a69e f195 T5-core T5-core
35503248954 1766 4eb3af36c7b055987d5c0764482135658b28c550 f193 T3-mid T3-mid
35503753154 2246 5b4ea78927772d00f11abc41f217f89e714ace54 f195 T5-core T5-core
35508860520 2247 9b1dfda875736a91073c57badce3dea6d0f03080 f196 T3-mid T3-mid
35509728718 2248 96a2fc35dfcf14662cee491430366f4e8d8b8149 f094 T1-tests T1-tests
35514366896 2249 051702c6f9163f0a19dda19ea14ac1b72dc7eb3d f197 T5-core T5-core
35515754123 2090 7036c4d5996253c05d720ec4d0b3008509b4cad4 f198 T2-extract T2-extract
35517816031 2090 f9350caae0ccccf823d8cde85683a900b257f239 f198 T2-extract T2-extract
35517948650 2251 151d6077cf418a42628bc7e38bda4f873877f147 f199 T4-platform T4-platform
35518289769 2252 4ecd8ab2c3995cc92c1a8df4f5e2a1e2d3a8c342 f200 T2-extract T2-extract
35519906910 2253 458b0db1ba3274ceee1e975cfd7300f13aef82e9 f201 T4-platform T4-platform
35519951214 2254 8aa77f89ce1ef56c98d989b03bc35fa4c6289b8a f017 T2-extract T2-extract
35520101152 2249 238f9e6c6cad0e086eb06c19f1a6a303fbb30fdc f197 T5-core T5-core
35520168086 2247 9af73b327e3899c45fc1faf13b590c09efa2078e f196 T3-mid T3-mid
35520178069 2005 fcf451c6a6dda768dfec5a8f61a01ac1e1cfa6bf f034 T2-extract T2-extract
35520796432 2255 0541b033fa874c38167e4f4d204ad452f1d96d08 f202 T2-extract T2-extract
35520869334 2256 b565e9d8b06e8703f1c15a4263a987837dd0c5f3 f013 T3-mid T3-mid
35522168154 2257 af3e003ad8e67cd1fe3b20dafaf4d33a4b351852 f203 T3-mid T3-mid
35522190984 2258 de4a88f22242f53f724fc6cc8dcaed79642dc5cd f204 T2-extract T2-extract
35523164377 2259 460ebefc87df4dd5142bdc339702edd4feec4696 f078 T3-mid T3-mid
35523331086 2260 25d08258e567ec417bf7b18b14a63e30162fc7da f205 T4-platform T4-platform
35532624016 2261 28d62001fd1e5d0a18085a8a662a1b806747815f f206 T0a-docs T0a-docs
35532636527 2262 16a7291b56aafdcde27928f892d477e8b94269eb f207 T0a-docs T0a-docs
35533089935 2257 c1128db8187db2dac27a7ad14120fc09802c8018 f208 T3-mid T3-mid
35533328007 2263 cda5e3c9e5c4d2251467ced00ff305205c368d17 f209 T4-platform T4-platform
35537157817 2005 6eb8992462e506bf0c58aec88274a9a62a07676b f210 T3-mid T3-mid
35537525864 2005 fcf451c6a6dda768dfec5a8f61a01ac1e1cfa6bf f034 T2-extract T2-extract
35551212771 2236 39a8bc3f2a7fbbc4d86ec8379b669a31ed1f8318 f191 T5-core T5-core
35556558511 2140 af4604189bf23c14dceee9bccd89a16f5bfcad8c f140 T4-platform T4-platform
35561381902 1724 42a3d7105548c65ee12f6dffabea718573501601 f211 T5-core T5-core
35565389237 1809 19d9e4ac4d0a8c6839b627c0cd5890188121fc2b f046 T4-platform T4-platform
35565392645 2075 e321b1fceaaf4c9f03fbe82547f02c1079e3b760 f104 T4-platform T4-platform
35565401625 1784 381f64c15ebd0a676b85347acfcfd3f45c806a11 f009 T4-platform T4-platform
35565472800 2260 2dba3cf81750f245d8c1d912997f04be5615df99 f205 T4-platform T4-platform
35565493742 2005 7d01d3ec031dc26759283fff96e979c3e0ce270e f034 T2-extract T2-extract
35565496531 2259 6f2f83618134bee208c98c47931579f27a712809 f078 T3-mid T3-mid
35565502343 1768 6115e5785dc01f5b403cbfe7f184ffbfba058e45 f055 T1-tests T1-tests
35572275939 2158 d4931241adaef732066afb5ebf1cb225e2474403 f141 T2-extract T2-extract
35573014946 1716 95fa63441bf80c9ed871fa090ba925b8b6106d83 f096 T5-core T5-core
35574966373 1724 81ff2cabcfeb4619e24307c0267a1244f38afb40 f211 T5-core T5-core
35575687750 2160 2e4b45931b69671a92402f8fdf51f85542eff268 f125 T4-platform T4-platform
35578606399 2199 fa138acf44525ef94cfd6e16772d601da5623aef f166 T5-core T5-core
35585540293 2198 9574c316e43527d6ca90eb007a9c8fc24fcfd88b f212 T5-core T5-core
35597253045 2268 f0fac62b7140f2cc3ccc011c74e798be7681ec61 f213 T5-core T5-core
35606545382 1724 93f86be139b4bff1d079205fc776babc67f67349 f211 T5-core T5-core
35607169136 1716 bc5b3dd216568c864b41303a674020a2d456ced9 f096 T5-core T5-core
35623513958 1823 c017685d66ed9bca5476313ede52837ad07f17db f076 T5-core T5-core
35632304230 1724 bc81c4a84f19f2f5d90d985ec69dcd5fc357ea25 f214 T5-core T5-core
35637728335 2140 fca33717d1b6a9171decaf4d1755c88f48dcb572 f140 T4-platform T4-platform
35638029971 1724 d48afe8d3070e360ca908d841d85adad344b2ed6 f214 T5-core T5-core
35638134068 1724 3743fe95a0fc40f584b88bf4cbaa0f9bdfd8e52c f214 T5-core T5-core
35639932135 2227 df83d25ace9885e252363cb04be25a17da85c9e8 f180 T4-platform T4-platform
35640451450 2269 65d60e72f5b86f5982b15f0d88826ba79a085aa2 f215 T5-core T5-core
35640454709 2270 f428d583008ce87281bac138c02bd4375c56d735 f216 T0a-docs T0a-docs
35641634636 1999 e032a8ee9409c7a53a5d732ddff67c15721bf1ae f021 T2-extract T2-extract
35641641970 2158 61cfb09b52ba40e8fb3ee9703c618b28739e3298 f141 T2-extract T2-extract
35643957306 1724 5033f97d5f5154cddecee760e5b2a5ad290b181a f217 T5-core T5-core
35664547053 2272 da4bd25a01b612517512ee628bf0ec652000e80a f218 T1-tests T1-tests
35665885098 2273 c314dea4c1409a3a68fb33706b49e272809b5079 f219 T2-extract T2-extract
35665890640 2274 343d7dd5e47b817999098605370f0eab706675f0 f017 T2-extract T2-extract
35666550961 2275 a041794f8e51ddf42342899be7b2ac26f951d777 f128 T4-platform T4-platform
35667708865 1741 c0bfd073ff25484c2d7b3b59dea937be6c92ed08 f072 T0b-nonproduct T0b-nonproduct
35667710693 1742 dfae618b0e9508cd4998b583ebbeb36cf458278e f073 T1-tests T1-tests
35667714864 2170 36730c7d895ae7541f780d650151b3590fc766cd f053 T3-mid T3-mid
35667829346 1724 879aa5f9623bab14e8fb903da23bf84a8d0bc32e f217 T5-core T5-core
35669146114 1724 89030aacb21df9b72f23b42286e3a4eee63fd43e f217 T5-core T5-core
35669196507 2140 2f557f53986976b2df1eb2a1a540c9d48cf91a8e f140 T4-platform T4-platform
35670303027 1724 b47ba846bbd1a6fe7359c86314c81fb33f5adaa4 f217 T5-core T5-core
35696787516 1832 6ae6cd4c3f280cb8e3ffec2858269055e390ffec f220 T4-platform T5-core makefile-by-name
35697012777 1832 9a9534cbc45d40afa4e8bf070030a806acea1f5f f221 T4-platform T5-core makefile-by-name
35697183623 2022 7498e2b75b535171b5a560c6493fe86b5ad881cc f222 T4-platform T4-platform
35697752061 1724 3889b47527084eccc7c54c8a3c7e2592c5d5d30a f217 T5-core T5-core
35698464152 1724 08d5e2062f1ee9aaf8dce8eaf77662cc7c96a1c0 f223 T5-core T5-core
35714885737 2280 f930ed6026c6f1e5f5b146a07664efed9d4da9c7 f224 T2-extract T2-extract
35730933863 2198 f913f0eaf5ee308619ef1fdd4f60534d4509fab5 f212 T5-core T5-core
35734984657 2281 34d172c9038a9b7cee78e42bb7768b93fbb8ed70 f225 T5-core T5-core
35734995796 2282 1d2bbc425988eb78fcd966dc8c9cfbdb814f0685 f226 T4-platform T4-platform
35766202700 2288 427bb73c638481842bd13d73ed171953469eca02 f227 T3-mid T3-mid
35767446435 2288 d7eba5a71d2a99d7b7bc588c666d22516ac0b563 f227 T3-mid T3-mid
35772088984 2289 38fc6ea995c9db78cedb10b9bba2b6aa79f616fc f228 T4-platform T4-platform
35797101033 2290 255463126650ab6deab3e62aab441b71479704f1 f229 T4-platform T4-platform
35797928618 1702 aecfb315d8ddbfab2cacbbd6e4a26b408b973975 f085 T3-mid T3-mid
35800424142 1702 26bd387753a9a08fa6de647481bf36cf338e5c28 f085 T3-mid T3-mid
35812291988 1702 6233394d8e893546af92876fb8d8da81e592cc2c f085 T3-mid T3-mid
35813226724 1702 5770ed83741009e71c12876c40b655758f4f4440 f085 T3-mid T3-mid
35822291393 1702 8165c34015699379d31fd31060c0f16fbe5447de f085 T3-mid T3-mid
35824077381 1702 24795fc665df85200ddc112aaddfe34572171ef8 f085 T3-mid T3-mid
35824588131 1430 6bb8ad6a7a1c99849d101322ca8fe81b6adfbc50 f230 T4-platform T4-platform
35826272552 1430 0d2f840c8ed2695db206acbcad1ad1c36a1f3958 f230 T4-platform T4-platform
35836874903 1702 f5ec03f0e1a7e90ace14c21490dca39c4c8bb092 f085 T3-mid T3-mid
35841856084 1702 492e59a181a27af324c82a8db3c4e2e37382d567 f085 T3-mid T3-mid
35841866366 1702 9e92e67649d62a2de63e57893290851d7247476a f085 T3-mid T3-mid
35842147817 2158 246e4190c3086d1074adbe8afe39bf4261db59b8 f141 T2-extract T2-extract
35846258328 1702 e9339ff1f5b28cb85d2810f434471700e2fc3b5e f085 T3-mid T3-mid
35862574659 2294 54c143ac932b6e291a17be0327fb1b5e86b99984 f013 T3-mid T3-mid
35899891384 1823 4a8c2eb422d9083ac3bf1e01e18e558e470ad099 f076 T5-core T5-core
35918117863 2295 f4c897110cb1f02edee37db7c8849e63894ed8a4 f231 T3-mid T3-mid
35936681521 2296 59345314187021f50c5e5c12da793b27b8485d7b f232 T2-extract T2-extract
35948281533 1724 48f9de8ccd7cc75d7e466b7d5167e2930dfe33f3 f217 T5-core T5-core
35948963693 1724 a8da9a366353bb0a67806b40f840500444ac963f f217 T5-core T5-core
35948982011 2272 9b5bf70dc400fdfe989ed2bf1b623b2103bbf2f0 f218 T1-tests T1-tests
35952295014 2181 282fbfcdabfec79c19c36f5bec5f75c22eb36df5 f091 T3-mid T3-mid
35957866374 2158 cf7eecd7d0b1288c7f2e290a1d1bfc1b1b6f8698 f141 T2-extract T2-extract
35960900747 1716 a6d3d5d820b07ba8544ddae1668e8107d7961532 f096 T5-core T5-core
35960904447 2199 f07c18702ac82957d0eb73f96f37d30184b7e6b5 f166 T5-core T5-core
35960910227 2263 fcdc6bedb7d05489462639da38c4ac755137734f f209 T4-platform T4-platform
35960928169 1768 92cafd1ec888362ef3223ad669a5aa4f378a54d5 f055 T1-tests T1-tests
36006341620 2301 64297ff3e8e142887fc36c933d57fd4d32924cc2 f233 T4-platform T4-platform
36057012376 2304 6f91a16fc2a753db248f122dea7e4b50763972d1 f055 T1-tests T1-tests
36057063533 2305 69b84cacff1b08f449e563c2d24861e6f10694e4 f234 T3-mid T3-mid
36081432074 2307 2a6dd92f658b15d571130b0eee14a67b95a36465 f235 T2-extract T2-extract
36081473175 2308 707f6f0db58a16b706b29d249bced477094ef92c f236 T3-mid T3-mid
36081733263 2309 a9a25357ec2dc57f3c6f2e353e7ca61a7575df06 f168 T4-platform T4-platform
36081918547 2310 2f922141972a18f0d95de4505b33f5362f51164b f237 T4-platform T4-platform
36082054353 2310 8955d98983e0ca1e1df4a2173b4b4273904566c7 f237 T4-platform T4-platform
36087900420 2310 7c24b020457924fee9a7acdd6cb8c5f7e262ca4e f237 T4-platform T4-platform
36093255940 2310 b2e9199773977f28d73a052d41c37b8c111750a5 f237 T4-platform T4-platform
36102327028 2310 d636aa7f21fb001325b61cd41fc431d5f255e21e f237 T4-platform T4-platform
36137231318 2315 9527694e90ee9dfd29d64940425f9f23290ed702 f238 T4-platform T4-platform
36146313096 2310 9b10ba281cfdba2676e9d24c445470c4b6f7d4e3 f237 T4-platform T4-platform
36150749742 2022 7a307abbf8c1c879edee3193dd482ef427c83287 f222 T4-platform T4-platform
36160883414 1766 9d53f32dc1753fcece6c0d2af27a5958f8ce1799 f193 T3-mid T3-mid
36164363201 2318 6d508350c7713e10e0e935625ad74d8401fe89e2 f239 T3-mid T3-mid
36164368040 2319 3faecc78144175a81acf76721c4f695cf80b5676 f240 T3-mid T3-mid
36164372390 2320 4f5c951dde792c9cee4e33e45119cef8fc62765e f017 T2-extract T2-extract
36164376373 2321 c6ccdd9395dbbafc6f66a49040b478b445387946 f241 T2-extract T2-extract
36164382846 2322 19f50de3d2e2bdea5f9054caf07af70f46d74244 f242 T4-platform T4-platform
36164406666 2323 e9f6596e82cac5b3377cf212e7199a286815418f f243 T4-platform T4-platform
36164410674 2324 a808295986d7599ddbfe8324beefacfdd5161c04 f244 T4-platform T4-platform
36164415830 2325 d56e046ddd08ac6bf8b14f5049a88382a10a1636 f245 T3-mid T3-mid
36164420947 2326 5ef8ccff7023db106df18cef0615776e7ed94686 f246 T2-extract T5-core makefile-by-name
36164427320 2327 098fcc883e2c720ee838df90f3475938715d4056 f247 T5-core T5-core
36164450931 2328 a8c4040e8a92dcf831c64f57458694ff8fa6e9f9 f248 T2-extract T2-extract
36164460893 2329 12f51855cab461cda3bd4c4d63d79f09fcb69732 f249 T5-core T5-core
36164465591 2330 e7324bb6904c6d0a295b956e83c8585219198358 f250 T2-extract T2-extract
36164472858 2331 a0c71225f7c0c5e4bee36ef235f966866abffb24 f251 T3-mid T3-mid
36164477427 2332 b9ad9ed1449540e06e9f814ded548d9f2bf52f49 f252 T3-mid T3-mid
36164497120 2333 e65a4e70be9c87366f66e93e252c0f307aa50ac2 f253 T4-platform T4-platform
36164502402 2334 357d71e198ae3cd4614b9e06e54957423291e5c4 f013 T3-mid T3-mid
36164505003 2335 1c73f8676c31e227c6ed478ccbb55662b34d6db2 f254 T4-platform T4-platform
36164512328 2336 b8b88bcf088b8cf3f6b44949e917d172aaa82d2e f255 T2-extract T2-extract
36164516453 2337 8e12fc8a69dc6f83296e660191448ef8ba7819bc f256 T5-core T5-core
36164537929 2338 dad40ccdb633c10557baba4049aec35049fa3b15 f257 T4-platform T4-platform
36164542709 2339 793e0b6127db3c8333b1dc29faf0b35167e3ceda f258 T3-mid T3-mid
36164547188 2340 b3bc2a80aad766d37477a163ae5b158913a20f45 f259 T2-extract T2-extract
36164550014 2341 40dad610eec783fe243e6b6141c1bebc75c23f0d f260 T4-platform T5-core makefile-by-name
36164556221 2342 c650292433534127b5f45edd066e56a06b3376d1 f261 T3-mid T5-core makefile-by-name
36165117354 2181 65fc0bb4f77fdbb6449199c147e1ed112654b151 f091 T3-mid T3-mid
36165123674 2158 d772b1e5af0ce80d08aa6603ea47d1f51f324193 f141 T2-extract T2-extract
36165128132 1716 ff127ea44c56ce21bd5a4f9cf9f51bb27523d0aa f096 T5-core T5-core
36165431134 2343 45f0c430d28cf188753f44536d2cac26564a04ba f262 T3-mid T3-mid
36165866718 2344 4b2b3b90adc44694d70624c9d0372752ffd0e2bf f013 T3-mid T3-mid
36171533714 2326 c0a06b3d0a90513e0d80f79514b869f91cb958f1 f246 T2-extract T5-core makefile-by-name
36171575767 2345 772ea06a6115128c3160f9631322b089aeb85a53 f263 T3-mid T3-mid
36176174515 2347 b220ecec48010db71ac0a77b2888fefe802c9182 f264 T3-mid T3-mid
36176260818 2348 886e6da479891b6dfe386ef25dfe5abe7ed2f080 f265 T4-platform T4-platform
36181164078 2289 8cd2ee5f421d0b7c06022091d59dbe71002de30d f266 T4-platform T4-platform
36183268529 2315 c27a53512da9469dc50fb243c1ecd3a55975ebfc f267 T3-mid T3-mid
36183549298 2350 99ccc7d64a275bba1c015b30df036597637363f5 f268 T0a-docs T0a-docs
36184450486 2351 951b2fddf68a6e6605b1664bd3252a2a6578ef35 f014 T3-mid T3-mid
36185562216 2352 8093d10b812d9a7840ae99d8f9d24a04cb0f9559 f269 T3-mid T3-mid
36185568093 2353 c64ea20feae160d1eeddadf09e7e0df36b08e818 f055 T1-tests T1-tests
36185818550 2354 3ef7d8cdc201115dd76cc373ea2c64a567a2defe f012 T2-extract T2-extract
36186982935 2318 b7bcb15c219761f27ff146063a50481303527563 f239 T3-mid T3-mid
36186985986 2331 6b7628bb6bbf041a8bdf069dd9d6bc8861d84e73 f251 T3-mid T3-mid
36186989409 2332 1d5ff68e508e78b149589396d547166ad41362e0 f270 T3-mid T3-mid
36186989799 2334 87471e1f1d6a01ca8be6ca45b968145d0de95362 f013 T3-mid T3-mid
36186993787 2342 7abaa91dea653af3e890759a63e40a09cfcb81d8 f261 T3-mid T5-core makefile-by-name
36187019606 2327 f5c8631f2032c5598b1bfe12d329b875532f814d f247 T5-core T5-core
36188745373 2355 ae578bc1cc32bb8f159a69c486f938ad745c643f f086 T4-platform T4-platform
36189334816 2356 d5a3110d9e1e82de5416807d1f81e3ef85049c1a f271 T5-core T5-core
36189454232 2357 28b41c35b71cb039988f39f72689dd657e30522d f272 T4-platform T4-platform
36189574830 2358 f3754d6197b626d5432ac84b0363b0d3a5544c5c f273 T3-mid T3-mid
36197747139 2301 ff8dd47011c43c622d3dc82b33be32b4493e4ea9 f274 T4-platform T4-platform
36199747285 2344 bac3fc80b532eb294fd6eda6a2cc93d656df9369 f013 T3-mid T3-mid
36199749518 2345 1810a8964caae06e33b7f3c6f944de570c21c292 f263 T3-mid T3-mid
36201323199 1702 5557f0ec4a54bcaf99bd7442b866579ab68578f4 f085 T3-mid T3-mid
36210099904 2359 efb231285d188ffda16a58f7a5ce8e0e6448638f f101 T1-tests T1-tests
36222963285 2315 2d2c0fd0c008973c63565c3e7f4591ed0b1bb379 f275 T4-platform T4-platform
36227652952 2160 bc960d87d663b7b4fc4c3ba558013c4609073762 f055 T1-tests T1-tests
36232110014 2360 1ccbb432ae1281ac96f743850be317fb541cf1db f276 T3-mid T3-mid
36238137680 2361 4dfeb2cefc402c7d1f1d3d01603a2e8409ed24cc f277 T3-mid T3-mid
36239758139 2281 9dca8d2fede860605943ee8dbd57ba6b927ae41b f225 T5-core T5-core
36239789418 2362 ed991154f212928df63ce5a5cd1159cfc8196f00 f278 T4-platform T4-platform
36239835633 2290 26e2d0cba6df7b1cb53e655d0b902bdb7debdbd4 f279 T4-platform T4-platform
36241153105 1816 3e67838b05e67915d472ec7698c80649ab8ad03a f071 T4-platform T4-platform
36241168954 1766 8f441c19fb8a32b8ccf43ffcf65df7f4dc2bc16a f193 T3-mid T3-mid
36242675954 2363 363575af920b05f911fc76ca964dcfa7ef1c62bb f150 T5-core T5-core
36242676040 2294 6e3d8c296f2f99375d9693e706b4818c72caad13 f280 T3-mid T3-mid
36242724677 2364 e94f5a70d01e4cf62b0c9f496175dcd930bcbd0e f151 T0b-nonproduct T0b-nonproduct
36242808005 2365 7549e6d09c021f6d3a385a37898f591626ae62b8 f150 T5-core T5-core
36245108554 2366 06f97613a28a18bef0145f37c1036fa60e73533e f281 T3-mid T3-mid
36248365534 2367 8f803f7262d1d1e58068f677f3e365048050b728 f012 T2-extract T2-extract
36248371079 2368 a59d53a14b779401621c44aa757e3bc9f0588ef0 f282 T5-core T5-core
36259321198 2370 79a0c92e33a53758c36d87d4124b12cc83dac5be f236 T3-mid T3-mid
36259389383 2371 0dba49208e8537eda2c3355387c0307f6ee95075 f283 T2-extract T2-extract
36259523618 2372 6f17caa7a763d9987ebcd7ed33063d47d34bf635 f284 T4-platform T4-platform
36262536017 2370 12de590a4935488d83380a33865cbb59a7a16c32 f236 T3-mid T3-mid
36266366437 2373 77ab4bea87008547acee60435a8e3df29d90a5cb f285 T2-extract T2-extract
36266372862 2374 7c7811f81aabb4c1fdc0914e78d46b71493a9dce f013 T3-mid T3-mid
36266444913 2375 3ad76fd7728f3b19ca1100b10830e986b7f631b0 f235 T2-extract T2-extract
36266447071 2376 5a9c72d8f8c65b38c66a29a34184b49edf3378ff f104 T4-platform T4-platform
36268746769 1702 8ffd03ecd9ccae5a32508801645fb1cac7e4a4d1 f085 T3-mid T3-mid
36270666016 1784 ca58149ae4757325158af07e4e3b8968d315e5da f009 T4-platform T4-platform
36271539681 1784 71ac6ecc15d3ab3ebcd31f0e467071a506006ca2 f009 T4-platform T4-platform
36271900288 2367 075f40e9d4ccbfead4df0e199c7730f94c2154ff f012 T2-extract T2-extract
36274019750 2304 262d7ca7659b3f1f6ee619cda2b5f6aad2815314 f055 T1-tests T1-tests
36274021183 2305 3d9e5ed5cf1ad1e4bf89b385d0be28c8f011f52f f234 T3-mid T3-mid
36284225343 2370 208be7d9374bb27d2875e3568265b591ae7c4b5b f236 T3-mid T3-mid
36284230521 2371 eafaab15c802c46ee22233b50f0e9474b0a3085d f283 T2-extract T2-extract
36284315887 2280 756d7f42d4aab0045d75e91651cead399e4a7b35 f224 T2-extract T2-extract
36285289867 2315 a1ac80740ed800d84a9d5e44d725bdc29d99b88e f275 T4-platform T4-platform
36286798412 2072 1a37f0dd52ab62cfbb439c5a2f72afddca4cb009 f286 T5-core T5-core
36299419426 2207 631317536426136f580e34d3c25dd5a951d443a8 f171 T4-platform T4-platform
36313881874 2381 9d8f51d7f3340381afa450aa33cfd320879d6ae9 f060 T4-platform T4-platform
36323957389 1702 b46c6d43f2efe6f0a1de8c77b73085183aa5da1a f085 T3-mid T3-mid
36324735265 2376 07eb426c95386e299a6aa6adbf2b4eb9f272ff3b f104 T4-platform T4-platform
36339435944 2370 e9058e11db9ed94e401ad711b2016249970a9040 f236 T3-mid T3-mid
36339438890 2371 4d21c3bbd34b420c8987e8588b88b5fc5446116f f283 T2-extract T2-extract
36346241955 2360 09f704d23f0d990dfc72b916d9daea80acce73ce f287 T3-mid T3-mid
36347358048 2361 0d720d52b253cfb64e7bfb1eee034bc3035f1a04 f288 T3-mid T3-mid
36348236142 2366 06ef2f64caea016fc792e9069d5a3befc30bca4a f289 T3-mid T3-mid
36352906531 2375 7707c9d7918263cd446a4b54d4eb48c3fea91048 f235 T2-extract T2-extract
36353006100 2247 fa2292111802b5f3b66db3e4dd168280b53ac17e f196 T3-mid T3-mid
36353528883 2249 a45a99c9c73dd54a4625d680da700f7cb0387eff f197 T5-core T5-core
36353741468 2385 1b2e8f1fac105b0eaf15e15b95c3ef336bf0a44e f290 T4-platform T4-platform
36354332711 2181 82a6ddff81c6c8583c73f3d628037e41a9b412f0 f091 T3-mid T3-mid
36354336229 1781 fd2456316e7b5dc27125497f640dc7e41760c104 f174 T4-platform T4-platform
36354339289 2065 87ba464596337fc6574a825ef88392e70a733dc9 f093 T3-mid T3-mid
36354790786 2252 df8dd1011bd1987d0ca5a45f324775aad2411f54 f200 T2-extract T2-extract
36355512051 2089 11957ae6554b7d768095dcda95168331bd654363 f046 T4-platform T4-platform
36355610699 2371 ee9d1365e2c17d4e2bbdd84ac10ec6b89cffca5f f283 T2-extract T2-extract
36357921046 2344 7f649b29dc778b0f54c28dfcc2f25b76f3d1947a f013 T3-mid T3-mid
36357921739 2326 259dbb67bee1d3547ae61a98c9d0c1cf4e491d44 f246 T2-extract T5-core makefile-by-name
36357923794 2352 ea6fa49fc2c209de69c1b0f393d9d2cc25b61419 f291 T5-core T5-core
36358160313 2389 27c87104a57eefe33543a04e3e43eb0721df8b4a f292 T2-extract T5-core makefile-by-name
36358451648 2207 8bd9d37b156641ecf1c82b16b0ec0a8246e8c201 f171 T4-platform T4-platform
36358713716 2295 9f5f5ab56fce2baf095b75f1e7a8dc98368dbcad f231 T3-mid T3-mid
36359201669 2073 9130fab63d23b5386f12fa4cfa57961b247ff07b f101 T1-tests T1-tests
36359210035 2385 c03575ef1ab1abb51793d03303ae3e5d4cd3b708 f290 T4-platform T4-platform
36359646097 2391 d450275e20b663e3c5c8a06627030702e61e4184 f017 T2-extract T2-extract
36359899206 2385 cb307a496de36c9803dffea0815f3ea96bf849db f290 T4-platform T4-platform
36360333971 2389 5ff3e8d6fd3fd87d46807084058e2944f6486d76 f293 T2-extract T5-core makefile-by-name
36360464106 2305 8a49fc684ad08ac2d99591e0b364249570c9e676 f234 T3-mid T3-mid
36364475838 2392 19d144477b9558dad148d3f555ec6ede16caa3f5 f294 T4-platform T4-platform
36364478399 2393 dc735c01fbbb4eddbbb73ff4df6007ab68413a4d f295 T4-platform T4-platform
36364479687 2394 26cbccd998819c428589156a82e167db061522ae f296 T5-core T5-core
36370343201 2395 f200d7477f192caedaa5b4cda7d9eccc2022a069 f297 T2-extract T2-extract
36371911963 2396 629e9c2c1f87e2ac8b5d644d38ff4e935928752a f298 T3-mid T3-mid
36371921838 2397 4c7bc08ff02d49afa07808309d5586bc61724ba4 f299 T5-core T5-core
36416318776 2315 3327ee5935a7b03ef5f850c12c163c11ef9d91e2 f300 T4-platform T4-platform
36440070485 2305 853192aa7e76eedceb1fce6e2a6874948cf1c1a4 f301 T3-mid T3-mid
36440993073 2207 343cc949270a3e18e317fd6cd781167ed2fb3dad f171 T4-platform T4-platform
36441585217 2368 f277c5e0a9aad35c730e4189b969b74964614d87 f302 T5-core T5-core
36451035334 2207 1c840df7495e3b71924b8efd7e3bbf53c9076abc f171 T4-platform T4-platform
36453341395 2401 9d5b6e998514b76dcc95b55f362325b3d49945da f264 T3-mid T3-mid
36455603700 2401 874286abb9817db94e7789c7938921f6a03f3448 f264 T3-mid T3-mid
36456464356 2402 555727b50da6086f00c00f57876e2632caa69c45 f303 T2-extract T2-extract
36459403945 2394 78a2ec399fec3dc28e61439a4bcd900b7c64c23f f296 T5-core T5-core
36459418705 2344 b205f0b51dacd89da57f6fcbeb46f55617606091 f013 T3-mid T3-mid
36459497707 2403 95eaf01755d18c501fa8d1c5dae2255cf460b9a2 f001 T0a-docs T0a-docs
36459501942 2404 fe3c285fdc63bd9abe027026edd7adffe5f62677 f013 T3-mid T3-mid
36459506386 2405 460153204ba9833913e5245ca894f783c1d7229d f304 T4-platform T4-platform
36459537782 2406 a1b7c05685e18597f3461783d4bc98661f05427d f305 T2-extract T2-extract
36459545006 2407 c02dff113a90aa6bbc3d89fedda962a53f8b4c64 f306 T2-extract T2-extract
36460081689 2280 6ba9a21eed2165932b90f7ede772d77b046eb08a f224 T2-extract T2-extract
36460156038 2408 23d48336d3367fb27cbde54049b1414a2aea1b48 f307 T1-tests T1-tests
36460162959 2409 4c6ecffdcdb8d78c0704f0d7e825f676ad77c240 f308 T1-tests T1-tests
36460990240 1907 b74cecc53cea20abdc64dbdc484f5ba0b44f5789 f033 T3-mid T3-mid
36461038395 1913 71ebbb9a9b54f2dcef84321de939429dd7707600 f041 T3-mid T3-mid
36466048726 2410 533eee07ece1d01ff2c88da55972c7720cd1604b f309 T4-platform T4-platform
36466057374 2411 0d783bcca4edde896d5716e8a4060c3100595973 f076 T5-core T5-core
36466065600 2412 ed76cf8e88cfb1f26c82110e46a518c13577f788 f310 T4-platform T4-platform
36469132661 2360 5a77358277a83d7651965f762a673be76fb157b3 f311 T3-mid T3-mid
36474434156 2402 0a658840b8f029e7632910284281e32286c1f0aa f303 T2-extract T2-extract
36474499483 2402 f88aaeb8f9db801545c21e085812559df9f80083 f303 T2-extract T2-extract
36474752681 2418 3db66f22ad7d5c435559869a34b6e14b1e54793b f312 T3-mid T3-mid
36476594662 2402 33a9bbe85eb922bf01c5297f09cc50044f39e351 f303 T2-extract T2-extract
36476662334 2419 3f53a308f4034f4d280130fbcc275f545d7bf05e f313 T2-extract T2-extract
36476726058 2420 e36930742a63efd2bbca491996ba8e37bfa5822c f314 T2-extract T2-extract
36476760149 2421 fd1b88374c0c21892c991c29909d22ed499c20fa f315 T3-mid T3-mid
36476819919 2422 ffa0b320047f4db99dc53f09e228c94703436ae1 f316 T2-extract T2-extract
36477490833 2423 ba5e7dd639a3d29c54023d1203ef18fc4e330c7f f013 T3-mid T3-mid
36484216122 2391 f297737db0510ecc573edf57a2a30a25e5fbfcfc f317 T3-mid T3-mid
36484725527 2391 538ac25edda676ca7d8cb8a4c7c5fb3a3a36fda2 f317 T3-mid T3-mid
36488109160 2418 702c3afb6bbab15e18e13f22880d5a61fa3317e1 f312 T3-mid T3-mid
36488232420 2419 82361ffdd66ffe39efc1ff9e00e0cdcc61223e4f f313 T2-extract T2-extract
36488247857 2420 a46dc5a9fbb39310027812234d0459d4bbcbe99c f314 T2-extract T2-extract
36488262530 2421 a85bef05f169e4f2c36cf4cf121a7b6d1309e155 f315 T3-mid T3-mid
36488285902 2422 c695cb728e4850b4152cd7cc54f06b6520372371 f316 T2-extract T2-extract
36491809136 2309 48265f5367d93c22cdaf2203d19dc65fd6eefea4 f318 T4-platform T4-platform
36492368152 2424 904eccabe39b03d8104de989a56f224ccf6d81d3 f319 T3-mid T3-mid
36495192814 2425 e548d971b3e471ca5a149201c39ba62ab1a72261 f013 T3-mid T3-mid
36499773006 2385 534274c1601034b5eca97ec286c0346389076a8f f290 T4-platform T4-platform
36500785757 2360 bb3f1cdd6f4f45ab416ba97ad09a404aebf21e36 f311 T3-mid T3-mid
36500790466 2361 8116eaaa426ff39a37b4078e0abd018b09398e58 f320 T3-mid T3-mid
36500796009 2366 4a0f9660623b53d4f9eccd7149d54482707fdc22 f321 T3-mid T3-mid
36504138642 2371 96f91005f0777e3fee5f5b5f9da69962ddf93695 f283 T2-extract T2-extract
36506100535 2427 f4efd0b86844bf2ac965ba9ec30c8805cc774e40 f322 T3-mid T3-mid
36508613643 2315 232730cb091d852bd7820bf1d5aeedc43aa860f8 f300 T4-platform T4-platform
36509039978 2170 5a3e82520408db86161b8fff08175aa30a0adc6c f323 T3-mid T3-mid
36535949326 2368 cc71a2087d32a7c06b0dcc2f925a9a623335c2ef f302 T5-core T5-core
36538846045 2170 1ebd1e40fe715ac9efb328b2c7cebd2c0a4e140f f323 T3-mid T3-mid
1 # Every PR push of September 2026 (one pr.yml run each) with the tier the
2 # lane analysis assigned it (sel_classify.py) and the tier the selector must
3 # produce. They differ only where a deviation is documented in `note`:
4 # makefile-by-name the analysis classified Makefile.cbm by its DIFF
5 # (source registration / test flags -> no tier of its
6 # own); a file list cannot show that, so the selector
7 # treats every Makefile.cbm change as a build-flag
8 # change: T5-core, full. Deliberately conservative.
9 # Columns (TAB): run_id, pr, head_sha, fileset, analysis_tier, expected_tier, note.
10 33464275799 1697 22f90fe883f641c2c460a210a1de3a8df74c4285 f001 T0a-docs T0a-docs
11 33464725171 1986 9d91891cb5dc6b9d67a0d4919d44d6346799af23 f002 T5-core T5-core
12 33484752078 1939 e11d5900cd8735fd2ff5ca5308bc028fbe5ed2a5 f003 T3-mid T3-mid
13 33492041913 1657 7f158e950f874bea6d9530eba16f44b0032f5cac f004 T5-core T5-core
14 33512870122 1941 35efca813cb2e280fd98168e037cc2a098e365c3 f005 T3-mid T5-core makefile-by-name
15 33512876012 1971 a5af586d9275249ff9ab8fd7504c9a1d12f7ef85 f005 T3-mid T5-core makefile-by-name
16 33512878163 1968 9e954104ff5c23bbbb05eccce024b74e6e0a1a6a f006 T5-core T5-core
17 33525282952 1739 2a2e817bbc11c64755e9d9e3c2e3bb951cf29083 f007 T4-platform T4-platform
18 33525334366 1817 e7bf16ac616d3d04733caa075a59902fa204d664 f008 T4-platform T4-platform
19 33529411666 1784 fad564e1e1ad683657ae604bec2c5f0852f19151 f009 T4-platform T4-platform
20 33530652927 1992 abd82a108b459d70a923d7b4a43651fbeb835912 f001 T0a-docs T0a-docs
21 33530742647 1697 22f90fe883f641c2c460a210a1de3a8df74c4285 f001 T0a-docs T0a-docs
22 33533600441 1993 da61c81af243e3277e3089fb474ee5c08fe5c148 f010 T2-extract T2-extract
23 33533605337 1697 4b844b7d9e782ff26284248c072578215cda3d2f f001 T0a-docs T0a-docs
24 33541770974 1697 957b27fca50d2c3b1ac86d771733f39612bae841 f001 T0a-docs T0a-docs
25 33542424474 1811 fb2e38f5bbd45a2504d9daee09fc4c57a8d7db7a f011 T3-mid T3-mid
26 33542805417 1697 4f3412b78a5fa64ad01cb9ffedea603d101262ec f001 T0a-docs T0a-docs
27 33542807266 1896 50eaeebeaa2edb2ce14cabbf0d128e47f47b9c5b f012 T2-extract T2-extract
28 33542815580 1703 359f7360ddd39dc155637076e76a4e855c27611f f013 T3-mid T3-mid
29 33543233252 1811 b149247a0fd78d912ec906f44c2cf30bae7e5209 f011 T3-mid T3-mid
30 33549536985 1918 738174f74a2fbc2ad53ee9f4b2aed1eea2214d8e f014 T3-mid T3-mid
31 33550775043 1996 5e328814fc99ffe588daae4f0f1e8cf5066e084b f015 T4-platform T4-platform
32 33556880174 808 c19373cd4eba3dc836ccd9b12569cceb9aeab970 f016 T4-platform T5-core makefile-by-name
33 33557912833 808 909d01dd80de2c2d2b3518b60e33901dd4f284d0 f016 T4-platform T5-core makefile-by-name
34 33558106767 808 4b33971df0796fe841df867ca202f28a59a5824f f016 T4-platform T5-core makefile-by-name
35 33558842421 1697 40e5e373ab27bba3129405f346aa5937f9c93850 f001 T0a-docs T0a-docs
36 33558848235 1895 230f42454d76e103209e3baa54f6b7bf563cd2bd f017 T2-extract T2-extract
37 33560684540 1998 45b97287c75a0d2f49a28ac900c2dc00d518e594 f014 T3-mid T3-mid
38 33564199875 1986 b2ac3c58a99c534d4882674b5651deb61296be42 f018 T5-core T5-core
39 33564348882 1697 40e5e373ab27bba3129405f346aa5937f9c93850 f001 T0a-docs T0a-docs
40 33564426767 1976 82e1de8cef4121db80c53f4e144530dcf5dcb747 f019 T5-core T5-core
41 33565344188 1876 9d4ac2b98b9c19633ce2cbf8d9c6bf9edc57bba6 f020 T4-platform T4-platform
42 33569142503 808 89f1edcb50ecdec1e0db64002f1d8eed07164d46 f016 T4-platform T5-core makefile-by-name
43 33569301401 1999 2641ec858dff11307d42e60e8e3dccc627d02f0e f021 T2-extract T2-extract
44 33571644044 1875 25a7aacc87250bff752ff61cd33e9236be5d1310 f014 T3-mid T3-mid
45 33571903536 1877 62b44519c68b403ea44d9a85ba8ee74cddee66e6 f022 T3-mid T3-mid
46 33572811960 1912 63f79365e3217d2ef7a3615567bb96627f3fd6b3 f023 T3-mid T3-mid
47 33574864607 1968 a3e8fa3d08691f050f16d75879fd8a84b73ad76a f024 T5-core T5-core
48 33576560737 1986 e8204092333ee1c38cd681a81abb36f125288894 f018 T5-core T5-core
49 33577534823 1999 a739a2d81b43eb6938d014e005b306ab27e02396 f021 T2-extract T2-extract
50 33578326689 2002 976f312ea24acb8b1840ee5f766f3e2588af333e f014 T3-mid T3-mid
51 33578871318 1723 b769ad6324f866a580d731f5579a9d076cc45cf0 f025 T5-core T5-core
52 33580573384 1832 844a36b78aa10f16338d24a1a819ca9a31525129 f026 T4-platform T5-core makefile-by-name
53 33586571817 1836 2f37f61e8848f31f308bce8623f66dc2fdb50619 f027 T3-mid T3-mid
54 33588509339 1824 fd73c347fbcfd7f66c210577b4d713fc6ce7780b f028 T3-mid T3-mid
55 33590570368 1862 0e8fc16abce48346d07af78acf3293a805085e23 f029 T4-platform T4-platform
56 33593858493 1874 c0173a3362878e3cb9e7bf9b65c7ab79b46e73ef f030 T4-platform T4-platform
57 33593864739 1871 4772f4715172173559ba3f9130ddd90df4416ed7 f013 T3-mid T3-mid
58 33596260489 1779 98e386e58b753bcf63f67aeeacbfcb94961d035c f013 T3-mid T3-mid
59 33599715238 1739 4c1b23477b8e0de27feee74eac11c21a1fd64151 f007 T4-platform T4-platform
60 33600443796 808 53a97f0f74e34a55076ea3d1f1dc0be562b1c7b9 f016 T4-platform T5-core makefile-by-name
61 33600512620 808 003f8f4c2efbc22fe72e2dcab7fec5cfd1b42cd0 f016 T4-platform T5-core makefile-by-name
62 33605426923 1931 5cb90f64adaf9c5a38b591971aa7c09e3d9e958c f031 T2-extract T2-extract
63 33605453120 1936 27d4d34f7973823530922ec5485ce19a64589e22 f032 T3-mid T3-mid
64 33605453184 1907 651ae3c2af1a22ec61a498b39350785aee480973 f033 T3-mid T3-mid
65 33605453306 1913 a6b1a69a7c9a9ba67ac16c88e9c11de3524f87cc f032 T3-mid T3-mid
66 33605454090 1915 fcbd12ee1ba8580c1c83ce7ba797613ff5af00e7 f032 T3-mid T3-mid
67 33608282620 2005 256b3d3722247f451a2213023dc36a1ebd5827af f034 T2-extract T2-extract
68 33610039868 1811 cd8a5ed0d7c5674072f5d422b12302aa6424fda3 f011 T3-mid T3-mid
69 33610440955 1946 4bfb074b2892ec181e6b16dee3ee984af6dda201 f035 T3-mid T3-mid
70 33612193345 2006 349aecba9528427fa635975e246f5b8959b14f30 f036 T3-mid T3-mid
71 33612686565 2002 3d9a6cf5cb7fd39c6b5fb4dfa43011aa70f736ea f014 T3-mid T3-mid
72 33614840408 1947 ec0ca565a6571cad6e1c90914400b8dd4c2098e9 f037 T3-mid T3-mid
73 33615574338 1912 97517a4652f376cb6bf096255e235edf456f86b1 f038 T3-mid T3-mid
74 33615892072 1777 7fedaba331d8fbd241582f3ceeccd70af0ade149 f039 T4-platform T4-platform
75 33616176807 1949 91a3c6a5173de50fb47c4ebc5a8ba448598f7d5a f040 T2-extract T2-extract
76 33617264651 1913 dbba7573016e17f20fe5afaafe5cc2cddd98744e f041 T3-mid T3-mid
77 33617265496 1936 ac46c17fc37f78ae518145e73d51b46ca3013fac f042 T3-mid T3-mid
78 33617265877 1915 adff62e7f20dd439129d17c5dc028c7069af92f2 f042 T3-mid T3-mid
79 33620018950 2008 c36b4fbc446f9085704a6073b81ec743fd4180fc f043 T2-extract T2-extract
80 33620040078 2009 592894a4387a50e1d128ff6a2695ff48f8cc32a5 f044 T2-extract T2-extract
81 33641828637 2013 40f2722d6166c1f97fa1667c1e63e8c97e6f05f0 f045 T2-extract T2-extract
82 33653455785 1809 88269ac2ee70ab88f3ee8d84bd509399e3a8f3d6 f046 T4-platform T4-platform
83 33654490848 1809 b026a6907807f7a411e7da4de45d0cedc2e93dc9 f046 T4-platform T4-platform
84 33656319321 1819 64ea496a7b3623f5f8b28343aba99535b282d86a f047 T0b-nonproduct T0b-nonproduct
85 33658603433 1773 572725e61a00b9deda7407971dc9290477170515 f048 T2-extract T2-extract
86 33658798739 1811 3f08fac5139a4acd3f393e9ca78538c1ba8b94d7 f011 T3-mid T3-mid
87 33663175876 2016 0e3c9a3d52fbdd234db180894e94f434efce3505 f049 T4-platform T4-platform
88 33663200103 2017 1e92269d7562d89a816271c4d99904ee53a94606 f050 T4-platform T4-platform
89 33669707175 2016 e0785b353af1af3812d5d14cad55c839efc5749d f049 T4-platform T4-platform
90 33683309620 1649 ff8714662728f55a5ff368462fd8daedc80b89a6 f051 T4-platform T4-platform
91 33687771558 1832 69a9c9005a6d81889cc7fe5e0355e610d2a08e6a f026 T4-platform T5-core makefile-by-name
92 33688645901 1723 83ca40ced11f51e479d4d868ed167b47cfe993d9 f025 T5-core T5-core
93 33700059940 1896 c4e902849f8062e37a0f0ddaed1c6cc042780cb3 f012 T2-extract T2-extract
94 33700061299 1976 7b528b3247257fdc8d503a4af617007bbe3f8454 f012 T2-extract T2-extract
95 33702992083 1874 4529db7eb56c5793227bdce34723827030796a8b f030 T4-platform T4-platform
96 33702997094 1871 2283bcb9eb85b17863786608a0c41b0937521b29 f013 T3-mid T3-mid
97 33703360466 1998 41e649281bb9b27ead5d6ec223a0f0c78924c8f8 f014 T3-mid T3-mid
98 33705255783 1918 426e415f5427f2fb2e9c6f918cc7cc9abd9c109b f014 T3-mid T3-mid
99 33705617151 1881 57ef0a6d8662e9854dc5711922f801c3da381071 f052 T3-mid T3-mid
100 33706085211 1897 2c76563a137876b110cec15b9b6fa08d35069f73 f053 T3-mid T3-mid
101 33706763915 1880 6b801eec68d6465aad2020de438e629cead99f5c f054 T4-platform T4-platform
102 33708048488 2019 ad04339030bbfea4e77139bcfa9a87356c1e0e5b f055 T1-tests T1-tests
103 33727721488 2022 c846ed7b37331696543e71a70a8e5c8dd301467d f056 T4-platform T4-platform
104 33731062466 1874 c38d19139ad9657ab563951ccbf83dcbd1bb2435 f030 T4-platform T4-platform
105 33731071275 1871 adb49b3a28826e6eef871b1777b63668199c0463 f013 T3-mid T3-mid
106 33732861010 1811 da09b73bc0c03f3909a0153d75b8479220c31e2c f011 T3-mid T3-mid
107 33739760521 1811 e79a7d73b45644d86ab3ca3c7af84aa04aebe75f f011 T3-mid T3-mid
108 33740406928 2024 1770d68a2c934501b889e625dc94ef933e3a1815 f057 T3-mid T3-mid
109 33745916176 1811 bbecc9b9e92af203a03433e8cb6968e1e8d21cb2 f011 T3-mid T3-mid
110 33751987784 1996 7c4b5fc8a7c9ed2f1cf3b675bb821dbbccf2c1bb f015 T4-platform T4-platform
111 33752032210 1811 b64e2170009159a5111b1339312512f4e66b1b57 f011 T3-mid T3-mid
112 33754008677 1874 956489dcf88d0fd571efc493e6a93bb1d96760c7 f030 T4-platform T4-platform
113 33757191193 1767 06576511ce84b54b2177a75ae82c3af7dc897160 f058 T4-platform T4-platform
114 33758229230 2026 73f4a432e4ac1d575635a490d96e0f5ea588344d f059 T4-platform T4-platform
115 33763283492 2027 0ac290e2e243148b9e84bb682cf1101fe38bc6e3 f060 T4-platform T4-platform
116 33765101593 2028 1d38be3331576f50ce555d99717974060a6a16a8 f061 T5-core T5-core
117 33765957247 2029 be66a2d2c6f1538a4a831eb2c91622997be5b5ec f062 T4-platform T4-platform
118 33771464451 1809 e65378e6a5faf49abd7a0987994d7c98a9f834ad f046 T4-platform T4-platform
119 33775413774 1809 d402623c20e1dbff62e560a865fd4955798dfbfe f046 T4-platform T4-platform
120 33779258079 2030 3507d9555d73aa746cde23139588081c6c02d2e2 f063 T4-platform T4-platform
121 33799475629 1819 b25b1f65b402ba5395da846f05a306746055594f f047 T0b-nonproduct T0b-nonproduct
122 33799483900 1918 74700067c48b967edc4ef723b658a0fbb04f291c f014 T3-mid T3-mid
123 33799487158 1880 e63292a8d4036fc2618ecbee973bd6cccaaaffc4 f054 T4-platform T4-platform
124 33799492841 1881 4d4af28672729a5c1326974684728c9ee150926a f052 T3-mid T3-mid
125 33799495512 1897 429141261daefc04fc94f35300f7dbd899ed28f2 f053 T3-mid T3-mid
126 33799501343 1767 8a74d814e5149be17f2fc70f9a59d0c166604273 f058 T4-platform T4-platform
127 33799505794 2024 380b1c5af1d5f3e94248752266031b3035080793 f057 T3-mid T3-mid
128 33799508762 1811 f47cd47afe9e7e144f3c6d49df37c5b53d3d1cdb f011 T3-mid T3-mid
129 33809990745 2040 12f5e2c3636b694cbcc76b197815a983349fff5a f064 T4-platform T4-platform
130 33816056229 2041 588cd5543a617c73646be61de6926b9ac7004064 f055 T1-tests T1-tests
131 33816348076 2042 9933548586760fd81f2cf76673ec42c338ff62eb f065 T0b-nonproduct T0b-nonproduct
132 33818108315 2043 7109e5e7b013f43230f735f93043ae608be5d98f f066 T4-platform T4-platform
133 33831172851 2005 ec28e66434bc03ec33dcd05563b3bfd7f58efe86 f034 T2-extract T2-extract
134 33849949280 2047 9104feb67f78959e64ddff78d236e0dea475a434 f067 T4-platform T4-platform
135 33850387435 1976 aa3627d3bfb38627d700043a599622bb0e80ef2f f012 T2-extract T2-extract
136 33850532415 1709 2efb7a6323f992d5c407cc85e37b6d167c184ca1 f068 T2-extract T2-extract
137 33850538524 1721 dc703dad4a706a0f2c8130a869922677dba09ae3 f044 T2-extract T2-extract
138 33850542851 1730 90ba8990764674b2eeaca6fa372c654a63a04f51 f069 T2-extract T2-extract
139 33864581134 2048 f404dfbc7ae47f9912b18a0554a5e5d91f65be1a f070 T0b-nonproduct T0b-nonproduct
140 33868849245 1816 2ea73059f894df564e21be409908c2b5d3d53b37 f071 T4-platform T4-platform
141 33879400189 1996 8a1f2503c33bdd9a4d87782493af5640a505594f f015 T4-platform T4-platform
142 33879404796 2041 e74f041b3387e89fc3d3bc727cae772516099e54 f055 T1-tests T1-tests
143 33879411345 1809 b435bd87b6d0e6a8da43c2964829927fff331f5c f046 T4-platform T4-platform
144 33879415252 2002 c6ff57df0c216dc0fc86442f111607d8b3bd7ef8 f014 T3-mid T3-mid
145 33879421262 1741 fbda6bacf78b47e4c498dfb44ae653f9d10a4add f072 T0b-nonproduct T0b-nonproduct
146 33879427336 1742 e63bc6f06a03e93fad9e962c9b9aaf4b0309306e f073 T1-tests T1-tests
147 33879430001 1976 6f7a4af4a3a1716a5aeb5cae60a112e2231195a5 f012 T2-extract T2-extract
148 33879554682 2043 7e47043acce9bdfd87e7becc7fe7a348b02753ec f066 T4-platform T4-platform
149 33883643915 2042 6b0c44afcfeae65eed82e2dbbd58f00b043f21f6 f065 T0b-nonproduct T0b-nonproduct
150 33883715988 1649 f195c6aa81f425882626cca1186116ec64a0143e f051 T4-platform T4-platform
151 33884431041 1649 89186a26bcc37ae51f943b785270aa66716c832c f051 T4-platform T4-platform
152 33885636468 1933 8d46d2586255fc85e35f00ff8b814424b140e623 f074 T4-platform T4-platform
153 33887517545 2051 e2195dcb8f6fba33b72da60c1f3fffc83cdcd714 f075 T3-mid T3-mid
154 33891234168 1874 8069c7cd4c73ff7ea4513d38d7dfd08c350472a4 f030 T4-platform T4-platform
155 33892259683 2055 36bee9039848829c7d2271c44a46515cdaa8d870 f076 T5-core T5-core
156 33892321505 2056 24f54c0eb64c516941abee2ad0d7de758b34ac86 f077 T2-extract T2-extract
157 33892540012 2055 132e8fc32309e843ccd97138c8f032e317333673 f076 T5-core T5-core
158 33910645341 2051 1067e519c04f6273dd6e2b95b16bd0a9db4c3056 f078 T3-mid T3-mid
159 33913569180 1851 20c7dd3dbbba55770b82a99f6fcec6120f362873 f079 T4-platform T4-platform
160 33914255147 1914 7b92d2a6218936767b112081b9aa4e79d5b81dd9 f080 T5-core T5-core
161 33917205956 1849 39c74bdd5c8b5dc3bcd43f962195add0ba794150 f081 T4-platform T4-platform
162 33918309198 1796 ad479b61e9f23c01606f42c7d44ca174f104a2c8 f082 T4-platform T4-platform
163 33921848127 1597 335279ee403922c0b84c3972c3fc4782ffe878bc f083 T4-platform T4-platform
164 33923008470 1597 0094133cca7d92e587c4d876ccdb37140515fa7c f084 T4-platform T4-platform
165 33923354326 1766 797a11a15eff6154428aeb268c0b55edb5647a7a f085 T3-mid T3-mid
166 33924300711 2059 e0f37d7cb305cd2ff1d709c3ba1198cf950e83df f086 T4-platform T4-platform
167 33924381537 1597 3cd0043d0a3dc282c6a36ae0a262ebe02a4cec0f f084 T4-platform T4-platform
168 33932138466 2060 3a8b8e82120872a56ca4d0157260569d90abe85c f087 T4-platform T4-platform
169 33934626037 1784 4cf2acf2b7483ffa0fe3918f1a38e6d72d08b743 f009 T4-platform T4-platform
170 33935475331 1597 b900338a71c7395ade83c4e371ca277f2cb5498b f084 T4-platform T4-platform
171 33940288019 2062 9b4a3bc91e2f4d50e00deccf236c6b0db177f4eb f065 T0b-nonproduct T0b-nonproduct
172 33941972045 1597 10d05b38a63ff1efa62bfe2d419b01c72bcb2bc9 f084 T4-platform T4-platform
173 33944160492 1597 a15d99e39fd9ea04722f064ed02dc726b13d2943 f084 T4-platform T4-platform
174 33947308282 1874 9ae4ba7648101603eada6e7cd54c6cdc284617f6 f030 T4-platform T4-platform
175 33947974349 2063 f8132b42ac9ee1fde464866b5f3b08939528f267 f088 T0b-nonproduct T0b-nonproduct
176 33948072527 2064 3979f12a749232e1f934949f58f68c83a8b66014 f089 T0b-nonproduct T0b-nonproduct
177 33949446878 1597 9427f435158662074d6576053a6675a403696543 f084 T4-platform T4-platform
178 33951152502 2065 7dfd2c064911a7f2b246b7edd610fab4f993240c f090 T3-mid T3-mid
179 33951945765 2066 4b73a518d720bbc8773970402ca96f5661726b78 f013 T3-mid T3-mid
180 33963617997 1874 41f52a0f3b948468c468ddcca36489eb5f42fff3 f030 T4-platform T4-platform
181 33970672289 2063 3b173288feafebac3f0845f53e481f69adcd70b1 f088 T0b-nonproduct T0b-nonproduct
182 33971609329 1784 b9da6dca03126ef0c8cacccb3321dd5d69ccf34d f009 T4-platform T4-platform
183 33979110285 1822 7fbaf02904363d38c88f9b749b4523a614feecd5 f091 T3-mid T3-mid
184 33979848957 1759 683d4bc312625a3d26819635673529ab02a0d0e1 f092 T2-extract T5-core makefile-by-name
185 33980369725 2056 4f82a1e56f0eef435fed0eac5c8dd06b5b30c2fc f077 T2-extract T2-extract
186 33980647419 2065 3e1a22897ccea3404b95126112009e8b1a719a39 f093 T3-mid T3-mid
187 33980897876 2065 73c509f4246b0f97ca772b6d9a3159181a7fa6b6 f093 T3-mid T3-mid
188 33980899537 2066 c4bec4a6b38ed66ccd97df06490e051d3d9d81cd f094 T1-tests T1-tests
189 33982832791 2067 b7e18c8940d2bb58d85339d27da0754e5aba92e5 f095 T3-mid T3-mid
190 33984123524 1716 439659b333895a49174dca1fe4343f356616210c f096 T5-core T5-core
191 33985769258 1894 b49c7a736c0789069e5c2d07a301143e382af53c f097 T4-platform T4-platform
192 33986188532 1739 b51411753e9423f1c67f810ecf54f9fce903cefc f007 T4-platform T4-platform
193 33986691207 2069 3b9052c955a9aafbb3baff874a93bba19afeaace f076 T5-core T5-core
194 33990039141 1941 b04f54506ab42eddcbd89232a0d47d8355edd596 f005 T3-mid T5-core makefile-by-name
195 33990592318 1971 f89f01e9456c8241a28fb909d930ec8d88b23239 f005 T3-mid T5-core makefile-by-name
196 33993950950 2059 3007dbf8426764a92ace4da111836b86bec6b508 f086 T4-platform T4-platform
197 34001348423 2056 e384f88f90d1f13ac95220f15de72e888a654c98 f077 T2-extract T2-extract
198 34001350759 1822 eb3bd818b597b9c1f317906b350cbf323f2a4995 f091 T3-mid T3-mid
199 34001352917 1716 26682d58c7595c4166d7b7d274885115f2dac324 f096 T5-core T5-core
200 34001355464 1759 b35f73123302d8d101323c3621e6fe755d4970c6 f092 T2-extract T5-core makefile-by-name
201 34001357884 2069 f8951524252b71be8d8d3ea1f86d03eb97d6e9d5 f076 T5-core T5-core
202 34001359042 2066 7da8bf15838c42f8e3f366fc2188ca63f2b61942 f094 T1-tests T1-tests
203 34001473433 1739 9a5e3f36a9580f76211300faa8b7ed5942169b47 f007 T4-platform T4-platform
204 34002279390 2070 c1b9c451c7f124936e635835ca4dd0cf8bf7d5b3 f098 T4-platform T4-platform
205 34003689551 1860 a44732b6f8ee6409cd12b135f14867f1b5ecab33 f099 T5-core T5-core
206 34010341126 1998 04ba2fa368db12a80091315bfa649626a36145e1 f014 T3-mid T3-mid
207 34011344933 1968 17b6dfe68b65ae7fae797487c69efdf110b0cbae f024 T5-core T5-core
208 34012479012 2065 c72fe3222a5f3caac2e87636252931f3cfce2619 f093 T3-mid T3-mid
209 34012788868 2041 874a972fb7c4eb06e739dce0a390fffb85d31f83 f055 T1-tests T1-tests
210 34012788874 1976 eb80a4ecff04d8bfacef20e1aa2ee29445a782ff f012 T2-extract T2-extract
211 34014013202 1874 a3500040e69af8284e3cac1398e00ae3d00a0524 f030 T4-platform T4-platform
212 34014527172 2065 60449f895dbaedb4e74c4f155a8e214d78486e12 f093 T3-mid T3-mid
213 34018899805 2072 b0f9398d59c0c8a5bb03b80eba84d30e630160ec f100 T4-platform T4-platform
214 34019712929 2072 06644428b6d0a6295414d23d2c0613f29a73e1d7 f100 T4-platform T4-platform
215 34019861168 2072 bae6977e91daf703b812136c70556cefe4e3c283 f100 T4-platform T4-platform
216 34035459918 1968 f3e1253859c299d194c608d91d5c89065ca0c9ff f024 T5-core T5-core
217 34037162475 2073 5abd77f5d9580d75f8600914b46495be21e9a4a5 f101 T1-tests T1-tests
218 34037415003 1971 9540d64c52aecf89ce8b0524b8fea69c2aafb0e9 f005 T3-mid T5-core makefile-by-name
219 34037489952 2056 9cb2e9dd1c9fd4e6b3cb9f939ec46264b05c75ab f077 T2-extract T2-extract
220 34037491375 1716 8ab6bb6edd69a4ec64df07c3910a5c035b42c937 f096 T5-core T5-core
221 34037493874 1739 53f55ebe72870695c15ace98101df5af6eeef38e f007 T4-platform T4-platform
222 34037496632 1759 bb21edbbbba442364b88598af97478a97e62243a f092 T2-extract T5-core makefile-by-name
223 34037543448 2041 99fa463023fe63520d5a23c29f777b9d91d553b7 f055 T1-tests T1-tests
224 34038315061 1941 ddb6533cd9c16e8e2be12aa791521420fb427881 f005 T3-mid T5-core makefile-by-name
225 34038319912 2059 306c02d44e9a464faf68640f183fa6e82fea0907 f086 T4-platform T4-platform
226 34038901925 1971 ede0e3b111054c165b0237124084894b45e0e052 f005 T3-mid T5-core makefile-by-name
227 34039040074 2070 9846c3f1711d557c78426913efaff340e37feb4e f102 T4-platform T4-platform
228 34044235267 2074 6db22aadb6e5a92b9b193608e2ac3f420e461ec3 f103 T3-mid T3-mid
229 34044659567 1998 c8ea1cad0dd34e6f6f8c542014a1a22885f67ee3 f014 T3-mid T3-mid
230 34044858364 2075 086422020f5fd9396b46384b10413cdca6994c96 f104 T4-platform T4-platform
231 34044911261 2065 d550dcbe73558bb773211a0ff9463daff91721fd f093 T3-mid T3-mid
232 34045088264 2076 a08b9ecbbda54793275cbd4e93fecf0ad4ff1c0a f105 T5-core T5-core
233 34063281067 1739 1d35def402f586b4c18bba41de33c881eda8cfa2 f007 T4-platform T4-platform
234 34064340654 2079 e410c86f270a013d79639577d82009698b207e8b f106 T3-mid T3-mid
235 34065487385 1998 98899ba5d62c9a66d038b24f3857b137d03d44b0 f014 T3-mid T3-mid
236 34066407580 1716 e3c0010267e783b4270e0ac7c8ee6332b61aeabb f096 T5-core T5-core
237 34066408930 2076 1b89228d90b32ddd46f494df8755f496f6aba030 f105 T5-core T5-core
238 34066410797 1759 fbe9eae11c237459da50a06ebd83f709d655d491 f092 T2-extract T5-core makefile-by-name
239 34066413612 2065 8eb63ecfe79ea1f33fd911b8913a8b704ecde9d8 f093 T3-mid T3-mid
240 34066415650 1739 76d0382f8f958238119c8e36bbdd204838d15db5 f007 T4-platform T4-platform
241 34066415871 2079 dab58c0701d8806ddac2783949f1a478225f80bb f106 T3-mid T3-mid
242 34066418484 1998 a143b0882d0457f2f56b2d734128f914325d555f f014 T3-mid T3-mid
243 34068996256 2082 16a739d32314faeee5539386a92afd2363dc1879 f107 T3-mid T3-mid
244 34069613782 2083 77a0c7d94156a43f19047330c7356be4ee12cba5 f108 T3-mid T3-mid
245 34071276309 1874 4ef5672c53777f9dd316510dacfa3b88e2166fe2 f030 T4-platform T4-platform
246 34074515665 1894 088fedb2d25b869621fd93db05b2f3aa69e30e65 f097 T4-platform T4-platform
247 34074587051 1894 4d3d06ac2aba832ba774cad396a3f445b290f0b4 f097 T4-platform T4-platform
248 34081576339 1971 e5c17de6217ec5938d18d8c51e2c0d7ead116a19 f048 T2-extract T2-extract
249 34081831353 1968 2b1dafd83d6df8b0ad8a064e1b23bc27ecf4de7d f109 T5-core T5-core
250 34085524473 2084 4e0b7e1d168744438f0602735c4fee3970f3caf1 f008 T4-platform T4-platform
251 34086907163 2082 d8dbf01c3f90257d98c7e23a21c5c6f7f64d3a4d f107 T3-mid T3-mid
252 34090735558 1723 c8e7f7e3cc26f1faf8eaff5889d6ff0abd75ae9e f025 T5-core T5-core
253 34090831729 1724 70b411d5cf392e1ad6528e9f78d391304368ee30 f110 T5-core T5-core
254 34090999743 1725 8152277aebdd974badfbe0ffce13dfd231a23732 f111 T5-core T5-core
255 34091031555 1725 980934b2a2cf4c1dd32163894b500e0c96587bc6 f111 T5-core T5-core
256 34091168476 1726 fa11ee4193bb7537e18865fb649d1e73e8be1db9 f111 T5-core T5-core
257 34091245271 1727 f9a7e1fece6264d1677ed5dbbc2894553f7ecebf f112 T5-core T5-core
258 34091261353 1728 4602cb100fb0dd404d49531158204fa3f00d9ac2 f112 T5-core T5-core
259 34091378540 2072 c72cb8bb950019b5451325e52940da956250f0d0 f100 T4-platform T4-platform
260 34091504731 2072 bc945a4d6c6716ee22d314b6b53a7d50ed007081 f100 T4-platform T4-platform
261 34091593211 1725 ca6a5d5bc408d59af6ae9ca1edab0d169664092c f111 T5-core T5-core
262 34091642820 1726 f870ce4cd80c0882568ba19438a3cb6ebb2e43a4 f111 T5-core T5-core
263 34091649819 1727 8d4c416579db0148869f3f5c7ffed3e0e90c4417 f112 T5-core T5-core
264 34091656935 1728 37e213860e8f8390f54880bfff717345d2da5689 f112 T5-core T5-core
265 34091819685 2022 84fffefa48cbb60ca1533d2433921787c8ea1031 f056 T4-platform T4-platform
266 34092213233 2072 838fb7090f3a08920363e9ee793824b4d6202bef f100 T4-platform T4-platform
267 34098390879 2085 9f57edfa576072398728eda2c6fc2585d2ecb063 f113 T3-mid T3-mid
268 34109049475 2086 b9d550b6f3e1c08ce6cf59140601205d609dcd52 f114 T5-core T5-core
269 34109055503 2087 45b28f0b11d3e74e28e492c6c234a0f7ed0b30e4 f115 T5-core T5-core
270 34113927999 2086 2082b2d2890ceb007e04b9ba77129d14a8f4df52 f114 T5-core T5-core
271 34113933101 2087 9a7445e93de55eaca53855057ce6ce1a6f234564 f115 T5-core T5-core
272 34115081932 2073 8e29e1568f442ed9422e0fcd24d2594ae0ba7893 f101 T1-tests T1-tests
273 34118689934 2089 dc34e63a0a0aa29cb70456b692733124aba65350 f046 T4-platform T4-platform
274 34126118251 2090 6b56d78e1bbb5715c9d0c715453244187bd299bc f116 T2-extract T2-extract
275 34132427862 2073 aec9bfe3751d8c1f2eb2cb48d8fe584b9d3fd973 f101 T1-tests T1-tests
276 34134175243 1725 8d57fa45ae548e23b11083072447a62ff053da86 f111 T5-core T5-core
277 34134183283 1726 c263f48d0307540b0837726ba5a3c2cd9d2ea012 f111 T5-core T5-core
278 34134191503 1727 94119b3803e276a4cc45f4b92ddaf3d1377e3889 f112 T5-core T5-core
279 34134201351 1728 f588bc374ab91697ccb4467f5df9686ada5cb62c f112 T5-core T5-core
280 34138662959 1784 85482e3c599d454d76ccef5e862a5dc6c38a898e f009 T4-platform T4-platform
281 34139187715 2091 b974adf6ed9b82d8669d1a65b1757816ce0d12df f117 T2-extract T2-extract
282 34139262637 1784 a21562666317f752f9921109a0c70427f3febe04 f009 T4-platform T4-platform
283 34141564461 2092 d1abca20c091b9623187c5cfc61f251e7bd7796e f118 T4-platform T4-platform
284 34143404498 2091 ec56ea8f60f0058bd1cd0fffb38cc0153b99f895 f117 T2-extract T2-extract
285 34143899077 2092 1f08f82adcd979ba2f0efb38a1ccfed13901f76c f118 T4-platform T4-platform
286 34146676636 2092 4f8290aae42ad3f2ca4767089057e74756311743 f119 T4-platform T4-platform
287 34159409001 1561 b939a2d6ee76e482288a0dbbf712cd94d0d57164 f120 T3-mid T3-mid
288 34168455959 2094 7219a24cc692500cdeb5cc192adbd47ac8bb6ee1 f086 T4-platform T4-platform
289 34168458726 2095 d3eb7e9823b43a8e2e5917d2d234367c1f88436d f091 T3-mid T3-mid
290 34168461878 2096 9e1faa7327f8192101f16d8d6d81020937475166 f121 T4-platform T4-platform
291 34180076668 1561 6875483ffe40017f79884872a325e2ff6b37befb f122 T5-core T5-core
292 34180195791 1561 a7941a4d2094aa6bbb0683e3a17371a0463de209 f122 T5-core T5-core
293 34186497464 2097 d653052508998144e39357a37f72a12448c769f1 f123 T4-platform T4-platform
294 34187802611 2098 5f9ac15818e3bbda9e031db54867dfab2b6c531e f124 T2-extract T2-extract
295 34187888044 2099 95f252447de4ae08707b199f02f296f28f8dcb21 f125 T4-platform T4-platform
296 34188664789 2100 2a47a6556cc14dd5ac036b3c2f3225c9925cdeec f013 T3-mid T3-mid
297 34190172584 2060 8622d6a0ed4935aedaa336d216d8b6b269732de5 f126 T4-platform T4-platform
298 34192737413 2098 f6c310c08e6dad38b92e953c85c7c5876f6df839 f124 T2-extract T2-extract
299 34194384003 2097 d9dec5dd3b334a514113670b42732ca9ea4a15de f123 T4-platform T4-platform
300 34194639858 2100 669f6a3741ef7b875aa5eaeda317acf1900bcd33 f013 T3-mid T3-mid
301 34195390569 2101 0c1d6f68dbf3f2c8215ae9c52003d785ac874a22 f127 T1-tests T1-tests
302 34195424010 2060 3c854ae217c4c5d2f48d8aaeb136541bb0d8b52a f126 T4-platform T4-platform
303 34196235240 2103 9f876f70ed13636a8f9a25ba043e8f682c6688f6 f128 T4-platform T4-platform
304 34236364592 2105 22e0335d94552dc1a9d9ef0ee8221e7665608209 f013 T3-mid T3-mid
305 34240237866 1784 18b2f8670c64fd83221617c5e7970fb95af17454 f009 T4-platform T4-platform
306 34244310689 2105 dcb10fe902905926eaf9325bb03254dde9cea6ab f013 T3-mid T3-mid
307 34249699686 1823 b09ef3df3289e74bae129df4df29886fefc75484 f076 T5-core T5-core
308 34249700997 1823 b09ef3df3289e74bae129df4df29886fefc75484 f076 T5-core T5-core
309 34253442941 2109 e65fffc5b1ac269c16d4371fc8c1ccbd102e267a f129 T5-core T5-core
310 34255410324 2109 840de91e384d2ea36dc2360ee12ace26d25b680f f129 T5-core T5-core
311 34257609138 2111 7b4eb4a00ac600060be4836ee4e1bbfa7bbedf24 f130 T4-platform T4-platform
312 34257629348 2112 21591d51168d26f931f64d62edc36013cd1f6249 f131 T4-platform T4-platform
313 34257632907 2113 00060cec854dbd38d7864aeb95a2d8ba1baa8ab2 f132 T4-platform T4-platform
314 34260896139 2110 d40ed1a6bbc968bbddf90abd3b38e74a131a1b5a f133 T4-platform T5-core makefile-by-name
315 34261770203 2113 0567dc70a097f36a953368978d6728b0a412b229 f132 T4-platform T4-platform
316 34261855843 1768 e8dcb28ba719e4272f605f3b961422bb1f3c3fad f055 T1-tests T1-tests
317 34263433901 2110 7bf366f63b3fd5211cec19e483ee2ed27145ca14 f133 T4-platform T5-core makefile-by-name
318 34263688699 2115 013e1f7ec7d4b981a1cd28bf35d5767c195f8447 f134 T4-platform T4-platform
319 34264299704 2116 3a4160b4610f83ea26daf69f1ffe7cb898bf48ad f135 T4-platform T4-platform
320 34273160491 2119 8135be28a764fad4ee55abe466762bbda7ebab74 f136 T4-platform T4-platform
321 34274955966 2116 7e73b48ef155a0e14a6e058be56287232e3976ea f135 T4-platform T4-platform
322 34275249072 2111 91e31211df9dd61459617d857d46b71699e34df8 f130 T4-platform T4-platform
323 34278140735 2110 a3c24a71b4c28e805b912f9f928b6b0b151f9495 f133 T4-platform T5-core makefile-by-name
324 34282381014 2113 ecfb9642e153ffc656d0bc2344b17e9d454ff916 f132 T4-platform T4-platform
325 34288397854 2115 92cb3b9a4bebbc79bd283e447104669ca068ea64 f134 T4-platform T4-platform
326 34315414268 2116 e45d705105a63f2fd9b35ab9e37ffbc640be8231 f135 T4-platform T4-platform
327 34315782814 2124 8134712dc43b159fd863244ce53ba23641f20f8c f070 T0b-nonproduct T0b-nonproduct
328 34327374288 1723 21ae02154a198265d7dbed13af4280aa2b1e5690 f025 T5-core T5-core
329 34327382260 1724 87da4422c34feb4df8ff369dab8d770c33d142eb f110 T5-core T5-core
330 34327387577 1725 d82f760040412d51d424f3c287d277e89d4fb199 f111 T5-core T5-core
331 34327494146 1726 9380b22a24b3ec85482fd209475f5b1641b5f699 f111 T5-core T5-core
332 34327502315 1727 579336cbdaa6493f47ed4de10f64387b6f7f16b8 f112 T5-core T5-core
333 34327526954 1728 9d5cfa53098003cb68bf1812a94ca51f5e046056 f112 T5-core T5-core
334 34379806132 1823 5e5e433d94880c283400215ba79094225a8b33df f076 T5-core T5-core
335 34392601650 2116 9ef7da8cbfba8de5526ccd05e7196f5fa73af8f1 f135 T4-platform T4-platform
336 34392735468 2110 91c63be5d4e41d44e0746ef533af4a0b1967d6b3 f133 T4-platform T5-core makefile-by-name
337 34392998828 2136 557cb0102863e66cbd5cc622509a29a4964d1831 f137 T5-core T5-core
338 34393093741 2137 4fa11d1ab0b5807a7eaed645a6e3675f0663def8 f125 T4-platform T4-platform
339 34408696449 2138 8116672a7e3a02aecb5cb0e6fd9e685cd5066fe1 f138 T4-platform T4-platform
340 34413434571 2139 df579830f3941e5ebaef73dc87d183aee358fbcb f139 T4-platform T4-platform
341 34414749652 2139 618849512d01a3d14fcb704532911f9d02c23436 f139 T4-platform T4-platform
342 34415791973 2139 62bafb76b380d3af1cd1d6228b4afea0082c2a7b f139 T4-platform T4-platform
343 34433093443 2140 a1d11e79087bb1284d6126b02b22c3ce7e72ab07 f140 T4-platform T4-platform
344 34446903088 2089 8f3c15044b8200a15ee1895c5127f4bf117da493 f046 T4-platform T4-platform
345 34456518090 2160 f7b56b900d4726221de2882d5a5d076b29f6758f f125 T4-platform T4-platform
346 34456521294 2161 c8e244d8ff1d51e57c9d757a8e6bad80dc67f107 f091 T3-mid T3-mid
347 34460157213 2159 8d353a573dcc4be7ae69a74685eeb819a5626b5f f013 T3-mid T3-mid
348 34460157936 2158 b5f119c3f27bc3601bc048b16bbfaf57af3908f2 f141 T2-extract T2-extract
349 34464851279 2161 6790e1ac0fc134360cda6544befc1af2e29b9605 f091 T3-mid T3-mid
350 34464853698 2160 b3af44832790fbc6dd6a7e09d18362f55149a4da f125 T4-platform T4-platform
351 34465154636 2164 70926a2956ce986bfacbf6e7ebfd398fd91ff266 f142 T2-extract T2-extract
352 34465653349 2164 9115f5154383bd091e125c956aa4f241ebac1c7b f143 T3-mid T3-mid
353 34470429949 2159 7e111f877b85b79efdd371475d7f71e61174f95e f013 T3-mid T3-mid
354 34484066063 2092 b2bb18cfc79e129f50b5da940413e5735bce09b1 f144 T4-platform T4-platform
355 34491583983 2092 b317506b5a62a3383e763011e5b148a5afd92a89 f144 T4-platform T4-platform
356 34491670375 2092 b037b488be4620a5e92424e7e390ee625cd7e5af f144 T4-platform T4-platform
357 34524166740 2170 5fc107a0a61419faf0a2495ab923aba25a37ddb5 f053 T3-mid T3-mid
358 34528400072 1823 ac75e587425c60154dbcaa7392c0522764c93647 f076 T5-core T5-core
359 34551561701 2171 4842e11b2d9f5e6c2ced39d12dc487bcc7d915be f145 T3-mid T3-mid
360 34595459266 2030 e65bc6b71e32d20aeafb42c00b6097a0e2ded2c0 f146 T4-platform T4-platform
361 34595482014 2030 eeff3f19ff97d60af4f34298aa38a652fe0705b2 f146 T4-platform T4-platform
362 34636198205 2179 6091db7104bcaa0b61566272290e04ce448851a7 f147 T4-platform T4-platform
363 34636557089 2116 f6a29270b72a5373e4d949ed3f4078a40830c57d f135 T4-platform T4-platform
364 34640188970 2180 527016258e0da33ef883966f618609f4cf58abf8 f148 T4-platform T4-platform
365 34649361704 2139 53700ee1c72f1a5c2551a83c74d20278e4ac664b f139 T4-platform T4-platform
366 34649395431 2137 ca36316c6b34a6081de24d69699665a20b16de0e f125 T4-platform T4-platform
367 34649399412 2113 ca86fdb057b3d7f6d31e179ccbe64629fb1b0921 f132 T4-platform T4-platform
368 34649404928 2110 33534653f10c052f2a5c7f1953756aeb10499be9 f133 T4-platform T5-core makefile-by-name
369 34649406520 2115 78a6d19f1eaaa73054639ab0f14c372c53cd1bb3 f134 T4-platform T4-platform
370 34649409769 2111 951b9ab022df391410528fe98769d091c203fc60 f130 T4-platform T4-platform
371 34651276769 2181 c8184ed94887a6b5e130726885b44a07c63a47b7 f091 T3-mid T3-mid
372 34656706072 2182 14bacf8919b64bea19b537b74c894f0f82783bdf f149 T4-platform T4-platform
373 34676336284 2185 eb5e34cfc141454e849daf413f648e73f74c455a f150 T5-core T5-core
374 34676377540 2186 f6281874bba9a78ac7543564ff8f76b5262daeef f151 T0b-nonproduct T0b-nonproduct
375 34676429093 2187 cea35fcc0520a0ba95139bd0210052f09b9cfa20 f150 T5-core T5-core
376 34696991657 2115 9a460b389ce3002ac164605c3627bb86aa3b9be3 f152 T4-platform T4-platform
377 34700514102 2188 734906379f235c07cce960ce6a57746ea3be48bb f153 T5-core T5-core
378 34703996478 2115 fa99ff64eb6579fb15f3aae1aff7da349cb2999a f154 T5-core T5-core
379 34708249832 2189 22cdf7d03db87bba19a76a072dd517dd88a54d40 f155 T5-core T5-core
380 34708361984 2190 8c2692c1ee32284c813c05746e833dea5dfedfe0 f156 T0b-nonproduct T0b-nonproduct
381 34710253103 2189 1ad52f5fb1b3d96255756de01ce30912b1899e09 f157 T5-core T5-core
382 34712092850 2191 27cacee1c2d3022fa0fe915b03ca4310042b7514 f124 T2-extract T2-extract
383 34713347879 2190 044fef7957d48a097b48f4ac3c505aa67de8164f f156 T0b-nonproduct T0b-nonproduct
384 34713405483 2192 9073fe4469bd222ca01bd48249b52bbf7ed5710d f158 T1-tests T1-tests
385 34720983404 2076 2b80b31306b3dbb9ac8bc0ad50372f724f1dd243 f105 T5-core T5-core
386 34725488475 2194 09c0e88a6484c5fb696d7fc7b5a5e3f32b8cba68 f159 T1-tests T1-tests
387 34729075944 1561 e0b9642010cbf003bef9b8b29ba4ebb02b38171c f120 T3-mid T3-mid
388 34729170802 1561 789c60c86bc75b342c52ddedcaf174d12ccdfa73 f120 T3-mid T3-mid
389 34745069386 2195 469c3dd996f23cdb629f1bbd6a751bc0f29a99f2 f160 T1-tests T1-tests
390 34746469011 2195 f9038b84eb3bf82c2dff4cc4ea7f1ec1d8d13f01 f161 T4-platform T4-platform
391 34747474867 2195 d21e6cc762a1816d8c274174c9bba01601b888ee f162 T5-core T5-core
392 34747919526 2195 085017ab2172c2f3d4cd3e27251033ecbc5b814b f162 T5-core T5-core
393 34748079069 2195 ac8f5b8a8137f15e95478be71ee6742e928ba5bc f162 T5-core T5-core
394 34751940360 2073 232764a12295a59f05619a2bd032c8f92af6f17d f101 T1-tests T1-tests
395 34752354822 1716 8d4fed61db63559a0961f11f821db21c26e7d24f f096 T5-core T5-core
396 34752961647 2196 81a8a03c820cb8bccafd0ebac355eede21b5e8bf f163 T5-core T5-core
397 34752965817 2197 823e720982e1bce047a9243bd9b24d9688dadb72 f164 T5-core T5-core
398 34752971903 2198 b5b69af7f8d0d1083974e993f0e9b92a435fa253 f165 T5-core T5-core
399 34753122921 2199 d14bc7051c8a32b2678bf10d5ee711637d606edf f166 T5-core T5-core
400 34758237901 2201 1167972031d427be6773b86ec6dc9903317360d7 f013 T3-mid T3-mid
401 34764451388 2202 7fedfe85c63446d3695b75d463a9b8767655730f f167 T5-core T5-core
402 34769627346 2203 848ce778766d73ab4eb09ba8c98001da8f587fe0 f168 T4-platform T4-platform
403 34769883104 2203 00411ce7bc41995783bfb16aada673515585446d f168 T4-platform T4-platform
404 34769958569 2203 d64c9f2afe6b2add648ee1e3eead48acda025579 f168 T4-platform T4-platform
405 34797944694 2203 581f0c74006beb3edd28babbebeda7e48b3fa1d6 f168 T4-platform T4-platform
406 34805101784 2202 7b77fd4834afe355cc4c7326ac3a55f0502f7264 f169 T5-core T5-core
407 34822774278 2205 e0a6133293556d87de6c2ca43de674e3a643e339 f170 T4-platform T4-platform
408 34823711563 2158 334e0b0849241430d26c3fdb6ffc8ec7a676965c f141 T2-extract T2-extract
409 34823714967 2159 63cdde702d9eb366b58006986e1498a18ae7f7d8 f013 T3-mid T3-mid
410 34823719320 2160 c1dd44151fee8cfd815c693392a7c1c72def98fd f125 T4-platform T4-platform
411 34823723515 2161 80f8bda5670c8ac1956e2a8fac7221c40c12da47 f091 T3-mid T3-mid
412 34833703378 2203 3b7f2559213ee80f5cfe0e2329c418211e09749e f168 T4-platform T4-platform
413 34853838753 2207 45c94be627a3088c275ff4f6acdc1f118515849a f171 T4-platform T4-platform
414 34858382857 1767 036562df35486924c62cdb11a8c9518e479959d1 f172 T4-platform T4-platform
415 34876200704 2202 9724d903046626df0640ced218259cb58070e4e8 f173 T5-core T5-core
416 34884655464 2202 9e69cb7d9b7ad4b40fa5f8cbbb653dc70e108130 f173 T5-core T5-core
417 34886874258 2202 d89ccabef064661794269c0de726021736d72b51 f173 T5-core T5-core
418 34891515627 2202 d70b79417978a9f3ab0d1aa849879dbf049793cf f173 T5-core T5-core
419 34898586377 1823 d4ae05a03dae831972a9dae7d2a2dd2ffce39e35 f076 T5-core T5-core
420 34943783091 1781 7c88e64eb954e9400273d3ebb9f6f88f1aebc165 f174 T4-platform T4-platform
421 34986091885 1823 955e4c9c58504b3c8e7d3e42866d16fadbb6ca7d f076 T5-core T5-core
422 34993109423 2212 172787de9c18352bb29545da2426d9d61b260fc3 f175 T5-core T5-core
423 35010526411 2213 d401186ca09e3992bc0e804af3945fe1cf81d6d2 f123 T4-platform T4-platform
424 35010533052 2214 1f1f8334ca25502c3ed1d4e91e8c6144d07bb7e9 f013 T3-mid T3-mid
425 35010542675 2216 729c3acc0fedae0ff77629281caf6dba1ac487fb f013 T3-mid T3-mid
426 35010543243 2215 783547e4fd79bb6b38f0741b6118a719e4109059 f127 T1-tests T1-tests
427 35010546755 2217 193c60d66db7eb36c58a7d0803d3936f5dae1d70 f149 T4-platform T4-platform
428 35010550773 2218 0a1d229ba89c5a7ad6c63c1587f5a938cc890270 f124 T2-extract T2-extract
429 35010559332 2219 a335422f63be3b5711ad40742bbaba40b2600f25 f158 T1-tests T1-tests
430 35016014963 1907 50ee111714baa9d3bfe9bdba3b81e27cd1bdfe38 f033 T3-mid T3-mid
431 35018285825 1913 da7619474d5ad27ff5073cf505b0b6cd5d0db36e f041 T3-mid T3-mid
432 35037109751 2220 62fd80b842f00bd72e655d23c6afbfe12e199b6b f176 T4-platform T4-platform
433 35037798343 2221 22ce867efd88dff9c219f789967ed2612329ee6d f001 T0a-docs T0a-docs
434 35047093950 2072 e5076d215e3a31ee7f7528d9e5ceda83fd8e8a8a f100 T4-platform T4-platform
435 35048046352 2072 8199efa4710893eb77d7c98d2730683a6c51327f f100 T4-platform T4-platform
436 35072023020 2223 17a326a58b591e786790e79ceb3be6dc14ee3988 f177 T0b-nonproduct T0b-nonproduct
437 35113639467 2223 43620d39d27554548fc2d613ed707efc8676748c f177 T0b-nonproduct T0b-nonproduct
438 35129477140 2225 58c1c0caa7061ae868305dbe59a6c5e67004849d f178 T0a-docs T0a-docs
439 35142303658 2227 dce2c5be9710c364eaa3a654d417ffcc392b62a4 f179 T4-platform T4-platform
440 35145042859 2227 3d848acaa7d22885d5040c957b3aecdcc4376107 f180 T4-platform T4-platform
441 35186676917 2140 b74c03558045bf29b7ef95c8aca803c15d3c7594 f140 T4-platform T4-platform
442 35272232918 2230 e0becdeb094dd5e898997a2add826a9f84befbd7 f181 T3-mid T3-mid
443 35273896570 2231 7054e4ff8af76f4fc1fe89407874e9910d0e412d f017 T2-extract T2-extract
444 35295711667 2233 f0a86ef2c2bc352a479162cca79d1aa64598f09e f182 T5-core T5-core
445 35303378012 2140 b52df4b0ce5c38667277716cd97ede964684802c f183 T4-platform T4-platform
446 35303488221 2140 ca3461170fefc959fa7688fae4e212d6e9f2f99a f183 T4-platform T4-platform
447 35312734228 2140 d85fe1d513ac97c8044b1065bbd0b7b9ebc87ec9 f184 T4-platform T4-platform
448 35314622268 2234 9b4d697a57a03f242164db956843a5313168005d f185 T4-platform T4-platform
449 35330927778 2236 949492b50c7dd81271f0180d8425d32316becbcf f186 T5-core T5-core
450 35397596933 2233 1d58385b513a414ead34544346e86549dda3c4b1 f187 T5-core T5-core
451 35443109227 2203 e176ae552b99f0fd08a2dde8bdf52025a3e20059 f168 T4-platform T4-platform
452 35446976110 2233 8c1a9d61c27292bf7a52f10fb54186d4e24de28f f188 T5-core T5-core
453 35448941856 2233 9db5217bdbb779212c5d066ed1a84f7ca6fc0282 f188 T5-core T5-core
454 35467285032 2233 9db483402ea2fd4e0a5ff3bdd2b1fc50a7518ec9 f188 T5-core T5-core
455 35470450948 808 bf2c54e0ef45d0d9767d7945a160f630f890f27d f016 T4-platform T5-core makefile-by-name
456 35470489882 808 80d277834a233f2e7a2df68b8c08a1f233cf3dfa f016 T4-platform T5-core makefile-by-name
457 35472952969 808 5515ca4ec6b20b62ed0530489785e4f131fd5159 f016 T4-platform T5-core makefile-by-name
458 35473919864 2225 c067c771b9f93d08a3e614e68aee7d710d2f256c f178 T0a-docs T0a-docs
459 35475706415 2241 6166934defd9bc5b687ab9c8de3779e71a844395 f014 T3-mid T3-mid
460 35475754479 2242 e8a46c16e233bff111092dff18985cca94890bcc f189 T2-extract T2-extract
461 35481840120 2236 aac550b732040acfe6ab6c0bfe5d0cbef5d9f9e9 f186 T5-core T5-core
462 35483670414 2243 243b405afeb3998707ea4d556f3178c8e8419cab f150 T5-core T5-core
463 35483768695 2236 d2dd3b347bbdcc695f911e80b1b7f7a32d790e5a f190 T5-core T5-core
464 35486887056 1999 aa4da14d23dc28df58b9881dc66d89c0d1c0d788 f021 T2-extract T2-extract
465 35493947102 2236 2caafc0c41bd083092afeb692824e718a8e1e007 f191 T5-core T5-core
466 35495099219 808 83667268d9c435c2634994efef45b09daa98904a f016 T4-platform T5-core makefile-by-name
467 35498544795 2227 656fe99f9934654bbab927d29add13f7822e1bc8 f180 T4-platform T4-platform
468 35499978643 2244 56de845327dce03cdd1269b8aa73c6a1ecdb5cfa f125 T4-platform T4-platform
469 35500380927 2245 ed78ff056e89da814ff5ae05da6fb32b7e10c0db f192 T4-platform T4-platform
470 35501823298 2089 e171bd48b5e5845ec6f00db65ff300caac4f7304 f046 T4-platform T4-platform
471 35502589515 2140 618fbca0c39da7157c7f01a26071ef3a4d4a5b00 f140 T4-platform T4-platform
472 35502728596 1766 81820262e61f6e7f11920a5c4596b4297e793cf8 f193 T3-mid T3-mid
473 35502852336 2246 fed0c1b4e3563f1567b3ff85652ec51229014a83 f194 T5-core T5-core
474 35502954849 2246 2eeb15f434d9520e0050fe1f5b6c4c8316a4a69e f195 T5-core T5-core
475 35503248954 1766 4eb3af36c7b055987d5c0764482135658b28c550 f193 T3-mid T3-mid
476 35503753154 2246 5b4ea78927772d00f11abc41f217f89e714ace54 f195 T5-core T5-core
477 35508860520 2247 9b1dfda875736a91073c57badce3dea6d0f03080 f196 T3-mid T3-mid
478 35509728718 2248 96a2fc35dfcf14662cee491430366f4e8d8b8149 f094 T1-tests T1-tests
479 35514366896 2249 051702c6f9163f0a19dda19ea14ac1b72dc7eb3d f197 T5-core T5-core
480 35515754123 2090 7036c4d5996253c05d720ec4d0b3008509b4cad4 f198 T2-extract T2-extract
481 35517816031 2090 f9350caae0ccccf823d8cde85683a900b257f239 f198 T2-extract T2-extract
482 35517948650 2251 151d6077cf418a42628bc7e38bda4f873877f147 f199 T4-platform T4-platform
483 35518289769 2252 4ecd8ab2c3995cc92c1a8df4f5e2a1e2d3a8c342 f200 T2-extract T2-extract
484 35519906910 2253 458b0db1ba3274ceee1e975cfd7300f13aef82e9 f201 T4-platform T4-platform
485 35519951214 2254 8aa77f89ce1ef56c98d989b03bc35fa4c6289b8a f017 T2-extract T2-extract
486 35520101152 2249 238f9e6c6cad0e086eb06c19f1a6a303fbb30fdc f197 T5-core T5-core
487 35520168086 2247 9af73b327e3899c45fc1faf13b590c09efa2078e f196 T3-mid T3-mid
488 35520178069 2005 fcf451c6a6dda768dfec5a8f61a01ac1e1cfa6bf f034 T2-extract T2-extract
489 35520796432 2255 0541b033fa874c38167e4f4d204ad452f1d96d08 f202 T2-extract T2-extract
490 35520869334 2256 b565e9d8b06e8703f1c15a4263a987837dd0c5f3 f013 T3-mid T3-mid
491 35522168154 2257 af3e003ad8e67cd1fe3b20dafaf4d33a4b351852 f203 T3-mid T3-mid
492 35522190984 2258 de4a88f22242f53f724fc6cc8dcaed79642dc5cd f204 T2-extract T2-extract
493 35523164377 2259 460ebefc87df4dd5142bdc339702edd4feec4696 f078 T3-mid T3-mid
494 35523331086 2260 25d08258e567ec417bf7b18b14a63e30162fc7da f205 T4-platform T4-platform
495 35532624016 2261 28d62001fd1e5d0a18085a8a662a1b806747815f f206 T0a-docs T0a-docs
496 35532636527 2262 16a7291b56aafdcde27928f892d477e8b94269eb f207 T0a-docs T0a-docs
497 35533089935 2257 c1128db8187db2dac27a7ad14120fc09802c8018 f208 T3-mid T3-mid
498 35533328007 2263 cda5e3c9e5c4d2251467ced00ff305205c368d17 f209 T4-platform T4-platform
499 35537157817 2005 6eb8992462e506bf0c58aec88274a9a62a07676b f210 T3-mid T3-mid
500 35537525864 2005 fcf451c6a6dda768dfec5a8f61a01ac1e1cfa6bf f034 T2-extract T2-extract
501 35551212771 2236 39a8bc3f2a7fbbc4d86ec8379b669a31ed1f8318 f191 T5-core T5-core
502 35556558511 2140 af4604189bf23c14dceee9bccd89a16f5bfcad8c f140 T4-platform T4-platform
503 35561381902 1724 42a3d7105548c65ee12f6dffabea718573501601 f211 T5-core T5-core
504 35565389237 1809 19d9e4ac4d0a8c6839b627c0cd5890188121fc2b f046 T4-platform T4-platform
505 35565392645 2075 e321b1fceaaf4c9f03fbe82547f02c1079e3b760 f104 T4-platform T4-platform
506 35565401625 1784 381f64c15ebd0a676b85347acfcfd3f45c806a11 f009 T4-platform T4-platform
507 35565472800 2260 2dba3cf81750f245d8c1d912997f04be5615df99 f205 T4-platform T4-platform
508 35565493742 2005 7d01d3ec031dc26759283fff96e979c3e0ce270e f034 T2-extract T2-extract
509 35565496531 2259 6f2f83618134bee208c98c47931579f27a712809 f078 T3-mid T3-mid
510 35565502343 1768 6115e5785dc01f5b403cbfe7f184ffbfba058e45 f055 T1-tests T1-tests
511 35572275939 2158 d4931241adaef732066afb5ebf1cb225e2474403 f141 T2-extract T2-extract
512 35573014946 1716 95fa63441bf80c9ed871fa090ba925b8b6106d83 f096 T5-core T5-core
513 35574966373 1724 81ff2cabcfeb4619e24307c0267a1244f38afb40 f211 T5-core T5-core
514 35575687750 2160 2e4b45931b69671a92402f8fdf51f85542eff268 f125 T4-platform T4-platform
515 35578606399 2199 fa138acf44525ef94cfd6e16772d601da5623aef f166 T5-core T5-core
516 35585540293 2198 9574c316e43527d6ca90eb007a9c8fc24fcfd88b f212 T5-core T5-core
517 35597253045 2268 f0fac62b7140f2cc3ccc011c74e798be7681ec61 f213 T5-core T5-core
518 35606545382 1724 93f86be139b4bff1d079205fc776babc67f67349 f211 T5-core T5-core
519 35607169136 1716 bc5b3dd216568c864b41303a674020a2d456ced9 f096 T5-core T5-core
520 35623513958 1823 c017685d66ed9bca5476313ede52837ad07f17db f076 T5-core T5-core
521 35632304230 1724 bc81c4a84f19f2f5d90d985ec69dcd5fc357ea25 f214 T5-core T5-core
522 35637728335 2140 fca33717d1b6a9171decaf4d1755c88f48dcb572 f140 T4-platform T4-platform
523 35638029971 1724 d48afe8d3070e360ca908d841d85adad344b2ed6 f214 T5-core T5-core
524 35638134068 1724 3743fe95a0fc40f584b88bf4cbaa0f9bdfd8e52c f214 T5-core T5-core
525 35639932135 2227 df83d25ace9885e252363cb04be25a17da85c9e8 f180 T4-platform T4-platform
526 35640451450 2269 65d60e72f5b86f5982b15f0d88826ba79a085aa2 f215 T5-core T5-core
527 35640454709 2270 f428d583008ce87281bac138c02bd4375c56d735 f216 T0a-docs T0a-docs
528 35641634636 1999 e032a8ee9409c7a53a5d732ddff67c15721bf1ae f021 T2-extract T2-extract
529 35641641970 2158 61cfb09b52ba40e8fb3ee9703c618b28739e3298 f141 T2-extract T2-extract
530 35643957306 1724 5033f97d5f5154cddecee760e5b2a5ad290b181a f217 T5-core T5-core
531 35664547053 2272 da4bd25a01b612517512ee628bf0ec652000e80a f218 T1-tests T1-tests
532 35665885098 2273 c314dea4c1409a3a68fb33706b49e272809b5079 f219 T2-extract T2-extract
533 35665890640 2274 343d7dd5e47b817999098605370f0eab706675f0 f017 T2-extract T2-extract
534 35666550961 2275 a041794f8e51ddf42342899be7b2ac26f951d777 f128 T4-platform T4-platform
535 35667708865 1741 c0bfd073ff25484c2d7b3b59dea937be6c92ed08 f072 T0b-nonproduct T0b-nonproduct
536 35667710693 1742 dfae618b0e9508cd4998b583ebbeb36cf458278e f073 T1-tests T1-tests
537 35667714864 2170 36730c7d895ae7541f780d650151b3590fc766cd f053 T3-mid T3-mid
538 35667829346 1724 879aa5f9623bab14e8fb903da23bf84a8d0bc32e f217 T5-core T5-core
539 35669146114 1724 89030aacb21df9b72f23b42286e3a4eee63fd43e f217 T5-core T5-core
540 35669196507 2140 2f557f53986976b2df1eb2a1a540c9d48cf91a8e f140 T4-platform T4-platform
541 35670303027 1724 b47ba846bbd1a6fe7359c86314c81fb33f5adaa4 f217 T5-core T5-core
542 35696787516 1832 6ae6cd4c3f280cb8e3ffec2858269055e390ffec f220 T4-platform T5-core makefile-by-name
543 35697012777 1832 9a9534cbc45d40afa4e8bf070030a806acea1f5f f221 T4-platform T5-core makefile-by-name
544 35697183623 2022 7498e2b75b535171b5a560c6493fe86b5ad881cc f222 T4-platform T4-platform
545 35697752061 1724 3889b47527084eccc7c54c8a3c7e2592c5d5d30a f217 T5-core T5-core
546 35698464152 1724 08d5e2062f1ee9aaf8dce8eaf77662cc7c96a1c0 f223 T5-core T5-core
547 35714885737 2280 f930ed6026c6f1e5f5b146a07664efed9d4da9c7 f224 T2-extract T2-extract
548 35730933863 2198 f913f0eaf5ee308619ef1fdd4f60534d4509fab5 f212 T5-core T5-core
549 35734984657 2281 34d172c9038a9b7cee78e42bb7768b93fbb8ed70 f225 T5-core T5-core
550 35734995796 2282 1d2bbc425988eb78fcd966dc8c9cfbdb814f0685 f226 T4-platform T4-platform
551 35766202700 2288 427bb73c638481842bd13d73ed171953469eca02 f227 T3-mid T3-mid
552 35767446435 2288 d7eba5a71d2a99d7b7bc588c666d22516ac0b563 f227 T3-mid T3-mid
553 35772088984 2289 38fc6ea995c9db78cedb10b9bba2b6aa79f616fc f228 T4-platform T4-platform
554 35797101033 2290 255463126650ab6deab3e62aab441b71479704f1 f229 T4-platform T4-platform
555 35797928618 1702 aecfb315d8ddbfab2cacbbd6e4a26b408b973975 f085 T3-mid T3-mid
556 35800424142 1702 26bd387753a9a08fa6de647481bf36cf338e5c28 f085 T3-mid T3-mid
557 35812291988 1702 6233394d8e893546af92876fb8d8da81e592cc2c f085 T3-mid T3-mid
558 35813226724 1702 5770ed83741009e71c12876c40b655758f4f4440 f085 T3-mid T3-mid
559 35822291393 1702 8165c34015699379d31fd31060c0f16fbe5447de f085 T3-mid T3-mid
560 35824077381 1702 24795fc665df85200ddc112aaddfe34572171ef8 f085 T3-mid T3-mid
561 35824588131 1430 6bb8ad6a7a1c99849d101322ca8fe81b6adfbc50 f230 T4-platform T4-platform
562 35826272552 1430 0d2f840c8ed2695db206acbcad1ad1c36a1f3958 f230 T4-platform T4-platform
563 35836874903 1702 f5ec03f0e1a7e90ace14c21490dca39c4c8bb092 f085 T3-mid T3-mid
564 35841856084 1702 492e59a181a27af324c82a8db3c4e2e37382d567 f085 T3-mid T3-mid
565 35841866366 1702 9e92e67649d62a2de63e57893290851d7247476a f085 T3-mid T3-mid
566 35842147817 2158 246e4190c3086d1074adbe8afe39bf4261db59b8 f141 T2-extract T2-extract
567 35846258328 1702 e9339ff1f5b28cb85d2810f434471700e2fc3b5e f085 T3-mid T3-mid
568 35862574659 2294 54c143ac932b6e291a17be0327fb1b5e86b99984 f013 T3-mid T3-mid
569 35899891384 1823 4a8c2eb422d9083ac3bf1e01e18e558e470ad099 f076 T5-core T5-core
570 35918117863 2295 f4c897110cb1f02edee37db7c8849e63894ed8a4 f231 T3-mid T3-mid
571 35936681521 2296 59345314187021f50c5e5c12da793b27b8485d7b f232 T2-extract T2-extract
572 35948281533 1724 48f9de8ccd7cc75d7e466b7d5167e2930dfe33f3 f217 T5-core T5-core
573 35948963693 1724 a8da9a366353bb0a67806b40f840500444ac963f f217 T5-core T5-core
574 35948982011 2272 9b5bf70dc400fdfe989ed2bf1b623b2103bbf2f0 f218 T1-tests T1-tests
575 35952295014 2181 282fbfcdabfec79c19c36f5bec5f75c22eb36df5 f091 T3-mid T3-mid
576 35957866374 2158 cf7eecd7d0b1288c7f2e290a1d1bfc1b1b6f8698 f141 T2-extract T2-extract
577 35960900747 1716 a6d3d5d820b07ba8544ddae1668e8107d7961532 f096 T5-core T5-core
578 35960904447 2199 f07c18702ac82957d0eb73f96f37d30184b7e6b5 f166 T5-core T5-core
579 35960910227 2263 fcdc6bedb7d05489462639da38c4ac755137734f f209 T4-platform T4-platform
580 35960928169 1768 92cafd1ec888362ef3223ad669a5aa4f378a54d5 f055 T1-tests T1-tests
581 36006341620 2301 64297ff3e8e142887fc36c933d57fd4d32924cc2 f233 T4-platform T4-platform
582 36057012376 2304 6f91a16fc2a753db248f122dea7e4b50763972d1 f055 T1-tests T1-tests
583 36057063533 2305 69b84cacff1b08f449e563c2d24861e6f10694e4 f234 T3-mid T3-mid
584 36081432074 2307 2a6dd92f658b15d571130b0eee14a67b95a36465 f235 T2-extract T2-extract
585 36081473175 2308 707f6f0db58a16b706b29d249bced477094ef92c f236 T3-mid T3-mid
586 36081733263 2309 a9a25357ec2dc57f3c6f2e353e7ca61a7575df06 f168 T4-platform T4-platform
587 36081918547 2310 2f922141972a18f0d95de4505b33f5362f51164b f237 T4-platform T4-platform
588 36082054353 2310 8955d98983e0ca1e1df4a2173b4b4273904566c7 f237 T4-platform T4-platform
589 36087900420 2310 7c24b020457924fee9a7acdd6cb8c5f7e262ca4e f237 T4-platform T4-platform
590 36093255940 2310 b2e9199773977f28d73a052d41c37b8c111750a5 f237 T4-platform T4-platform
591 36102327028 2310 d636aa7f21fb001325b61cd41fc431d5f255e21e f237 T4-platform T4-platform
592 36137231318 2315 9527694e90ee9dfd29d64940425f9f23290ed702 f238 T4-platform T4-platform
593 36146313096 2310 9b10ba281cfdba2676e9d24c445470c4b6f7d4e3 f237 T4-platform T4-platform
594 36150749742 2022 7a307abbf8c1c879edee3193dd482ef427c83287 f222 T4-platform T4-platform
595 36160883414 1766 9d53f32dc1753fcece6c0d2af27a5958f8ce1799 f193 T3-mid T3-mid
596 36164363201 2318 6d508350c7713e10e0e935625ad74d8401fe89e2 f239 T3-mid T3-mid
597 36164368040 2319 3faecc78144175a81acf76721c4f695cf80b5676 f240 T3-mid T3-mid
598 36164372390 2320 4f5c951dde792c9cee4e33e45119cef8fc62765e f017 T2-extract T2-extract
599 36164376373 2321 c6ccdd9395dbbafc6f66a49040b478b445387946 f241 T2-extract T2-extract
600 36164382846 2322 19f50de3d2e2bdea5f9054caf07af70f46d74244 f242 T4-platform T4-platform
601 36164406666 2323 e9f6596e82cac5b3377cf212e7199a286815418f f243 T4-platform T4-platform
602 36164410674 2324 a808295986d7599ddbfe8324beefacfdd5161c04 f244 T4-platform T4-platform
603 36164415830 2325 d56e046ddd08ac6bf8b14f5049a88382a10a1636 f245 T3-mid T3-mid
604 36164420947 2326 5ef8ccff7023db106df18cef0615776e7ed94686 f246 T2-extract T5-core makefile-by-name
605 36164427320 2327 098fcc883e2c720ee838df90f3475938715d4056 f247 T5-core T5-core
606 36164450931 2328 a8c4040e8a92dcf831c64f57458694ff8fa6e9f9 f248 T2-extract T2-extract
607 36164460893 2329 12f51855cab461cda3bd4c4d63d79f09fcb69732 f249 T5-core T5-core
608 36164465591 2330 e7324bb6904c6d0a295b956e83c8585219198358 f250 T2-extract T2-extract
609 36164472858 2331 a0c71225f7c0c5e4bee36ef235f966866abffb24 f251 T3-mid T3-mid
610 36164477427 2332 b9ad9ed1449540e06e9f814ded548d9f2bf52f49 f252 T3-mid T3-mid
611 36164497120 2333 e65a4e70be9c87366f66e93e252c0f307aa50ac2 f253 T4-platform T4-platform
612 36164502402 2334 357d71e198ae3cd4614b9e06e54957423291e5c4 f013 T3-mid T3-mid
613 36164505003 2335 1c73f8676c31e227c6ed478ccbb55662b34d6db2 f254 T4-platform T4-platform
614 36164512328 2336 b8b88bcf088b8cf3f6b44949e917d172aaa82d2e f255 T2-extract T2-extract
615 36164516453 2337 8e12fc8a69dc6f83296e660191448ef8ba7819bc f256 T5-core T5-core
616 36164537929 2338 dad40ccdb633c10557baba4049aec35049fa3b15 f257 T4-platform T4-platform
617 36164542709 2339 793e0b6127db3c8333b1dc29faf0b35167e3ceda f258 T3-mid T3-mid
618 36164547188 2340 b3bc2a80aad766d37477a163ae5b158913a20f45 f259 T2-extract T2-extract
619 36164550014 2341 40dad610eec783fe243e6b6141c1bebc75c23f0d f260 T4-platform T5-core makefile-by-name
620 36164556221 2342 c650292433534127b5f45edd066e56a06b3376d1 f261 T3-mid T5-core makefile-by-name
621 36165117354 2181 65fc0bb4f77fdbb6449199c147e1ed112654b151 f091 T3-mid T3-mid
622 36165123674 2158 d772b1e5af0ce80d08aa6603ea47d1f51f324193 f141 T2-extract T2-extract
623 36165128132 1716 ff127ea44c56ce21bd5a4f9cf9f51bb27523d0aa f096 T5-core T5-core
624 36165431134 2343 45f0c430d28cf188753f44536d2cac26564a04ba f262 T3-mid T3-mid
625 36165866718 2344 4b2b3b90adc44694d70624c9d0372752ffd0e2bf f013 T3-mid T3-mid
626 36171533714 2326 c0a06b3d0a90513e0d80f79514b869f91cb958f1 f246 T2-extract T5-core makefile-by-name
627 36171575767 2345 772ea06a6115128c3160f9631322b089aeb85a53 f263 T3-mid T3-mid
628 36176174515 2347 b220ecec48010db71ac0a77b2888fefe802c9182 f264 T3-mid T3-mid
629 36176260818 2348 886e6da479891b6dfe386ef25dfe5abe7ed2f080 f265 T4-platform T4-platform
630 36181164078 2289 8cd2ee5f421d0b7c06022091d59dbe71002de30d f266 T4-platform T4-platform
631 36183268529 2315 c27a53512da9469dc50fb243c1ecd3a55975ebfc f267 T3-mid T3-mid
632 36183549298 2350 99ccc7d64a275bba1c015b30df036597637363f5 f268 T0a-docs T0a-docs
633 36184450486 2351 951b2fddf68a6e6605b1664bd3252a2a6578ef35 f014 T3-mid T3-mid
634 36185562216 2352 8093d10b812d9a7840ae99d8f9d24a04cb0f9559 f269 T3-mid T3-mid
635 36185568093 2353 c64ea20feae160d1eeddadf09e7e0df36b08e818 f055 T1-tests T1-tests
636 36185818550 2354 3ef7d8cdc201115dd76cc373ea2c64a567a2defe f012 T2-extract T2-extract
637 36186982935 2318 b7bcb15c219761f27ff146063a50481303527563 f239 T3-mid T3-mid
638 36186985986 2331 6b7628bb6bbf041a8bdf069dd9d6bc8861d84e73 f251 T3-mid T3-mid
639 36186989409 2332 1d5ff68e508e78b149589396d547166ad41362e0 f270 T3-mid T3-mid
640 36186989799 2334 87471e1f1d6a01ca8be6ca45b968145d0de95362 f013 T3-mid T3-mid
641 36186993787 2342 7abaa91dea653af3e890759a63e40a09cfcb81d8 f261 T3-mid T5-core makefile-by-name
642 36187019606 2327 f5c8631f2032c5598b1bfe12d329b875532f814d f247 T5-core T5-core
643 36188745373 2355 ae578bc1cc32bb8f159a69c486f938ad745c643f f086 T4-platform T4-platform
644 36189334816 2356 d5a3110d9e1e82de5416807d1f81e3ef85049c1a f271 T5-core T5-core
645 36189454232 2357 28b41c35b71cb039988f39f72689dd657e30522d f272 T4-platform T4-platform
646 36189574830 2358 f3754d6197b626d5432ac84b0363b0d3a5544c5c f273 T3-mid T3-mid
647 36197747139 2301 ff8dd47011c43c622d3dc82b33be32b4493e4ea9 f274 T4-platform T4-platform
648 36199747285 2344 bac3fc80b532eb294fd6eda6a2cc93d656df9369 f013 T3-mid T3-mid
649 36199749518 2345 1810a8964caae06e33b7f3c6f944de570c21c292 f263 T3-mid T3-mid
650 36201323199 1702 5557f0ec4a54bcaf99bd7442b866579ab68578f4 f085 T3-mid T3-mid
651 36210099904 2359 efb231285d188ffda16a58f7a5ce8e0e6448638f f101 T1-tests T1-tests
652 36222963285 2315 2d2c0fd0c008973c63565c3e7f4591ed0b1bb379 f275 T4-platform T4-platform
653 36227652952 2160 bc960d87d663b7b4fc4c3ba558013c4609073762 f055 T1-tests T1-tests
654 36232110014 2360 1ccbb432ae1281ac96f743850be317fb541cf1db f276 T3-mid T3-mid
655 36238137680 2361 4dfeb2cefc402c7d1f1d3d01603a2e8409ed24cc f277 T3-mid T3-mid
656 36239758139 2281 9dca8d2fede860605943ee8dbd57ba6b927ae41b f225 T5-core T5-core
657 36239789418 2362 ed991154f212928df63ce5a5cd1159cfc8196f00 f278 T4-platform T4-platform
658 36239835633 2290 26e2d0cba6df7b1cb53e655d0b902bdb7debdbd4 f279 T4-platform T4-platform
659 36241153105 1816 3e67838b05e67915d472ec7698c80649ab8ad03a f071 T4-platform T4-platform
660 36241168954 1766 8f441c19fb8a32b8ccf43ffcf65df7f4dc2bc16a f193 T3-mid T3-mid
661 36242675954 2363 363575af920b05f911fc76ca964dcfa7ef1c62bb f150 T5-core T5-core
662 36242676040 2294 6e3d8c296f2f99375d9693e706b4818c72caad13 f280 T3-mid T3-mid
663 36242724677 2364 e94f5a70d01e4cf62b0c9f496175dcd930bcbd0e f151 T0b-nonproduct T0b-nonproduct
664 36242808005 2365 7549e6d09c021f6d3a385a37898f591626ae62b8 f150 T5-core T5-core
665 36245108554 2366 06f97613a28a18bef0145f37c1036fa60e73533e f281 T3-mid T3-mid
666 36248365534 2367 8f803f7262d1d1e58068f677f3e365048050b728 f012 T2-extract T2-extract
667 36248371079 2368 a59d53a14b779401621c44aa757e3bc9f0588ef0 f282 T5-core T5-core
668 36259321198 2370 79a0c92e33a53758c36d87d4124b12cc83dac5be f236 T3-mid T3-mid
669 36259389383 2371 0dba49208e8537eda2c3355387c0307f6ee95075 f283 T2-extract T2-extract
670 36259523618 2372 6f17caa7a763d9987ebcd7ed33063d47d34bf635 f284 T4-platform T4-platform
671 36262536017 2370 12de590a4935488d83380a33865cbb59a7a16c32 f236 T3-mid T3-mid
672 36266366437 2373 77ab4bea87008547acee60435a8e3df29d90a5cb f285 T2-extract T2-extract
673 36266372862 2374 7c7811f81aabb4c1fdc0914e78d46b71493a9dce f013 T3-mid T3-mid
674 36266444913 2375 3ad76fd7728f3b19ca1100b10830e986b7f631b0 f235 T2-extract T2-extract
675 36266447071 2376 5a9c72d8f8c65b38c66a29a34184b49edf3378ff f104 T4-platform T4-platform
676 36268746769 1702 8ffd03ecd9ccae5a32508801645fb1cac7e4a4d1 f085 T3-mid T3-mid
677 36270666016 1784 ca58149ae4757325158af07e4e3b8968d315e5da f009 T4-platform T4-platform
678 36271539681 1784 71ac6ecc15d3ab3ebcd31f0e467071a506006ca2 f009 T4-platform T4-platform
679 36271900288 2367 075f40e9d4ccbfead4df0e199c7730f94c2154ff f012 T2-extract T2-extract
680 36274019750 2304 262d7ca7659b3f1f6ee619cda2b5f6aad2815314 f055 T1-tests T1-tests
681 36274021183 2305 3d9e5ed5cf1ad1e4bf89b385d0be28c8f011f52f f234 T3-mid T3-mid
682 36284225343 2370 208be7d9374bb27d2875e3568265b591ae7c4b5b f236 T3-mid T3-mid
683 36284230521 2371 eafaab15c802c46ee22233b50f0e9474b0a3085d f283 T2-extract T2-extract
684 36284315887 2280 756d7f42d4aab0045d75e91651cead399e4a7b35 f224 T2-extract T2-extract
685 36285289867 2315 a1ac80740ed800d84a9d5e44d725bdc29d99b88e f275 T4-platform T4-platform
686 36286798412 2072 1a37f0dd52ab62cfbb439c5a2f72afddca4cb009 f286 T5-core T5-core
687 36299419426 2207 631317536426136f580e34d3c25dd5a951d443a8 f171 T4-platform T4-platform
688 36313881874 2381 9d8f51d7f3340381afa450aa33cfd320879d6ae9 f060 T4-platform T4-platform
689 36323957389 1702 b46c6d43f2efe6f0a1de8c77b73085183aa5da1a f085 T3-mid T3-mid
690 36324735265 2376 07eb426c95386e299a6aa6adbf2b4eb9f272ff3b f104 T4-platform T4-platform
691 36339435944 2370 e9058e11db9ed94e401ad711b2016249970a9040 f236 T3-mid T3-mid
692 36339438890 2371 4d21c3bbd34b420c8987e8588b88b5fc5446116f f283 T2-extract T2-extract
693 36346241955 2360 09f704d23f0d990dfc72b916d9daea80acce73ce f287 T3-mid T3-mid
694 36347358048 2361 0d720d52b253cfb64e7bfb1eee034bc3035f1a04 f288 T3-mid T3-mid
695 36348236142 2366 06ef2f64caea016fc792e9069d5a3befc30bca4a f289 T3-mid T3-mid
696 36352906531 2375 7707c9d7918263cd446a4b54d4eb48c3fea91048 f235 T2-extract T2-extract
697 36353006100 2247 fa2292111802b5f3b66db3e4dd168280b53ac17e f196 T3-mid T3-mid
698 36353528883 2249 a45a99c9c73dd54a4625d680da700f7cb0387eff f197 T5-core T5-core
699 36353741468 2385 1b2e8f1fac105b0eaf15e15b95c3ef336bf0a44e f290 T4-platform T4-platform
700 36354332711 2181 82a6ddff81c6c8583c73f3d628037e41a9b412f0 f091 T3-mid T3-mid
701 36354336229 1781 fd2456316e7b5dc27125497f640dc7e41760c104 f174 T4-platform T4-platform
702 36354339289 2065 87ba464596337fc6574a825ef88392e70a733dc9 f093 T3-mid T3-mid
703 36354790786 2252 df8dd1011bd1987d0ca5a45f324775aad2411f54 f200 T2-extract T2-extract
704 36355512051 2089 11957ae6554b7d768095dcda95168331bd654363 f046 T4-platform T4-platform
705 36355610699 2371 ee9d1365e2c17d4e2bbdd84ac10ec6b89cffca5f f283 T2-extract T2-extract
706 36357921046 2344 7f649b29dc778b0f54c28dfcc2f25b76f3d1947a f013 T3-mid T3-mid
707 36357921739 2326 259dbb67bee1d3547ae61a98c9d0c1cf4e491d44 f246 T2-extract T5-core makefile-by-name
708 36357923794 2352 ea6fa49fc2c209de69c1b0f393d9d2cc25b61419 f291 T5-core T5-core
709 36358160313 2389 27c87104a57eefe33543a04e3e43eb0721df8b4a f292 T2-extract T5-core makefile-by-name
710 36358451648 2207 8bd9d37b156641ecf1c82b16b0ec0a8246e8c201 f171 T4-platform T4-platform
711 36358713716 2295 9f5f5ab56fce2baf095b75f1e7a8dc98368dbcad f231 T3-mid T3-mid
712 36359201669 2073 9130fab63d23b5386f12fa4cfa57961b247ff07b f101 T1-tests T1-tests
713 36359210035 2385 c03575ef1ab1abb51793d03303ae3e5d4cd3b708 f290 T4-platform T4-platform
714 36359646097 2391 d450275e20b663e3c5c8a06627030702e61e4184 f017 T2-extract T2-extract
715 36359899206 2385 cb307a496de36c9803dffea0815f3ea96bf849db f290 T4-platform T4-platform
716 36360333971 2389 5ff3e8d6fd3fd87d46807084058e2944f6486d76 f293 T2-extract T5-core makefile-by-name
717 36360464106 2305 8a49fc684ad08ac2d99591e0b364249570c9e676 f234 T3-mid T3-mid
718 36364475838 2392 19d144477b9558dad148d3f555ec6ede16caa3f5 f294 T4-platform T4-platform
719 36364478399 2393 dc735c01fbbb4eddbbb73ff4df6007ab68413a4d f295 T4-platform T4-platform
720 36364479687 2394 26cbccd998819c428589156a82e167db061522ae f296 T5-core T5-core
721 36370343201 2395 f200d7477f192caedaa5b4cda7d9eccc2022a069 f297 T2-extract T2-extract
722 36371911963 2396 629e9c2c1f87e2ac8b5d644d38ff4e935928752a f298 T3-mid T3-mid
723 36371921838 2397 4c7bc08ff02d49afa07808309d5586bc61724ba4 f299 T5-core T5-core
724 36416318776 2315 3327ee5935a7b03ef5f850c12c163c11ef9d91e2 f300 T4-platform T4-platform
725 36440070485 2305 853192aa7e76eedceb1fce6e2a6874948cf1c1a4 f301 T3-mid T3-mid
726 36440993073 2207 343cc949270a3e18e317fd6cd781167ed2fb3dad f171 T4-platform T4-platform
727 36441585217 2368 f277c5e0a9aad35c730e4189b969b74964614d87 f302 T5-core T5-core
728 36451035334 2207 1c840df7495e3b71924b8efd7e3bbf53c9076abc f171 T4-platform T4-platform
729 36453341395 2401 9d5b6e998514b76dcc95b55f362325b3d49945da f264 T3-mid T3-mid
730 36455603700 2401 874286abb9817db94e7789c7938921f6a03f3448 f264 T3-mid T3-mid
731 36456464356 2402 555727b50da6086f00c00f57876e2632caa69c45 f303 T2-extract T2-extract
732 36459403945 2394 78a2ec399fec3dc28e61439a4bcd900b7c64c23f f296 T5-core T5-core
733 36459418705 2344 b205f0b51dacd89da57f6fcbeb46f55617606091 f013 T3-mid T3-mid
734 36459497707 2403 95eaf01755d18c501fa8d1c5dae2255cf460b9a2 f001 T0a-docs T0a-docs
735 36459501942 2404 fe3c285fdc63bd9abe027026edd7adffe5f62677 f013 T3-mid T3-mid
736 36459506386 2405 460153204ba9833913e5245ca894f783c1d7229d f304 T4-platform T4-platform
737 36459537782 2406 a1b7c05685e18597f3461783d4bc98661f05427d f305 T2-extract T2-extract
738 36459545006 2407 c02dff113a90aa6bbc3d89fedda962a53f8b4c64 f306 T2-extract T2-extract
739 36460081689 2280 6ba9a21eed2165932b90f7ede772d77b046eb08a f224 T2-extract T2-extract
740 36460156038 2408 23d48336d3367fb27cbde54049b1414a2aea1b48 f307 T1-tests T1-tests
741 36460162959 2409 4c6ecffdcdb8d78c0704f0d7e825f676ad77c240 f308 T1-tests T1-tests
742 36460990240 1907 b74cecc53cea20abdc64dbdc484f5ba0b44f5789 f033 T3-mid T3-mid
743 36461038395 1913 71ebbb9a9b54f2dcef84321de939429dd7707600 f041 T3-mid T3-mid
744 36466048726 2410 533eee07ece1d01ff2c88da55972c7720cd1604b f309 T4-platform T4-platform
745 36466057374 2411 0d783bcca4edde896d5716e8a4060c3100595973 f076 T5-core T5-core
746 36466065600 2412 ed76cf8e88cfb1f26c82110e46a518c13577f788 f310 T4-platform T4-platform
747 36469132661 2360 5a77358277a83d7651965f762a673be76fb157b3 f311 T3-mid T3-mid
748 36474434156 2402 0a658840b8f029e7632910284281e32286c1f0aa f303 T2-extract T2-extract
749 36474499483 2402 f88aaeb8f9db801545c21e085812559df9f80083 f303 T2-extract T2-extract
750 36474752681 2418 3db66f22ad7d5c435559869a34b6e14b1e54793b f312 T3-mid T3-mid
751 36476594662 2402 33a9bbe85eb922bf01c5297f09cc50044f39e351 f303 T2-extract T2-extract
752 36476662334 2419 3f53a308f4034f4d280130fbcc275f545d7bf05e f313 T2-extract T2-extract
753 36476726058 2420 e36930742a63efd2bbca491996ba8e37bfa5822c f314 T2-extract T2-extract
754 36476760149 2421 fd1b88374c0c21892c991c29909d22ed499c20fa f315 T3-mid T3-mid
755 36476819919 2422 ffa0b320047f4db99dc53f09e228c94703436ae1 f316 T2-extract T2-extract
756 36477490833 2423 ba5e7dd639a3d29c54023d1203ef18fc4e330c7f f013 T3-mid T3-mid
757 36484216122 2391 f297737db0510ecc573edf57a2a30a25e5fbfcfc f317 T3-mid T3-mid
758 36484725527 2391 538ac25edda676ca7d8cb8a4c7c5fb3a3a36fda2 f317 T3-mid T3-mid
759 36488109160 2418 702c3afb6bbab15e18e13f22880d5a61fa3317e1 f312 T3-mid T3-mid
760 36488232420 2419 82361ffdd66ffe39efc1ff9e00e0cdcc61223e4f f313 T2-extract T2-extract
761 36488247857 2420 a46dc5a9fbb39310027812234d0459d4bbcbe99c f314 T2-extract T2-extract
762 36488262530 2421 a85bef05f169e4f2c36cf4cf121a7b6d1309e155 f315 T3-mid T3-mid
763 36488285902 2422 c695cb728e4850b4152cd7cc54f06b6520372371 f316 T2-extract T2-extract
764 36491809136 2309 48265f5367d93c22cdaf2203d19dc65fd6eefea4 f318 T4-platform T4-platform
765 36492368152 2424 904eccabe39b03d8104de989a56f224ccf6d81d3 f319 T3-mid T3-mid
766 36495192814 2425 e548d971b3e471ca5a149201c39ba62ab1a72261 f013 T3-mid T3-mid
767 36499773006 2385 534274c1601034b5eca97ec286c0346389076a8f f290 T4-platform T4-platform
768 36500785757 2360 bb3f1cdd6f4f45ab416ba97ad09a404aebf21e36 f311 T3-mid T3-mid
769 36500790466 2361 8116eaaa426ff39a37b4078e0abd018b09398e58 f320 T3-mid T3-mid
770 36500796009 2366 4a0f9660623b53d4f9eccd7149d54482707fdc22 f321 T3-mid T3-mid
771 36504138642 2371 96f91005f0777e3fee5f5b5f9da69962ddf93695 f283 T2-extract T2-extract
772 36506100535 2427 f4efd0b86844bf2ac965ba9ec30c8805cc774e40 f322 T3-mid T3-mid
773 36508613643 2315 232730cb091d852bd7820bf1d5aeedc43aa860f8 f300 T4-platform T4-platform
774 36509039978 2170 5a3e82520408db86161b8fff08175aa30a0adc6c f323 T3-mid T3-mid
775 36535949326 2368 cc71a2087d32a7c06b0dcc2f925a9a623335c2ef f302 T5-core T5-core
776 36538846045 2170 1ebd1e40fe715ac9efb328b2c7cebd2c0a4e140f f323 T3-mid T3-mid
@@ -0,0 +1,26 @@
# The 22 real (non-flake, non-infra) job failures of the September 2026 PR
# pushes, from the lane analysis ground truth: which lane caught each. The
# replay asserts the selector selects that lane for that push.
# Columns (TAB): run_id, attempt, pr, head_sha, lane, classification.
34263688699 1 2115 013e1f7ec7d4b981a1cd28bf35d5767c195f8447 unix-arm64 real: cli suite hang on slow runners (arm64, then intel)
34288397854 1 2115 92cb3b9a4bebbc79bd283e447104669ca068ea64 unix-arm64 real: cli suite hang on slow runners (arm64, then intel)
34649406520 1 2115 78a6d19f1eaaa73054639ab0f14c372c53cd1bb3 unix-arm64 real: cli suite hang on slow runners (arm64, then intel)
34649406520 2 2115 78a6d19f1eaaa73054639ab0f14c372c53cd1bb3 unix-arm64 real: cli suite hang on slow runners (arm64, then intel)
34676336284 1 2185 eb5e34cfc141454e849daf413f648e73f74c455a codeql-gate real: codeql-action bump broke CodeQL (still caught by requiring CodeQL/analyze)
34676429093 1 2187 cea35fcc0520a0ba95139bd0210052f09b9cfa20 codeql-gate real: codeql-action bump broke CodeQL (still caught by requiring CodeQL/analyze)
34696991657 1 2115 9a460b389ce3002ac164605c3627bb86aa3b9be3 unix-macos-intel real: cli suite hang on slow runners (arm64, then intel)
34805101784 1 2202 7b77fd4834afe355cc4c7326ac3a55f0502f7264 lsan-macos real: macOS-only LSan leaks (memory-core)
34805101784 1 2202 7b77fd4834afe355cc4c7326ac3a55f0502f7264 msan real?: MSan stack overflow (memory-core, branch-specific)
34805101784 1 2202 7b77fd4834afe355cc4c7326ac3a55f0502f7264 tsan-arm real?: backpressure nap-cycle bound (memory-core)
34805101784 1 2202 7b77fd4834afe355cc4c7326ac3a55f0502f7264 tsan-mac real?: backpressure nap-cycle bound (memory-core)
34876200704 1 2202 9724d903046626df0640ced218259cb58070e4e8 lsan-macos real: macOS-only LSan leaks (memory-core)
34876200704 1 2202 9724d903046626df0640ced218259cb58070e4e8 msan real?: MSan stack overflow (memory-core, branch-specific)
34876200704 1 2202 9724d903046626df0640ced218259cb58070e4e8 tsan-arm real?: backpressure nap-cycle bound (memory-core)
35448941856 1 2233 9db5217bdbb779212c5d066ed1a84f7ca6fc0282 tsan-arm real: TSan data race system_info.c:274
35448941856 1 2233 9db5217bdbb779212c5d066ed1a84f7ca6fc0282 tsan-mac real: TSan data race system_info.c:274
35448941856 1 2233 9db5217bdbb779212c5d066ed1a84f7ca6fc0282 tsan-x86 real: TSan data race system_info.c:274
35483768695 1 2236 d2dd3b347bbdcc695f911e80b1b7f7a32d790e5a pkg-wrappers real?: wrapper publication crash-gate (cache-management)
35483768695 1 2236 d2dd3b347bbdcc695f911e80b1b7f7a32d790e5a smoke-mac real: cache CLI broken on macOS/Windows
35483768695 1 2236 d2dd3b347bbdcc695f911e80b1b7f7a32d790e5a smoke-win real: cache CLI broken on macOS/Windows
35493947102 1 2236 2caafc0c41bd083092afeb692824e718a8e1e007 smoke-mac real: cache CLI broken on macOS/Windows
36242675954 1 2363 363575af920b05f911fc76ca964dcfa7ef1c62bb codeql-gate real: codeql-action bump broke CodeQL (still caught by requiring CodeQL/analyze)
1 # The 22 real (non-flake, non-infra) job failures of the September 2026 PR
2 # pushes, from the lane analysis ground truth: which lane caught each. The
3 # replay asserts the selector selects that lane for that push.
4 # Columns (TAB): run_id, attempt, pr, head_sha, lane, classification.
5 34263688699 1 2115 013e1f7ec7d4b981a1cd28bf35d5767c195f8447 unix-arm64 real: cli suite hang on slow runners (arm64, then intel)
6 34288397854 1 2115 92cb3b9a4bebbc79bd283e447104669ca068ea64 unix-arm64 real: cli suite hang on slow runners (arm64, then intel)
7 34649406520 1 2115 78a6d19f1eaaa73054639ab0f14c372c53cd1bb3 unix-arm64 real: cli suite hang on slow runners (arm64, then intel)
8 34649406520 2 2115 78a6d19f1eaaa73054639ab0f14c372c53cd1bb3 unix-arm64 real: cli suite hang on slow runners (arm64, then intel)
9 34676336284 1 2185 eb5e34cfc141454e849daf413f648e73f74c455a codeql-gate real: codeql-action bump broke CodeQL (still caught by requiring CodeQL/analyze)
10 34676429093 1 2187 cea35fcc0520a0ba95139bd0210052f09b9cfa20 codeql-gate real: codeql-action bump broke CodeQL (still caught by requiring CodeQL/analyze)
11 34696991657 1 2115 9a460b389ce3002ac164605c3627bb86aa3b9be3 unix-macos-intel real: cli suite hang on slow runners (arm64, then intel)
12 34805101784 1 2202 7b77fd4834afe355cc4c7326ac3a55f0502f7264 lsan-macos real: macOS-only LSan leaks (memory-core)
13 34805101784 1 2202 7b77fd4834afe355cc4c7326ac3a55f0502f7264 msan real?: MSan stack overflow (memory-core, branch-specific)
14 34805101784 1 2202 7b77fd4834afe355cc4c7326ac3a55f0502f7264 tsan-arm real?: backpressure nap-cycle bound (memory-core)
15 34805101784 1 2202 7b77fd4834afe355cc4c7326ac3a55f0502f7264 tsan-mac real?: backpressure nap-cycle bound (memory-core)
16 34876200704 1 2202 9724d903046626df0640ced218259cb58070e4e8 lsan-macos real: macOS-only LSan leaks (memory-core)
17 34876200704 1 2202 9724d903046626df0640ced218259cb58070e4e8 msan real?: MSan stack overflow (memory-core, branch-specific)
18 34876200704 1 2202 9724d903046626df0640ced218259cb58070e4e8 tsan-arm real?: backpressure nap-cycle bound (memory-core)
19 35448941856 1 2233 9db5217bdbb779212c5d066ed1a84f7ca6fc0282 tsan-arm real: TSan data race system_info.c:274
20 35448941856 1 2233 9db5217bdbb779212c5d066ed1a84f7ca6fc0282 tsan-mac real: TSan data race system_info.c:274
21 35448941856 1 2233 9db5217bdbb779212c5d066ed1a84f7ca6fc0282 tsan-x86 real: TSan data race system_info.c:274
22 35483768695 1 2236 d2dd3b347bbdcc695f911e80b1b7f7a32d790e5a pkg-wrappers real?: wrapper publication crash-gate (cache-management)
23 35483768695 1 2236 d2dd3b347bbdcc695f911e80b1b7f7a32d790e5a smoke-mac real: cache CLI broken on macOS/Windows
24 35483768695 1 2236 d2dd3b347bbdcc695f911e80b1b7f7a32d790e5a smoke-win real: cache CLI broken on macOS/Windows
25 35493947102 1 2236 2caafc0c41bd083092afeb692824e718a8e1e007 smoke-mac real: cache CLI broken on macOS/Windows
26 36242675954 1 2363 363575af920b05f911fc76ca964dcfa7ef1c62bb codeql-gate real: codeql-action bump broke CodeQL (still caught by requiring CodeQL/analyze)
+232
View File
@@ -0,0 +1,232 @@
#!/usr/bin/env python3
"""predict.py — map a detect_changes impact set to the C test suites it can
affect: the PR test-impact SHADOW (scripts/ci/test-impact-shadow.sh).
Report-only; nothing here decides a merge.
The mapping is the one the September 2026 test-impact replay measured
(detect_changes at depth 15 plus rule fallbacks, "dcF@15"):
- impacted test FUNCTIONS (graph rows in tests/*.c) map to the suites that
RUN_TEST them, resolved in their own file first; a nested suite (RUN_SUITE
inside another) resolves to the registered suite that runs it; only suites
registered in tests/test_main.c count, perf suites excluded;
- a changed tests/*.c file always selects its whole suite;
- a changed fixture (tests/fixtures/<key>/...) selects the suites whose test
files name <key>; a changed grammar selects the grammar suites;
- run-all: the lane selector's full, a changed test header or
test-infrastructure file, a saturated or truncated impact set, or a C
change the graph was not asked about -> every suite.
Usage: predict.py --root DIR --changed FILE --selection FILE [--detect FILE]
[--depth N] [--meta JSON] --out FILE [--summary FILE]
predict.py --needs-graph --changed FILE (prints yes|no)
"""
import argparse
import json
import os
import re
import sys
GRAMMAR_SUITES = re.compile(r"^(grammar_|lang_contract|parse_coverage|extraction|matrix_|"
r"edge_types_probe|convergence_probe|ac$|language$)")
C_SOURCE = re.compile(r"\.(c|h|cc|cpp|inc)$")
SUITE_DEF = re.compile(r"^\s*(?:SUITE\((\w+)\)|void\s+suite_(\w+)\s*\(\s*void\s*\))\s*\{", re.M)
RUN_TEST = re.compile(r"\bRUN_TEST\((\w+)\)")
RUN_SUITE = re.compile(r"\bRUN_SUITE\((\w+)\)")
def needs_graph(changed):
"""A C source change the graph can place (prod or test code)."""
return any(C_SOURCE.search(p) and (p.startswith(("src/", "tests/")) or (
p.startswith("internal/cbm/") and not p.startswith("internal/cbm/vendored/")))
for p in changed)
def suitemap(root):
"""file -> suites defined there, test -> suites running it, suite universe,
and the resolver from any suite to the registered suites that run it."""
file_suites, test_suites, sources, parents = {}, {}, {}, {}
for directory, dirs, names in os.walk(os.path.join(root, "tests")):
dirs[:] = sorted(d for d in dirs if not (directory == os.path.join(root, "tests")
and d == "fixtures"))
for name in sorted(names):
if not name.endswith(".c"):
continue
path = os.path.join(directory, name)
rel = os.path.relpath(path, root).replace(os.sep, "/")
with open(path, encoding="utf-8", errors="replace") as fh:
src = fh.read()
sources[rel] = src
for m in SUITE_DEF.finditer(src):
suite = m.group(1) or m.group(2)
start = depth = m.end() - 1
end = start
depth = 0
while end < len(src): # the body is the balanced { } block
if src[end] == "{":
depth += 1
elif src[end] == "}":
depth -= 1
if depth == 0:
break
end += 1
file_suites.setdefault(rel, []).append(suite)
for test in RUN_TEST.findall(src[start:end]):
test_suites.setdefault(test, [])
if suite not in test_suites[test]:
test_suites[test].append(suite)
for child in RUN_SUITE.findall(src[start:end]):
parents.setdefault(child, set()).add(suite)
main = sources.get("tests/test_main.c", "")
registered = set(re.findall(r"RUN_SELECTED_SUITE(?:_PERF)?\((\w+)\)", main))
universe = registered - set(re.findall(r"RUN_SELECTED_SUITE_PERF\((\w+)\)", main))
def runnable(suite, seen=frozenset()):
"""A registered suite runs itself; a nested one runs inside its parents."""
if suite in registered:
return {suite} & universe
out = set()
for parent in parents.get(suite, ()):
if parent not in seen:
out |= runnable(parent, seen | {suite})
return out
return file_suites, test_suites, universe, sources, runnable
def predict(root, changed, selection, detect, depth):
file_suites, test_suites, universe, sources, runnable = suitemap(root)
reasons, run_all = [], False
if selection.get("full"):
run_all = True
reasons.append(f"run-all: the lane selector ran everything ({selection.get('tier')})")
for p in changed:
if p.startswith("tests/") and not p.startswith("tests/fixtures/") and p.endswith(".h"):
run_all = True
reasons.append(f"run-all: test header {p}")
elif p.startswith("test-infrastructure/"):
run_all = True
reasons.append(f"run-all: test infrastructure {p}")
graph = {"consulted": detect is not None}
picked = {} # suite -> "ALL" | set(tests)
if detect is not None:
rows = detect.get("impacted", [])
total = detect.get("impacted_total", len(rows))
shown = detect.get("impacted_shown", len(rows))
graph.update(impacted_total=total, impacted_shown=shown,
saturated=bool(detect.get("engine_saturated")))
if graph["saturated"]:
run_all = True
reasons.append("run-all: detect_changes saturated")
if total > len(rows) or shown > len(rows):
run_all = True
reasons.append(f"run-all: impact set truncated ({len(rows)} of {total} rows)")
tests = 0
for r in rows:
fp, name = r.get("file", ""), r.get("qn", "").rsplit(".", 1)[-1]
if not fp.startswith("tests/") or r.get("label") != "Function" or r.get("hop", 0) > depth:
continue
local = file_suites.get(fp, [])
if name in local:
continue # the SUITE function itself
candidates = test_suites.get(name, [])
for suite in [s for s in candidates if s in local] or candidates:
for runner in runnable(suite):
picked.setdefault(runner, set()).add(name)
tests += 1
graph["test_rows"] = tests
elif needs_graph(changed):
run_all = True
reasons.append("run-all: C change but no impact set (graph not consulted)")
for p in changed:
if p.startswith("tests/fixtures/"): # fixture sources are data, not tests
key = p.split("/")[2] if p.count("/") >= 2 else p
for f, src in sources.items():
if key in src:
for suite in file_suites.get(f, []):
for runner in runnable(suite):
picked[runner] = "ALL"
reasons.append(f"changed fixture {p}: suites whose tests name '{key}'")
elif p.startswith("tests/") and p.endswith(".c"):
for suite in file_suites.get(p, []):
for runner in runnable(suite):
picked[runner] = "ALL"
reasons.append(f"changed test file {p}: suite {runner}")
elif p.startswith("internal/cbm/vendored/grammars/"):
for suite in universe:
if GRAMMAR_SUITES.match(suite):
picked[suite] = "ALL"
reasons.append(f"changed grammar {p}: grammar suites")
if run_all:
mode, suites = "all", {s: "ALL" for s in sorted(universe)}
else:
suites = {s: v if v == "ALL" else sorted(v) for s, v in sorted(picked.items())}
mode = "graph" if (graph["consulted"] or suites) else "none"
return {"version": 1, "mode": mode, "depth": depth,
"selector": {"tier": selection.get("tier"), "full": bool(selection.get("full"))},
"universe": len(universe), "selected_suites": len(suites), "suites": suites,
"graph": graph, "reasons": sorted(set(reasons))}
def summary(result, meta):
g = result["graph"]
whole = sum(1 for v in result["suites"].values() if v == "ALL")
tests = sum(len(v) for v in result["suites"].values() if v != "ALL")
lines = ["### Test-impact shadow (report-only, never gates)", "",
"| | |", "|---|---|",
f"| selector | {result['selector']['tier']} (full: {'yes' if result['selector']['full'] else 'no'}) |",
f"| prediction | **{result['mode']}**: {result['selected_suites']} of {result['universe']} "
f"suites ({whole} whole, {tests} tests in the rest) |"]
if g.get("consulted"):
lines.append(f"| graph | {g.get('impacted_shown')} of {g.get('impacted_total')} impacted rows at "
f"depth {result['depth']}, {g.get('test_rows')} test rows"
f"{', saturated' if g.get('saturated') else ''} |")
for key in sorted(meta):
lines.append(f"| {key} | {meta[key]} |")
if result["reasons"]:
lines += ["", "Reasons: " + "; ".join(result["reasons"][:12])]
if result["mode"] == "graph" and result["suites"]:
lines += ["", "Suites: " + ", ".join(result["suites"])[:1500]]
return "\n".join(lines) + "\n"
def main():
ap = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
ap.add_argument("--needs-graph", action="store_true")
ap.add_argument("--root")
ap.add_argument("--changed", required=True)
ap.add_argument("--selection")
ap.add_argument("--detect")
ap.add_argument("--depth", type=int, default=15)
ap.add_argument("--meta", default="{}")
ap.add_argument("--out")
ap.add_argument("--summary")
a = ap.parse_args()
with open(a.changed, encoding="utf-8") as fh:
changed = sorted({line.strip() for line in fh if line.strip()})
if a.needs_graph:
print("yes" if needs_graph(changed) else "no")
return 0
if not (a.root and a.selection and a.out):
ap.error("--root, --selection and --out are required")
with open(a.selection, encoding="utf-8") as fh:
selection = json.load(fh)
detect = None
if a.detect:
with open(a.detect, encoding="utf-8") as fh:
detect = json.load(fh)
result = predict(a.root, changed, selection, detect, a.depth)
meta = json.loads(a.meta)
result["meta"] = meta
with open(a.out, "w", encoding="utf-8") as fh:
json.dump(result, fh, indent=1, sort_keys=True)
fh.write("\n")
if a.summary:
with open(a.summary, "w", encoding="utf-8") as fh:
fh.write(summary(result, meta))
return 0
if __name__ == "__main__":
sys.exit(main())
+730
View File
@@ -0,0 +1,730 @@
"""Pure bounded single-image profile-map v2 encoding and row reduction.
Evidence fields are caller claims, not authentication or closure certificates.
Names/hashes/shapes retain exact profile identity; source names are not inferred.
Limits count logical data, not interpreter RSS. Runtime allocation/deallocation
is not preemptible. This module performs no I/O and leaves format 1 unchanged.
"""
from dataclasses import dataclass
from enum import Enum
import hashlib
import sys
from typing import Callable, Optional, Tuple
from profile_text_parser import (
LimitExceededError, MalformedProfileError, ParseCancelledError,
ParseLimits, parse_profile_text,
)
class SuiteOutcome(Enum):
PASSED = "passed"
FAILED = "failed"
TIMED_OUT = "timed_out"
UNKNOWN = "unknown"
class ObservationRole(Enum):
PARENT = "parent"
SETUP = "setup"
CHILD = "child"
@dataclass(frozen=True)
class ExpectedSuite:
__slots__ = ("suite", "tests", "outcome")
suite: bytes
tests: Tuple[bytes, ...]
outcome: SuiteOutcome
@dataclass(frozen=True)
class ProfileObservation:
__slots__ = ("suite", "test", "key", "role", "text")
suite: bytes
test: bytes
key: bytes
role: ObservationRole
text: Optional[bytes] # None means an explicitly unavailable observation.
@dataclass(frozen=True)
class RowEvidence:
__slots__ = (
"suite", "test", "interval_complete", "runtime_compatible",
"same_image", "children_complete",
)
suite: bytes
test: bytes
interval_complete: bool
runtime_compatible: bool
same_image: bool
children_complete: bool
@dataclass(frozen=True)
class ProfileMapLimits:
__slots__ = (
"max_input_bytes", "max_rows", "max_observations", "max_ids",
"max_records", "max_counter_values", "max_name_bytes",
"max_id_references", "max_output_bytes",
)
max_input_bytes: int
max_rows: int
max_observations: int
max_ids: int
max_records: int
max_counter_values: int
max_name_bytes: int
max_id_references: int
max_output_bytes: int
@dataclass(frozen=True)
class ProducedMap:
__slots__ = (
"profiles_tsv", "tests_tsv", "profiles_sha256", "tests_sha256",
)
profiles_tsv: bytes
tests_tsv: bytes
profiles_sha256: bytes
tests_sha256: bytes
class ProfileMapError(ValueError):
"""Base for classified build failures; no ProducedMap is returned."""
class ProfileMapInvalidArgumentError(ProfileMapError):
"""Invalid exact type, field, routing, membership, duplicate or callback result."""
class ProfileMapEnumerationError(ProfileMapError):
"""Enumeration text is malformed or unsupported, including empty text."""
class ProfileMapCompatibilityError(ProfileMapError):
"""An observation contains an unknown full identity or mismatching shape."""
class ProfileMapLimitExceededError(ProfileMapError):
"""A declared logical or fixed representation bound would be exceeded."""
class ProfileMapCancelledError(ProfileMapError):
"""The caller's pure prompt predicate requested cancellation."""
_INT_MAX = 2147483647
_SHA_BYTES_MAX = ((1 << 64) - 1) // 8
_POLL_INTERVAL = 4096
_HEX = b"0123456789abcdef"
_REASONS = (
b"suite_failed", b"suite_timeout", b"suite_unknown",
b"row_evidence_missing", b"interval_unproved", b"runtime_unproved",
b"image_unproved", b"children_unproved", b"missing_parent",
b"missing_setup", b"observation_unavailable", b"observation_malformed",
b"partial_stream",
)
_SUITE_FAILED, _SUITE_TIMEOUT, _SUITE_UNKNOWN = 0, 1, 2
_NO_EVIDENCE, _INTERVAL, _RUNTIME, _IMAGE, _CHILDREN = 3, 4, 5, 6, 7
_NO_PARENT, _NO_SETUP, _UNAVAILABLE, _MALFORMED, _PARTIAL = 8, 9, 10, 11, 12
class _Gate:
__slots__ = ("cancel", "context", "work", "callback_error")
def __init__(self, cancel, context):
self.cancel = cancel
self.context = context
self.work = 0
self.callback_error = None
def poll(self):
self.work = 0
if self.cancel is None:
return
try:
answer = self.cancel(self.context)
except BaseException as error:
# Identity distinguishes a callback's parser-shaped exception from
# a parser failure; neither its class nor message is sufficient.
self.callback_error = error
raise
if type(answer) is not bool:
raise ProfileMapInvalidArgumentError("cancel must return bool")
if answer:
raise ProfileMapCancelledError("profile map build cancelled")
def step(self):
self.work += 1
if self.work == _POLL_INTERVAL:
self.poll()
def parser_cancel(self, _context):
self.poll()
return False
def _bounded_add(current, amount, cap, label):
if amount > cap - current:
raise ProfileMapLimitExceededError(label)
return current + amount
class _Budget:
__slots__ = (
"limits", "input_bytes", "rows", "records", "counter_values",
"name_bytes", "id_references", "output_bytes",
)
def __init__(self, limits, enumeration_text):
self.limits = limits
self.input_bytes = _bounded_add(
32, len(enumeration_text), limits.max_input_bytes, "input byte limit"
)
self.rows = 0
self.records = 0
self.counter_values = 0
self.name_bytes = 0
self.id_references = 0
self.output_bytes = 0
def name(self, value):
self.input_bytes = _bounded_add(
self.input_bytes, len(value), self.limits.max_input_bytes,
"input byte limit",
)
self.name_bytes = _bounded_add(
self.name_bytes, len(value), self.limits.max_name_bytes,
"name byte limit",
)
def text(self, value):
self.input_bytes = _bounded_add(
self.input_bytes, len(value), self.limits.max_input_bytes,
"input byte limit",
)
def add_rows(self, count):
self.rows = _bounded_add(
self.rows, count, min(self.limits.max_rows, _INT_MAX), "row limit"
)
def parsed(self, records, gate):
self.records = _bounded_add(
self.records, len(records), self.limits.max_records, "record limit"
)
for record in records:
gate.step()
self.counter_values = _bounded_add(
self.counter_values, len(record.counters),
self.limits.max_counter_values, "counter value limit",
)
self.name_bytes = _bounded_add(
self.name_bytes, len(record.raw_name),
self.limits.max_name_bytes, "name byte limit",
)
def reference(self):
self.id_references = _bounded_add(
self.id_references, 1, self.limits.max_id_references,
"ID reference limit",
)
def output(self, count):
self.output_bytes = _bounded_add(
self.output_bytes, count,
min(self.limits.max_output_bytes, _SHA_BYTES_MAX), "output byte limit",
)
class _Row:
__slots__ = (
"suite", "test", "outcome", "evidence", "observations", "ids",
"reasons", "parent_seen", "primary_parsed",
)
def __init__(self, suite, test, outcome):
self.suite = suite
self.test = test
self.outcome = outcome
self.evidence = None
self.observations = []
self.ids = set()
self.reasons = 0
self.parent_seen = False
self.primary_parsed = False
def mark(self, reason):
self.reasons |= 1 << reason
def _exact(value, cls, label):
if type(value) is not cls:
raise ProfileMapInvalidArgumentError(label)
def _validate_top(image, enumeration, suites, observations, evidence, limits, cancel):
_exact(image, bytes, "image digest must be bytes")
if len(image) != 32:
raise ProfileMapInvalidArgumentError("image digest must contain 32 bytes")
_exact(enumeration, bytes, "enumeration must be bytes")
_exact(suites, tuple, "expected suites must be a tuple")
_exact(observations, tuple, "observations must be a tuple")
_exact(evidence, tuple, "row evidence must be a tuple")
_exact(limits, ProfileMapLimits, "invalid limits type")
for field in ProfileMapLimits.__slots__:
value = getattr(limits, field)
if type(value) is not int or value <= 0 or value > sys.maxsize:
raise ProfileMapInvalidArgumentError("limits must be positive bounded ints")
if limits.max_ids > _INT_MAX:
raise ProfileMapInvalidArgumentError("max_ids exceeds INT_MAX")
if cancel is not None and not callable(cancel):
raise ProfileMapInvalidArgumentError("cancel must be callable or None")
def _name(value, budget, gate, *, suite=False, declared_test=False):
_exact(value, bytes, "names and keys must be bytes")
if not value:
raise ProfileMapInvalidArgumentError("empty name or key")
budget.name(value)
for char in value:
gate.step()
if char in (0, 9, 10, 13) or (suite and char == 58):
raise ProfileMapInvalidArgumentError("unrepresentable name or key")
if declared_test and _is_setup(value):
raise ProfileMapInvalidArgumentError("setup name is reserved")
def _is_setup(test):
return len(test) == 1 and test[0] == 42
def _compare_bytes(left, right, gate):
for index in range(min(len(left), len(right))):
gate.step()
a, b = left[index], right[index]
if a != b:
return -1 if a < b else 1
gate.step()
return (len(left) > len(right)) - (len(left) < len(right))
def _compare_fields(left, right, fields, gate):
for field in fields:
gate.step()
order = _compare_bytes(getattr(left, field), getattr(right, field), gate)
if order:
return order
return 0
def _merge(source, target, start, middle, end, compare, gate):
left, right = start, middle
for destination in range(start, end):
gate.step()
take_left = right == end or (
left < middle and compare(source[left], source[right]) <= 0
)
if take_left:
target[destination] = source[left]
left += 1
else:
target[destination] = source[right]
right += 1
def _sort(values, compare, gate):
source, target = [], []
for value in values:
gate.step()
source.append(value)
target.append(None)
width = 1
count = len(source)
while width < count:
gate.step()
for start in range(0, count, width * 2):
gate.step()
middle, end = min(start + width, count), min(start + width * 2, count)
_merge(source, target, start, middle, end, compare, gate)
source, target = target, source
width *= 2
return source
def _sort_unique(values, fields, gate, label):
compare = lambda left, right: _compare_fields(left, right, fields, gate)
result = _sort(values, compare, gate)
for index in range(1, len(result)):
gate.step()
if compare(result[index - 1], result[index]) == 0:
raise ProfileMapInvalidArgumentError(label)
return result
def _suites(suites, budget, gate):
rows = []
if len(suites) > min(budget.limits.max_rows, _INT_MAX):
raise ProfileMapLimitExceededError("row limit")
for suite in suites:
gate.step()
_exact(suite, ExpectedSuite, "invalid expected suite type")
_name(suite.suite, budget, gate, suite=True)
_exact(suite.tests, tuple, "expected tests must be a tuple")
_exact(suite.outcome, SuiteOutcome, "invalid suite outcome")
budget.add_rows(1 + len(suite.tests))
rows.append(_Row(suite.suite, b"*", suite.outcome))
for test in suite.tests:
gate.step()
_name(test, budget, gate, declared_test=True)
rows.append(_Row(suite.suite, test, suite.outcome))
_sort_unique(suites, ("suite",), gate, "duplicate expected suite")
return _sort_unique(rows, ("suite", "test"), gate, "duplicate expected test")
def _compare_target(row, suite, test, gate):
order = _compare_bytes(row.suite, suite, gate)
return order if order else _compare_bytes(row.test, test, gate)
def _find_row(rows, suite, test, gate):
low, high = 0, len(rows)
while low < high:
gate.step()
middle = (low + high) // 2
order = _compare_target(rows[middle], suite, test, gate)
if order == 0:
return rows[middle]
if order < 0:
low = middle + 1
else:
high = middle
raise ProfileMapInvalidArgumentError("observation/evidence outside expected rows")
def _observation_schema(observation, budget, gate):
_exact(observation, ProfileObservation, "invalid observation type")
_name(observation.suite, budget, gate, suite=True)
_name(observation.test, budget, gate)
_name(observation.key, budget, gate)
_exact(observation.role, ObservationRole, "invalid observation role")
if observation.text is not None:
_exact(observation.text, bytes, "observation text must be bytes or None")
budget.text(observation.text)
def _route_observation(observation, row):
setup = _is_setup(row.test)
if ((setup and observation.role is ObservationRole.PARENT)
or (not setup and observation.role is ObservationRole.SETUP)):
raise ProfileMapInvalidArgumentError("invalid role for row")
if observation.role is ObservationRole.PARENT:
if row.parent_seen:
raise ProfileMapInvalidArgumentError("multiple parent observations")
row.parent_seen = True
row.observations.append(observation)
def _observations(observations, rows, budget, gate):
if len(observations) > budget.limits.max_observations:
raise ProfileMapLimitExceededError("observation limit")
for observation in observations:
gate.step()
_observation_schema(observation, budget, gate)
ordered = _sort_unique(
observations, ("suite", "test", "key"), gate, "duplicate observation key"
)
for observation in ordered:
gate.step()
row = _find_row(rows, observation.suite, observation.test, gate)
_route_observation(observation, row)
def _evidence(evidence, rows, budget, gate):
if len(evidence) > len(rows):
raise ProfileMapInvalidArgumentError("too many row evidence records")
for item in evidence:
gate.step()
_exact(item, RowEvidence, "invalid row evidence type")
_name(item.suite, budget, gate, suite=True)
_name(item.test, budget, gate)
for field in ("interval_complete", "runtime_compatible", "same_image", "children_complete"):
gate.step()
_exact(getattr(item, field), bool, "evidence claims must be bool")
ordered = _sort_unique(evidence, ("suite", "test"), gate, "duplicate row evidence")
for item in ordered:
gate.step()
_find_row(rows, item.suite, item.test, gate).evidence = item
def _preflight(suites, observations, evidence, budget, gate):
rows = _suites(suites, budget, gate)
_observations(observations, rows, budget, gate)
_evidence(evidence, rows, budget, gate)
gate.poll()
return rows
def _parser_limits(budget, enumeration):
limits = budget.limits
records = limits.max_records - budget.records
counters = limits.max_counter_values - budget.counter_values
names = limits.max_name_bytes - budget.name_bytes
if enumeration:
records = min(records, limits.max_ids)
if records <= 0 or counters <= 0 or names <= 0:
raise ProfileMapLimitExceededError("no remaining parser item allowance")
return ParseLimits(limits.max_input_bytes, records, counters, names)
def _parse(text, budget, gate, *, enumeration=False):
limits = _parser_limits(budget, enumeration)
try:
records = parse_profile_text(text, limits, cancel=gate.parser_cancel)
except MalformedProfileError as error:
if error is gate.callback_error:
raise
if enumeration:
raise ProfileMapEnumerationError("invalid profile enumeration") from error
return None
except LimitExceededError as error:
if error is gate.callback_error:
raise
raise ProfileMapLimitExceededError("profile parser resource limit") from error
except ParseCancelledError as error:
if error is gate.callback_error:
raise
raise ProfileMapCancelledError("profile parsing cancelled") from error
budget.parsed(records, gate)
return records
def _record_order(left, right, gate):
order = _compare_bytes(left.raw_name, right.raw_name, gate)
if order:
return order
gate.step()
return (left.function_hash > right.function_hash) - (left.function_hash < right.function_hash)
def _has_hit(record, gate):
for value in record.counters:
gate.step()
if value:
return True
return False
def _join(records, universe, row, budget, gate):
position = 0
for record in records:
gate.step()
while position < len(universe):
gate.step()
order = _record_order(universe[position], record, gate)
if order >= 0:
break
position += 1
if position == len(universe) or order != 0:
raise ProfileMapCompatibilityError("observation identity outside universe")
if len(record.counters) != len(universe[position].counters):
raise ProfileMapCompatibilityError("observation counter shape mismatch")
if _has_hit(record, gate) and position not in row.ids:
budget.reference()
row.ids.add(position)
position += 1
if len(records) != len(universe):
row.mark(_PARTIAL)
def _base_reasons(row, gate):
outcomes = (
(SuiteOutcome.FAILED, _SUITE_FAILED),
(SuiteOutcome.TIMED_OUT, _SUITE_TIMEOUT),
(SuiteOutcome.UNKNOWN, _SUITE_UNKNOWN),
)
for outcome, reason in outcomes:
gate.step()
if row.outcome is outcome:
row.mark(reason)
if row.evidence is None:
row.mark(_NO_EVIDENCE)
return
for field, reason in (
("interval_complete", _INTERVAL), ("runtime_compatible", _RUNTIME),
("same_image", _IMAGE), ("children_complete", _CHILDREN),
):
gate.step()
if not getattr(row.evidence, field):
row.mark(reason)
def _reduce_row(row, universe, budget, gate):
_base_reasons(row, gate)
for observation in row.observations:
gate.step()
if observation.text is None:
row.mark(_UNAVAILABLE)
continue
records = _parse(observation.text, budget, gate)
if records is None:
row.mark(_MALFORMED)
continue
_join(records, universe, row, budget, gate)
if observation.role in (ObservationRole.PARENT, ObservationRole.SETUP):
row.primary_parsed = True
if not row.primary_parsed:
row.mark(_NO_SETUP if _is_setup(row.test) else _NO_PARENT)
class _Wire:
__slots__ = ("budget", "gate", "data")
def __init__(self, budget, gate):
self.budget = budget
self.gate = gate
self.data = bytearray()
def emit(self, value):
self.budget.output(len(value))
for char in value:
self.gate.step()
self.data.append(char)
def hex(self, value):
self.budget.output(len(value) * 2)
for char in value:
self.gate.step()
self.gate.step()
self.data.append(_HEX[char >> 4])
self.gate.step()
self.data.append(_HEX[char & 15])
def decimal(self, value):
# All emitted integers are <= uint64, so conversion is <=20 digits.
self.gate.poll()
self.emit(str(value).encode("ascii"))
def _values(self):
for char in self.data:
self.gate.step()
yield char
def finish(self):
self.gate.poll()
result = bytes(self._values())
self.gate.poll()
return result
def _profile_wire(image, universe, budget, gate):
out = _Wire(budget, gate)
out.emit(b"CBM_PROFILE_MAP\t2\t")
out.hex(image)
out.emit(b"\t")
out.decimal(len(universe))
out.emit(b"\n")
for ident, record in enumerate(universe):
gate.step()
out.decimal(ident)
out.emit(b"\t")
out.hex(record.raw_name)
out.emit(b"\t")
gate.poll()
out.emit(format(record.function_hash, "016x").encode("ascii"))
out.emit(b"\t")
out.decimal(len(record.counters))
out.emit(b"\n")
return out.finish()
def _reason_wire(row, out, gate):
first = True
for index, reason in enumerate(_REASONS):
gate.step()
if row.reasons & (1 << index):
if not first:
out.emit(b";")
out.emit(reason)
first = False
def _id_wire(row, out, gate):
def compare(left, right):
gate.step()
return (left > right) - (left < right)
ordered = _sort(row.ids, compare, gate)
for index, ident in enumerate(ordered):
gate.step()
if index:
out.emit(b" ")
out.decimal(ident)
def _test_wire(rows, budget, gate):
out = _Wire(budget, gate)
for row in rows:
gate.step()
out.emit(row.suite)
out.emit(b":")
out.emit(row.test)
out.emit(b"\tincomplete\t" if row.reasons else b"\tcomplete\t")
_reason_wire(row, out, gate)
out.emit(b"\t")
_id_wire(row, out, gate)
out.emit(b"\n")
return out.finish()
def _digest(data, gate):
digest = hashlib.sha256()
for start in range(0, len(data), _POLL_INTERVAL):
gate.poll()
chunk = data[start:start + _POLL_INTERVAL]
gate.poll()
digest.update(chunk)
gate.poll()
return digest.digest()
def build_profile_map_v2(
image_sha256: bytes,
enumeration_text: bytes,
expected_suites: Tuple[ExpectedSuite, ...],
observations: Tuple[ProfileObservation, ...],
row_evidence: Tuple[RowEvidence, ...],
limits: ProfileMapLimits,
*,
cancel: Optional[Callable[[object], bool]] = None,
cancel_context: object = None,
) -> ProducedMap:
"""Produce exact v2 wires or raise, without I/O or a partial result.
Inputs are immutable exact types. Evidence is supplied by the caller;
parsing cannot establish image/process closure, provenance or admission.
"""
_validate_top(image_sha256, enumeration_text, expected_suites, observations,
row_evidence, limits, cancel)
gate = _Gate(cancel, cancel_context)
gate.poll()
budget = _Budget(limits, enumeration_text)
rows = _preflight(expected_suites, observations, row_evidence, budget, gate)
universe = _parse(enumeration_text, budget, gate, enumeration=True)
for row in rows:
gate.step()
_reduce_row(row, universe, budget, gate)
profiles = _profile_wire(image_sha256, universe, budget, gate)
tests = _test_wire(rows, budget, gate)
profiles_digest = _digest(profiles, gate)
tests_digest = _digest(tests, gate)
gate.poll()
return ProducedMap(profiles, tests, profiles_digest, tests_digest)
+295
View File
@@ -0,0 +1,295 @@
"""Strict data-only parser for the observed LLVM 22.1.8 frontend proftext.
parse_profile_text(bytes, ParseLimits, *, cancel=None, cancel_context=None)
returns an immutable tuple of ProfileRecord(raw_name, function_hash, counters),
ordered by unsigned raw-name bytes and then uint64 hash. Zero values survive.
Each record is a nonempty name line, '# Func Hash:', uint64, '# Num Counters:',
positive uint64, '# Counter Values:', exactly that many uint64 lines, and one
mandatory blank line. All delimiters are LF. Decimals are canonical. Empty
documents, CR/NUL, extra sections and duplicate name/hash tuples fail closed.
Names remain opaque bytes: no normalization, decoding or comment processing.
Four exact positive integer limits bound input bytes, records, total counter
values and total name bytes. Errors derive from ProfileTextError; MemoryError
and callback exceptions propagate without a partial result. A pure, prompt
cancel(context) predicate must return bool. It is called at entry/final publish
and within explicit loops at intervals of at most 4096 byte/element operations.
Python allocation/resizing/deallocation and callbacks are not preemptible.
Limits bound logical input and result items, not exact interpreter heap use.
This module performs no I/O and makes no image, authentication or closure claim.
"""
from __future__ import annotations
from dataclasses import dataclass
import sys
from typing import Callable
_UINT64_MAX = (1 << 64) - 1
_POLL_INTERVAL = 4096
@dataclass(frozen=True)
class ProfileRecord:
__slots__ = ("raw_name", "function_hash", "counters")
raw_name: bytes
function_hash: int
counters: tuple[int, ...]
@dataclass(frozen=True)
class ParseLimits:
__slots__ = ("max_input_bytes", "max_records", "max_counter_values", "max_name_bytes")
max_input_bytes: int
max_records: int
max_counter_values: int
max_name_bytes: int
class ProfileTextError(ValueError):
"""Base for parser failures; no partial result is published."""
class InvalidArgumentError(ProfileTextError):
"""Invalid public argument, limit, or callback return type."""
class MalformedProfileError(ProfileTextError):
"""Unsupported or malformed text, including duplicate identities."""
class LimitExceededError(ProfileTextError):
"""A configured logical resource bound would be exceeded."""
class ParseCancelledError(ProfileTextError):
"""The caller's predicate requested cancellation."""
class _Gate:
__slots__ = ("cancel", "context", "work")
def __init__(self, cancel, context):
self.cancel = cancel
self.context = context
self.work = 0
def poll(self):
self.work = 0
if self.cancel is None:
return
answer = self.cancel(self.context)
if type(answer) is not bool:
raise InvalidArgumentError("cancel must return bool")
if answer:
raise ParseCancelledError("profile parsing cancelled")
def step(self):
self.work += 1
if self.work == _POLL_INTERVAL:
self.poll()
class _Parser:
__slots__ = ("data", "limits", "gate", "position", "names", "values")
def __init__(self, data, limits, gate):
self.data = data
self.limits = limits
self.gate = gate
self.position = 0
self.names = 0
self.values = 0
def line(self):
start = self.position
while self.position < len(self.data):
self.gate.step()
value = self.data[self.position]
self.position += 1
if value == 0 or value == 13:
raise MalformedProfileError("NUL and CR are unsupported")
if value == 10:
return start, self.position - 1
raise MalformedProfileError("unterminated or missing line")
def marker(self, expected):
start, end = self.line()
if end - start != len(expected):
raise MalformedProfileError("unexpected record marker")
for index, value in enumerate(expected):
self.gate.step()
if self.data[start + index] != value:
raise MalformedProfileError("unexpected record marker")
def uint64(self):
start, end = self.line()
length = end - start
if length == 0 or length > 20:
raise MalformedProfileError("invalid unsigned decimal")
if length > 1 and self.data[start] == 48:
raise MalformedProfileError("noncanonical unsigned decimal")
value = 0
for index in range(start, end):
self.gate.step()
digit = self.data[index] - 48
if digit < 0 or digit > 9:
raise MalformedProfileError("invalid unsigned decimal")
if value > (_UINT64_MAX - digit) // 10:
raise MalformedProfileError("unsigned decimal overflow")
value = value * 10 + digit
return value
def name_values(self, start, end):
for index in range(start, end):
self.gate.step()
yield self.data[index]
def counter_values(self, count):
for _ in range(count):
self.gate.step()
yield self.uint64()
def record(self):
start, end = self.line()
length = end - start
if length == 0:
raise MalformedProfileError("empty record name")
if length > self.limits.max_name_bytes - self.names:
raise LimitExceededError("total name bytes limit exceeded")
self.names += length
self.marker(b"# Func Hash:")
function_hash = self.uint64()
self.marker(b"# Num Counters:")
count = self.uint64()
if count == 0:
raise MalformedProfileError("counter count must be positive")
if count > self.limits.max_counter_values - self.values:
raise LimitExceededError("total counter values limit exceeded")
self.values += count
self.marker(b"# Counter Values:")
# Each remaining value needs at least '0\n', then the final blank line.
remaining = len(self.data) - self.position
if remaining < 1 or count > (remaining - 1) // 2:
raise MalformedProfileError("truncated counter values")
self.gate.poll()
counters = tuple(self.counter_values(count))
self.gate.poll()
blank_start, blank_end = self.line()
if blank_start != blank_end:
raise MalformedProfileError("missing record blank line")
self.gate.poll()
name = bytes(self.name_values(start, end))
self.gate.poll()
return ProfileRecord(name, function_hash, counters)
def records(self):
self.gate.poll()
records = []
while self.position < len(self.data):
self.gate.step()
if len(records) == self.limits.max_records:
raise LimitExceededError("record count limit exceeded")
record = self.record()
self.gate.poll()
records.append(record)
if not records:
raise MalformedProfileError("empty document")
return records
def _compare(left, right, gate):
common = min(len(left.raw_name), len(right.raw_name))
for index in range(common):
gate.step()
a = left.raw_name[index]
b = right.raw_name[index]
if a != b:
return -1 if a < b else 1
gate.step()
if len(left.raw_name) != len(right.raw_name):
return -1 if len(left.raw_name) < len(right.raw_name) else 1
if left.function_hash == right.function_hash:
return 0
return -1 if left.function_hash < right.function_hash else 1
def _merge(source, target, start, middle, end, gate):
left, right = start, middle
for destination in range(start, end):
gate.step()
take_left = right == end or (
left < middle and _compare(source[left], source[right], gate) <= 0
)
if take_left:
target[destination] = source[left]
left += 1
else:
target[destination] = source[right]
right += 1
def _sorted_unique(records, gate):
gate.poll()
scratch = []
for _ in records:
gate.step()
scratch.append(None)
gate.poll()
width = 1
length = len(records)
while width < length:
for start in range(0, length, width * 2):
middle = min(start + width, length)
end = min(start + width * 2, length)
_merge(records, scratch, start, middle, end, gate)
records, scratch = scratch, records
width *= 2
for index in range(1, length):
gate.step()
if _compare(records[index - 1], records[index], gate) == 0:
raise MalformedProfileError("duplicate name/hash identity")
return records
def _record_values(records, gate):
for record in records:
gate.step()
yield record
def _validate_arguments(data, limits, cancel):
if type(data) is not bytes or type(limits) is not ParseLimits:
raise InvalidArgumentError("expected bytes and ParseLimits")
for value in (limits.max_input_bytes, limits.max_records,
limits.max_counter_values, limits.max_name_bytes):
if type(value) is not int or value <= 0 or value > sys.maxsize:
raise InvalidArgumentError("limits must be positive bounded integers")
if cancel is not None and not callable(cancel):
raise InvalidArgumentError("cancel must be callable or None")
def parse_profile_text(
data: bytes,
limits: ParseLimits,
*,
cancel: Callable[[object], bool] | None = None,
cancel_context: object = None,
) -> tuple[ProfileRecord, ...]:
"""Parse all records or raise a specific error; never return a partial result."""
_validate_arguments(data, limits, cancel)
gate = _Gate(cancel, cancel_context)
gate.poll()
if len(data) > limits.max_input_bytes:
raise LimitExceededError("input bytes limit exceeded")
parser = _Parser(data, limits, gate)
records = _sorted_unique(parser.records(), gate)
gate.poll()
result = tuple(_record_values(records, gate))
gate.poll()
return result
+148
View File
@@ -0,0 +1,148 @@
#!/usr/bin/env bash
# replay-selector.sh — replay every September 2026 PR push through the lane
# selector (scripts/ci/select-lanes.sh) and hold it to the measured history.
#
# The rule set was chosen by replaying this history offline; this keeps the
# committed selector honest against the same data, as a contract step:
# 1. every push (767) gets exactly the tier recorded in fixtures/2026-09-
# pushes.tsv. Where that differs from the analysis tier the row must name
# its documented deviation, and a deviation may only ever raise the tier
# (the fail-safe direction) -- never quietly select less than was measured;
# 2. every real failure of the month (22, fixtures/2026-09-real-failures.tsv)
# had its catching lane selected for its push;
# 3. every push runs the static contract steps (the contracts lane or any
# test leg) -- they police docs, test-infrastructure/ and lint config.
# Pure data, one selector process (--batch over the distinct file sets).
#
# Usage: scripts/test-impact/replay-selector.sh [repo-root]
set -euo pipefail
case "${1:-}" in
-h | --help)
sed -n '2,17p' "$0" | sed 's/^# \{0,1\}//'
exit 0
;;
-*)
echo "replay-selector.sh: unknown option '$1'. Please consult --help." >&2
exit 2
;;
esac
ROOT="${1:-$(cd "$(dirname "$0")/../.." && pwd)}"
FIXTURES="$ROOT/scripts/test-impact/fixtures"
WORK=$(mktemp -d "${TMPDIR:-/tmp}/cbm-replay-selector.XXXXXX")
trap 'rm -rf "$WORK"' EXIT
python3 - "$FIXTURES" "$WORK/batch.jsonl" <<'PY'
import json
import sys
fixtures, batch = sys.argv[1:3]
def rows(name):
with open(f"{fixtures}/{name}", encoding="utf-8") as fh:
for line in fh:
line = line.rstrip("\r\n")
if line and not line.startswith("#"):
yield line.split("\t")
with open(batch, "w", encoding="utf-8") as out:
for fileset, paths in rows("2026-09-filesets.tsv"):
out.write(json.dumps({"id": fileset, "files": paths.split(" ")}) + "\n")
PY
bash "$ROOT/scripts/ci/select-lanes.sh" --batch "$WORK/batch.jsonl" >"$WORK/decisions.jsonl"
python3 - "$FIXTURES" "$WORK/decisions.jsonl" <<'PY'
import collections
import json
import sys
fixtures, decisions_path = sys.argv[1:3]
TIERS = ["T0a-docs", "T0b-nonproduct", "T1-tests", "T2-extract", "T3-mid",
"T4-platform", "T5-core"]
DEVIATIONS = {"makefile-by-name"}
def rows(name, width):
with open(f"{fixtures}/{name}", encoding="utf-8") as fh:
for number, line in enumerate(fh, 1):
line = line.rstrip("\r\n")
if not line or line.startswith("#"):
continue
cols = line.split("\t")
if len(cols) != width:
sys.exit(f"FAIL: {name}:{number}: expected {width} columns, got {len(cols)}")
yield cols
decisions = {}
with open(decisions_path, encoding="utf-8") as fh:
for line in fh:
row = json.loads(line)
decisions[row["id"]] = row
failures = []
pushes = {}
matched = deviated = 0
tiers = collections.Counter()
for run_id, pr, sha, fileset, analysis, expected, note in rows("2026-09-pushes.tsv", 7):
decision = decisions.get(fileset)
if decision is None:
failures.append(f"run {run_id}: fileset {fileset} missing")
continue
pushes[run_id] = decision
tiers[decision["tier"]] += 1
if decision["tier"] != expected:
failures.append(f"run {run_id} (PR #{pr}, {sha[:10]}): selector tier "
f"{decision['tier']}, expected {expected} -- reasons: "
f"{'; '.join(decision['reasons'])}")
continue
if analysis == expected:
if note:
failures.append(f"run {run_id}: note '{note}' on a row with no deviation")
matched += 1
elif note not in DEVIATIONS:
failures.append(f"run {run_id}: tier differs from the analysis ({analysis}) "
f"without a documented deviation")
elif TIERS.index(expected) < TIERS.index(analysis):
failures.append(f"run {run_id}: deviation '{note}' LOWERS the tier "
f"{analysis} -> {expected}")
else:
deviated += 1
# The static contract steps police docs, test-infrastructure/ and lint config,
# so every push runs them: in the contracts job or inside any test leg.
CONTRACT_CARRIERS = {"contracts", "unix-x86", "unix-arm64", "unix-macos14",
"unix-macos-intel", "windows"}
uncovered = collections.Counter(d["tier"] for d in pushes.values()
if not set(d["lanes"]) & CONTRACT_CARRIERS)
for tier, count in sorted(uncovered.items()):
failures.append(f"{count} {tier} push(es) run no contract step (neither the "
f"contracts lane nor a test leg)")
caught = 0
for run_id, attempt, pr, sha, lane, why in rows("2026-09-real-failures.tsv", 6):
decision = pushes.get(run_id)
if decision is None:
failures.append(f"real failure run {run_id}: no such push in the replay")
elif lane not in decision["lanes"]:
failures.append(f"real failure run {run_id} (PR #{pr}): lane {lane} NOT selected "
f"(tier {decision['tier']}) -- {why}")
else:
caught += 1
if failures:
print("SELECTOR HISTORY REPLAY FAILED:")
for failure in failures:
print(f" {failure}")
sys.exit(1)
print(f"selector history replay OK: {len(pushes)} pushes, tier as expected for all "
f"({matched} equal to the analysis, {deviated} documented fail-safe deviations); "
f"{caught} real failures, every catching lane selected; every push runs the "
f"contract steps")
print(" pushes per tier: " + ", ".join(f"{t} {tiers[t]}" for t in TIERS))
print(f" full runs: {sum(1 for d in pushes.values() if d['full'])}")
PY
+137
View File
@@ -0,0 +1,137 @@
#!/usr/bin/env python3
"""Turn a test-impact answer (`codebase-memory-mcp test-impact select`) into
what a CI consumer runs.
selection.py suites ANSWER
One line per suite whose tests the answer selects, or a single `*` when a
lane runs everything. The team-artifact producer re-runs exactly these
suites with coverage (scripts/ci/test-impact-publish.sh).
selection.py filter ANSWER --lane LANE --out FILE [--optional-out FILE2]
exit 0 narrowed: FILE holds the lane's CBM_TEST_ONLY_FILE lines, and
FILE2 the selected suites the engine saw registered only
conditionally (CONDITIONAL): a build may legitimately lack them
(scripts/run-tests-parallel.sh, CBM_TEST_SELECTION_OPTIONAL)
exit 11 the lane selects nothing: FILE is empty
anything else: run everything (FILE and FILE2 are removed)
It fails safe by construction: a run-all answer, a lane it cannot find, an
unknown schema or filter encoding, a token that is not `suite` or
`suite:test`, and a filter that disagrees with the lane's own suite list all
mean "run everything", and so does any crash (exit 1).
"""
import argparse
import json
import os
import re
import sys
SCHEMAS = {"cbm.test_impact.v0", "cbm.test_impact.v1"}
TOKEN = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*(:[A-Za-z_][A-Za-z0-9_]*)?$")
NARROWED, NOTHING, RUN_ALL = 0, 11, 10
def suites(answer):
if answer.get("decision") == "run_all":
return ["*"]
out = []
for lane in answer.get("lanes") or []:
if lane.get("decision") == "run_all" or lane.get("suites") is None:
return ["*"]
out += [suite["suite"] for suite in lane["suites"]]
return sorted(set(out))
def run_all(why):
print(f"selection: run everything: {why}", file=sys.stderr)
return RUN_ALL
def lane_filter(answer, name):
"""-> (exit code, lines)"""
if answer.get("schema") not in SCHEMAS:
return run_all(f"unknown schema {answer.get('schema')!r}"), []
if answer.get("decision") == "run_all":
return run_all("the answer runs everything"), []
lanes = [lane for lane in answer.get("lanes") or [] if lane.get("lane") == name]
if len(lanes) != 1:
return run_all(f"lane {name!r} appears {len(lanes)} times"), []
lane = lanes[0]
decision = lane.get("decision")
if decision == "skip":
if lane.get("suites") or lane.get("runner_filter") is not None:
return run_all("a skipped lane that still lists suites or a filter"), []
return NOTHING, []
if decision != "selected" or not isinstance(lane.get("suites"), list):
return run_all(f"lane decision {decision!r}"), []
flt = lane.get("runner_filter")
if not isinstance(flt, dict) or flt.get("encoding") != "cbm-test-only-lines" or \
not isinstance(flt.get("value"), str):
return run_all("no cbm-test-only-lines filter"), []
lines = [line for line in flt["value"].split("\n") if line]
bad = [line for line in lines if not TOKEN.match(line)]
if not lines or bad:
return run_all(f"filter tokens unusable ({bad[:3] or 'empty'})"), []
expected = set()
for suite in lane["suites"]:
if suite.get("mode") == "whole":
expected.add(suite["suite"])
elif suite.get("mode") == "tests" and suite.get("tests"):
expected |= {f"{suite['suite']}:{test['test']}" for test in suite["tests"]}
else:
return run_all(f"suite {suite.get('suite')!r} has mode {suite.get('mode')!r}"), []
if set(lines) != expected:
return run_all("the filter disagrees with the lane's suite list"), []
return NARROWED, lines
def conditional_suites(answer, name):
"""The lane's selected suites whose registration the engine saw as conditional."""
for lane in answer.get("lanes") or []:
if lane.get("lane") == name:
return sorted({suite["suite"] for suite in lane.get("suites") or []
if "CONDITIONAL" in (suite.get("whole_reason") or [])})
return []
def main():
parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
sub = parser.add_subparsers(dest="command", required=True)
s = sub.add_parser("suites")
s.add_argument("answer")
f = sub.add_parser("filter")
f.add_argument("answer")
f.add_argument("--lane", required=True)
f.add_argument("--out", required=True)
f.add_argument("--optional-out")
args = parser.parse_args()
if args.command == "suites":
# LF on every host, like the files below: a Windows python would
# otherwise write CRLF.
sys.stdout.reconfigure(newline="\n")
with open(args.answer, encoding="utf-8") as handle:
print("\n".join(suites(json.load(handle))))
return 0
for stale in (args.out, args.optional_out):
if stale and os.path.exists(stale):
os.remove(stale)
try:
with open(args.answer, encoding="utf-8") as handle:
answer = json.load(handle)
except (OSError, ValueError) as error:
return run_all(f"unreadable answer ({error})")
if not isinstance(answer, dict):
return run_all("the answer is not an object")
code, lines = lane_filter(answer, args.lane)
if code in (NARROWED, NOTHING):
with open(args.out, "w", encoding="utf-8", newline="\n") as handle:
handle.write("".join(line + "\n" for line in lines))
if args.optional_out:
optional = conditional_suites(answer, args.lane) if code == NARROWED else []
with open(args.optional_out, "w", encoding="utf-8", newline="\n") as handle:
handle.write("".join(suite + "\n" for suite in optional))
return code
if __name__ == "__main__":
sys.exit(main())
+100 -3
View File
@@ -10,7 +10,7 @@ cd "$ROOT"
usage() {
cat <<'EOF'
Usage: scripts/test.sh [--suites LIST] [--arch ARCH] [VAR=VAL ...]
Usage: scripts/test.sh [--suites LIST | --tsan | --contracts-only] [--arch ARCH] [VAR=VAL ...]
The canonical test entry: identical in local CI, PR CI, dry run and release.
DEFAULT (no --suites) is exactly what CI runs: static contract checks
@@ -29,6 +29,10 @@ Modes:
--tsan ThreadSanitizer leg (data-race gate): builds and runs the
widened TSan runner via make test-tsan — the same leg CI's
tsan jobs and the compose test-tsan service run.
--contracts-only
Only the static contract steps (Step 0*) of the default leg:
no compiler, no build, no suites. PR CI runs this for
docs-only and non-product changes (tiers T0a, T0b).
Options:
--arch ARCH Force target arch (arm64 | x86_64), e.g. under Rosetta.
@@ -50,6 +54,11 @@ Environment:
CBM_RUN_HANG_TEST=1 Opt-in C++ index-hang regression (#410, needs prod).
CBM_NO_CCACHE=1 Disable the content-verified compiler cache.
CBM_TEST_SHARD/_LEG Set by CI's sharded legs; leave unset locally.
CBM_TEST_SELECTION_DIR PR CI's test selection (smart CI): a directory with
test-only.txt (suite / suite:test lines) and
optional-suites.txt. Narrows the parallel suite step
only; contract steps and CBM_TEST_SEQUENTIAL=1 runs
keep running everything.
Examples:
scripts/test.sh # the full venue leg (what CI runs)
@@ -64,11 +73,13 @@ EOF
# silently swallowed — agents must know exactly what a run will do.
SUITES=""
TSAN=0
CONTRACTS_ONLY=0
prev_arg=""
for arg in "$@"; do
case "$arg" in
-h|--help) usage; exit 0 ;;
--tsan) :;;
--contracts-only) :;;
--suites) :;; # next arg is the value, handled below
--suites=*) SUITES="${arg#--suites=}" ;;
--arch) :;; # next arg is the value, handled below
@@ -96,6 +107,7 @@ done
for arg in "$@"; do
case "$arg" in
--tsan) TSAN=1 ;;
--contracts-only) CONTRACTS_ONLY=1 ;;
arm64|x86_64)
if [[ "${prev_arg2:-}" == "--arch" ]]; then
export CBM_ARCH="$arg"
@@ -121,6 +133,10 @@ if [ "$TSAN" -eq 1 ] && [ -n "$SUITES" ]; then
echo "test.sh: --tsan and --suites are separate modes (the TSan leg has its own suite set). Please consult --help." >&2
exit 2
fi
if [ "$CONTRACTS_ONLY" -eq 1 ] && { [ "$TSAN" -eq 1 ] || [ -n "$SUITES" ]; }; then
echo "test.sh: --contracts-only is its own mode (no build, no suites). Please consult --help." >&2
exit 2
fi
prev_arg=""
# Also support --arch=value
@@ -152,7 +168,7 @@ for arg in "$@"; do
CC=*|CXX=*) export "${arg}" ;;
--arch|--arch=*) ;; # already handled
arm64|x86_64) ;; # already handled
--tsan) ;; # already handled
--tsan|--contracts-only) ;; # already handled
--suites|--suites=*) ;; # already handled (value skipped via prev_arg below)
BUILD_DIR=*) BUILD_DIR="${arg#BUILD_DIR=}"; MAKE_ARGS+=("$arg") ;;
SANITIZE=*)
@@ -338,6 +354,45 @@ bash "$ROOT/tests/test_version_metadata_contract.sh"
echo "=== Step 0y: VM leg verdict contract ==="
bash "$ROOT/tests/test_vm_verdict_contract.sh"
echo "=== Step 0y2: isolated Windows venue contract ==="
bash "$ROOT/tests/test_vm_isolation_contract.sh"
echo "=== Step 0y3: setup scripts install through the installers' verified path ==="
bash "$ROOT/tests/test_setup_scripts_contract.sh"
# Step 0z: PR CI runs only the lanes scripts/ci/select-lanes.sh selects, so a
# wrong selection is a silent gate loss. The decision table, and the replay of
# every September 2026 PR push and real failure against it.
echo "=== Step 0z: PR lane selector contract ==="
bash "$ROOT/tests/test_select_lanes.sh"
echo "=== Step 0z2: lane selector history replay ==="
bash "$ROOT/scripts/test-impact/replay-selector.sh"
echo "=== Step 0z3: lane-aware aggregate gates (ci-ok, shard union) ==="
bash "$ROOT/tests/test_lane_gate_contract.sh"
echo "=== Step 0z4: lane wiring (PRs select, dry run and release run all) ==="
bash "$ROOT/tests/test_lane_wiring_contract.sh"
echo "=== Step 0z5: test-impact shadow prediction contract ==="
bash "$ROOT/tests/test_test_impact_predict.sh"
# Step 0z6: the per-test coverage map must never report a test as executing
# less than it did: every case where the builder cannot know (a killed forked
# child, an unreadable profile, a failed suite) has to come out `incomplete`.
echo "=== Step 0z6: per-test coverage map builder contract ==="
bash "$ROOT/tests/test_coverage_map.sh"
# Step 0z7: a push's incremental coverage map carries the suites it did not
# re-run onto the new image's function table; a wrong remap would hand the PR
# selection a map that claims tests ran functions they never touched.
echo "=== Step 0z7: incremental coverage map merge contract ==="
bash "$ROOT/tests/test_coverage_merge.sh"
# Step 0z8: the one place a test-impact answer becomes what PR CI runs. Only a
# well-formed, self-consistent selection may narrow; everything else runs all.
echo "=== Step 0z8: test selection (answer -> runner filter) contract ==="
bash "$ROOT/tests/test_test_impact_selection.sh"
if [ "$CONTRACTS_ONLY" -eq 1 ]; then
echo "=== test.sh: contracts-only — every contract step passed ==="
exit 0
fi
# Verify compiler supports target arch
verify_compiler "$CC"
@@ -351,12 +406,54 @@ BUILD_DIR="$BUILD_DIR" scripts/clean.sh
# CBM_TEST_SEQUENTIAL=1 restores the single-process runner.
make -j"$NPROC" -f Makefile.cbm "$BUILD_DIR/test-runner" ${MAKE_ARGS[@]+"${MAKE_ARGS[@]}"}
assert_test_runner_build_config "$BUILD_DIR/test-runner" "$EXPECTED_SANITIZED"
# Step 2b: per-test selection process regression. CBM_TEST_ONLY must run exactly
# the tests it names and fail on a token that matches nothing; a suite cannot
# assert that about the runner executing it. Runs against the runner just built
# and BEFORE the suites: it takes seconds, and a selection that silently drops
# tests is a property of the harness every later result depends on.
echo "=== Step 2b: per-test selection regression (CBM_TEST_ONLY) ==="
CBM_TEST_RUNNER="$ROOT/$BUILD_DIR/test-runner" bash "$ROOT/tests/test_harness_test_only.sh"
# Step 2c: PR CI's test selection (smart CI) through the parallel harness — the
# selected suites only, unknown suites and tests fail, conditional ones drop.
echo "=== Step 2c: parallel-harness test selection regression ==="
CBM_TEST_RUNNER="$ROOT/$BUILD_DIR/test-runner" bash "$ROOT/tests/test_harness_selection.sh"
# PR CI's test selection (smart CI, the select-tests job) narrows the parallel
# suite step and nothing else (scripts/run-tests-parallel.sh). A selection that
# was promised but is missing is a plumbing fault: fail, never run silently.
SELECTION_ENV=()
if [ -n "${CBM_TEST_SELECTION_DIR:-}" ]; then
selection_dir="$CBM_TEST_SELECTION_DIR"
if command -v cygpath > /dev/null 2>&1; then
selection_dir="$(cygpath -u "$selection_dir")"
fi
if [ ! -s "$selection_dir/test-only.txt" ]; then
echo "FAIL: CBM_TEST_SELECTION_DIR=$CBM_TEST_SELECTION_DIR holds no test-only.txt" >&2
exit 1
fi
SELECTION_ENV=(CBM_TEST_SELECTION_FILE="$selection_dir/test-only.txt")
if [ -f "$selection_dir/optional-suites.txt" ]; then
SELECTION_ENV+=(CBM_TEST_SELECTION_OPTIONAL="$selection_dir/optional-suites.txt")
fi
if [ "${CBM_TEST_SEQUENTIAL:-0}" = "1" ]; then
echo "test.sh: CBM_TEST_SEQUENTIAL=1 runs every test; the selection narrows the parallel harness only"
fi
fi
if [ "${CBM_TEST_SEQUENTIAL:-0}" = "1" ]; then
make -f Makefile.cbm test ${MAKE_ARGS[@]+"${MAKE_ARGS[@]}"}
else
make -f Makefile.cbm test-par ${MAKE_ARGS[@]+"${MAKE_ARGS[@]}"}
env ${SELECTION_ENV[@]+"${SELECTION_ENV[@]}"} \
make -f Makefile.cbm test-par ${MAKE_ARGS[@]+"${MAKE_ARGS[@]}"}
fi
# Step 3a: the runner must ignore an inherited git repository environment
# (#2003: the pre-commit hook exports GIT_DIR, which overrides `git -C` and
# pointed fixture git commands at the committer's real repository).
echo "=== Step 3a: inherited git environment isolation (#2003) ==="
bash "$ROOT/tests/test_git_env_isolation_contract.sh" "$ROOT/$BUILD_DIR/test-runner"
# Step 4: C++ large-TU index-hang regression guard (#410). Runs the PROD binary
# in a subprocess with a wall-clock timeout — a hang must fail, not block the run.
# Opt-in via CBM_RUN_HANG_TEST=1 (it needs the prod binary, which the ASan unit
+6 -6
View File
@@ -5,7 +5,7 @@ c5cfb43042b6b72045f4ba997834d0a7786d2793d91680868b5815b39f14fc78 internal/cbm/v
b29c1c9fb7cc82f58c84b376df1297d6e2737a1d655fd356db0859e3c29c2fea internal/cbm/vendored/common/tree_sitter/alloc.h
31e60a1bff6f715afacce03b5b70efe42b58371b4f9595dd4af52a577ff9608c internal/cbm/vendored/common/tree_sitter/array.h
180b893c8734778fd32f372dfbc27bd6ad1cd2221f26150b31256ff6716320d2 internal/cbm/vendored/common/tree_sitter/parser.h
6cb436878b820c14e97c7ea80fbf8c91f037c140c3bce283c6eedd724dbebd99 internal/cbm/vendored/grammars/MANIFEST.md
44bade69b14ac382b08835965eeec5f0fd6e54ccb7a80da21c0161128a10503f internal/cbm/vendored/grammars/MANIFEST.md
ad8425038de519f8c4e3e9339feebf99dfad8a6002dcf79227d91402780a32cc internal/cbm/vendored/grammars/ada/LICENSE
02805ec13939b749c891567be36bf024b09034e04c80683a1ae667272458d549 internal/cbm/vendored/grammars/ada/parser.c
115a75d000bef9c70c4de7dfbb7f2a80b90cbb14ba265056dc7abb1ef3b9b6db internal/cbm/vendored/grammars/ada/tree_sitter/alloc.h
@@ -89,11 +89,11 @@ c4b9482069f61a2a26a590181baa34f08eb6dcc57a138def578e6f9720c35ea5 internal/cbm/v
02d139d44303b0b0b85a899839dbc1346c478e7318d1f2971fad2b38fd57cb73 internal/cbm/vendored/grammars/c/tree_sitter/array.h
c4b9482069f61a2a26a590181baa34f08eb6dcc57a138def578e6f9720c35ea5 internal/cbm/vendored/grammars/c/tree_sitter/parser.h
778fb7d63b8c1844da315648c02f325c4713a6f4a5d19644fd413421422776d3 internal/cbm/vendored/grammars/c_sharp/LICENSE
720746e4d6bdbe1ba76f83a4c157a8ec3ce20c30a6b4744f30ca3993e55b8469 internal/cbm/vendored/grammars/c_sharp/parser.c
83dad602517523bc185e195d9446a92f73786364661bc87b564e5206ca52d6be internal/cbm/vendored/grammars/c_sharp/scanner.c
115a75d000bef9c70c4de7dfbb7f2a80b90cbb14ba265056dc7abb1ef3b9b6db internal/cbm/vendored/grammars/c_sharp/tree_sitter/alloc.h
8c814560c31d0157013bc3399a9282fc5049d06790a36f55115373d6c3abe16b internal/cbm/vendored/grammars/c_sharp/tree_sitter/array.h
c4b9482069f61a2a26a590181baa34f08eb6dcc57a138def578e6f9720c35ea5 internal/cbm/vendored/grammars/c_sharp/tree_sitter/parser.h
0a2651e49de7c7237c535c41a132a7ec0424da79d12f197f1df3edd7d6ea4427 internal/cbm/vendored/grammars/c_sharp/parser.c
00920daa0fdf3050d441475e8effe996b60ee8acc1bdbcbbc4c83d56a6987705 internal/cbm/vendored/grammars/c_sharp/scanner.c
b29c1c9fb7cc82f58c84b376df1297d6e2737a1d655fd356db0859e3c29c2fea internal/cbm/vendored/grammars/c_sharp/tree_sitter/alloc.h
31e60a1bff6f715afacce03b5b70efe42b58371b4f9595dd4af52a577ff9608c internal/cbm/vendored/grammars/c_sharp/tree_sitter/array.h
180b893c8734778fd32f372dfbc27bd6ad1cd2221f26150b31256ff6716320d2 internal/cbm/vendored/grammars/c_sharp/tree_sitter/parser.h
108bfb047e0d45962dcadedfce730c86c278bc2a67f6939df405a706bec27e20 internal/cbm/vendored/grammars/cairo/LICENSE
1631b184694158672da430675d3c3990c6db48eafb9e888b769866dc9be0092b internal/cbm/vendored/grammars/cairo/parser.c
681bd8135f2381810e4c15d6ff103622d1150e5ab9474d66b3b33a9c9a83fde2 internal/cbm/vendored/grammars/cairo/scanner.c
+162 -47
View File
@@ -1504,7 +1504,11 @@ static const char skill_content[] =
"| Text search | `search_code` or Grep |\n"
"\n"
"## Exploration Workflow\n"
"1. `list_projects` — check if project is indexed\n"
/* #1690: a guessed repo or folder name misses the project whenever the
* checkout directory differs from it; the root_path is what identifies it. */
"1. `list_projects` — pick the project whose `root_path` contains your working directory; "
"pass its `name` (or the absolute directory itself) as `project`, never a repo or folder "
"name\n"
"2. `get_graph_schema` — understand node/edge types\n"
"3. `search_graph(label=\"Function\", name_pattern=\".*Pattern.*\")` — find code\n"
"4. `get_code_snippet(qualified_name=\"project.path.FuncName\")` — read source\n"
@@ -1560,7 +1564,7 @@ static const char skill_content[] =
"`check_index_coverage`, `manage_adr`, `ingest_traces`\n"
"\n"
"## Edge Types\n"
"CALLS, HTTP_CALLS, ASYNC_CALLS, DATA_FLOWS, IMPORTS, DEFINES, DEFINES_METHOD,\n"
"CALLS, HTTP_CALLS, ASYNC_CALLS, SPAWNS, DATA_FLOWS, IMPORTS, DEFINES, DEFINES_METHOD,\n"
"HANDLES, IMPLEMENTS, OVERRIDE, USAGE, CALL_REFERENCE, CONFIGURES, REFERENCES_FILE,\n"
"FILE_CHANGES_WITH, SIMILAR_TO, SEMANTICALLY_RELATED, CONTAINS_FILE, CONTAINS_FOLDER,\n"
"CONTAINS_PACKAGE\n"
@@ -2774,10 +2778,41 @@ static void cbm_kiro_home_dir(const char *home_dir, char *out, size_t out_sz) {
cbm_env_home_dir("KIRO_HOME", home_dir, ".kiro", out, out_sz);
}
static void cbm_hermes_home_dir(const char *home_dir, char *out, size_t out_sz) {
cbm_env_home_dir("HERMES_HOME", home_dir, ".hermes", out, out_sz);
/* #1180: native Windows Hermes -- the install.ps1 CLI, the MSIX package and
* the Desktop app -- defaults HERMES_HOME to %LOCALAPPDATA%\hermes and keeps a
* legacy ~/.hermes only while the new directory is absent (hermes-agent
* website/docs/user-guide/windows-native.md "Data layout";
* apps/desktop/electron/data-paths.mjs resolveDesktopHermesHome). Probing
* only ~/.hermes missed those installs and aimed config.yaml at a home Hermes
* never reads. Other platforms use ~/.hermes. */
static void cbm_hermes_home_dir_for(const char *home_dir, bool windows, char *out, size_t out_sz) {
const char *fallback = ".hermes";
if (windows) {
char native[CLI_BUF_1K];
char legacy[CLI_BUF_1K];
snprintf(native, sizeof(native), "%s/AppData/Local/hermes", home_dir);
snprintf(legacy, sizeof(legacy), "%s/.hermes", home_dir);
if (dir_exists(native) || !dir_exists(legacy)) {
fallback = "AppData/Local/hermes";
}
}
cbm_env_home_dir("HERMES_HOME", home_dir, fallback, out, out_sz);
}
static void cbm_hermes_home_dir(const char *home_dir, char *out, size_t out_sz) {
#ifdef _WIN32
cbm_hermes_home_dir_for(home_dir, true, out, out_sz);
#else
cbm_hermes_home_dir_for(home_dir, false, out, out_sz);
#endif
}
#ifdef CBM_CLI_ENABLE_TEST_API
void cbm_hermes_home_dir_for_testing(const char *home_dir, bool windows, char *out, size_t out_sz) {
cbm_hermes_home_dir_for(home_dir, windows, out, out_sz);
}
#endif
static void cbm_qwen_home_dir(const char *home_dir, char *out, size_t out_sz) {
cbm_env_home_dir("QWEN_HOME", home_dir, ".qwen", out, out_sz);
}
@@ -3079,6 +3114,53 @@ static int cbm_resolve_released_hook_command(const char *script_name, char *out,
return written > 0 && (size_t)written < out_sz ? CLI_OK : CLI_ERR;
}
/* #1180: a bare ~/.claude directory is not a Claude Code install -- other
* tools create it -- and treating it as one wrote hooks, skills and MCP
* entries for a client the user never installed. Claude Code itself leaves
* its user config .claude.json (under CLAUDE_CONFIG_DIR when set), its
* settings.json, or the claude CLI. */
static bool cbm_claude_code_detected(const char *home_dir) {
char dir[CLI_BUF_1K];
char path[CLI_BUF_1K];
cbm_claude_config_dir(home_dir, dir, sizeof(dir));
int written = snprintf(path, sizeof(path), "%s/settings.json", dir);
if (dir[0] && written > 0 && (size_t)written < sizeof(path) && cbm_file_exists(path)) {
return true;
}
cbm_claude_user_root(home_dir, dir, sizeof(dir));
written = snprintf(path, sizeof(path), "%s/.claude.json", dir);
if (dir[0] && written > 0 && (size_t)written < sizeof(path) && cbm_file_exists(path)) {
return true;
}
return cbm_agent_cli_exists("claude", home_dir);
}
/* OpenCode keeps its data (auth, logs, sessions; the global config itself on
* older installs) in ~/.local/share/opencode on every OS --
* %USERPROFILE%\.local\share\opencode on Windows (opencode.ai/docs/
* troubleshooting, "Storage"). The CLI and the Desktop app's bundled
* opencode-cli server both create it, so it identifies an install that has
* no config file, no ~/.config/opencode and no `opencode` on PATH
* (#1167, #1180). */
static bool cbm_opencode_detected(const char *home_dir) {
char path[CLI_BUF_1K];
cbm_opencode_config_path(home_dir, path, sizeof(path));
if (cbm_file_exists(path) || cbm_agent_cli_exists("opencode", home_dir)) {
return true;
}
snprintf(path, sizeof(path), "%s/.config/opencode", home_dir);
if (dir_exists(path)) {
return true;
}
char env_buf[CLI_BUF_1K];
const char *config_dir = cbm_safe_getenv("OPENCODE_CONFIG_DIR", env_buf, sizeof(env_buf), NULL);
if (config_dir && config_dir[0] && dir_exists(config_dir)) {
return true;
}
snprintf(path, sizeof(path), "%s/.local/share/opencode", home_dir);
return dir_exists(path);
}
cbm_detected_agents_t cbm_detect_agents(const char *home_dir) {
cbm_detected_agents_t agents;
memset(&agents, 0, sizeof(agents));
@@ -3088,8 +3170,7 @@ cbm_detected_agents_t cbm_detect_agents(const char *home_dir) {
char path[CLI_BUF_1K];
cbm_claude_config_dir(home_dir, path, sizeof(path));
agents.claude_code = path[0] != '\0' && dir_exists(path);
agents.claude_code = cbm_claude_code_detected(home_dir);
cbm_codex_config_dir(home_dir, path, sizeof(path));
agents.codex = path[0] != '\0' && dir_exists(path);
@@ -3103,18 +3184,7 @@ cbm_detected_agents_t cbm_detect_agents(const char *home_dir) {
cbm_zed_config_dir(home_dir, path, sizeof(path));
agents.zed = dir_exists(path);
cbm_opencode_config_path(home_dir, path, sizeof(path));
agents.opencode = cbm_file_exists(path) || cbm_agent_cli_exists("opencode", home_dir);
if (!agents.opencode) {
snprintf(path, sizeof(path), "%s/.config/opencode", home_dir);
agents.opencode = dir_exists(path);
}
if (!agents.opencode) {
char env_buf[CLI_BUF_1K];
const char *config_dir =
cbm_safe_getenv("OPENCODE_CONFIG_DIR", env_buf, sizeof(env_buf), NULL);
agents.opencode = config_dir && config_dir[0] && dir_exists(config_dir);
}
agents.opencode = cbm_opencode_detected(home_dir);
agents.aider = cbm_agent_cli_exists("aider", home_dir);
@@ -3274,6 +3344,9 @@ static const char agent_instructions_content[] =
"### Session resets and subagents\n"
"- At session start or after compaction, confirm the nearest graph project and generation with "
"`list_projects` or `index_status`, then choose Scout, Verify, or Auditor.\n"
"- The graph project is the `list_projects` entry whose `root_path` contains your working "
"directory; pass its `name` (or the absolute directory itself) as `project`, never a repo or "
"folder name.\n"
"- Before spawning a subagent, query the graph and coverage in the parent. Pass the tier, "
"project, generation/freshness, bounded scope, queries and pagination state, qualified "
"symbols, "
@@ -3679,6 +3752,8 @@ static const char aider_instructions_content[] =
"First use in a repo: codebase-memory-mcp cli index_repository '{\"repo_path\":\"<abs "
"path>\"}'\n"
"List indexed projects (for <name>): codebase-memory-mcp cli list_projects '{}'\n"
"<name> is the entry whose root_path contains your working directory; pass it (or the "
"absolute directory itself), never a repo or folder name.\n"
"\n"
"## When to fall back to grep/glob\n"
"- Searching for string literals, error messages, config values\n"
@@ -3841,8 +3916,11 @@ int cbm_upsert_codex_mcp(const char *binary_path, const char *config_path) {
}
int cbm_remove_codex_mcp(const char *config_path) {
/* #2228: only our table goes; foreign tables found between the markers
* (Codex Desktop appends below a block that ends the file) stay. */
if (!config_path ||
cbm_toml_remove_managed_block(config_path, CODEX_MCP_BEGIN, CODEX_MCP_END) != 0) {
cbm_toml_remove_managed_block_owned(config_path, CODEX_MCP_BEGIN, CODEX_MCP_END,
CODEX_CMM_SECTION "\n") != 0) {
return CLI_ERR;
}
return cbm_remove_codex_legacy_mcp(config_path) >= 0 ? CLI_OK : CLI_ERR;
@@ -4466,7 +4544,8 @@ static int cbm_upsert_grok_mcp(const char *binary_path, const char *config_path)
static int cbm_remove_grok_mcp_owned(const char *binary_path, const char *config_path) {
(void)binary_path;
if (!config_path ||
cbm_toml_remove_managed_block(config_path, GROK_MCP_BEGIN, GROK_MCP_END) != 0) {
cbm_toml_remove_managed_block_owned(config_path, GROK_MCP_BEGIN, GROK_MCP_END,
GROK_CMM_SECTION "\n") != 0) {
return CLI_ERR;
}
return cbm_remove_grok_legacy_mcp(config_path) >= 0 ? CLI_OK : CLI_ERR;
@@ -5032,7 +5111,8 @@ static int cbm_upsert_kimi_context_hook(const char *config_path, const char *bin
}
static int cbm_remove_kimi_context_hook(const char *config_path) {
return cbm_toml_remove_managed_block(config_path, KIMI_HOOK_BEGIN, KIMI_HOOK_END) == 0
return cbm_toml_remove_managed_block_owned(config_path, KIMI_HOOK_BEGIN, KIMI_HOOK_END,
"[[hooks]]\n") == 0
? CLI_OK
: CLI_ERR;
}
@@ -7450,6 +7530,11 @@ bool cbm_config_watcher_enabled(cbm_config_t *cfg) {
return cbm_config_get_bool(cfg, CBM_CONFIG_WATCHER_ENABLED, true);
}
/* Opt-in tree polling of non-git roots (#1948); see cli.h. */
bool cbm_config_watch_non_git(cbm_config_t *cfg) {
return cbm_config_get_bool(cfg, CBM_CONFIG_WATCH_NON_GIT, false);
}
bool cbm_config_load_index_policy(cbm_config_t *cfg, cbm_index_resource_policy_t *policy,
char *error, size_t error_size) {
if (!cfg || !policy) {
@@ -7490,6 +7575,8 @@ static const config_key_def_t CONFIG_KEYS[] = {
{CBM_CONFIG_AUTO_WATCH, "true", "Register background git watcher on session connect"},
{CBM_CONFIG_WATCHER_ENABLED, "true",
"Run the background watcher thread (auto-reindex); false to disable"},
{CBM_CONFIG_WATCH_NON_GIT, "false",
"Also poll non-git project roots for changes (file-tree scan); off by default"},
{CBM_CONFIG_UI_LANG, "auto", "Pin graph UI language: en, zh, or auto"},
{CBM_CONFIG_UI_ENABLED, "false", "Serve the graph UI on a loopback HTTP port"},
{CBM_CONFIG_UI_PORT, "9749", "Port for the graph UI listener when enabled"},
@@ -12856,6 +12943,41 @@ static int cli_uninstall_activate(void *opaque) {
return CLI_OK;
}
/* A generic auto-answer is not consent to erase project indexes. An explicit
* --delete-indexes takes precedence even over --no; otherwise only a separate
* interactive answer can select deletion. This helper decides and reports,
* but all deletion remains in the final guarded uninstall activation. */
static bool uninstall_decide_index_deletion(const char *home, bool requested, bool dry_run) {
int index_count = count_db_indexes(home);
if (index_count <= 0) {
return false;
}
printf("\nFound %d index(es):\n", index_count);
cbm_list_indexes(home);
bool delete_indexes = requested;
if (!requested) {
bool can_ask = g_auto_answer == 0;
#ifdef _WIN32
can_ask = can_ask && _isatty(_fileno(stdin));
#else
can_ask = can_ask && isatty(fileno(stdin));
#endif
delete_indexes = can_ask && prompt_yn("Delete these indexes? (default: no)");
}
if (!delete_indexes) {
const char *cache_dir = get_cache_dir(home);
printf("Indexes kept in %s. To remove them, uninstall with --delete-indexes "
"or delete the project .db files there.\n",
cache_dir ? cache_dir : "the cache directory");
return false;
}
if (dry_run) {
printf("(dry-run — indexes would be deleted)\n");
return false;
}
return true;
}
int cbm_cmd_uninstall(int argc, char **argv) {
/* `uninstall --help` used to UNINSTALL.
*
@@ -12869,20 +12991,25 @@ int cbm_cmd_uninstall(int argc, char **argv) {
* cannot auto-confirm the destruction we are trying to prevent. */
for (int i = 0; i < argc; i++) {
if (argv && argv[i] && (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0)) {
printf("Usage: codebase-memory-mcp uninstall [options]\n\n"
"Removes the codebase-memory-mcp binary, its agent configurations and,\n"
"with confirmation, its indexes. THIS IS DESTRUCTIVE.\n\n"
"Options:\n"
" --dry-run Show what would be removed, change nothing\n"
" --dir=PATH Uninstall from a custom install directory\n"
" -y, --yes Do not prompt for confirmation\n"
" -h, --help Show this help and exit\n\n"
"Run with --dry-run first if you are unsure.\n");
printf(
"Usage: codebase-memory-mcp uninstall [options]\n\n"
"Removes the codebase-memory-mcp binary and its agent configurations.\n"
"THIS IS DESTRUCTIVE. Project indexes are kept by default. An interactive\n"
"terminal is asked separately; the default answer is to keep them.\n\n"
"Options:\n"
" --dry-run Show what would be removed, change nothing\n"
" --dir=PATH Uninstall from a custom install directory\n"
" --delete-indexes Also delete every project index (overrides --no)\n"
" -y, --yes Do not prompt; indexes are kept unless explicitly deleted\n"
" -n, --no Decline prompts; --delete-indexes still takes precedence\n"
" -h, --help Show this help and exit\n\n"
"Run with --dry-run first if you are unsure.\n");
return CLI_OK;
}
}
parse_auto_answer(argc, argv);
bool dry_run = false;
bool delete_indexes_requested = false;
/* An install into a custom --dir must be removable from that same dir:
* without this, anyone who installed outside ~/.local/bin has no supported
* uninstall path at all. Mirrors cbm_cmd_install's parsing. */
@@ -12895,6 +13022,8 @@ int cbm_cmd_uninstall(int argc, char **argv) {
}
if (strcmp(argv[i], "--dry-run") == 0) {
dry_run = true;
} else if (strcmp(argv[i], "--delete-indexes") == 0) {
delete_indexes_requested = true;
} else if (strncmp(argv[i], "--dir=", SLEN("--dir=")) == 0) {
requested_bin_dir = argv[i] + SLEN("--dir=");
if (!requested_bin_dir[0]) {
@@ -12926,23 +13055,9 @@ int cbm_cmd_uninstall(int argc, char **argv) {
agent_uninstall_failures_reset();
cbm_detected_agents_t agents = cbm_detect_agents(home);
/* Confirm index removal outside the startup lock, but defer the mutation
* until the final guarded activation. Dry-run never removes indexes. */
bool delete_indexes = false;
int index_count = count_db_indexes(home);
if (index_count > 0) {
printf("\nFound %d index(es):\n", index_count);
cbm_list_indexes(home);
if (prompt_yn("Delete these indexes?")) {
if (dry_run) {
printf("(dry-run — indexes would be deleted)\n");
} else {
delete_indexes = true;
}
} else {
printf("Indexes kept.\n");
}
}
/* Decide outside the startup lock; mutate only in the final guarded
* activation. Dry-run never authorizes actual index removal. */
bool delete_indexes = uninstall_decide_index_deletion(home, delete_indexes_requested, dry_run);
char bin_path_storage[CLI_BUF_1K];
const char *bin_path = bin_path_storage;
+16 -3
View File
@@ -182,11 +182,11 @@ int cbm_remove_zed_mcp_owned(const char *binary_path, const char *config_path);
/* Detected coding agents on the system. */
typedef struct {
bool claude_code; /* ~/.claude/ exists */
bool claude_code; /* settings.json, .claude.json, or claude CLI exists */
bool codex; /* $CODEX_HOME or ~/.codex exists */
bool gemini; /* Gemini settings or executable exists */
bool zed; /* platform-specific Zed config dir exists */
bool opencode; /* opencode on PATH or config exists */
bool opencode; /* opencode on PATH, config, or data dir exists */
bool antigravity; /* Antigravity CLI config or executable exists */
bool aider; /* aider on PATH */
bool kilocode; /* KiloCode globalStorage dir exists */
@@ -197,7 +197,7 @@ typedef struct {
bool openclaw; /* ~/.openclaw/ exists */
bool kiro; /* ~/.kiro/ exists */
bool junie; /* ~/.junie/ exists */
bool hermes; /* ~/.hermes/ or hermes CLI exists */
bool hermes; /* Hermes home (Windows: %LOCALAPPDATA%\hermes) or CLI */
bool openhands; /* ~/.openhands/ or openhands CLI exists */
bool cline; /* ~/.cline/ or cline CLI exists */
bool warp; /* Warp footprint or oz/oz-preview/warp-cli exists */
@@ -217,6 +217,13 @@ cbm_detected_agents_t cbm_detect_agents(const char *home_dir);
/* Install or refresh every detected agent integration below home. */
int cbm_install_agent_configs(const char *home, const char *binary_path, bool force, bool dry_run);
#ifdef CBM_CLI_ENABLE_TEST_API
/* #1180: the Hermes home as resolved on Windows (`windows`) or elsewhere, so
* the %LOCALAPPDATA%\hermes default and its legacy ~/.hermes fallback are
* exercised on every host. */
void cbm_hermes_home_dir_for_testing(const char *home_dir, bool windows, char *out, size_t out_sz);
#endif
#ifdef CBM_CLI_ENABLE_TEST_API
/* #1558: client-selector vocabulary, exposed so a test can prove every token
* resolves and an unknown one is rejected. */
@@ -446,6 +453,7 @@ bool cbm_config_load_index_policy(cbm_config_t *cfg, cbm_index_resource_policy_t
#define CBM_CONFIG_AUTO_WATCH "auto_watch"
#define CBM_CONFIG_UI_LANG "ui-lang"
#define CBM_CONFIG_WATCHER_ENABLED "watcher_enabled"
#define CBM_CONFIG_WATCH_NON_GIT "watch_non_git"
/* #1558: the graph UI's loopback listener. Stored in the UI config file rather
* than the key-value store, but surfaced through `config` so it is findable. */
#define CBM_CONFIG_UI_ENABLED "ui_enabled"
@@ -460,6 +468,11 @@ bool cbm_config_load_index_policy(cbm_config_t *cfg, cbm_index_resource_policy_t
* the watcher. */
bool cbm_config_watcher_enabled(cbm_config_t *cfg);
/* Whether the watcher also polls NON-GIT project roots (default false, #1948).
* Non-git roots are otherwise never refreshed after their first index. Read
* once at daemon startup, like watcher_enabled. NULL-safe (NULL → false). */
bool cbm_config_watch_non_git(cbm_config_t *cfg);
/* ── Binary activation safety ─────────────────────────────────── */
typedef void *cbm_cli_activation_lock_t;
+476 -22
View File
@@ -149,6 +149,10 @@ typedef struct {
static int toml_managed_block_conflicts(const char *existing, size_t existing_len,
size_t exclude_start, size_t exclude_end, const char *block,
size_t block_len);
static int toml_managed_rewrite(const char *existing, size_t existing_len,
const toml_line_t *begin_line, const toml_line_t *end_line,
const char *begin_marker, const char *end_marker, const char *owned,
size_t owned_len, int install, toml_buffer_t *output);
static void toml_buffer_dispose(toml_buffer_t *buffer) {
if (!buffer) {
@@ -1067,15 +1071,20 @@ int cbm_toml_escape_basic_string(const char *input, char *out, size_t out_size)
return TOML_EDIT_OK;
}
static int toml_managed_body_valid(const char *body, const char *begin_marker,
const char *end_marker, size_t *body_len) {
return body && toml_bounded_length(body, TOML_EDIT_MAX_BYTES, body_len) == TOML_EDIT_OK &&
toml_text_is_safe(body, *body_len, 1) &&
!toml_contains_marker_line(body, *body_len, begin_marker, end_marker) &&
toml_validate_lexical_strings(body, *body_len) == TOML_EDIT_OK;
}
int cbm_toml_upsert_managed_block(const char *file_path, const char *begin_marker,
const char *end_marker, const char *block) {
size_t block_len = 0U;
if (!toml_valid_path(file_path) || !toml_valid_marker(begin_marker) ||
!toml_valid_marker(end_marker) || strcmp(begin_marker, end_marker) == 0 || !block ||
toml_bounded_length(block, TOML_EDIT_MAX_BYTES, &block_len) != TOML_EDIT_OK ||
!toml_text_is_safe(block, block_len, 1) ||
toml_contains_marker_line(block, block_len, begin_marker, end_marker) ||
toml_validate_lexical_strings(block, block_len) != TOML_EDIT_OK) {
!toml_valid_marker(end_marker) || strcmp(begin_marker, end_marker) == 0 ||
!toml_managed_body_valid(block, begin_marker, end_marker, &block_len)) {
return TOML_EDIT_ERR;
}
@@ -1104,23 +1113,26 @@ int cbm_toml_upsert_managed_block(const char *file_path, const char *begin_marke
}
toml_buffer_t output = {0};
size_t prefix_len = has_pair ? begin_line.start : existing_len;
if (has_pair && prefix_len == 0U && existing_len >= 3U && (unsigned char)existing[0] == 0xefU &&
(unsigned char)existing[1] == 0xbbU && (unsigned char)existing[2] == 0xbfU) {
prefix_len = 3U;
}
const char *newline = toml_newline_style(existing, existing_len);
size_t payload_start = existing_len >= 3U && (unsigned char)existing[0] == 0xefU &&
(unsigned char)existing[1] == 0xbbU &&
(unsigned char)existing[2] == 0xbfU
? 3U
: 0U;
if (toml_buffer_append(&output, existing, prefix_len) != TOML_EDIT_OK ||
(!has_pair && existing_len > payload_start && existing[existing_len - 1] != '\n' &&
toml_buffer_append_cstr(&output, newline) != TOML_EDIT_OK) ||
toml_append_managed(&output, begin_marker, end_marker, block, newline) != TOML_EDIT_OK ||
(has_pair && toml_buffer_append(&output, existing + end_line.full_end,
existing_len - end_line.full_end) != TOML_EDIT_OK)) {
int built = TOML_EDIT_OK;
if (has_pair) {
/* #2228: rebuild the span around the block, keeping what it holds that
* is not ours instead of discarding the whole span. */
built = toml_managed_rewrite(existing, existing_len, &begin_line, &end_line, begin_marker,
end_marker, block, block_len, 1, &output);
} else if (toml_buffer_append(&output, existing, existing_len) != TOML_EDIT_OK ||
(existing_len > payload_start && existing[existing_len - 1] != '\n' &&
toml_buffer_append_cstr(&output, newline) != TOML_EDIT_OK) ||
toml_append_managed(&output, begin_marker, end_marker, block, newline) !=
TOML_EDIT_OK) {
built = TOML_EDIT_ERR;
}
if (built != TOML_EDIT_OK) {
toml_buffer_dispose(&output);
free(existing);
return TOML_EDIT_ERR;
@@ -1133,10 +1145,14 @@ int cbm_toml_upsert_managed_block(const char *file_path, const char *begin_marke
return result;
}
int cbm_toml_remove_managed_block(const char *file_path, const char *begin_marker,
const char *end_marker) {
/* owned == NULL removes the whole span (the historical contract); otherwise
* only the tables it declares go and the rest of the span stays (#2228). */
static int toml_remove_managed(const char *file_path, const char *begin_marker,
const char *end_marker, const char *owned) {
size_t owned_len = 0U;
if (!toml_valid_path(file_path) || !toml_valid_marker(begin_marker) ||
!toml_valid_marker(end_marker) || strcmp(begin_marker, end_marker) == 0) {
!toml_valid_marker(end_marker) || strcmp(begin_marker, end_marker) == 0 ||
(owned && !toml_managed_body_valid(owned, begin_marker, end_marker, &owned_len))) {
return TOML_EDIT_ERR;
}
char *existing = NULL;
@@ -1187,9 +1203,14 @@ int cbm_toml_remove_managed_block(const char *file_path, const char *begin_marke
(unsigned char)existing[1] == 0xbbU && (unsigned char)existing[2] == 0xbfU) {
prefix_len = 3U;
}
if (toml_buffer_append(&output, existing, prefix_len) != TOML_EDIT_OK ||
toml_buffer_append(&output, existing + end_line.full_end,
existing_len - end_line.full_end) != TOML_EDIT_OK) {
int built = owned ? toml_managed_rewrite(existing, existing_len, &begin_line, &end_line,
begin_marker, end_marker, owned, owned_len, 0, &output)
: toml_buffer_append(&output, existing, prefix_len) == TOML_EDIT_OK &&
toml_buffer_append(&output, existing + end_line.full_end,
existing_len - end_line.full_end) == TOML_EDIT_OK
? TOML_EDIT_OK
: TOML_EDIT_ERR;
if (built != TOML_EDIT_OK) {
toml_buffer_dispose(&output);
free(existing);
return TOML_EDIT_ERR;
@@ -1201,6 +1222,17 @@ int cbm_toml_remove_managed_block(const char *file_path, const char *begin_marke
return result;
}
int cbm_toml_remove_managed_block(const char *file_path, const char *begin_marker,
const char *end_marker) {
return toml_remove_managed(file_path, begin_marker, end_marker, NULL);
}
int cbm_toml_remove_managed_block_owned(const char *file_path, const char *begin_marker,
const char *end_marker, const char *owned_tables) {
return owned_tables ? toml_remove_managed(file_path, begin_marker, end_marker, owned_tables)
: TOML_EDIT_ERR;
}
static int toml_hex_digit(unsigned char ch) {
if (ch >= '0' && ch <= '9') {
return ch - '0';
@@ -1845,6 +1877,428 @@ static int toml_managed_block_conflicts(const char *existing, size_t existing_le
return multiline_state == TOML_STRING_NONE ? TOML_EDIT_OK : TOML_EDIT_ERR;
}
/* ── #2228: content found between the managed markers ─────────────────
*
* The span between our markers does not only hold what we wrote. A client
* that appends tables at the end of the file (Codex Desktop does) puts them
* INSIDE the markers whenever our closing marker is the last line, and users
* add keys to our own table. Rewriting or removing the whole span deleted
* all of that.
*
* The span is read as TOML sections -- the lines before its first header
* (the lead) and one section per header -- and each is classified against
* the tables the block declares:
* owned a declared table; the k-th [[element]] of a declared array.
* Keys the block writes are ours; every other key, comment and
* blank line in it is the user's and stays in the table.
* attached a sub-table of a declared table: part of our entry, kept with
* it on install and removed with it on uninstall.
* foreign anything else: moved byte for byte, in order, to just after
* the closing marker (install) or left in place (removal).
* lead moved above the opening marker, where it keeps the table it
* belongs to. A block that itself starts with bare keys owns the
* lead, which is then replaced exactly as before.
* Whatever cannot be attributed safely is refused and the file is left
* untouched: a second copy of a declared table, an undeclared [[element]]
* of a declared array, a child of a declared array element, an unparsable
* line in an owned table, a user key colliding with one the block writes, a
* move that would hand the keys below the block to another table, or a
* result whose tables would conflict with the block. */
#define TOML_MANAGED_MAX_TABLES 16U
enum {
TOML_SPAN_LEAD = -1,
TOML_SPAN_FOREIGN = 0,
TOML_SPAN_OWNED = 1,
TOML_SPAN_ATTACHED = 2,
};
typedef struct {
size_t start;
size_t body;
size_t end;
toml_header_t header;
} toml_section_t;
typedef struct {
const char *data;
size_t lead_end;
size_t span_end;
const char *owned;
size_t owned_len;
size_t owned_first;
} toml_managed_span_t;
/* End of the section whose body starts at pos: the next header line outside
* a multiline string, or len. */
static int toml_section_end(const char *data, size_t pos, size_t len, size_t *end) {
size_t cursor = pos;
toml_line_t line;
int multiline_state = TOML_STRING_NONE;
while (toml_next_line(data, len, &cursor, &line)) {
if (multiline_state == TOML_STRING_NONE) {
toml_header_t header;
if (toml_parse_header(data, &line, "", &header) != TOML_EDIT_OK) {
return TOML_EDIT_ERR;
}
int present = header.present;
toml_header_dispose(&header);
if (present) {
*end = line.start;
return TOML_EDIT_OK;
}
}
if (toml_scan_line_strings(data, &line, &multiline_state) != TOML_EDIT_OK) {
return TOML_EDIT_ERR;
}
}
*end = len;
return multiline_state == TOML_STRING_NONE ? TOML_EDIT_OK : TOML_EDIT_ERR;
}
/* The section whose header line starts at pos. */
static int toml_section_at(const char *data, size_t pos, size_t len, toml_section_t *section) {
memset(section, 0, sizeof(*section));
section->start = pos;
size_t cursor = pos;
toml_line_t line;
if (!toml_next_line(data, len, &cursor, &line) ||
toml_parse_header(data, &line, "", &section->header) != TOML_EDIT_OK ||
!section->header.present ||
toml_section_end(data, line.full_end, len, &section->end) != TOML_EDIT_OK) {
toml_header_dispose(&section->header);
return TOML_EDIT_ERR;
}
section->body = line.full_end;
return TOML_EDIT_OK;
}
static int toml_lines_significant(const char *data, size_t start, size_t end) {
size_t cursor = start;
toml_line_t line;
while (toml_next_line(data, end, &cursor, &line)) {
if (!toml_line_is_blank_or_comment(data, &line)) {
return 1;
}
}
return 0;
}
/* Declared table `table` (1-based) of the block, or the number of declared
* tables in *count when table is 0. */
static int toml_declared_table(const toml_managed_span_t *span, size_t table, size_t *count,
toml_section_t *out) {
size_t pos = span->owned_first;
size_t index = 0U;
while (pos < span->owned_len) {
toml_section_t declared;
if (++index > TOML_MANAGED_MAX_TABLES ||
toml_section_at(span->owned, pos, span->owned_len, &declared) != TOML_EDIT_OK) {
return TOML_EDIT_ERR;
}
if (index == table) {
*out = declared;
return TOML_EDIT_OK;
}
pos = declared.end;
toml_header_dispose(&declared.header);
}
if (count) {
*count = index;
}
return table == 0U ? TOML_EDIT_OK : TOML_EDIT_ERR;
}
/* Classify one span header. seen[] marks the declared tables the span has
* already supplied, so a second copy is refused instead of guessed at. */
static int toml_managed_classify(const toml_managed_span_t *span, const toml_header_t *header,
unsigned char *seen, int *kind, size_t *table) {
size_t exact = 0U;
int exact_taken = 0;
size_t parent = 0U;
size_t parent_depth = 0U;
int result = TOML_EDIT_OK;
size_t pos = span->owned_first;
for (size_t index = 1U; pos < span->owned_len && result == TOML_EDIT_OK; ++index) {
toml_section_t declared;
if (toml_section_at(span->owned, pos, span->owned_len, &declared) != TOML_EDIT_OK) {
return TOML_EDIT_ERR;
}
pos = declared.end;
const toml_key_path_t *path = &declared.header.path;
if (toml_key_path_equal(path, &header->path)) {
if (declared.header.array != header->array) {
result = TOML_EDIT_ERR;
} else if (!seen[index] && exact == 0U) {
exact = index;
} else {
exact_taken = 1;
}
} else if (toml_key_path_has_prefix(&header->path, path)) {
if (declared.header.array) {
result = TOML_EDIT_ERR;
} else if (path->count > parent_depth) {
parent = index;
parent_depth = path->count;
}
}
toml_header_dispose(&declared.header);
}
if (result != TOML_EDIT_OK || (exact == 0U && exact_taken)) {
return TOML_EDIT_ERR;
}
if (exact != 0U) {
seen[exact] = 1U;
}
*kind = exact != 0U ? TOML_SPAN_OWNED : parent != 0U ? TOML_SPAN_ATTACHED : TOML_SPAN_FOREIGN;
*table = exact != 0U ? exact : parent;
return TOML_EDIT_OK;
}
/* Sets *written when the declared table assigns key; a key that only shares
* a dotted prefix with one it assigns would redefine it and is refused. */
static int toml_declared_writes_key(const toml_managed_span_t *span, const toml_section_t *declared,
const toml_key_path_t *key, int *written) {
*written = 0;
size_t cursor = declared->body;
toml_line_t line;
int multiline_state = TOML_STRING_NONE;
while (toml_next_line(span->owned, declared->end, &cursor, &line)) {
if (multiline_state == TOML_STRING_NONE) {
toml_assignment_t assignment;
if (toml_parse_assignment(span->owned, &line, &assignment) != TOML_EDIT_OK) {
return TOML_EDIT_ERR;
}
int equal = assignment.present && toml_key_path_equal(&assignment.key, key);
int collides = assignment.present && !equal &&
(toml_key_path_has_prefix(&assignment.key, key) ||
toml_key_path_has_prefix(key, &assignment.key));
toml_assignment_dispose(&assignment);
if (collides) {
return TOML_EDIT_ERR;
}
*written |= equal;
}
if (toml_scan_line_strings(span->owned, &line, &multiline_state) != TOML_EDIT_OK) {
return TOML_EDIT_ERR;
}
}
return TOML_EDIT_OK;
}
static size_t toml_line_first_char(const char *data, const toml_line_t *line) {
size_t pos = line->start;
while (pos < line->content_end && (data[pos] == ' ' || data[pos] == '\t')) {
pos++;
}
return pos;
}
/* Is this line, outside any multiline string, one the user owns? Comments
* are; an assignment is unless the declared table writes its key. */
static int toml_line_is_users(const toml_managed_span_t *span, const toml_section_t *declared,
const toml_line_t *line, int *users) {
if (span->data[toml_line_first_char(span->data, line)] == '#') {
*users = 1;
return TOML_EDIT_OK;
}
toml_assignment_t assignment;
int written = 0;
if (toml_parse_assignment(span->data, line, &assignment) != TOML_EDIT_OK ||
!assignment.present ||
toml_declared_writes_key(span, declared, &assignment.key, &written) != TOML_EDIT_OK) {
toml_assignment_dispose(&assignment);
return TOML_EDIT_ERR;
}
toml_assignment_dispose(&assignment);
*users = !written;
return TOML_EDIT_OK;
}
/* The lines of an owned section the block does not write, with the blank
* lines between them; blank lines before a replaced key or at the end of the
* section go with the section. */
static int toml_append_user_lines(const toml_managed_span_t *span, const toml_section_t *section,
size_t table, toml_buffer_t *output) {
toml_section_t declared;
if (toml_declared_table(span, table, NULL, &declared) != TOML_EDIT_OK) {
return TOML_EDIT_ERR;
}
size_t cursor = section->body;
toml_line_t line;
int multiline_state = TOML_STRING_NONE;
int keep = 0;
size_t blank_start = SIZE_MAX;
int result = TOML_EDIT_OK;
while (result == TOML_EDIT_OK && toml_next_line(span->data, section->end, &cursor, &line)) {
if (multiline_state == TOML_STRING_NONE) {
if (toml_line_first_char(span->data, &line) == line.content_end) {
blank_start = blank_start == SIZE_MAX ? line.start : blank_start;
continue;
}
result = toml_line_is_users(span, &declared, &line, &keep);
if (!keep) {
blank_start = SIZE_MAX;
}
}
if (result == TOML_EDIT_OK && keep) {
size_t from = blank_start == SIZE_MAX ? line.start : blank_start;
blank_start = SIZE_MAX;
result = toml_buffer_append(output, span->data + from, line.full_end - from);
}
if (result == TOML_EDIT_OK &&
toml_scan_line_strings(span->data, &line, &multiline_state) != TOML_EDIT_OK) {
result = TOML_EDIT_ERR;
}
}
toml_header_dispose(&declared.header);
return result == TOML_EDIT_OK && multiline_state == TOML_STRING_NONE ? TOML_EDIT_OK
: TOML_EDIT_ERR;
}
/* One pass over the span's tables in file order. Appends each section of
* want_kind (and, unless foreign, of declared table want_table): foreign and
* attached sections verbatim, an owned one as its user lines. Also reports
* how many sections are foreign and the kind of the last one. */
static int toml_managed_span_pass(const toml_managed_span_t *span, int want_kind, size_t want_table,
toml_buffer_t *output, size_t *foreign, int *last_kind) {
unsigned char seen[TOML_MANAGED_MAX_TABLES + 1U] = {0};
*foreign = 0U;
*last_kind = TOML_SPAN_LEAD;
size_t pos = span->lead_end;
while (pos < span->span_end) {
toml_section_t section;
int kind = TOML_SPAN_FOREIGN;
size_t table = 0U;
if (toml_section_at(span->data, pos, span->span_end, &section) != TOML_EDIT_OK) {
return TOML_EDIT_ERR;
}
int result = toml_managed_classify(span, &section.header, seen, &kind, &table);
toml_header_dispose(&section.header);
pos = section.end;
*foreign += kind == TOML_SPAN_FOREIGN;
*last_kind = kind;
if (result == TOML_EDIT_OK && kind == want_kind &&
(kind == TOML_SPAN_FOREIGN || table == want_table)) {
result = kind == TOML_SPAN_OWNED
? toml_append_user_lines(span, &section, table, output)
: toml_buffer_append(output, span->data + section.start,
section.end - section.start);
}
if (result != TOML_EDIT_OK) {
return TOML_EDIT_ERR;
}
}
return TOML_EDIT_OK;
}
/* The declared tables of an install, each followed by what the span keeps
* for it: the user lines of its owned section, then its attached tables. */
static int toml_append_managed_tables(const toml_managed_span_t *span, size_t tables,
const char *newline, toml_buffer_t *output) {
size_t foreign = 0U;
int last_kind = TOML_SPAN_LEAD;
if (toml_append_normalized_text(output, span->owned, span->owned_first, newline) !=
TOML_EDIT_OK) {
return TOML_EDIT_ERR;
}
for (size_t table = 1U; table <= tables; ++table) {
toml_section_t declared;
if (toml_declared_table(span, table, NULL, &declared) != TOML_EDIT_OK) {
return TOML_EDIT_ERR;
}
toml_header_dispose(&declared.header);
if (toml_append_normalized_text(output, span->owned + declared.start,
declared.end - declared.start, newline) != TOML_EDIT_OK ||
(output->data[output->len - 1U] != '\n' &&
toml_buffer_append_cstr(output, newline) != TOML_EDIT_OK) ||
toml_managed_span_pass(span, TOML_SPAN_OWNED, table, output, &foreign, &last_kind) !=
TOML_EDIT_OK ||
toml_managed_span_pass(span, TOML_SPAN_ATTACHED, table, output, &foreign, &last_kind) !=
TOML_EDIT_OK) {
return TOML_EDIT_ERR;
}
}
return output->len == 0U || output->data[output->len - 1U] == '\n' ||
toml_buffer_append_cstr(output, newline) == TOML_EDIT_OK
? TOML_EDIT_OK
: TOML_EDIT_ERR;
}
/* Rebuild existing with its managed span rewritten around `owned` (install)
* or removed (uninstall), keeping everything the span holds that is not
* ours. `owned` is the block on install and the declaration of our tables on
* removal. */
static int toml_managed_rewrite(const char *existing, size_t existing_len,
const toml_line_t *begin_line, const toml_line_t *end_line,
const char *begin_marker, const char *end_marker, const char *owned,
size_t owned_len, int install, toml_buffer_t *output) {
toml_managed_span_t span = {
.data = existing,
.span_end = end_line->start,
.owned = owned,
.owned_len = owned_len,
};
size_t tables = 0U;
size_t tail_lead_end = existing_len;
if (toml_section_end(owned, 0U, owned_len, &span.owned_first) != TOML_EDIT_OK ||
toml_declared_table(&span, 0U, &tables, NULL) != TOML_EDIT_OK ||
toml_section_end(existing, begin_line->full_end, span.span_end, &span.lead_end) !=
TOML_EDIT_OK ||
toml_section_end(existing, end_line->full_end, existing_len, &tail_lead_end) !=
TOML_EDIT_OK) {
return TOML_EDIT_ERR;
}
toml_buffer_t moved = {0};
size_t foreign = 0U;
int last_kind = TOML_SPAN_LEAD;
if (toml_managed_span_pass(&span, TOML_SPAN_FOREIGN, 0U, &moved, &foreign, &last_kind) !=
TOML_EDIT_OK ||
(foreign != 0U && last_kind != TOML_SPAN_FOREIGN &&
toml_lines_significant(existing, end_line->full_end, tail_lead_end))) {
toml_buffer_dispose(&moved);
return TOML_EDIT_ERR;
}
size_t prefix_len = begin_line->start;
if (prefix_len == 0U && existing_len >= 3U && (unsigned char)existing[0] == 0xefU &&
(unsigned char)existing[1] == 0xbbU && (unsigned char)existing[2] == 0xbfU) {
prefix_len = 3U;
}
const char *newline = toml_newline_style(existing, existing_len);
int keep_lead = !toml_lines_significant(owned, 0U, span.owned_first);
size_t block_start = 0U;
size_t block_end = 0U;
int result = toml_buffer_append(output, existing, prefix_len) == TOML_EDIT_OK &&
(!keep_lead ||
toml_buffer_append(output, existing + begin_line->full_end,
span.lead_end - begin_line->full_end) == TOML_EDIT_OK)
? TOML_EDIT_OK
: TOML_EDIT_ERR;
if (result == TOML_EDIT_OK && install) {
block_start = output->len;
result =
toml_buffer_append_cstr(output, begin_marker) == TOML_EDIT_OK &&
toml_buffer_append_cstr(output, newline) == TOML_EDIT_OK &&
toml_append_managed_tables(&span, tables, newline, output) == TOML_EDIT_OK &&
toml_buffer_append_cstr(output, end_marker) == TOML_EDIT_OK &&
toml_buffer_append_cstr(output, newline) == TOML_EDIT_OK
? TOML_EDIT_OK
: TOML_EDIT_ERR;
block_end = output->len;
}
if (result == TOML_EDIT_OK &&
(toml_buffer_append(output, moved.data, moved.len) != TOML_EDIT_OK ||
toml_buffer_append(output, existing + end_line->full_end,
existing_len - end_line->full_end) != TOML_EDIT_OK ||
(install && toml_managed_block_conflicts(output->data, output->len, block_start, block_end,
owned, owned_len) != TOML_EDIT_OK))) {
result = TOML_EDIT_ERR;
}
toml_buffer_dispose(&moved);
return result;
}
static int toml_finish_target_table(toml_target_table_t *current, toml_table_scan_t *result) {
if (!current->active) {
return TOML_EDIT_OK;
+11 -1
View File
@@ -24,11 +24,21 @@ int cbm_toml_escape_basic_string(const char *input, char *out, size_t out_size);
*
* Insert or replace one managed, line-delimited block. block is the body
* between the two marker lines. Duplicate or unbalanced markers are rejected
* without changing the file. */
* without changing the file. An existing span keeps what is not ours (#2228):
* keys and comments the block does not write stay in the tables it declares,
* with those tables' sub-tables; lines before the span's first table move
* above the block and any other table moves, byte for byte and in order, to
* just after it. Content whose ownership is ambiguous fails closed. */
int cbm_toml_upsert_managed_block(const char *file_path, const char *begin_marker,
const char *end_marker, const char *block);
/* Removes the whole span between the markers. */
int cbm_toml_remove_managed_block(const char *file_path, const char *begin_marker,
const char *end_marker);
/* Removes the markers and only the tables owned_tables declares (e.g. the
* block's header lines), with their keys and sub-tables; every other line of
* the span stays in place. Ambiguous ownership fails closed (#2228). */
int cbm_toml_remove_managed_block_owned(const char *file_path, const char *begin_marker,
const char *end_marker, const char *owned_tables);
/* Remove one pre-marker codebase-memory-mcp table only when it has the known
* historical schema: one owned command basename, optional empty args, optional
+20 -8
View File
@@ -59,8 +59,9 @@
*
* Observability (#858): a fired deadline is otherwise indistinguishable from
* "no matches", so the handler first write()s a pre-formatted breadcrumb to
* ~/.cache/codebase-memory-mcp/logs/hook-augment-timeouts.log (fd and message
* prepared at arm time — only async-signal-safe write/_exit in the handler). */
* <cache_dir>/logs/hook-augment-timeouts.log (by default
* ~/.cache/codebase-memory-mcp/logs; fd and message prepared at arm time —
* only async-signal-safe write/_exit in the handler). */
#ifndef _WIN32
#define HA_DEADLINE_DEFAULT_MS 2000 /* in-process budget; see ha_deadline_ms() */
#define HA_DEADLINE_MIN_MS 50
@@ -103,20 +104,31 @@ static void ha_deadline_exit(int sig) {
_exit(0);
}
/* Whether snprintf wrote the whole string into a buffer of `cap` bytes. */
static bool ha_fits(int written, size_t cap) {
return written > 0 && (size_t)written < cap;
}
static void ha_open_crumb_log(int deadline_ms) {
const char *override = getenv("CBM_HOOK_TIMEOUT_LOG"); /* tests + power users */
char path[CBM_SZ_1K];
/* A path cut off at the buffer names some other file, and O_CREAT would
* create it. Such a path means no breadcrumb; the hook itself runs on. */
if (override && override[0]) {
snprintf(path, sizeof(path), "%s", override);
} else {
const char *home = getenv("HOME");
if (!home || !home[0]) {
if (!ha_fits(snprintf(path, sizeof(path), "%s", override), sizeof(path))) {
return;
}
} else {
/* <cache_dir>/logs, where every cbm component logs: the cache dir is
* CBM_CACHE_DIR, else HOME (then USERPROFILE) + the default. */
const char *cache = cbm_resolve_cache_dir();
char dir[CBM_SZ_1K];
snprintf(dir, sizeof(dir), "%s/.cache/codebase-memory-mcp/logs", home);
if (!cache || !ha_fits(snprintf(dir, sizeof(dir), "%s/logs", cache), sizeof(dir)) ||
!ha_fits(snprintf(path, sizeof(path), "%s/hook-augment-timeouts.log", dir),
sizeof(path))) {
return;
}
cbm_mkdir_p_ex(dir, 0755, CBM_MKDIR_FOLLOW_OWNED);
snprintf(path, sizeof(path), "%s/hook-augment-timeouts.log", dir);
}
g_ha_crumb_fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0644);
if (g_ha_crumb_fd < 0) {
+259 -24
View File
@@ -12,6 +12,8 @@
#include "foundation/platform.h"
#include "foundation/limits.h"
#include "foundation/log.h"
#include "foundation/hash_table.h"
#include "foundation/mem_core.h"
enum {
CYP_BUF_16 = 16,
@@ -2594,6 +2596,12 @@ static const char *resolve_condition_value(const cbm_condition_t *c, binding_t *
return n->name ? n->name : "";
}
/* Set when an `=~` or inline-property pattern was refused by the regex
* wrapper's compile-size guard during the CURRENT execution; cbm_cypher_execute
* turns it into result->warning so an empty result can be told from "no such
* name". Reset at the start of every execution. */
static _Thread_local bool g_cypher_regex_refused = false;
/* Evaluate a comparison operator between actual and expected strings. */
static bool eval_comparison_op(const char *op, const char *actual, const char *expected) {
if (strcmp(op, "=") == 0) {
@@ -2604,7 +2612,11 @@ static bool eval_comparison_op(const char *op, const char *actual, const char *e
}
if (strcmp(op, "=~") == 0) {
cbm_regex_t re;
if (cbm_regcomp(&re, expected, CBM_REG_EXTENDED | CBM_REG_NOSUB) != 0) {
int comp_rc = cbm_regcomp(&re, expected, CBM_REG_EXTENDED | CBM_REG_NOSUB);
if (comp_rc != 0) {
if (comp_rc == CBM_REG_ETOOBIG) {
g_cypher_regex_refused = true;
}
return false;
}
int rc = cbm_regexec(&re, actual, 0, NULL, 0);
@@ -2766,6 +2778,96 @@ static bool eval_where(const cbm_where_clause_t *w, binding_t *b) {
return is_and;
}
/* Three-valued WHERE evaluation for the early (seed-row) pass in
* execute_single: only the first MATCH alias is bound there, so a leaf on
* any other alias is UNKNOWN rather than true. The two-valued evaluator
* returns true for an unbound alias, which NOT / XOR then invert -- pruning
* every seed before relationship expansion (distilled from PR #1245). The
* full evaluation reruns after expansion (Step 3), so the early pass only has
* to prune on a definite false. Allocation-free, O(expression nodes). */
typedef enum { CYP_PARTIAL_FALSE = 0, CYP_PARTIAL_TRUE, CYP_PARTIAL_UNKNOWN } cyp_partial_t;
static cyp_partial_t partial_and(cyp_partial_t l, cyp_partial_t r) {
if (l == CYP_PARTIAL_FALSE || r == CYP_PARTIAL_FALSE) {
return CYP_PARTIAL_FALSE;
}
return (l == CYP_PARTIAL_TRUE && r == CYP_PARTIAL_TRUE) ? CYP_PARTIAL_TRUE
: CYP_PARTIAL_UNKNOWN;
}
static cyp_partial_t partial_or(cyp_partial_t l, cyp_partial_t r) {
if (l == CYP_PARTIAL_TRUE || r == CYP_PARTIAL_TRUE) {
return CYP_PARTIAL_TRUE;
}
return (l == CYP_PARTIAL_FALSE && r == CYP_PARTIAL_FALSE) ? CYP_PARTIAL_FALSE
: CYP_PARTIAL_UNKNOWN;
}
/* A condition whose alias is not bound yet is UNKNOWN; a literal-only LHS
* (func condition with no variable arg) has nothing to wait for. */
static cyp_partial_t eval_condition_partial(const cbm_condition_t *c, binding_t *b) {
if (c->variable && !binding_get(b, c->variable) && !binding_get_edge(b, c->variable)) {
return CYP_PARTIAL_UNKNOWN;
}
return eval_condition(c, b) ? CYP_PARTIAL_TRUE : CYP_PARTIAL_FALSE;
}
static cyp_partial_t eval_expr_partial(const cbm_expr_t *e, // NOLINT(misc-no-recursion)
binding_t *b) {
if (!e) {
return CYP_PARTIAL_TRUE;
}
if (e->type == EXPR_CONDITION) {
return eval_condition_partial(&e->cond, b);
}
cyp_partial_t left = eval_expr_partial(e->left, b);
if (e->type == EXPR_NOT) {
if (left == CYP_PARTIAL_UNKNOWN) {
return CYP_PARTIAL_UNKNOWN;
}
return left == CYP_PARTIAL_TRUE ? CYP_PARTIAL_FALSE : CYP_PARTIAL_TRUE;
}
/* Same short-circuits as eval_expr: a definite left decides AND / OR. */
if (e->type == EXPR_AND && left == CYP_PARTIAL_FALSE) {
return CYP_PARTIAL_FALSE;
}
if (e->type == EXPR_OR && left == CYP_PARTIAL_TRUE) {
return CYP_PARTIAL_TRUE;
}
cyp_partial_t right = eval_expr_partial(e->right, b);
if (e->type == EXPR_AND) {
return partial_and(left, right);
}
if (e->type == EXPR_OR) {
return partial_or(left, right);
}
/* EXPR_XOR */
if (left == CYP_PARTIAL_UNKNOWN || right == CYP_PARTIAL_UNKNOWN) {
return CYP_PARTIAL_UNKNOWN;
}
return left != right ? CYP_PARTIAL_TRUE : CYP_PARTIAL_FALSE;
}
static cyp_partial_t eval_where_partial(const cbm_where_clause_t *w, binding_t *b) {
if (!w) {
return CYP_PARTIAL_TRUE;
}
if (w->root) {
return eval_expr_partial(w->root, b);
}
/* Legacy flat evaluation */
if (w->count == 0) {
return CYP_PARTIAL_TRUE;
}
bool is_and = (w->op && strcmp(w->op, "AND") == 0) != 0;
cyp_partial_t result = is_and ? CYP_PARTIAL_TRUE : CYP_PARTIAL_FALSE;
for (int i = 0; i < w->count; i++) {
cyp_partial_t r = eval_condition_partial(&w->conditions[i], b);
result = is_and ? partial_and(result, r) : partial_or(result, r);
}
return result;
}
/* Check if a string value looks like a regex pattern. */
static bool looks_like_regex(const char *s) {
if (!s) {
@@ -2784,13 +2886,17 @@ static bool check_inline_props(const cbm_node_t *n, const cbm_prop_filter_t *pro
const char *actual = node_prop(n, props[i].key, store);
if (looks_like_regex(props[i].value)) {
cbm_regex_t re;
if (cbm_regcomp(&re, props[i].value, CBM_REG_EXTENDED | CBM_REG_NOSUB) == 0) {
int comp_rc = cbm_regcomp(&re, props[i].value, CBM_REG_EXTENDED | CBM_REG_NOSUB);
if (comp_rc == 0) {
bool matched = cbm_regexec(&re, actual, 0, NULL, 0) == 0;
cbm_regfree(&re);
if (!matched) {
return false;
}
} else if (strcmp(actual, props[i].value) != 0) {
if (comp_rc == CBM_REG_ETOOBIG) {
g_cypher_regex_refused = true;
}
return false;
}
} else if (strcmp(actual, props[i].value) != 0) {
@@ -2837,6 +2943,104 @@ static void rb_add_row(result_builder_t *rb, const char **values) {
rb->rows[rb->row_count++] = row;
}
/* ── DISTINCT while projecting (#1364) ─────────────────────────────
*
* RETURN DISTINCT used to project up to the engine row cap and only then drop
* duplicates, so a value first seen past the cap never reached the result: on
* a 150k-edge index `RETURN DISTINCT type(r)` lost its one HAS_BRANCH edge.
* The projection loops now de-duplicate as they go, so the cap counts DISTINCT
* rows. Memory stays bounded by the cap: only admitted rows are remembered;
* rows past the cap are probed (no insert) solely to decide `truncated`. */
typedef struct {
CBMHashTable *seen; /* row key -> the same heap key; NULL = not DISTINCT */
} rb_distinct_t;
static void rb_distinct_init(rb_distinct_t *d, bool distinct) {
d->seen = distinct ? cbm_ht_create(CBM_SZ_64) : NULL;
}
static void rb_distinct_free_key(const char *key, void *value, void *userdata) {
(void)key;
(void)userdata;
cbm_free(CBM_MEM_CLASS_OTHER, value);
}
static void rb_distinct_free(rb_distinct_t *d) {
if (d->seen) {
cbm_ht_foreach(d->seen, rb_distinct_free_key, NULL);
cbm_ht_free(d->seen);
d->seen = NULL;
}
}
/* Length-prefixed row key ("<len>:<value>" per column), so no value content
* can make two different rows collide. NULL projects as "" (as rb_add_row). */
static char *rb_distinct_row_key(const char **vals, int n) {
size_t total = SKIP_ONE;
for (int i = 0; i < n; i++) {
total += (vals[i] ? strlen(vals[i]) : 0) + CBM_SZ_32;
}
char *key = cbm_alloc(CBM_MEM_CLASS_OTHER, total);
if (!key) {
return NULL;
}
size_t pos = 0;
key[0] = '\0';
for (int i = 0; i < n; i++) {
const char *v = vals[i] ? vals[i] : "";
int w = snprintf(key + pos, total - pos, "%zu:%s", strlen(v), v);
if (w > 0) {
pos += (size_t)w;
}
}
return key;
}
/* Admission for one projected row. Returns false for a row DISTINCT already
* holds. On true, *key_out is the key to hand to rb_distinct_keep (NULL when
* not DISTINCT, or when the key allocation failed — the row is then admitted
* without de-duplication rather than dropped). */
static bool rb_distinct_is_new(const rb_distinct_t *d, const char **vals, int n, char **key_out) {
*key_out = NULL;
if (!d->seen) {
return true;
}
char *key = rb_distinct_row_key(vals, n);
if (!key) {
return true;
}
if (cbm_ht_has(d->seen, key)) {
cbm_free(CBM_MEM_CLASS_OTHER, key);
return false;
}
*key_out = key;
return true;
}
static void rb_distinct_keep(rb_distinct_t *d, char *key) {
if (key) {
cbm_ht_set(d->seen, key, key);
}
}
/* Add one projected row under a row cap, de-duplicating when DISTINCT.
* Returns false once a NEW row arrives with the cap already full: the caller
* stops there, and flags truncation when the cap is the engine budget. A
* duplicate past the cap is neither kept nor evidence of truncation. */
static bool rb_add_row_capped(result_builder_t *rb, rb_distinct_t *d, const char **vals, int cap) {
char *key = NULL;
if (!rb_distinct_is_new(d, vals, rb->col_count, &key)) {
return true;
}
if (rb->row_count >= cap) {
cbm_free(CBM_MEM_CLASS_OTHER, key);
return false;
}
rb_distinct_keep(d, key);
rb_add_row(rb, vals);
return true;
}
/* ── Main execution ─────────────────────────────────────────────── */
/* Hard ceiling: queries returning more than this trigger an error instead of data.
@@ -4359,14 +4563,20 @@ static void execute_return_star_after_with(cbm_query_t *q, binding_t *bindings,
cols[i] = resolve_item_alias(&wc->items[i], name_bufs[i], sizeof(name_bufs[i]));
}
rb_set_columns(rb, cols, col_n);
for (int bi = 0; bi < bind_count && rb->row_count < max_rows; bi++) {
rb_distinct_t seen;
rb_distinct_init(&seen, q->ret && q->ret->distinct);
for (int bi = 0; bi < bind_count; bi++) {
const char *vals[CYP_MAX_VARS];
for (int i = 0; i < col_n; i++) {
cbm_node_t *vn = binding_get(&bindings[bi], cols[i]);
vals[i] = vn && vn->name ? vn->name : "";
}
rb_add_row(rb, vals);
if (!rb_add_row_capped(rb, &seen, vals, max_rows)) {
g_cypher_truncated = true;
break;
}
}
rb_distinct_free(&seen);
}
static void execute_return_star(cbm_query_t *q, binding_t *bindings, int bind_count, int max_rows,
@@ -4381,19 +4591,22 @@ static void execute_return_star(cbm_query_t *q, binding_t *bindings, int bind_co
int projection_cap = max_rows;
bool cap_is_engine_budget = true;
cbm_return_clause_t *ret = q->ret;
if (ret && ret->limit >= 0 && ret->limit <= max_rows && !ret->distinct &&
ret->order_key_count == 0 && ret->skip <= 0) {
projection_cap = ret->limit;
if (ret && ret->limit >= 0 && ret->limit <= max_rows && ret->order_key_count == 0 &&
ret->skip <= 0) {
projection_cap = ret->limit; /* DISTINCT: the cap counts distinct rows (#1364) */
cap_is_engine_budget = false;
}
for (int bi = 0; bi < bind_count && rb->row_count < projection_cap; bi++) {
rb_distinct_t seen;
rb_distinct_init(&seen, ret && ret->distinct);
for (int bi = 0; bi < bind_count; bi++) {
const char *vals[CBM_SZ_128];
project_star_row(&bindings[bi], vars, vc, vals);
rb_add_row(rb, vals);
}
if (cap_is_engine_budget && bind_count > projection_cap) {
g_cypher_truncated = true;
if (!rb_add_row_capped(rb, &seen, vals, projection_cap)) {
g_cypher_truncated = g_cypher_truncated || cap_is_engine_budget;
break;
}
}
rb_distinct_free(&seen);
}
/* Format an aggregate value into buf based on function name */
@@ -4662,22 +4875,25 @@ static void execute_return_simple(cbm_return_clause_t *ret, binding_t *bindings,
int max_rows, result_builder_t *rb) {
int proj_cap = max_rows;
bool cap_is_engine_budget = true;
if (ret->limit >= 0 && !ret->distinct && ret->order_key_count == 0 && ret->skip <= 0) {
proj_cap = ret->limit;
if (ret->limit >= 0 && ret->order_key_count == 0 && ret->skip <= 0) {
proj_cap = ret->limit; /* DISTINCT: the cap counts distinct rows (#1364) */
cap_is_engine_budget = false;
}
for (int bi = 0; bi < bind_count && rb->row_count < proj_cap; bi++) {
rb_distinct_t seen;
rb_distinct_init(&seen, ret->distinct);
for (int bi = 0; bi < bind_count; bi++) {
const char *vals[CBM_SZ_32];
char func_bufs[CBM_SZ_32][CBM_SZ_512];
for (int ci = 0; ci < ret->count; ci++) {
vals[ci] =
project_item(&bindings[bi], &ret->items[ci], func_bufs[ci], sizeof(func_bufs[ci]));
}
rb_add_row(rb, vals);
}
if (cap_is_engine_budget && bind_count > proj_cap) {
g_cypher_truncated = true;
if (!rb_add_row_capped(rb, &seen, vals, proj_cap)) {
g_cypher_truncated = g_cypher_truncated || cap_is_engine_budget;
break;
}
}
rb_distinct_free(&seen);
}
/* Build default 3-column headers (name, qualified_name, label) per variable */
@@ -5019,7 +5235,9 @@ static void execute_return_clause(cbm_query_t *q, cbm_return_clause_t *ret, bind
}
}
if (ret->distinct) {
/* The simple and star projections de-duplicate while projecting (#1364);
* only aggregate rows still need the post-pass. */
if (ret->distinct && has_agg && !ret->star) {
rb_apply_distinct(rb);
}
rb_apply_order_by(rb, ret);
@@ -5138,7 +5356,11 @@ static int execute_single(cbm_store_t *store, cbm_query_t *q, const char *projec
/* Build initial bindings with early WHERE */
int bind_cap = scan_count > max_rows ? scan_count : (max_rows > 0 ? max_rows : SKIP_ONE);
binding_t *bindings = malloc((bind_cap + SKIP_ONE) * sizeof(binding_t));
binding_t *bindings = malloc(((size_t)bind_cap + SKIP_ONE) * sizeof(binding_t));
if (!bindings) {
cbm_store_free_nodes(scanned, scan_count);
return CBM_NOT_FOUND; /* initial binding array refused */
}
int bind_count = 0;
const char *var_name = pat0->nodes[0].variable ? pat0->nodes[0].variable : CYP_ANON_HEAD_VAR;
@@ -5149,7 +5371,7 @@ static int execute_single(cbm_store_t *store, cbm_query_t *q, const char *projec
binding_t b = {0};
b.store = store;
binding_set(&b, var_name, &scanned[i]);
bool pass = !q->where || eval_where(q->where, &b);
bool pass = eval_where_partial(q->where, &b) != CYP_PARTIAL_FALSE;
if (pass) {
bindings[bind_count++] = b;
} else {
@@ -5447,8 +5669,11 @@ int cbm_cypher_execute(cbm_store_t *store, const char *query, const char *projec
g_cypher_depth_clamped = 0;
g_cypher_trail_truncated = 0;
g_cypher_truncated = false;
g_cypher_regex_refused = false;
cypher_deadline_arm(); /* #601: start the wall-clock budget for this query */
if (max_rows <= 0) {
/* max_rows sizes the initial binding array: non-positive means the
* ceiling, and nothing above the ceiling is ever materialized anyway. */
if (max_rows <= 0 || max_rows > CYPHER_RESULT_CEILING) {
max_rows = CYPHER_RESULT_CEILING;
}
@@ -5534,8 +5759,8 @@ int cbm_cypher_execute(cbm_store_t *store, const char *query, const char *projec
/* Any internal ceiling that prevented exhaustive evaluation: a candidate or
* traversal budget, or a variable-length range clamped to the engine cap. */
out->truncated = g_cypher_truncated || g_cypher_trail_truncated != 0;
char wbuf[CBM_SZ_512] = "";
if (g_cypher_depth_clamped > 0 || g_cypher_trail_truncated) {
char wbuf[CBM_SZ_256];
if (g_cypher_depth_clamped > 0 && g_cypher_trail_truncated) {
snprintf(wbuf, sizeof(wbuf),
"variable-length hop range clamped to the engine ceiling (%d) and "
@@ -5550,6 +5775,16 @@ int cbm_cypher_execute(cbm_store_t *store, const char *query, const char *projec
snprintf(wbuf, sizeof(wbuf),
"variable-length traversal budget was exhausted — results may be partial");
}
}
/* A refused `=~` or property pattern matched nothing: say so, once per
* query, next to any traversal warning. */
if (g_cypher_regex_refused) {
size_t used = strlen(wbuf);
snprintf(wbuf + used, sizeof(wbuf) - used,
"%sa =~ or property " CBM_REG_ETOOBIG_REASON " — the comparison matched nothing",
used ? "; " : "");
}
if (wbuf[0]) {
out->warning = heap_strdup(wbuf);
}
+2 -1
View File
@@ -334,7 +334,8 @@ typedef struct {
} cbm_cypher_result_t;
/* Execute a Cypher query against a store.
* max_rows: limit on output rows (0 = use virtual ceiling of 100k).
* max_rows: limit on output rows; 0 or any value above the 100k ceiling uses
* the ceiling.
* project: project name filter (NULL = all projects).
* Returns -1 on error (check out->error for message). */
int cbm_cypher_execute(cbm_store_t *store, const char *query, const char *project, int max_rows,
+9
View File
@@ -1349,6 +1349,15 @@ static char *application_job_failure_response(const cbm_index_worker_result_t *r
(void)snprintf(message, sizeof(message),
"index worker containment failed (%s); inspect log: %s",
cbm_proc_outcome_str(result->outcome), log_path ? log_path : "unavailable");
} else if (result && result->response_missing) {
/* #1300: name the silent clean exit, where it stopped, and the log. */
const char *log = log_path ? log_path
: result->worker_log[0] ? result->worker_log
: "unavailable";
(void)snprintf(message, sizeof(message),
"index worker exited cleanly (exit 0) without writing a response; the "
"index was not published; last phase reached: %s; inspect log: %s",
result->last_phase[0] ? result->last_phase : "unknown", log);
} else if (result) {
(void)snprintf(message, sizeof(message),
"index worker ended with %s (exit=%d, signal=%d); inspect log: %s",
+4
View File
@@ -178,6 +178,10 @@ cbm_daemon_process_role_t cbm_daemon_process_role(int argc, char *const argv[])
* project, so it needs no daemon. Listed here rather than routed through
* the daemon so enrolling a root cannot depend on daemon state. */
"allow-root",
/* test-impact runs its own frozen index of a pinned tree in process
* (CI: publish the team artifact, select tests); it never reads or
* writes the daemon's projects, so it needs no daemon. */
"test-impact",
};
/* Stop at the first top-level mode token. Tool names, flag values, and JSON
* following `cli` are opaque user input: a search query named "install"
+6
View File
@@ -609,6 +609,12 @@ static bool host_state_prepare(host_state_t *host, const cbm_daemon_ipc_endpoint
if (watcher_enabled) {
host->watch_store = cbm_store_open_memory();
host->watcher = cbm_watcher_new(host->watch_store, host_watcher_index, host);
/* #1948: non-git roots stay unwatched unless watch_non_git opts in.
* Same read-once-at-startup contract as watcher_enabled. */
if (host->watcher && cbm_config_watch_non_git(host->runtime_config)) {
cbm_watcher_set_poll_non_git(host->watcher, true);
cbm_log_info("watcher.non_git", "mode", "tree_poll");
}
} else {
cbm_log_info("watcher.disabled", "reason", "config");
}
+18
View File
@@ -7078,3 +7078,21 @@ int cbm_daemon_ipc_receive_frame(cbm_daemon_ipc_connection_t *connection, uint32
return cbm_daemon_ipc_receive_frame_bounded(connection, timeout_ms, CBM_DAEMON_MAX_FRAME_SIZE,
frame_out, payload_out);
}
int cbm_daemon_ipc_receive_frame_after_failed_send(cbm_daemon_ipc_connection_t *connection,
uint32_t timeout_ms, uint32_t max_payload_length,
cbm_daemon_frame_t *frame_out,
uint8_t **payload_out) {
if (!connection) {
return cbm_daemon_ipc_receive_frame_bounded(NULL, timeout_ms, max_payload_length, frame_out,
payload_out);
}
/* The poison came from the failed send alone (nothing was read yet), and it
* guards the OUTBOUND frame boundary; the inbound one is intact. Lift it for
* this one receive, then restore it so no later send can reuse the stream. */
atomic_store_explicit(&connection->poisoned, false, memory_order_release);
int received = cbm_daemon_ipc_receive_frame_bounded(connection, timeout_ms, max_payload_length,
frame_out, payload_out);
atomic_store_explicit(&connection->poisoned, true, memory_order_release);
return received;
}
+16
View File
@@ -75,6 +75,22 @@ int cbm_daemon_ipc_receive_frame_bounded(cbm_daemon_ipc_connection_t *connection
uint32_t timeout_ms, uint32_t max_payload_length,
cbm_daemon_frame_t *frame_out, uint8_t **payload_out);
/* Bounded receive of an answer the peer queued before it closed, on a
* connection whose send just failed. A daemon at capacity or stopping answers
* from its accept loop without reading the request and closes at once; when
* that close lands before the client's first send, the send fails (EPIPE) and
* poisons the connection although the complete answer is still queued for
* reading -- POSIX keeps it readable after the peer's close (a Windows server
* close discards it, so there this fails like any other receive). A failed
* send consumes no inbound bytes, so the inbound frame boundary is intact;
* call this ONLY before anything was read from the connection. The connection
* stays poisoned for sends afterwards. Returns like
* cbm_daemon_ipc_receive_frame_bounded. */
int cbm_daemon_ipc_receive_frame_after_failed_send(cbm_daemon_ipc_connection_t *connection,
uint32_t timeout_ms, uint32_t max_payload_length,
cbm_daemon_frame_t *frame_out,
uint8_t **payload_out);
/* Narrow crash/fault seams for publication-state and retry-state tests. They
* are inert unless a test installs a hook/failure count in its own process. */
typedef enum {
+72 -8
View File
@@ -76,8 +76,26 @@ static _Atomic uint32_t runtime_ephemeral_linger_timeout_seam = UINT32_MAX;
void cbm_daemon_runtime_service_set_ephemeral_linger_timeout_for_testing(uint32_t timeout_ms) {
atomic_store(&runtime_ephemeral_linger_timeout_seam, timeout_ms);
}
/* Connection-cap seams (flaky-ledger item 12): see runtime.h. */
static atomic_bool runtime_hold_peer_waits_seam;
void cbm_daemon_runtime_hold_peer_waits_for_testing(bool hold) {
atomic_store(&runtime_hold_peer_waits_seam, hold);
}
static _Atomic(cbm_daemon_runtime_hello_hook_t) runtime_hello_hook_seam;
void cbm_daemon_runtime_set_hello_hook_for_testing(cbm_daemon_runtime_hello_hook_t hook) {
atomic_store(&runtime_hello_hook_seam, hook);
}
static void runtime_hello_point(cbm_daemon_runtime_hello_point_t point) {
cbm_daemon_runtime_hello_hook_t hook = atomic_load(&runtime_hello_hook_seam);
if (hook) {
hook(point);
}
}
#define RUNTIME_HELLO_POINT(point) runtime_hello_point(CBM_DAEMON_RUNTIME_HELLO_POINT_##point)
#else
static void runtime_note_peer_image_hash(void) {}
#define RUNTIME_HELLO_POINT(point) ((void)0)
#endif
#ifdef _WIN32
@@ -1989,6 +2007,29 @@ static void runtime_worker_handle_activation_shutdown(cbm_daemon_runtime_worker_
runtime_worker_finish(worker);
}
/* The first frame is due within the request deadline. Under the item-12 seam a
* connection that never sends one holds its slot until it closes; stop still
* interrupts the wait like any other worker receive. */
static uint32_t runtime_first_frame_timeout_ms(const cbm_daemon_runtime_service_t *service) {
#ifdef CBM_ENABLE_TEST_SEAMS
if (atomic_load(&runtime_hold_peer_waits_seam)) {
return CBM_DAEMON_IPC_WAIT_FOREVER;
}
#endif
return service->request_timeout_ms;
}
/* Under the same seam an inline rejection's drain ends only on the peer's
* close, never on the 250 ms bound (the drain only waits on Windows). */
static uint32_t runtime_reject_drain_timeout_ms(void) {
#ifdef CBM_ENABLE_TEST_SEAMS
if (atomic_load(&runtime_hold_peer_waits_seam)) {
return CBM_DAEMON_IPC_WAIT_FOREVER;
}
#endif
return RUNTIME_REJECT_DRAIN_TIMEOUT_MS;
}
static void *runtime_connection_worker(void *opaque) {
cbm_daemon_runtime_worker_t *worker = opaque;
cbm_daemon_runtime_service_t *service = worker->service;
@@ -1998,7 +2039,7 @@ static void *runtime_connection_worker(void *opaque) {
char requested_build[CBM_DAEMON_BUILD_FINGERPRINT_SIZE];
int received = cbm_daemon_ipc_receive_frame_bounded(
worker->connection, service->request_timeout_ms,
worker->connection, runtime_first_frame_timeout_ms(service),
CBM_DAEMON_ACTIVATION_SHUTDOWN_REQUEST_SIZE, &frame, &payload);
if (received != 1 || frame.type != CBM_DAEMON_FRAME_REQUEST) {
free(payload);
@@ -2274,14 +2315,19 @@ static cbm_daemon_runtime_worker_t *runtime_find_free_worker_locked(
return NULL;
}
/* Answers WITHOUT reading the peer's HELLO, so on POSIX (where the drain is a
* no-op) the close can land before the peer has even sent it; the client reads
* this answer after its failed send (cbm_daemon_runtime_client_connect). */
static void runtime_reject_inline(cbm_daemon_ipc_connection_t *connection, const char *message) {
cbm_daemon_runtime_connect_result_t result;
runtime_result_rejected(&result, message);
RUNTIME_HELLO_POINT(DAEMON_REJECTING);
(void)runtime_send_hello_response(connection, &result);
/* Same Windows named-pipe discard hazard as runtime_worker_finish: the
* peer must get to read the rejection before the handle closes. */
cbm_daemon_ipc_connection_drain(connection, RUNTIME_REJECT_DRAIN_TIMEOUT_MS);
cbm_daemon_ipc_connection_drain(connection, runtime_reject_drain_timeout_ms());
cbm_daemon_ipc_connection_close(connection);
RUNTIME_HELLO_POINT(DAEMON_REJECTED);
}
static void runtime_accept_connection(cbm_daemon_runtime_service_t *service,
@@ -3063,21 +3109,39 @@ cbm_daemon_runtime_client_t *cbm_daemon_runtime_client_connect(
return NULL;
}
cbm_daemon_ipc_connection_t *connection = cbm_daemon_ipc_connect(endpoint, timeout_ms);
if (!connection || !cbm_daemon_ipc_send_frame(connection, CBM_DAEMON_FRAME_REQUEST,
CBM_DAEMON_RUNTIME_OP_HELLO, request,
(uint32_t)sizeof(request))) {
cbm_daemon_ipc_connection_close(connection);
if (!connection) {
return NULL;
}
RUNTIME_HELLO_POINT(CLIENT_CONNECTED);
bool hello_sent =
cbm_daemon_ipc_send_frame(connection, CBM_DAEMON_FRAME_REQUEST, CBM_DAEMON_RUNTIME_OP_HELLO,
request, (uint32_t)sizeof(request));
RUNTIME_HELLO_POINT(CLIENT_SENT);
cbm_daemon_frame_t frame = {0};
uint8_t *payload = NULL;
int received = cbm_daemon_ipc_receive_frame_bounded(
connection, timeout_ms, CBM_DAEMON_RENDEZVOUS_RESPONSE_SIZE, &frame, &payload);
/* A daemon at capacity or stopping answers from its accept loop without
* reading this HELLO and closes at once. When that close lands first, the
* send fails (EPIPE) with the whole answer already queued here: read it,
* or the caller gets a bare transport error and bootstrap classifies by
* lock state instead of by the daemon's own "capacity" / "stopping". */
int received =
hello_sent
? cbm_daemon_ipc_receive_frame_bounded(
connection, timeout_ms, CBM_DAEMON_RENDEZVOUS_RESPONSE_SIZE, &frame, &payload)
: cbm_daemon_ipc_receive_frame_after_failed_send(
connection, timeout_ms, CBM_DAEMON_RENDEZVOUS_RESPONSE_SIZE, &frame, &payload);
bool valid = received == 1 && frame.type == CBM_DAEMON_FRAME_RESPONSE &&
frame.flags == CBM_DAEMON_RUNTIME_OP_HELLO &&
frame.length == CBM_DAEMON_RENDEZVOUS_RESPONSE_SIZE &&
runtime_hello_response_decode(payload, result_out);
free(payload);
if (!hello_sent && (!valid || result_out->status != CBM_DAEMON_RUNTIME_CONNECT_REJECTED)) {
/* Only a rejection can answer a HELLO the daemon never received;
* anything else stays the plain send failure it always was. */
memset(result_out, 0, sizeof(*result_out));
cbm_daemon_ipc_connection_close(connection);
return NULL;
}
if (received != 1) {
/* The kernel completed the pipe/socket connect, so a server process
* exists, yet the HELLO went unanswered. Name that holder: a dead
+21
View File
@@ -471,6 +471,27 @@ void cbm_daemon_runtime_set_containment_hook_for_testing(
* in test time. UINT32_MAX restores the production constant; any other value
* (0 = expire immediately) overrides. Process-global; reset it after use. */
void cbm_daemon_runtime_service_set_ephemeral_linger_timeout_for_testing(uint32_t timeout_ms);
/* Connection-cap seams (flaky-ledger item 12). While held, the two bounded
* waits on a peer in the connection-cap path end only on that peer: a
* connection that has not sent its first frame keeps its worker slot until it
* closes (or the service stops) instead of until the request deadline, and an
* inline rejection's drain (Windows; a no-op on POSIX) lasts until the peer
* closes instead of 250 ms. A "slow hello" becomes a state the test releases,
* not a window it races. Process-global; reset it after use. */
void cbm_daemon_runtime_hold_peer_waits_for_testing(bool hold);
/* The four points that order a HELLO against an inline accept-loop rejection.
* The hook runs on the client thread for the CLIENT_* points and on the accept
* thread for the DAEMON_* points (never under the service mutex), so a test can
* pin either order: daemon answers after the HELLO is on the wire, or the
* daemon has answered and closed before the HELLO is sent. NULL removes it. */
typedef enum {
CBM_DAEMON_RUNTIME_HELLO_POINT_CLIENT_CONNECTED = 0, /* transport up, HELLO not sent */
CBM_DAEMON_RUNTIME_HELLO_POINT_CLIENT_SENT, /* HELLO send attempted */
CBM_DAEMON_RUNTIME_HELLO_POINT_DAEMON_REJECTING, /* inline rejection not yet written */
CBM_DAEMON_RUNTIME_HELLO_POINT_DAEMON_REJECTED, /* inline rejection written + closed */
} cbm_daemon_runtime_hello_point_t;
typedef void (*cbm_daemon_runtime_hello_hook_t)(cbm_daemon_runtime_hello_point_t point);
void cbm_daemon_runtime_set_hello_hook_for_testing(cbm_daemon_runtime_hello_hook_t hook);
#endif
#endif /* CBM_DAEMON_RUNTIME_H */
+35 -80
View File
@@ -10,6 +10,7 @@
* 5. Language detection for accepted files
*/
#include "discover/discover.h"
#include "discover/discover_policy_internal.h"
#include "cbm.h" // CBMLanguage, CBM_LANG_COUNT, CBM_LANG_JSON
#include "foundation/constants.h"
@@ -107,20 +108,6 @@ static const char *FAST_PATTERNS[] = {".d.ts", ".bundle.", ".chunk.", ".gen
"_string.go", "mock_", "_mock.", "_test_helpers.",
".stories.", ".spec.", ".test.", NULL};
/* ── Ignored JSON filenames ──────────────────────── */
static const char *IGNORED_JSON_FILES[] = {
"package.json", "package-lock.json", "tsconfig.json",
"jsconfig.json", "composer.json", "composer.lock",
"yarn.lock", "openapi.json", "swagger.json",
"jest.config.json", ".eslintrc.json", ".prettierrc.json",
".babelrc.json", "tslint.json", "angular.json",
"firebase.json", "renovate.json", "lerna.json",
"turbo.json", ".stylelintrc.json", "pnpm-lock.json",
"deno.json", "biome.json", "devcontainer.json",
".devcontainer.json", "launch.json", "settings.json",
"extensions.json", "tasks.json", NULL};
/* ── Helper: check if string is in NULL-terminated array ─────────── */
static bool str_in_list(const char *s, const char *const *list) {
@@ -134,8 +121,7 @@ static bool str_in_list(const char *s, const char *const *list) {
/* ── Helper: check if string ends with suffix ────────────── */
static bool ends_with(const char *s, const char *suffix) {
size_t slen = strlen(s);
static bool ends_with(const char *s, size_t slen, const char *suffix) {
size_t sufflen = strlen(suffix);
if (sufflen > slen) {
return false;
@@ -349,7 +335,7 @@ static bool resolve_global_excludes_path(char *out, size_t out_sz) {
}
/* True when rel_path equals a suffix rule or ends with "/" + the rule. */
static bool rel_path_has_skip_suffix(const char *rel_path) {
bool cbm_discover_path_has_skip_suffix(const char *rel_path) {
if (!rel_path) {
return false;
}
@@ -393,15 +379,16 @@ bool cbm_has_ignored_suffix(const char *filename, cbm_index_mode_t mode) {
return false;
}
size_t filename_length = strlen(filename);
for (int i = 0; ALWAYS_IGNORED_SUFFIXES[i]; i++) {
if (ends_with(filename, ALWAYS_IGNORED_SUFFIXES[i])) {
if (ends_with(filename, filename_length, ALWAYS_IGNORED_SUFFIXES[i])) {
return true;
}
}
if (mode != CBM_MODE_FULL) {
for (int i = 0; FAST_IGNORED_SUFFIXES[i]; i++) {
if (ends_with(filename, FAST_IGNORED_SUFFIXES[i])) {
if (ends_with(filename, filename_length, FAST_IGNORED_SUFFIXES[i])) {
return true;
}
}
@@ -549,8 +536,23 @@ static void file_list_add_ignored(file_list_t *fl, const char *rel_path, const c
fl->ignored_count++;
}
/* Nanosecond mtime from a stat result, per platform. Windows' wide_stat only
* carries whole seconds, so the value is seconds scaled to ns there. */
#define DISCOVER_NS_PER_SEC 1000000000LL
static int64_t discover_stat_mtime_ns(const struct stat *st) {
#ifdef __APPLE__
return ((int64_t)st->st_mtimespec.tv_sec * DISCOVER_NS_PER_SEC) +
(int64_t)st->st_mtimespec.tv_nsec;
#elif defined(_WIN32)
return (int64_t)st->st_mtime * DISCOVER_NS_PER_SEC;
#else
return ((int64_t)st->st_mtim.tv_sec * DISCOVER_NS_PER_SEC) + (int64_t)st->st_mtim.tv_nsec;
#endif
}
static void fl_add(file_list_t *fl, const char *abs_path, const char *rel_path, CBMLanguage lang,
int64_t size) {
const struct stat *st) {
int64_t size = (int64_t)st->st_size;
if (fl->max_files >= 0 && fl->count >= fl->max_files) {
fl->limit_exceeded = true;
return;
@@ -605,6 +607,7 @@ static void fl_add(file_list_t *fl, const char *abs_path, const char *rel_path,
fi->rel_path = relative_copy;
fi->language = lang;
fi->size = size;
fi->mtime_ns = discover_stat_mtime_ns(st);
}
/* ── Recursive walk ─────────────────────────────── */
@@ -709,7 +712,7 @@ static int gitignore_chain_result(const gitignore_link_t *link, const char *rel_
* (.worktrees / .claude-worktrees, the worktree entries in ALWAYS_SKIP_DIRS)
* contain parallel checkouts of the same repo whose indexing would duplicate
* the whole codebase (#802). */
static bool is_safety_core_dir(const char *name) {
bool cbm_discover_is_safety_core_directory(const char *name) {
static const char *const SAFETY_CORE_DIRS[] = {".git", "node_modules", ".worktrees",
".claude-worktrees", NULL};
return str_in_list(name, SAFETY_CORE_DIRS);
@@ -759,12 +762,12 @@ static bool should_skip_directory(const char *entry_name, const char *rel_path,
const cbm_gitignore_t *global_gi,
const cbm_gitignore_t *cbmignore) {
if (cbm_should_skip_dir(entry_name, opts ? opts->mode : CBM_MODE_FULL) ||
rel_path_has_skip_suffix(rel_path)) {
cbm_discover_path_has_skip_suffix(rel_path)) {
/* #500: a .cbmignore negation (e.g. "!obj/") whose rule is the last
* match for this dir un-skips a built-in skip-list dir — except the
* non-negatable safety core. Fall through so .gitignore/global/local
* rules still apply to the un-skipped dir. */
bool unskipped = cbmignore && !is_safety_core_dir(entry_name) &&
bool unskipped = cbmignore && !cbm_discover_is_safety_core_directory(entry_name) &&
cbm_gitignore_match_result(cbmignore, rel_path, true) < 0;
if (!unskipped) {
return true;
@@ -825,60 +828,11 @@ static const char *file_skip_reason(const char *entry_name, const char *rel_path
return global_ignored ? "gitignore" : NULL;
}
/* Detect language for a file, handling .m disambiguation and JSON filtering. */
/* Detect language for a file: its name, and its first bytes where the name is
* not enough (cbm_language_classify; the pinned test-impact inventory uses the
* same rule on git blobs). */
static CBMLanguage detect_file_language(const char *entry_name, const char *abs_path) {
CBMLanguage lang = cbm_language_for_filename(entry_name);
if (lang == CBM_LANG_COUNT) {
/* Filename/extension detection failed: fall back to a conservative
* shebang probe so extensionless scripts get indexed (#1199). Filename
* detection stays authoritative — this runs only when it returns
* unknown. */
return cbm_language_from_shebang(abs_path);
}
/* Special: .m files need content-based disambiguation */
const char *dot = strrchr(entry_name, '.');
if (dot && strcmp(dot, ".m") == 0) {
lang = cbm_disambiguate_m(abs_path);
}
/* Special: .cls is shared by ObjectScript UDL, Apex and VB6 class modules */
if (dot && strcmp(dot, ".cls") == 0) {
lang = cbm_disambiguate_cls(abs_path);
}
/* Special: .inc is shared by BitBake and ObjectScript include files */
if (dot && strcmp(dot, ".inc") == 0) {
lang = cbm_disambiguate_inc(abs_path);
}
/* Special: .cfc components may be script-dialect or tag-dialect (<cfcomponent>) */
if (dot && strcmp(dot, ".cfc") == 0) {
lang = cbm_disambiguate_cfc(abs_path);
}
/* Special: .frm is shared by FORM and VB6 forms (#721) */
if (dot && strcmp(dot, ".frm") == 0) {
lang = cbm_disambiguate_frm(abs_path);
}
/* Special: .res is also a binary Godot / Windows resource (#2176) */
if (dot && strcmp(dot, ".res") == 0) {
lang = cbm_disambiguate_res(abs_path);
}
/* Special: ObjectScript Studio Export XML (<Export generator="...">) is
* detected by content; otherwise .xml stays XML. */
if (lang == CBM_LANG_XML) {
FILE *xf = cbm_fopen(abs_path, "r");
if (xf) {
char xbuf[CBM_SZ_256];
size_t xn = fread(xbuf, SKIP_ONE, sizeof(xbuf) - SKIP_ONE, xf);
(void)fclose(xf);
xbuf[xn] = '\0';
if (strstr(xbuf, "<Export generator=")) {
return CBM_LANG_OBJECTSCRIPT_EXPORT;
}
}
}
/* Check ignored JSON files */
if (lang == CBM_LANG_JSON && str_in_list(entry_name, IGNORED_JSON_FILES)) {
return CBM_LANG_COUNT;
}
return lang;
return cbm_language_for_file(entry_name, abs_path);
}
/* UTF-8-safe stat: wide API on Windows, regular stat on POSIX. */
@@ -939,9 +893,10 @@ static void walk_dir_process_file(const char *abs_path, const char *rel_path, co
const cbm_discover_opts_t *opts,
const gitignore_link_t *ignore_chain,
const cbm_gitignore_t *global_gi,
const cbm_gitignore_t *cbmignore, off_t size, file_list_t *out) {
const cbm_gitignore_t *cbmignore, const struct stat *st,
file_list_t *out) {
const char *skip_reason =
file_skip_reason(name, rel_path, opts, ignore_chain, global_gi, cbmignore, size);
file_skip_reason(name, rel_path, opts, ignore_chain, global_gi, cbmignore, st->st_size);
if (skip_reason) {
/* Deliberately not indexed (#963) — record so callers can surface it.
* Unsupported-language files below are NOT recorded: "no grammar for
@@ -954,7 +909,7 @@ static void walk_dir_process_file(const char *abs_path, const char *rel_path, co
if (lang == CBM_LANG_COUNT) {
return;
}
fl_add(out, abs_path, rel_path, lang, size);
fl_add(out, abs_path, rel_path, lang, st);
}
typedef struct {
@@ -1066,7 +1021,7 @@ static void walk_dir_process_entry(cbm_dirent_t *entry, const walk_frame_t *fram
}
} else if (S_ISREG(st.st_mode)) {
walk_dir_process_file(abs_path, rel_path, entry->name, opts, frame->ignore_chain, global_gi,
cbmignore, st.st_size, out);
cbmignore, &st, out);
}
}
+28
View File
@@ -18,6 +18,7 @@
/* Use the existing CBMLanguage enum from extraction layer */
#include "cbm.h"
#include "foundation/index_policy.h"
#include "discover/userconfig.h"
/* ── Language detection ──────────────────────────────────────────── */
@@ -83,6 +84,32 @@ CBMLanguage cbm_language_from_shebang(const char *path);
* On read failure, defaults to CBM_LANG_CFSCRIPT. */
CBMLanguage cbm_disambiguate_cfc(const char *path);
/* The language of a file from its base name and, where the name is not enough,
* its first bytes: the one rule discovery and the pinned test-impact inventory
* share. cbm_language_probe_bytes says how many leading bytes the name needs
* (0: the name decides). cbm_language_classify takes those bytes raw, `more`
* when the file has bytes beyond them, and `readable` false when they could
* not be read (each probe then keeps the name's default, as a failed open
* always did). The name lookup consults the installed user language config. */
size_t cbm_language_probe_bytes(const char *filename);
CBMLanguage cbm_language_classify(const char *filename, const unsigned char *head, size_t head_len,
bool more, bool readable);
/* The same with the language config given explicitly (NULL: none) instead of
* the installed process-wide one, for a caller that classifies a snapshot
* under that snapshot's config while another index may have its own
* installed. */
size_t cbm_language_probe_bytes_with(const cbm_userconfig_t *config, const char *filename);
CBMLanguage cbm_language_classify_with(const cbm_userconfig_t *config, const char *filename,
const unsigned char *head, size_t head_len, bool more,
bool readable);
/* The most leading bytes any name asks cbm_language_probe_bytes for. */
#define CBM_LANGUAGE_PROBE_MAX 16384
/* cbm_language_classify on the file at `path`, read raw. */
CBMLanguage cbm_language_for_file(const char *filename, const char *path);
/* ── Gitignore pattern matching ──────────────────────────────────── */
typedef struct cbm_gitignore cbm_gitignore_t;
@@ -147,6 +174,7 @@ typedef struct {
char *rel_path; /* relative to repo root (heap-allocated) */
CBMLanguage language; /* detected language */
int64_t size; /* file size in bytes */
int64_t mtime_ns; /* modification time, ns (whole seconds on Windows) */
} cbm_file_info_t;
typedef struct {
+9
View File
@@ -0,0 +1,9 @@
#ifndef CBM_DISCOVER_POLICY_INTERNAL_H
#define CBM_DISCOVER_POLICY_INTERNAL_H
#include <stdbool.h>
/* Pure legacy policy predicates; callers provide live terminated strings. */
bool cbm_discover_path_has_skip_suffix(const char *relative_path);
bool cbm_discover_is_safety_core_directory(const char *name);
#endif
+8 -266
View File
@@ -13,8 +13,9 @@
*/
#include "foundation/constants.h"
#include "foundation/compat_fs.h"
#include "discover/gitignore_internal.h"
enum { GI_INIT_CAP = 16, GI_CHAR_IDX1 = 1, GI_CHAR_IDX2 = 2, GI_SKIP3 = 3 };
enum { GI_INIT_CAP = 16 };
#include "discover/discover.h"
#include <ctype.h>
@@ -24,236 +25,22 @@ enum { GI_INIT_CAP = 16, GI_CHAR_IDX1 = 1, GI_CHAR_IDX2 = 2, GI_SKIP3 = 3 };
/* ── Pattern representation ──────────────────────────────────────── */
typedef struct {
char *pattern; /* the glob pattern (normalized) */
bool negated; /* starts with ! */
bool dir_only; /* ends with / */
bool rooted; /* contains / (anchored to root) */
} gi_pattern_t;
struct cbm_gitignore {
gi_pattern_t *patterns;
int count;
int capacity;
};
/* ── Pattern matching engine ─────────────────────────────────────── */
/* Backtracking budget. `**` retries the remainder at every position, and
* consecutive literal + `**` groups multiply, so cost is exponential in the
* number of groups. Patterns come from a committed .gitignore / .cbmignore and
* every discovered path is matched against every pattern, so a small ignore file
* can make discovery take unbounded time. Cap the match steps per
* (pattern, path) pair.
*
* Exhausting the budget reports "no match", so a pathological pattern fails to
* ignore rather than ignoring the wrong files: the file gets indexed, which is
* the recoverable direction. The cap is far above any real pattern — ordinary
* globs finish in tens of steps. */
enum { GI_MATCH_MAX_STEPS = 20000 };
/* Forward declaration for recursive calls. */
static bool glob_match(const char *pat, const char *str,
int *budget); // NOLINT(misc-no-recursion)
/* Match a ** (doublestar-slash) pattern: try rest at every / boundary. */
static bool glob_match_doublestar_slash(const char *pat, // NOLINT(misc-no-recursion)
const char *str, int *budget) {
if (glob_match(pat, str, budget)) {
return true;
}
for (const char *s = str; *s; s++) {
if (*s == '/' && glob_match(pat, s + SKIP_ONE, budget)) {
return true;
}
}
return false;
}
/* Match a ** (doublestar) followed by non-slash: try at every position. */
static bool glob_match_doublestar_any(const char *pat, // NOLINT(misc-no-recursion)
const char *str, int *budget) {
for (const char *s = str;; s++) {
if (glob_match(pat, s, budget)) {
return true;
}
if (!*s) {
return false;
}
}
}
/* Match a * (single star): match any sequence not containing /. */
static bool glob_match_star(const char *pat, const char *str,
int *budget) { // NOLINT(misc-no-recursion)
for (const char *s = str;; s++) {
if (glob_match(pat, s, budget)) {
return true;
}
if (!*s || *s == '/') {
return false;
}
}
}
/* Match a [...] character class at current position.
* Returns true if matched. Advances *pat_out past the closing ']'. */
static bool glob_match_charclass(const char *pat, char ch, const char **pat_out) {
bool negate_class = false;
if (*pat == '!' || *pat == '^') {
negate_class = true;
pat++;
}
bool matched = false;
char prev = 0;
while (*pat && *pat != ']') {
if (*pat == '-' && prev && pat[GI_CHAR_IDX1] && pat[GI_CHAR_IDX1] != ']') {
pat++;
if (ch >= prev && ch <= *pat) {
matched = true;
}
prev = *pat;
pat++;
} else {
if (ch == *pat) {
matched = true;
}
prev = *pat;
pat++;
}
}
if (*pat == ']') {
pat++;
}
*pat_out = pat;
return negate_class ? !matched : matched;
}
/*
* Match a glob pattern against a string.
* Handles: * (non-slash), ** (any path), ? (single non-slash), [class]
*/
/* Handle ** at current position. Returns match result. */
static bool glob_match_doublestar(const char *pat, const char *str,
int *budget) { // NOLINT(misc-no-recursion)
if (pat[GI_CHAR_IDX2] == '/') {
return glob_match_doublestar_slash(pat + GI_SKIP3, str, budget);
}
if (pat[GI_CHAR_IDX2] == '\0') {
return true;
}
return glob_match_doublestar_any(pat + GI_CHAR_IDX2, str, budget);
}
static bool glob_match(const char *pat, const char *str,
int *budget) { // NOLINT(misc-no-recursion)
if (--*budget <= 0) {
return false; /* budget exhausted — see GI_MATCH_MAX_STEPS */
}
while (*pat && *str) {
if (pat[0] == '*' && pat[GI_CHAR_IDX1] == '*') {
return glob_match_doublestar(pat, str, budget);
}
if (*pat == '*') {
return glob_match_star(pat + SKIP_ONE, str, budget);
}
if (*pat == '?') {
if (*str == '/') {
return false;
}
pat++;
str++;
continue;
}
if (*pat == '[') {
const char *new_pat = NULL;
if (!glob_match_charclass(pat + SKIP_ONE, *str, &new_pat)) {
return false;
}
pat = new_pat;
str++;
continue;
}
if (*pat != *str) {
return false;
}
pat++;
str++;
}
while (*pat == '*') {
pat++;
}
return *pat == '\0' && *str == '\0';
}
/* Match one pattern against one path with a fresh backtracking budget. Not
* recursive itself — it seeds the budget and hands off to the engine. */
static bool glob_match_bounded(const char *pat, const char *str) {
int budget = GI_MATCH_MAX_STEPS;
return glob_match(pat, str, &budget);
}
/* ── Pattern parsing ─────────────────────────────────────────────── */
static void gi_add_pattern(cbm_gitignore_t *gi, const char *line, int len) {
/* Trim trailing whitespace */
while (len > 0 && (line[len - SKIP_ONE] == ' ' || line[len - SKIP_ONE] == '\t' ||
line[len - SKIP_ONE] == '\r')) {
len--;
}
if (len == 0) {
gi_view_t view;
if (!gi_normalize(line, (size_t)len, &view, NULL) || !view.length) {
return;
}
gi_pattern_t p = {0};
/* Check for negation */
const char *start = line;
if (*start == '!') {
p.negated = true;
start++;
len--;
}
if (len == 0) {
return;
}
/* Check for trailing / (directory-only) */
if (start[len - SKIP_ONE] == '/') {
p.dir_only = true;
len--;
}
if (len == 0) {
return;
}
/* Check for leading / (rooted) */
if (*start == '/') {
p.rooted = true;
start++;
len--;
}
if (len == 0) {
return;
}
/* Check if pattern contains / anywhere (makes it rooted) */
if (!p.rooted) {
for (int i = 0; i < len; i++) {
if (start[i] == '/') {
p.rooted = true;
break;
}
}
}
gi_pattern_t p = {.negated = view.negated, .dir_only = view.dir_only, .rooted = view.rooted};
const char *start = view.text;
len = (int)view.length;
/* Copy pattern */
p.pattern = malloc(len + SKIP_ONE);
@@ -345,56 +132,11 @@ cbm_gitignore_t *cbm_gitignore_load(const char *path) {
return gi;
}
/* Match a non-rooted pattern against basename and path suffixes. */
static bool match_unrooted(const char *pattern, const char *rel_path, const char *basename) {
if (glob_match_bounded(pattern, basename)) {
return true;
}
if (!strchr(rel_path, '/')) {
return false;
}
/* Try matching at every / boundary */
const char *s = rel_path;
while (*s) {
if (glob_match_bounded(pattern, s)) {
return true;
}
const char *next = strchr(s, '/');
if (!next) {
break;
}
s = next + SKIP_ONE;
}
return false;
}
int cbm_gitignore_match_result(const cbm_gitignore_t *gi, const char *rel_path, bool is_dir) {
if (!gi || !rel_path) {
return 0;
}
/* Extract the basename for non-rooted pattern matching */
const char *basename = strrchr(rel_path, '/');
basename = basename ? basename + SKIP_ONE : rel_path;
int matched = 0;
for (int i = 0; i < gi->count; i++) {
const gi_pattern_t *p = &gi->patterns[i];
if (p->dir_only && !is_dir) {
continue;
}
bool this_match = p->rooted ? glob_match_bounded(p->pattern, rel_path)
: match_unrooted(p->pattern, rel_path, basename);
if (this_match) {
matched = p->negated ? -1 : 1;
}
}
return matched;
return gi_core_match(gi->patterns, (size_t)gi->count, rel_path, is_dir, NULL);
}
bool cbm_gitignore_matches(const cbm_gitignore_t *gi, const char *rel_path, bool is_dir) {
+114
View File
@@ -0,0 +1,114 @@
#include "discover/gitignore_internal.h"
#include <limits.h>
#include <string.h>
static bool gi_limits_valid(const cbm_ignore_checked_limits_t *limits) {
return limits && limits->max_bytes && limits->max_work && limits->max_patterns &&
limits->max_patterns <= INT_MAX && limits->max_arena_bytes && limits->max_depth &&
limits->max_depth <= CBM_IGNORE_CHECKED_DEPTH_MAX;
}
static cbm_ignore_checked_status_t gi_open(const cbm_ignore_checked_limits_t *limits,
cbm_ignore_checked_t **out,
cbm_ignore_checked_error_t *error, bool fault) {
if (out) {
*out = NULL;
}
gi_error(error, CBM_IGNORE_CHECKED_OK, CBM_IGNORE_CAP_NONE, SIZE_MAX);
if (!out || !gi_limits_valid(limits)) {
gi_error(error, CBM_IGNORE_CHECKED_INVALID, CBM_IGNORE_CAP_NONE, SIZE_MAX);
return CBM_IGNORE_CHECKED_INVALID;
}
if (limits->max_arena_bytes < sizeof(cbm_ignore_checked_t)) {
gi_error(error, CBM_IGNORE_CHECKED_LIMIT, CBM_IGNORE_CAP_ARENA, SIZE_MAX);
return CBM_IGNORE_CHECKED_LIMIT;
}
CBMArena arena;
cbm_arena_init_lazy(&arena, CBM_ARENA_DEFAULT_BLOCK_SIZE);
cbm_ignore_checked_t *owner = fault ? NULL : cbm_arena_alloc(&arena, sizeof(*owner));
if (!owner) {
cbm_arena_destroy(&arena);
gi_error(error, CBM_IGNORE_CHECKED_OOM, CBM_IGNORE_CAP_NONE, SIZE_MAX);
return CBM_IGNORE_CHECKED_OOM;
}
memset(owner, 0, sizeof(*owner));
owner->arena = arena;
owner->limits = *limits;
owner->usage.arena_requested_bytes = sizeof(*owner);
gi_error(&owner->error, CBM_IGNORE_CHECKED_OK, CBM_IGNORE_CAP_NONE, SIZE_MAX);
*out = owner;
return CBM_IGNORE_CHECKED_OK;
}
cbm_ignore_checked_status_t cbm_ignore_checked_open(const cbm_ignore_checked_limits_t *limits,
cbm_ignore_checked_t **out,
cbm_ignore_checked_error_t *error) {
return gi_open(limits, out, error, false);
}
static bool gi_path_valid(gi_guard_t *guard, const char *path, size_t length) {
for (size_t i = 0; i <= length; i++) {
guard->offset = i;
if (!gi_work(guard, 1)) {
return false;
}
if ((path[i] == '\0') != (i == length)) {
return gi_fail(guard, CBM_IGNORE_CHECKED_UNSUPPORTED, CBM_IGNORE_CAP_NONE);
}
}
guard->offset = SIZE_MAX;
return true;
}
cbm_ignore_checked_status_t cbm_ignore_checked_match(
cbm_ignore_checked_t *owner, const cbm_ignore_program_t *program, const char *path,
size_t length, bool is_directory, const cbm_ignore_checked_control_t *control,
cbm_ignore_decision_t *decision, cbm_ignore_checked_error_t *error) {
if (decision) {
*decision = CBM_IGNORE_UNAVAILABLE;
}
gi_error(error, CBM_IGNORE_CHECKED_OK, CBM_IGNORE_CAP_NONE, SIZE_MAX);
if (!owner || !program || !path || length == SIZE_MAX || !control || !control->deadline_ms ||
!decision || program->owner != owner) {
gi_error(error, CBM_IGNORE_CHECKED_INVALID, CBM_IGNORE_CAP_NONE, SIZE_MAX);
return CBM_IGNORE_CHECKED_INVALID;
}
gi_guard_t guard;
if (!gi_begin(owner, control, &guard) || !gi_bytes(&guard, length + 1) ||
!gi_path_valid(&guard, path, length)) {
return gi_finish(owner, error);
}
int result = gi_core_match(program->rows, program->count, path, is_directory, &guard);
if (gi_poll(&guard)) {
*decision = (cbm_ignore_decision_t)result;
}
return gi_finish(owner, error);
}
bool cbm_ignore_checked_usage(const cbm_ignore_checked_t *owner, cbm_ignore_checked_usage_t *out) {
if (!out) {
return false;
}
memset(out, 0, sizeof(*out));
if (!owner) {
return false;
}
*out = owner->usage;
return true;
}
void cbm_ignore_checked_free(cbm_ignore_checked_t *owner) {
if (owner) {
/* The owner itself lives in the arena being destroyed. */
CBMArena arena = owner->arena;
cbm_arena_destroy(&arena);
}
}
#if defined(CBM_ENABLE_TEST_SEAMS) && CBM_ENABLE_TEST_SEAMS
cbm_ignore_checked_status_t cbm_ignore_checked_open_oom_for_tests(
const cbm_ignore_checked_limits_t *limits, cbm_ignore_checked_t **out,
cbm_ignore_checked_error_t *error) {
return gi_open(limits, out, error, true);
}
#endif
+137
View File
@@ -0,0 +1,137 @@
/* Checked in-memory ignore parsing and matching; no filesystem/MCP dependency. */
#ifndef CBM_GITIGNORE_CHECKED_H
#define CBM_GITIGNORE_CHECKED_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
typedef struct cbm_ignore_checked cbm_ignore_checked_t;
typedef struct cbm_ignore_program cbm_ignore_program_t;
typedef enum {
CBM_IGNORE_CHECKED_OK = 0,
CBM_IGNORE_CHECKED_INVALID,
CBM_IGNORE_CHECKED_UNSUPPORTED,
CBM_IGNORE_CHECKED_LIMIT,
CBM_IGNORE_CHECKED_OOM,
CBM_IGNORE_CHECKED_CANCELLED,
CBM_IGNORE_CHECKED_DEADLINE
} cbm_ignore_checked_status_t;
typedef enum {
CBM_IGNORE_CAP_NONE = 0,
CBM_IGNORE_CAP_BYTES,
CBM_IGNORE_CAP_PATTERNS,
CBM_IGNORE_CAP_WORK,
CBM_IGNORE_CAP_ARENA,
CBM_IGNORE_CAP_DEPTH,
CBM_IGNORE_CAP_ATTEMPT,
CBM_IGNORE_CAP_REPRESENTATION
} cbm_ignore_cap_t;
typedef enum {
CBM_IGNORE_REINCLUDED = -1,
CBM_IGNORE_NO_OPINION = 0,
CBM_IGNORE_IGNORED = 1,
CBM_IGNORE_UNAVAILABLE = 2 /* every failure clears the decision to this */
} cbm_ignore_decision_t;
enum { CBM_IGNORE_CHECKED_DEPTH_MAX = 32 };
typedef struct {
uint64_t max_bytes; /* aggregate parse lengths and match path lengths + NUL */
uint64_t max_work; /* aggregate charged work, including parsing */
size_t max_patterns; /* cumulative reservations; positive, <= INT_MAX */
size_t max_arena_bytes; /* cumulative logical requests, not resident memory */
unsigned max_depth; /* 1..CBM_IGNORE_CHECKED_DEPTH_MAX active glob entries */
} cbm_ignore_checked_limits_t;
typedef struct {
uint64_t deadline_ms; /* positive absolute cbm_now_ms deadline */
bool (*cancelled)(void *context); /* optional prompt, pure, non-reentrant */
void *context; /* borrowed for this synchronous call only */
} cbm_ignore_checked_control_t;
typedef struct {
cbm_ignore_checked_status_t status;
cbm_ignore_cap_t cap; /* LIMIT only; otherwise NONE */
size_t byte_offset; /* input offset when known, otherwise SIZE_MAX */
char diagnostic[128]; /* bounded owned text, never input contents */
} cbm_ignore_checked_error_t;
typedef struct {
uint64_t bytes_reserved;
uint64_t work_used;
size_t patterns_reserved;
size_t arena_requested_bytes;
cbm_ignore_checked_status_t terminal_status;
} cbm_ignore_checked_usage_t;
typedef struct {
const cbm_ignore_program_t *program; /* borrowed until owner free */
size_t pattern_count; /* zero is successful empty/comment-only normalization */
} cbm_ignore_checked_parse_result_t;
/* All caps positive; copies limits. Initializes optional error and clears *out.
* No callbacks/IO. Failure publishes no owner. Uses a CBMArena, including owner.
*/
cbm_ignore_checked_status_t cbm_ignore_checked_open(const cbm_ignore_checked_limits_t *limits,
cbm_ignore_checked_t **out,
cbm_ignore_checked_error_t *error);
/* Exact span: NULL permitted only for length==0; terminator not required.
* Embedded NUL => UNSUPPORTED, never truncation. Result is required/cleared.
* Copies normalized patterns; caller may discard bytes after return.
* Every started failure is terminal for owner; earlier programs remain allocated
* but no further parse/match can succeed. No partial program is published.
*/
cbm_ignore_checked_status_t cbm_ignore_checked_parse(cbm_ignore_checked_t *owner, const void *bytes,
size_t length,
const cbm_ignore_checked_control_t *control,
cbm_ignore_checked_parse_result_t *out,
cbm_ignore_checked_error_t *error);
/* program must belong to this live owner. path is readable for length+1 bytes,
* with path[length]==0 and no earlier NUL. Empty path is permitted. No decoding
* or path normalization. Inputs borrowed for this call; no match allocation.
* Required decision is initialized to UNAVAILABLE and set only on full success.
*/
cbm_ignore_checked_status_t cbm_ignore_checked_match(
cbm_ignore_checked_t *owner, const cbm_ignore_program_t *program, const char *path,
size_t length, bool is_directory, const cbm_ignore_checked_control_t *control,
cbm_ignore_decision_t *decision, cbm_ignore_checked_error_t *error);
/* Read-only; clears output, false for NULL owner/output. Does not consume quotas,
* poll or clear a terminal failure. No operation may overlap any other call.
*/
bool cbm_ignore_checked_usage(const cbm_ignore_checked_t *owner, cbm_ignore_checked_usage_t *out);
/* NULL-safe; releases all programs/arena at once. No callback/IO or cancellation. */
void cbm_ignore_checked_free(cbm_ignore_checked_t *owner);
/* Test-only per-call allocation failure seams.
* No persistent/global fault, raw allocator replacement or production option.
*/
#if defined(CBM_ENABLE_TEST_SEAMS) && CBM_ENABLE_TEST_SEAMS
typedef enum {
CBM_IGNORE_PARSE_FAULT_NONE = 0,
CBM_IGNORE_PARSE_FAULT_PROGRAM,
CBM_IGNORE_PARSE_FAULT_PATTERN
} cbm_ignore_parse_fault_t;
/* Simulate NULL at the owner's first arena allocation, after normal quota checks. */
cbm_ignore_checked_status_t cbm_ignore_checked_open_oom_for_tests(
const cbm_ignore_checked_limits_t *limits, cbm_ignore_checked_t **out,
cbm_ignore_checked_error_t *error);
/* PROGRAM: program/header+row-storage allocation. PATTERN: first effective
* pattern-string allocation. Unreached boundary has no effect. Same normal
* implementation/error path; no extra allocations or changed matching logic.
*/
cbm_ignore_checked_status_t cbm_ignore_checked_parse_fault_for_tests(
cbm_ignore_checked_t *owner, const void *bytes, size_t length,
const cbm_ignore_checked_control_t *control, cbm_ignore_parse_fault_t fault,
cbm_ignore_checked_parse_result_t *out, cbm_ignore_checked_error_t *error);
#endif
#endif

Some files were not shown because too many files have changed in this diff Show More