Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion .claude/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ Project-level Claude Code configuration for aztec-packages. This file documents
1. Universal content lives at repository root: `CLAUDE.md`, `.claude/agents/`, `.claude/scripts/`, and the root `settings.json` hooks.
2. Subdirectory `.claude/` directories hold only component-specific skills, permission allowlists, and settings. Content is not merged upward.
3. A subdirectory that has its own `.claude/` must symlink `agents/` to the repository root's `.claude/agents/`. Claude Code's ancestor walk stops at the nearest `.claude/` and does not merge ancestors, so subdirectories otherwise shadow the root agents. Subdirectories without their own `.claude/` inherit the root automatically. `skills/` is intentionally *not* symlinked — skills are scoped to their subdir (root skills are repo-wide workflows; `yarn-project/.claude/skills/` are TS-specific; etc.).
4. Prefer XML-tagged sections inside `CLAUDE.md` for prose guidance. A `.claude/rules/` directory is only warranted when a rule needs YAML frontmatter (e.g. path-scoped `paths:` metadata) that `CLAUDE.md` cannot express. `.claude/rules/` without frontmatter does not auto-load when Claude Code is started from a subdirectory, so plain rules files are strictly worse than inlining into `CLAUDE.md`.
4. Tracked `.codex` symlinks point at the nearest `.claude/` directory. Keep `.claude/` canonical and do not duplicate agent or skill indexes in `AGENTS.md`.
5. Prefer XML-tagged sections inside `CLAUDE.md` for prose guidance. A `.claude/rules/` directory is only warranted when a rule needs YAML frontmatter (e.g. path-scoped `paths:` metadata) that `CLAUDE.md` cannot express. `.claude/rules/` without frontmatter does not auto-load when Claude Code is started from a subdirectory, so plain rules files are strictly worse than inlining into `CLAUDE.md`.

## Tests

Expand Down
62 changes: 62 additions & 0 deletions .claude/skills/chonk-inputs/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
name: chonk-inputs
description: Manage pinned Chonk IVC inputs and the Chonk/rollup UltraHonk proving checks. Use when updating, testing, benchmarking, or reviewing the CI flow for Chonk input refreshes.
argument-hint: <action> e.g. "download", "check", "refresh-pr", "benchmark", "ultrahonk"
---

# Chonk Inputs

Pinned Chonk IVC inputs live in an S3 tarball keyed by `barretenberg/cpp/scripts/chonk-inputs.hash`. The owner scripts are:

- `barretenberg/cpp/scripts/pinned_chonk_inputs.sh` for shared shell helpers.
- `barretenberg/cpp/scripts/chonk_inputs.sh` for download, check, and update.
- `barretenberg/cpp/scripts/ci_update_chonk_inputs.sh` for the PR push-back step.

Use the scripts instead of open-coding URLs, hashes, temp paths, or bucket listings.

## Common Commands

Download or repair the local fixture directory:

```bash
barretenberg/cpp/scripts/chonk_inputs.sh download
```

Check pinned VK compatibility without updating:

```bash
barretenberg/cpp/scripts/chonk_inputs.sh check
```

Refresh the pin locally:

```bash
barretenberg/cpp/scripts/chonk_inputs.sh update
```

In PR CI, refresh via the `ci-refresh-chonk` label or a `--ci-refresh-chonk` head-commit marker. The follow-up refresh commit includes `--ci-skip`.
Proving is handled by the pinned-input tests; the CI push-back step runs one small pinned flow before committing a refreshed hash.

## Benchmark and Proving Checks

Barretenberg owns the Chonk and rollup UltraHonk benchmark commands. Use `barretenberg/cpp/bootstrap.sh bench_cmds` to inspect the CI command list, or `bench_ivc` for a focused Chonk run.

The benchmark scripts restore or regenerate their inputs when missing:

```bash
barretenberg/cpp/scripts/chonk_inputs.sh download
barretenberg/cpp/scripts/ci_benchmark_ivc_flows.sh native

barretenberg/cpp/scripts/ci_benchmark_ultrahonk_circuits.sh parity_base \
../../yarn-project/end-to-end/ultrahonk-bench-inputs 8
```

Keep Chonk benchmark command enumeration in barretenberg; `yarn-project/end-to-end/bootstrap.sh build_bench_capture` is only for live input capture during pin refresh.

## Review Checklist

- Do not use `aws s3 ls` or depend on bucket listing. Verify exact object URLs.
- Use `.cache/` for scratch state and clean job-specific subdirectories.
- Keep refresh commits scoped to `barretenberg/cpp/scripts/chonk-inputs.hash`.
- Run `bash -n` on edited shell scripts and `yq e . .github/workflows/ci3.yml` after workflow edits.
- For update-step changes, run `.cache/chonk-ux-harness/run.sh` when available.
70 changes: 70 additions & 0 deletions .claude/skills/gate-counts/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
name: gate-counts
description: Manage the pinned barretenberg gate-count fixture and the ci-refresh-gates refresh flow. Use when updating, regenerating, or reviewing the gate_count_constants.hpp / gate-counts.json pair.
argument-hint: <action> e.g. "regen", "refresh-local", "refresh-pr"
---

# Pinned barretenberg gate counts

Measured gate counts pinned by the test suite live in
`barretenberg/cpp/scripts/gate-counts.json`. The generated
`barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_constants.hpp`
is derived from that JSON via `gen_gate_count_constants.py` and must not be
edited by hand.

Owner scripts:

- `barretenberg/cpp/scripts/gate-counts.json` — canonical fixture (committed).
- `barretenberg/cpp/scripts/gen_gate_count_constants.py` — renders the header.
- `barretenberg/cpp/scripts/merge_observed_gate_counts.py` — folds observed
values from `BB_GATE_COUNT_OBSERVED_DIR` JSONL files back into the JSON.
- `barretenberg/cpp/scripts/ci_update_gate_counts.sh` — PR push-back step.
- `barretenberg/cpp/src/barretenberg/dsl/acir_format/gate_count_fixture.{hpp,cpp}`
— `BB_OBSERVE_GATE_COUNT(key, value)` macro that records observed counts when
the env var is set, and a no-op otherwise.

Use the scripts instead of editing the `.hpp` directly. Tests use
`BB_OBSERVE_GATE_COUNT("KEY", measured_value)` alongside their `EXPECT_EQ`.

## Common Commands

Regenerate the header from the JSON (after editing the JSON by hand):

```bash
barretenberg/cpp/scripts/gen_gate_count_constants.py
```

Verify the on-disk header matches the JSON (CI guard against hand edits):

```bash
barretenberg/cpp/scripts/gen_gate_count_constants.py --check
```

Refresh the pinned values locally from real test runs:

```bash
OBS=$(mktemp -d)
BB_GATE_COUNT_OBSERVED_DIR="$OBS" \
barretenberg/cpp/scripts/run_test.sh dsl_tests '*GateCount*'
BB_GATE_COUNT_OBSERVED_DIR="$OBS" \
barretenberg/cpp/scripts/run_test.sh stdlib_eccvm_verifier_tests '*'
BB_GATE_COUNT_OBSERVED_DIR="$OBS" \
barretenberg/cpp/scripts/run_test.sh stdlib_honk_verifier_tests '*RecursiveVerifierTest*'
barretenberg/cpp/scripts/merge_observed_gate_counts.py "$OBS"
barretenberg/cpp/scripts/gen_gate_count_constants.py
```

In PR CI, refresh via the `ci-refresh-gates` label or a `--ci-refresh-gates`
head-commit marker. The follow-up refresh commit includes `--ci-skip` so the
push does not retrigger CI.

## Review Checklist

