Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ CheckOptions:
readability-magic-numbers.IgnoredIntegerValues: ""
readability-magic-numbers.IgnoredFloatingPointValues: ""
bugprone-easily-swappable-parameters.MinimumLength: 3
misc-include-cleaner.IgnoreHeaders: "sys/.*|mach/.*|dispatch/.*|_types/.*|Availability\\.h|secure/.*|_.*\\.h|zconf\\.h"
misc-include-cleaner.IgnoreHeaders: "sys/.*|asm-generic/.*|mach/.*|dispatch/.*|_types/.*|Availability\\.h|secure/.*|_.*\\.h|zconf\\.h"
readability-function-cognitive-complexity.Threshold: 25
readability-function-size.StatementThreshold: 200
readability-function-size.LineThreshold: 400
Expand Down
69 changes: 64 additions & 5 deletions .github/workflows/_security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ jobs:
codeql-gate:
runs-on: ubuntu-latest
timeout-minutes: 240
# Without security-events:read this job inherits the workflow's
# contents:read, the code-scanning alert API answers 403, and the gate
# below reported "0 alerts" on every run it has ever made. actions:read is
# what the CodeQL-run poll above needs for the same reason.
permissions:
contents: read
security-events: read
actions: read
steps:
- name: Wait for CodeQL on current commit (max 150 min)
env:
Expand Down Expand Up @@ -78,17 +86,68 @@ jobs:
- name: Check for open code scanning alerts
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# A PR is judged by its own CodeQL analysis (refs/pull/N/merge);
# without a ref the API answers for the default branch, so a fix in
# the PR could never clear the gate before it merged.
ALERT_REF: ${{ github.event_name == 'pull_request' && format('refs/pull/{0}/merge', github.event.pull_request.number) || '' }}
run: |
# FAIL CLOSED. The previous form was `gh api ... 2>/dev/null || echo "0"`,
# which looks defensive and is the opposite. gh writes the API error
# BODY to stdout, so a 403 made ALERTS the string
# {"message":"Resource not accessible by integration",...,"status":"403"}0
# every `[` comparison then failed as non-integer, the `if` took its
# false branch, and the step printed "CodeQL gate passed (0 alerts)"
# and exited 0. That is exactly how a security gate should NEVER fail:
# silently, in the direction of "everything is fine". An unreadable
# alert list is now an error, not a zero.
# NOTE: this runs in a command substitution, so it must `return`, not
# `exit` -- an `exit` here would only end the subshell and let the
# caller continue with an empty count, recreating the very bug this
# replaces. The callers below check the status explicitly.
# Scope: CodeQL alerts only, outside vendored third-party code.
# Scorecard findings are repository-hygiene signals reported by their
# own workflow, and alerts in vendored grammars/libraries are
# upstream code this repo does not patch, so neither blocks a merge.
# --paginate: the API returns 30 alerts per page by default, so an
# unpaginated count silently stopped at 30.
ALERTS_URL='repos/${{ github.repository }}/code-scanning/alerts?state=open&per_page=100'
[ -n "$ALERT_REF" ] && ALERTS_URL="$ALERTS_URL&ref=$ALERT_REF"
GATE_FILTER='.[] | select(.tool.name == "CodeQL")
| select((.most_recent_instance.location.path // "")
| test("^(internal/cbm/)?vendored/") | not)'
count_open_alerts() {
local body status
body=$(gh api --paginate \
"$ALERTS_URL" \
--jq "$GATE_FILTER | .number" 2>/dev/null)
status=$?
if [ $status -ne 0 ]; then
echo "BLOCKED: cannot read code scanning alerts (gh exit $status)." >&2
echo " Refusing to treat an unreadable alert list as zero." >&2
echo " Most likely cause: the job lacks security-events:read." >&2
return 1
fi
local count
count=$(printf '%s' "$body" | grep -c . || true)
case "$count" in
''|*[!0-9]*)
echo "BLOCKED: alert count was not a number: ${count:-<empty>}" >&2
return 1
;;
esac
printf '%s' "$count"
}
echo "Waiting 60s for alert API to settle..."
sleep 60
ALERTS=$(gh api 'repos/${{ github.repository }}/code-scanning/alerts?state=open' --jq 'length' 2>/dev/null || echo "0")
ALERTS=$(count_open_alerts) || exit 1
sleep 15
ALERTS2=$(gh api 'repos/${{ github.repository }}/code-scanning/alerts?state=open' --jq 'length' 2>/dev/null || echo "0")
ALERTS2=$(count_open_alerts) || exit 1
[ "$ALERTS" -lt "$ALERTS2" ] && ALERTS=$ALERTS2
if [ "$ALERTS" -gt 0 ]; then
echo "BLOCKED: $ALERTS open alert(s)"
gh api 'repos/${{ github.repository }}/code-scanning/alerts?state=open' \
--jq '.[] | " #\(.number) [\(.rule.security_severity_level // .rule.severity)] \(.rule.id) — \(.most_recent_instance.location.path):\(.most_recent_instance.location.start_line)"' 2>/dev/null || true
gh api --paginate \
"$ALERTS_URL" \
--jq "$GATE_FILTER"' | " #\(.number) [\(.rule.security_severity_level // .rule.severity)] \(.rule.id) — \(.most_recent_instance.location.path):\(.most_recent_instance.location.start_line)"' 2>/dev/null || true
exit 1
fi
echo "=== CodeQL gate passed (0 alerts) ==="
echo "=== CodeQL gate passed (0 gating CodeQL alerts) ==="
11 changes: 11 additions & 0 deletions .github/workflows/_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,17 @@ jobs:
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache

