Skip to content

Commit f2eef96

Browse files
committed
chore: merge public-next into next (raw, conflict markers) [PR 1/2]
2 parents 3dc4987 + 9ffa477 commit f2eef96

28 files changed

Lines changed: 857 additions & 162 deletions

File tree

.claude/skills/merge-train-infra/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ The merge-train system is fully automated via GitHub Actions in `.github/workflo
2121

2222
5. **Recreation & Wakeup** (`merge-train-recreate.yml`): Triggered when a PR is closed (merged). If the merged PR's head branch starts with `merge-train/`, recreates the branch from the base branch (usually `next`). Then runs `scripts/merge-train/wakeup-prs.sh` to add the `ci-wakeup-pr-after-merge` label to all open PRs targeting the branch that have passed CI and have automerge enabled. This triggers a CI re-run (typically a no-op via tree-hash cache) so those PRs can proceed through the merge queue. The label is immediately removed by a step in `ci3.yml` so it can be re-applied on subsequent merges.
2323

24-
6. **Failure Notification** (`merge-queue-dequeue-notify.yml`): Triggered when a PR is dequeued from the merge queue. If the PR's head branch starts with `merge-train/` and the PR was NOT merged, sends a Slack notification via `ci3/merge_train_failure_slack_notify`.
24+
6. **Failure Notification** (`merge-queue-dequeue-notify.yml`): Triggered when a PR is dequeued from the merge queue. If the PR's head branch starts with `merge-train/` and the PR was NOT merged, sends a Slack notification via `ci3/merge_train_failure_slack_notify`. That script also kicks off a ClaudeBox session to investigate/fix the dequeued PR (`ci3/slack_notify_with_claudebox_kickoff`), passing `--repo "$GITHUB_REPOSITORY"` so the session runs in the mode matching the repo the train lives on. When the train is on a private mirror (`…-private`), `claudebox.yml` selects private mode; otherwise it stays public. Without that repo hint a private-train fix session lands in public mode and cannot read the PR or open the fix.
2525

2626
## CI Integration Details
2727

.github/workflows/ci3.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@ on:
1616
merge_group: {}
1717

1818
concurrency:
19-
# Allow full concurrency for merge-train PRs, one-run-per-branch for everything else.
20-
group: ci3-${{ (startsWith(github.event.pull_request.head.ref, 'merge-train/') && github.run_id) || github.event.merge_group.head_ref || github.ref_name }}
19+
# merge-train PRs: one run per commit — distinct commits run concurrently (each is a real
20+
# train state to test), but duplicate events for the same commit (synchronize + the bot's
21+
# label re-applies) collapse. Keying on head.sha instead of run_id is what collapses them;
22+
# run_id is unique per run and never collapsed anything. Everything else: one run per branch.
23+
group: ci3-${{ (startsWith(github.event.pull_request.head.ref, 'merge-train/') && github.event.pull_request.head.sha) || github.event.merge_group.head_ref || github.ref_name }}
2124
cancel-in-progress: true
2225

2326
jobs:

.github/workflows/claudebox.yml

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ on:
1919
description: 'Git ref the session should work against (e.g., origin/merge-train/barretenberg)'
2020
required: false
2121
type: string
22+
repo:
23+
description: 'Repo the work targets (owner/repo). Defaults to this repo. A private target (…-private) makes the session run in private mode.'
24+
required: false
25+
type: string
26+
mode:
27+
description: 'ClaudeBox repo-access mode (public|private). Leave empty to infer from the target repo.'
28+
required: false
29+
type: string
2230
slack_channel:
2331
description: 'Slack channel ID to thread status into (set by the kickoff script)'
2432
required: false
@@ -124,15 +132,19 @@ jobs:
124132
CLAUDEBOX_LINK: ${{ steps.parse.outputs.link }}
125133
COMMENT_ID: ${{ github.event.comment.id || '' }}
126134
RUN_COMMENT_ID: ${{ steps.status_comment.outputs.run_comment_id || '' }}
127-
REPO: ${{ github.repository }}
135+
REPO: ${{ inputs.repo || github.repository }}
128136
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
129137
AUTHOR: ${{ github.event.comment.user.login || github.actor }}
130138
SLACK_CHANNEL: ${{ inputs.slack_channel || '' }}
131139
SLACK_THREAD_TS: ${{ inputs.slack_thread_ts || '' }}
132-
# Public mode grants the session read-write access to public repos
133-
# (aztec-packages). Without it the session only gets the group's
134-
# unscoped repos and cannot open PRs against aztec-packages.
135-
CLAUDEBOX_MODE: ${{ vars.CLAUDEBOX_MODE || 'public' }}
140+
# Mode selection, in priority order:
141+
# 1. an explicit `mode` input (caller knows best);
142+
# 2. private, when the target repo is a private mirror (…-private) —
143+
# e.g. a merge-train dequeue on aztec-packages-private, which a
144+
# public-mode session cannot read or open a fix PR against;
145+
# 3. the repo/org default (public) for ordinary /claudebox work on
146+
# aztec-packages.
147+
CLAUDEBOX_MODE: ${{ inputs.mode || (endsWith(inputs.repo || github.repository, '-private') && 'private' || vars.CLAUDEBOX_MODE || 'public') }}
136148
run: |
137149
if [ -z "${CLAUDEBOX_API_SECRET:-}" ]; then
138150
echo "ERROR: CLAUDEBOX_API_SECRET is not set; cannot dispatch ClaudeBox v2"

.github/workflows/merge-train-create-pr.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ jobs:
2424
branch="${{ github.ref_name }}"
2525
2626
# Determine base branch. Most trains target next; trains suffixed
27-
# with -v5 (e.g. spartan-v5, fairies-v5) target the v5 release line
28-
# (v5-next) instead.
27+
# with -v<N> (e.g. spartan-v5, fairies-v5, spartan-v6) target the
28+
# matching release line (v5-next, v6-next) instead.
2929
base_branch="next"
30-
if [[ "$branch" == *-v5 ]]; then
31-
base_branch="v5-next"
30+
if [[ "$branch" =~ -v([0-9]+)$ ]]; then
31+
base_branch="v${BASH_REMATCH[1]}-next"
3232
fi
3333
3434
# Skip if this is a merge commit (multiple parents) -- unless its
@@ -55,7 +55,7 @@ jobs:
5555
5656
# Create PR with ci-no-squash label
5757
labels="ci-no-squash"
58-
if [[ "$branch" == "merge-train/spartan" || "$branch" == "merge-train/spartan-v5" || "$branch" == "merge-train/ci" ]]; then
58+
if [[ "$branch" == "merge-train/spartan" || "$branch" == "merge-train/spartan-v5" || "$branch" == "merge-train/spartan-v6" || "$branch" == "merge-train/ci" ]]; then
5959
labels="$labels,ci-full-no-test-cache"
6060
fi
6161
# Trains targeting the v5 release line are ports into v5-next.

.github/workflows/merge-train-next-to-branches.yml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ on:
55
branches:
66
- next
77
- v5-next
8+
- v6-next
89

910
jobs:
1011
merge-to-trains:
@@ -27,13 +28,17 @@ jobs:
2728
GH_TOKEN: ${{ secrets.AZTEC_BOT_GITHUB_TOKEN }}
2829
COMMIT_SHA: ${{ github.sha }}
2930
run: |
30-
# The -v5 trains track the v5 release line (v5-next); every other
31-
# train tracks next. Sync only the trains fed by the branch that was
32-
# just pushed.
31+
# The -v<N> trains track the matching release line (v5-next, v6-next);
32+
# every other train tracks next. Sync only the trains fed by the
33+
# branch that was just pushed.
3334
if [[ "${{ github.ref_name }}" == "v5-next" ]]; then
3435
for branch in merge-train/spartan-v5 merge-train/fairies-v5; do
3536
./scripts/merge-train/merge-next.sh "$branch" v5-next || true
3637
done
38+
elif [[ "${{ github.ref_name }}" == "v6-next" ]]; then
39+
for branch in merge-train/spartan-v6; do
40+
./scripts/merge-train/merge-next.sh "$branch" v6-next || true
41+
done
3742
else
3843
for branch in merge-train/avm merge-train/barretenberg merge-train/ci merge-train/docs merge-train/fairies merge-train/spartan; do
3944
./scripts/merge-train/merge-next.sh "$branch" || true

.github/workflows/merge-train-stale-check.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,22 @@ jobs:
3838
BASE_BRANCH: v5-next
3939
run: ./ci3/merge_train_stale_check merge-train/spartan-v5 '#team-alpha'
4040

41+
spartan-v6:
42+
name: Check merge-train/spartan-v6
43+
if: ${{ github.event_name != 'schedule' || github.repository == 'AztecProtocol/aztec-packages' }}
44+
runs-on: ubuntu-latest
45+
permissions:
46+
contents: read
47+
pull-requests: read
48+
steps:
49+
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
50+
- name: Run stale check
51+
env:
52+
GH_TOKEN: ${{ secrets.AZTEC_BOT_GITHUB_TOKEN }}
53+
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
54+
BASE_BRANCH: v6-next
55+
run: ./ci3/merge_train_stale_check merge-train/spartan-v6 '#team-alpha'
56+
4157
fairies-v5:
4258
name: Check merge-train/fairies-v5
4359
runs-on: ubuntu-latest

.github/workflows/metrics-deploy.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ on:
4141
description: Optional Terraform list of Slack user IDs to mention on Grafana alert notifications
4242
required: false
4343
type: string
44-
default: '["U0AHB6VR8N5"]'
44+
default: '["U0AQPRA1FSL"]'
4545
secrets:
4646
GCP_SA_KEY:
4747
required: true
@@ -78,7 +78,7 @@ on:
7878
slack_alert_mention_user_ids:
7979
description: Optional Terraform list of Slack user IDs to mention on Grafana alert notifications
8080
required: false
81-
default: '["U0AHB6VR8N5"]'
81+
default: '["U0AQPRA1FSL"]'
8282

8383
jobs:
8484
metrics_deployment:

barretenberg/cpp/src/barretenberg/vm2/tracegen/lib/lookup_builder.hpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ template <typename LookupSettings_> class IndexedLookupTraceBuilder : public Int
5050
}
5151

5252
trace.set(LookupSettings::COUNTS, dst_row, trace.get(LookupSettings::COUNTS, dst_row) + 1);
53-
// Set the fine grained inner selector if it's not already one.
54-
if (LookupSettings::DST_SELECTOR != this->outer_dst_selector &&
55-
trace.get(LookupSettings::DST_SELECTOR, dst_row) != 1) {
56-
trace.set(LookupSettings::DST_SELECTOR, dst_row, 1);
53+
if (LookupSettings::DST_SELECTOR != this->outer_dst_selector) {
54+
// This step might write to the same cell from multiple threads, so we use atomic limbs to avoid UB.
55+
// Since we are always writing a 1, the end result will be 1 even under concurrency.
56+
trace.set(LookupSettings::DST_SELECTOR, dst_row, 1, /*use_atomic_limbs=*/true);
5757
}
5858
});
5959
}
@@ -198,10 +198,10 @@ template <typename LookupSettings> class LookupIntoDynamicTableSequential : publ
198198
if (dst_selector == 1 && src_values == trace.get_multiple(LookupSettings::DST_COLUMNS, dst_row)) {
199199
trace.set(LookupSettings::COUNTS, dst_row, trace.get(LookupSettings::COUNTS, dst_row) + 1);
200200

201-
// Set the fine grained inner selector if it's not already one.
202-
if (LookupSettings::DST_SELECTOR != this->outer_dst_selector &&
203-
trace.get(LookupSettings::DST_SELECTOR, dst_row) != 1) {
204-
trace.set(LookupSettings::DST_SELECTOR, dst_row, 1);
201+
if (LookupSettings::DST_SELECTOR != this->outer_dst_selector) {
202+
// This step might write to the same cell from multiple threads, so we use atomic limbs to avoid
203+
// UB. Since we are always writing a 1, the end result will be 1 even under concurrency.
204+
trace.set(LookupSettings::DST_SELECTOR, dst_row, 1, /*use_atomic_limbs=*/true);
205205
}
206206

207207
found = true;

barretenberg/cpp/src/barretenberg/vm2/tracegen/trace_container.cpp

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
#include <algorithm>
44

55
#include "barretenberg/common/assert.hpp"
6+
<<<<<<< HEAD
7+
=======
8+
#include "barretenberg/common/compiler_hints.hpp"
9+
#include "barretenberg/common/log.hpp"
10+
>>>>>>> origin/public-next
611
#include "barretenberg/common/ref_vector.hpp"
712
#include "barretenberg/vm2/tracegen/lib/trace_conversion.hpp"
813

@@ -11,6 +16,21 @@ namespace {
1116

1217
// We need a zero value to return (a reference to) when a value is not found.
1318
const FF zero = FF::zero();
19+
<<<<<<< HEAD
20+
=======
21+
22+
// Writes each 64-bit limb of the field with a relaxed atomic store. Each limb is naturally aligned (FF is
23+
// alignas(32), 4x uint64_t), so this lowers to 4 plain `movq` stores on x86-64 — no lock, no libatomic call
24+
// (unlike a whole-field std::atomic_ref<FF>, whose 32 bytes exceed the lock-free width). Lets set() make a
25+
// same-cell concurrent write data-race-free when every writer stores the same value.
26+
inline void store_per_limb(FF& cell, const FF& value)
27+
{
28+
static_assert(sizeof(FF) == 4 * sizeof(uint64_t));
29+
for (size_t i = 0; i < 4; ++i) {
30+
std::atomic_ref<uint64_t>(cell.data[i]).store(value.data[i], std::memory_order_relaxed);
31+
}
32+
}
33+
>>>>>>> origin/public-next
1434

1535
} // namespace
1636

@@ -59,7 +79,7 @@ TraceContainer::ColumnInterval& TraceContainer::get_or_create_shard(SparseColumn
5979
return *expected; // CAS failure loaded the winning pointer into `expected` (acquire).
6080
}
6181

62-
void TraceContainer::set(Column col, uint32_t row, const FF& value)
82+
void TraceContainer::set(Column col, uint32_t row, const FF& value, bool use_atomic_limbs)
6383
{
6484
auto& column_data = (*trace)[static_cast<size_t>(col)];
6585
const size_t shard_idx = row / INTERVAL_SIZE;
@@ -70,12 +90,26 @@ void TraceContainer::set(Column col, uint32_t row, const FF& value)
7090
// Lock-free: a single atomic load finds the shard (created on first write), then we write our
7191
// own dense cell directly. Different rows are distinct array elements, so concurrent writers of
7292
// this column (or even of the same shard, at a chunk boundary) never race and never serialize.
73-
get_or_create_shard(column_data, shard_idx).rows[offset] = value;
93+
auto& cell = get_or_create_shard(column_data, shard_idx).rows[offset];
94+
if (BB_UNLIKELY(use_atomic_limbs)) {
95+
store_per_limb(cell, value);
96+
} else {
97+
cell = value;
98+
}
7499
} else {
75-
// Zero value: clear if present. We never create a shard (clearing an absent row is a no-op).
100+
// Zero value: clear if present. We never create a shard, so sparse (mostly-zero) columns are not
101+
// materialized (an unset cell already reads as zero).
76102
ColumnInterval* shard = column_data.slots[shard_idx].load(std::memory_order_acquire);
77103
if (shard != nullptr) {
104+
<<<<<<< HEAD
78105
shard->rows[offset] = zero;
106+
=======
107+
if (BB_UNLIKELY(use_atomic_limbs)) {
108+
store_per_limb(shard->rows[offset], zero);
109+
} else {
110+
shard->rows[offset] = FF::zero();
111+
}
112+
>>>>>>> origin/public-next
79113
}
80114
}
81115
}

barretenberg/cpp/src/barretenberg/vm2/tracegen/trace_container.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,11 @@ class TraceContainer {
7272
// Extended version of get that works with shifted columns. More expensive.
7373
const FF& get_column_or_shift(ColumnAndShifts col, uint32_t row) const;
7474

75-
void set(Column col, uint32_t row, const FF& value);
75+
// Sets the value of a cell. Thread-safe if the same cell is not written to from multiple threads.
76+
// If writing to the same cell from multiple threads, use_atomic_limbs=true to use atomic limbs.
77+
// This makes the write slower, but it will not be UB. However, it is also not thread-safe.
78+
// Use only if you know what you are doing.
79+
void set(Column col, uint32_t row, const FF& value, bool use_atomic_limbs = false);
7680
// Bulk setting for a given row.
7781
void set(uint32_t row, std::span<const std::pair<Column, FF>> values);
7882
// Reserve column size. Useful for precomputed columns.

0 commit comments

Comments
 (0)