- Edits to `gate_count_constants.hpp` are only via the codegen. CI runs
`gen_gate_count_constants.py --check` to enforce that.
- Keep refresh commits scoped to `gate-counts.json` and the regenerated header.
- New gate-count test sites should pair their `EXPECT_EQ` with
`BB_OBSERVE_GATE_COUNT(key, observed)` so the refresh round-trip captures
them.
- New `HONK_RECURSION_CONSTANTS` flavors need entries in both the JSON and the
flavor switch inside `gen_gate_count_constants.py`.
38 changes: 37 additions & 1 deletion .claude/tests/agents_symlink_test
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ pass() { echo "${GREEN}✓${NC} $1"; ((CHECKED++)) || true; }
ROOT_AGENTS="$ROOT/.claude/agents"
[[ -d "$ROOT_AGENTS" ]] || { fail "root .claude/agents missing at $ROOT_AGENTS"; exit 1; }

ROOT_AGENTS_MD="$ROOT/AGENTS.md"
if [[ ! -L "$ROOT_AGENTS_MD" ]]; then
fail "AGENTS.md is not a symlink to CLAUDE.md"
elif [[ "$(readlink "$ROOT_AGENTS_MD")" != "CLAUDE.md" ]]; then
fail "AGENTS.md points at $(readlink "$ROOT_AGENTS_MD"), expected CLAUDE.md"
else
pass "AGENTS.md -> CLAUDE.md"
fi