# The harness prints only each failing suite's last 15 lines, which cuts
# off sanitizer reports. Keep the full per-suite logs of a failed shard.
- name: Upload test logs (on failure)
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: test-logs-${{ matrix.os }}-${{ matrix.msystem }}-${{ strategy.job-index }}
path: build/c/test-logs/*.log
retention-days: 7
if-no-files-found: ignore

- name: Shard manifest (completeness proof)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ on:
# - "src/**/*.js"
# - "src/**/*.jsx"

# Deny by default; each job grants only what it needs.
permissions: {}

jobs:
claude-review:
# Optional: Filter by PR author
Expand All @@ -27,13 +30,13 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
fetch-depth: 1

- name: Run Claude Code Review
id: claude-review
uses: anthropics/claude-code-action@v1
uses: anthropics/claude-code-action@fd1c128679612beff4ca259c78021c506e8aa7a7 # v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/claude.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ on:
pull_request_review:
types: [submitted]

# Deny by default; each job grants only what it needs.
permissions: {}

jobs:
claude:
if: |
Expand All @@ -26,13 +29,13 @@ jobs:
actions: read # Required for Claude to read CI results on PRs
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
fetch-depth: 1

- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@v1
uses: anthropics/claude-code-action@fd1c128679612beff4ca259c78021c506e8aa7a7 # v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}

Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/dry-run.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ jobs:
# ── Security (independent island — does not block main pipeline) ──
security:
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,
# so withholding this here fails the whole run at startup.
permissions:
contents: read
security-events: read
actions: read
secrets: inherit

# ── Lint (cppcheck + clang-format) ────────────────────────────
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ concurrency:
jobs:
security:
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,
# so withholding this here fails the whole run at startup.
permissions:
contents: read
security-events: read
actions: read
secrets: inherit

lint:
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ jobs:
# ── Security (starts immediately, blocks only verify) ───────────
security:
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,
# so withholding this here fails the whole run at startup.
permissions:
contents: read
security-events: read
actions: read
secrets: inherit

# ── 0. Preflight: refuse malformed dispatch inputs ──────────────
Expand Down
12 changes: 12 additions & 0 deletions BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@

## Open

- [ ] Add opt-in, single-run index metrics for performance diagnosis.
- Extend `index` with a one-shot flag (for example `--metrics-out PATH`),
rather than enabling persistent daemon timing by default.
- Emit versioned JSON for that invocation only: source revision, index mode,
discovery/index/staging/publish wall times, queue and lock waits, worker
peak/aggregate memory, files/definitions/edges, DB size, and outcome.
- Keep ordinary stdout and daemon logs quiet; no source contents or secrets
may enter the result. Reuse this result schema from the planned
`benchmark` command rather than introducing a second format.
- Acceptance: a normal index produces no metrics artifact; an opted-in
index produces one complete artifact even for a failed or cancelled run.

- [ ] Selectively evaluate remaining upstream fixes from
`../codebase-memory-mcp` before merging into the CLI fork. Do not
cherry-pick MCP-only changes or overwrite CP89/release-tooling work; apply
Expand Down
79 changes: 75 additions & 4 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ pipeline {
parameters {
string(name: 'CBM_RELEASE_VERSION', defaultValue: '',
description: 'Optional version passed to the release build, for example v0.11.0-rc.2')
string(name: 'CBM_TEST_SUITES', defaultValue: 'cli daemon daemon_ipc pipeline',
description: 'Focused incremental test suites; leave empty for the full gate')
string(name: 'CBM_TEST_SUITES', defaultValue: '',
description: '''Focused incremental test suites; leave blank for the full gate.
Reference list (space-separated):
arena hash_table dyn_array str_intern log str_util workspace platform diagnostics complexity subprocess private_file_lock lock_registry dump_verify ac extraction extraction_inheritance extraction_imports parse_coverage grammar_regression grammar_labels grammar_imports store_nodes store_edges store_search store_bulk store_pragmas store_checkpoint dump_verify_io cypher index_supervisor daemon project_lock version_cohort daemon_version daemon_runtime daemon_application daemon_bootstrap daemon_ipc language userconfig gitignore git_context discover graph_buffer registry pipeline importance index_format pipeline_semantic_manifest_repro call_reference_contract call_reference_language_complex_contract repro_call_scope_usages repro_call_argument_usages repro_reference_precision repro_call_argument_matrix_a repro_call_argument_matrix_b repro_call_node_behaviors repro_language_registry repro_call_node_manifest repro_lsp_ordered_signatures repro_lsp_ordered_local repro_ts_overload_return_chains repro_harness_cleanup repro_runner_filter cross_repo index_resilience fqn route_canon path_alias watcher lz4 zstd sqlite_writer artifact scope type_rep go_lsp c_lsp php_lsp cs_lsp cs_lsp_bench perl_lsp py_lsp kotlin_lsp rust_lsp py_lsp_bench py_lsp_stress py_lsp_scale ts_lsp java_lsp java_lsp_coverage store_arch traces configlink infrascan cli agent_clients legacy_agent_profiles config_json_like config_toml_edit config_yaml_edit config_text_edit activation_transaction system_info worker_pool parallel repro_lexical_binding_precision slab_alloc mem ui httpd security yaml semantic ast_profile simhash stack_overflow_a stack_overflow_b stack_overflow_c integration lang_contract edge_imports edge_structural lsp_resolution_probe node_creation_probe edge_types_probe convergence_probe matrix_known_classes matrix_new_constructs grammar_probe_a grammar_probe_b grammar_probe_c grammar_probe_d grammar_probe_e grammar_probe_f grammar_probe_g incremental''')
}

options {
Expand All @@ -19,6 +21,7 @@ pipeline {
steps {
sh '''
set -eu
rm -f .jenkins-installed-cli
scripts/lint.sh --ci CLANG_FORMAT=clang-format-20
'''
}
Expand Down Expand Up @@ -60,6 +63,17 @@ pipeline {
}
}
}
stage('Stop active CBM daemon') {
steps {
sh '''
set -eu
installed_cli="$HOME/.local/bin/codebase-memory-cli"
if [ -x "$installed_cli" ]; then
"$installed_cli" daemon stop >/dev/null 2>&1 || true
fi
'''
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
stage('Build') {
steps {
sh '''
Expand All @@ -72,6 +86,17 @@ pipeline {
'''
}
}
stage('Install built CLI') {
steps {
sh '''
set -eu
install_dir="$HOME/.local/bin"
mkdir -p "$install_dir"
install -m 0755 build/c/codebase-memory-cli "$install_dir/codebase-memory-cli"
printf '%s\n' "$install_dir/codebase-memory-cli" > .jenkins-installed-cli
'''
}
}
stage('Prepare test runner') {
when {
expression { !params.CBM_TEST_SUITES?.trim() }
Expand Down Expand Up @@ -135,12 +160,47 @@ pipeline {
sh 'CBM_TEST_PHASE=post CBM_TEST_SEAM_BINARY="$WORKSPACE/build/c-seams/codebase-memory-cli" scripts/test.sh'
}
}
stage('Focused tests') {
stage('Prepare focused test runner') {
when {
expression { params.CBM_TEST_SUITES?.trim() }
}
steps {
sh '''
set -eu
make -j"$(nproc)" -f Makefile.cbm build/c/test-runner
'''
}
}
stage('Focused test shards') {
when {
expression { params.CBM_TEST_SUITES?.trim() }
}
steps {
sh 'scripts/test.sh --suites "$CBM_TEST_SUITES"'
script {
def suites = params.CBM_TEST_SUITES.trim().split(/[\s,]+/).findAll { it }
if (!suites || !suites.every { it ==~ /[a-z0-9_]+/ }) {
error('CBM_TEST_SUITES must contain only suite names')
}

// Daemon-family suites share runtime/endpoint assumptions;
// keep them in one quiet shard and distribute the rest.
def daemonNames = ['cli', 'daemon', 'daemon_ipc', 'daemon_runtime',
'daemon_application', 'daemon_bootstrap',
'index_supervisor', 'watcher']
def daemon = suites.findAll { it in daemonNames }
def other = suites.findAll { !(it in daemonNames) }
def groups = [daemon, other].findAll { !it.isEmpty() }
def branches = [:]
groups.eachWithIndex { group, index ->
def shardSuites = group.join(' ')
branches["Focused shard ${index + 1}/${groups.size()}"] = {
withEnv(["CBM_FOCUSED_SUITE_ARGS=${shardSuites}"]) {
sh 'build/c/test-runner $CBM_FOCUSED_SUITE_ARGS'
}
}
}
parallel branches
}
}
}
stage('Package wrappers') {
Expand Down Expand Up @@ -199,4 +259,15 @@ pipeline {
}
}
}
post {
always {
sh '''
set -eu
if [ -s .jenkins-installed-cli ]; then
installed_cli=$(cat .jenkins-installed-cli)
"$installed_cli" daemon start >/dev/null 2>&1 || true
fi
'''
}
}
}
Loading
Loading