# Every subdir .claude/ (excluding the root one) must expose an agents entry
# that resolves to the same inode as the root agents/ directory.
while IFS= read -r dir; do
Expand Down Expand Up @@ -62,8 +71,35 @@ done < <(find "$ROOT" -type d -name .claude -not -path "$ROOT/noir/*" -not -path
echo
if (( FAIL == 0 )); then
echo "${GREEN}All $CHECKED subdir .claude/ directories expose agents/ correctly.${NC}"
exit 0
else
echo "${RED}$FAIL subdir .claude/ directories are missing or have broken agents symlinks.${NC}"
exit 1
fi

while IFS= read -r dir; do
case "$dir" in
"$ROOT"/node_modules/*|*"/node_modules/"*) continue;;
"$ROOT"/noir/noir-repo/*) continue;;
esac
codex="$(dirname "$dir")/.codex"
if [[ ! -L "$codex" ]]; then
fail "${codex#$ROOT/} is missing or is not a symlink to .claude"
continue
fi
resolved=$(cd "$(dirname "$codex")" && cd "$(readlink "$codex")" 2>/dev/null && pwd -P) || resolved=""
claude_resolved=$(cd "$dir" && pwd -P)
if [[ "$resolved" != "$claude_resolved" ]]; then
fail "${codex#$ROOT/} resolves to $resolved, expected $claude_resolved"
continue
fi
pass "${codex#$ROOT/}"
done < <(find "$ROOT" -type d -name .claude -not -path "$ROOT/noir/*" -not -path "$ROOT/**/node_modules/*")

echo
if (( FAIL == 0 )); then
echo "${GREEN}All checked .codex symlinks point at their sibling .claude directories.${NC}"
exit 0
else
echo "${RED}$FAIL .claude/.codex layout checks failed.${NC}"
exit 1
fi
1 change: 1 addition & 0 deletions .codex
96 changes: 92 additions & 4 deletions .github/ci3_labels_to_env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,33 @@ function has_label {
local label="$1"
for l in "${LABELS[@]}"; do
if [[ "$l" == "$label" ]]; then
echo "Label '$label' found" >&2
return 0
fi
done
return 1
}

function join_by {
local delimiter="$1"
shift
local result=""
local value
for value in "$@"; do
if [[ -n "$result" ]]; then
result+="$delimiter"
fi
result+="$value"
done
echo "$result"
}

function head_commit_has_marker {
local marker="$1"
local message
message="$(git log -1 --pretty=%B 2>/dev/null || true)"
grep -Eq "(^|[[:space:]])${marker}([[:space:]]|$)" <<< "$message"
}

function main {
LABELS=("$@")
echo "Labels: ${LABELS[*]}"
Expand Down Expand Up @@ -42,9 +62,73 @@ function main {
echo "SKIP_COMPAT_E2E=1" >> $GITHUB_ENV
fi

local chonk_input_update=0
local chonk_input_update_requested=0
if [ "${GITHUB_EVENT_NAME:-}" = "pull_request" ] && { has_label "ci-refresh-chonk" || head_commit_has_marker "--ci-refresh-chonk"; }; then
chonk_input_update_requested=1
fi
local gate_count_update=0
local gate_count_update_requested=0
if [ "${GITHUB_EVENT_NAME:-}" = "pull_request" ] && { has_label "ci-refresh-gates" || head_commit_has_marker "--ci-refresh-gates"; }; then
gate_count_update_requested=1
fi
local ci_skip_requested=0
if has_label "ci-skip" || head_commit_has_marker "--ci-skip"; then
ci_skip_requested=1
fi

local explicit_ci_mode_labels=()
local mode_label
for mode_label in ci-merge-queue ci-release-pr ci-full ci-full-no-test-cache ci-docs ci-barretenberg-full ci-barretenberg; do
if has_label "$mode_label"; then
explicit_ci_mode_labels+=("$mode_label")
fi
done

if [ "$ci_skip_requested" -eq 0 ] && [ "${#explicit_ci_mode_labels[@]}" -gt 1 ]; then
echo "ERROR: Conflicting CI mode labels: $(join_by ', ' "${explicit_ci_mode_labels[@]}"). Remove all but one mode label, or use ci-skip/--ci-skip to skip CI intentionally." >&2
exit 1
fi

if [ "$ci_skip_requested" -eq 0 ] && [ "$chonk_input_update_requested" -eq 1 ] && [ "${#explicit_ci_mode_labels[@]}" -gt 0 ]; then
echo "ERROR: ci-refresh-chonk cannot be combined with explicit CI mode labels: $(join_by ', ' "${explicit_ci_mode_labels[@]}"). Remove the mode label, or use ci-skip/--ci-skip to skip without refreshing inputs." >&2
exit 1
fi

if [ "$ci_skip_requested" -eq 0 ] && [ "$gate_count_update_requested" -eq 1 ] && [ "${#explicit_ci_mode_labels[@]}" -gt 0 ]; then
echo "ERROR: ci-refresh-gates cannot be combined with explicit CI mode labels: $(join_by ', ' "${explicit_ci_mode_labels[@]}"). Remove the mode label, or use ci-skip/--ci-skip to skip without refreshing gate counts." >&2
exit 1
fi

if [ "$ci_skip_requested" -eq 0 ] && [ "$gate_count_update_requested" -eq 1 ] && [ "$chonk_input_update_requested" -eq 1 ]; then
echo "ERROR: ci-refresh-gates and ci-refresh-chonk cannot run in the same PR run. Apply one label/marker at a time so each refresh pushes a clean diff." >&2
exit 1
fi

# Chonk input updates are side-effecting internal PR-only work. The main CI
# run behaves like ci-skip until post-actions regenerate and push the diff.
if [ "$chonk_input_update_requested" -eq 1 ] && [ "$ci_skip_requested" -eq 0 ]; then
chonk_input_update=1
fi
echo "CHONK_INPUT_UPDATE_REQUESTED=$chonk_input_update" >> $GITHUB_ENV

# Gate-count refresh follows the same shape: main CI skipped, the refresh
# script runs in post-actions and pushes the updated fixture + regenerated
# header back to the PR.
if [ "$gate_count_update_requested" -eq 1 ] && [ "$ci_skip_requested" -eq 0 ]; then
gate_count_update=1
fi
echo "GATE_COUNT_UPDATE_REQUESTED=$gate_count_update" >> $GITHUB_ENV

# Determine CI mode based on event, labels, and target branch
local ci_mode
if [ "${GITHUB_EVENT_NAME:-}" == "merge_group" ] || has_label "ci-merge-queue"; then
if [ "$ci_skip_requested" -eq 1 ]; then
echo "WARNING: Skipping CI because a ci-skip label or --ci-skip commit marker was present. Skip takes precedence over other CI signals." >&2
if [ "$chonk_input_update_requested" -eq 1 ]; then
echo "WARNING: Chonk input refresh was requested but ignored because CI skip was also requested." >&2
fi
ci_mode="skip"
elif [ "${GITHUB_EVENT_NAME:-}" == "merge_group" ] || has_label "ci-merge-queue"; then
ci_mode="merge-queue"
# Check if this is a merge-train/spartan PR entering the merge queue.
# If so, use the heavier merge-queue-heavy mode (10 grind runs).
Expand All @@ -62,8 +146,11 @@ function main {
fi
fi
fi
elif has_label "ci-skip"; then
echo "WARNING: Skipping CI due to the ci-skipok label! Make sure this is intended!" >&2
elif [ "$chonk_input_update" -eq 1 ]; then
echo "WARNING: Skipping main CI because Chonk input refresh was requested; the update step will run after this step succeeds." >&2
ci_mode="skip"
elif [ "$gate_count_update" -eq 1 ]; then
echo "WARNING: Skipping main CI because gate-count refresh was requested; the update step will run after this step succeeds." >&2
ci_mode="skip"
elif has_label "ci-release-pr"; then
ci_mode="release-pr"
Expand All @@ -84,6 +171,7 @@ function main {
else
ci_mode="fast"
fi

echo "CI_MODE=$ci_mode" >> $GITHUB_ENV
echo "CI mode: $ci_mode"

Expand Down
28 changes: 28 additions & 0 deletions .github/ci3_success.sh
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,38 @@ function handle_benchmarks {
fi
}

function handle_chonk_input_update {
local github_repository="$1"
if [ "${CHONK_INPUT_UPDATE_REQUESTED:-0}" -eq 0 ]; then
return 1
fi
echo_header "Chonk Input Update"
export GITHUB_REPOSITORY="${GITHUB_REPOSITORY:-$github_repository}"
./.github/ci3.sh chonk-input-update
}

function handle_gate_count_update {
local github_repository="$1"
if [ "${GATE_COUNT_UPDATE_REQUESTED:-0}" -eq 0 ]; then
return 1
fi
echo_header "Gate-Count Update"
export GITHUB_REPOSITORY="${GITHUB_REPOSITORY:-$github_repository}"
./.github/ci3.sh gate-count-update
}

function main {
echo_header "CI3 Post-Actions"
# Get repository from git remote
local github_repository=$(git remote get-url origin | sed -E 's|.*github\.com[/:]([^/]+/[^/]+)(\.git)?$|\1|')
if handle_chonk_input_update "${github_repository}"; then
echo_header "Post-Actions Complete"
return
fi
if handle_gate_count_update "${github_repository}"; then
echo_header "Post-Actions Complete"
return
fi
save_cache "${github_repository}"
handle_squash_merge "${github_repository}"
handle_benchmarks
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/ci3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ jobs:
environment: ${{ startsWith(github.ref, 'refs/tags/v') && 'master' || '' }}
permissions:
id-token: write # required for OIDC assume-role with AWS
contents: read
contents: read # checkout/fetch with the default github.token
pull-requests: read # merge_group mode looks up the source PR
steps:
- name: Remove wakeup label
if: contains(github.event.pull_request.labels.*.name, 'ci-wakeup-pr-after-merge')
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pull-request-title.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ on:
- synchronize

permissions:
contents: read
pull-requests: read

jobs:
Expand Down
Loading
Loading