Skip to content

Commit e72b263

Browse files
mason: wire raw replay review into CI
Co-authored-by: Alfonso <alfonso-magic-context@users.noreply.github.com>
1 parent a1e2093 commit e72b263

10 files changed

Lines changed: 671 additions & 20 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,8 +1025,9 @@ jobs:
10251025
set -euo pipefail
10261026
target="$GITHUB_WORKSPACE/packages/e2e-tests/.cache/rust-e2e-cargo-target"
10271027
mkdir -p "$target/prebuilt"
1028-
CARGO_TARGET_DIR="$target" cargo build --release -p mc-module
1028+
CARGO_TARGET_DIR="$target" cargo build --release -p mc-module --bin ck-mc --example transform_replay
10291029
cp "$target/release/ck-mc" "$target/prebuilt/ckdev-mc-e2e"
1030+
cp "$target/release/examples/transform_replay" "$target/prebuilt/rust-raw-replay-probe"
10301031
CARGO_TARGET_DIR="$target" cargo build --release -p mc-module --features drive-fault
10311032
cp "$target/release/ck-mc" "$target/prebuilt/ckdev-mc-e2e-drive-fault"
10321033
(cd "$GITHUB_WORKSPACE/.siblings/subconscious" && CARGO_TARGET_DIR="$target" cargo build --release -p subc-core --bins)
@@ -1098,11 +1099,79 @@ jobs:
10981099
MC_E2E_CK_SUBC_BIN: ${{ github.workspace }}/packages/e2e-tests/.cache/prebuilt/ckdev-subc
10991100
run: scripts/run-rust-hermetic-e2e.sh
11001101

1102+
rust-raw-replay-review:
1103+
name: Rust (raw replay review)
1104+
runs-on: ubuntu-latest
1105+
needs: [e2e-rust-hermetic-build]
1106+
timeout-minutes: 120
1107+
permissions:
1108+
contents: read
1109+
actions: read
1110+
steps:
1111+
- uses: actions/checkout@v5
1112+
- uses: dtolnay/rust-toolchain@stable
1113+
- uses: oven-sh/setup-bun@v2
1114+
with:
1115+
bun-version: latest
1116+
- name: Install workspace dependencies
1117+
run: bun install --frozen-lockfile
1118+
- uses: ./.github/actions/wait-ci-artifact
1119+
id: binaries
1120+
with:
1121+
artifact: rust-hermetic-binaries-${{ github.sha }}
1122+
producer: E2E (Rust hermetic build)
1123+
timeout-minutes: "120"
1124+
- uses: actions/download-artifact@v4
1125+
with:
1126+
artifact-ids: ${{ steps.binaries.outputs.artifact-id }}
1127+
merge-multiple: true
1128+
path: ${{ runner.temp }}/raw-replay-download
1129+
- name: Restore staged native driver
1130+
shell: bash
1131+
run: |
1132+
set -euo pipefail
1133+
(cd "$RUNNER_TEMP/raw-replay-download" && sha256sum --check rust-hermetic-binaries.sha256)
1134+
mkdir -p "$RUNNER_TEMP/raw-replay-artifact"
1135+
tar -xzf "$RUNNER_TEMP/raw-replay-download/rust-hermetic-binaries.tar.gz" -C "$RUNNER_TEMP/raw-replay-artifact"
1136+
test "$(cat "$RUNNER_TEMP/raw-replay-artifact/commit.txt")" = "$GITHUB_SHA"
1137+
test -x "$RUNNER_TEMP/raw-replay-artifact/rust-raw-replay-probe"
1138+
- name: Fetch pinned master baseline
1139+
shell: bash
1140+
run: |
1141+
set -euo pipefail
1142+
baseline_commit="$(tr -d '\r\n' < scripts/rust-raw-replay-baseline.commit)"
1143+
if [[ ! "$baseline_commit" =~ ^[0-9a-f]{40}$ ]]; then
1144+
echo "Invalid pinned Rust raw replay baseline commit: $baseline_commit" >&2
1145+
exit 1
1146+
fi
1147+
git fetch --no-tags --depth=1 origin "$baseline_commit"
1148+
git cat-file -e "${baseline_commit}^{commit}"
1149+
baseline_archive="$RUNNER_TEMP/rust-raw-replay-baseline.tar"
1150+
git archive --format=tar "$baseline_commit" \
1151+
crates/mc-module/src packages/plugin/src packages/plugin/package.json \
1152+
packages/retina-local-fs/src packages/retina-local-fs/package.json package.json \
1153+
> "$baseline_archive"
1154+
test -s "$baseline_archive"
1155+
echo "Pinned Rust raw replay baseline: $baseline_commit"
1156+
- name: Run staged raw replay witnesses
1157+
env:
1158+
MC_RAW_REPLAY_REQUIRED: "1"
1159+
MC_RAW_REPLAY_BASELINE_RELEASE: "1"
1160+
MC_REVIEW_ASSERT_LIVENESS: "1"
1161+
MC_RAW_REPLAY_PROBE: ${{ runner.temp }}/raw-replay-artifact/rust-raw-replay-probe
1162+
MC_RAW_REPLAY_BASELINE_ARCHIVE: ${{ runner.temp }}/rust-raw-replay-baseline.tar
1163+
run: |
1164+
bash scripts/test-rust-raw-replay.sh \
1165+
packages/plugin/review-tests/rust-cache-review.test.ts \
1166+
packages/plugin/review-tests/rust-cache-r2.test.ts \
1167+
packages/plugin/review-tests/rust-cache-restart.test.ts \
1168+
-t 'round-two|provider-invisible|JSON number|sorted digest|SIGKILL|release recovery census|raw ctx_reduce|durable replay cost|unchanged ID-less native|no-freeze SQL|seed (a19f37|badc0de|c001d00d|92e41b|67d5a3)'
1169+
11011170
ci-summary:
11021171
name: CI (required)
11031172
runs-on: ubuntu-latest
11041173
if: ${{ always() }}
1105-
needs: [preflight, check-docs-layout, check-plugin, check-pi-plugin, check-plugin-compat, unit-plugin, unit-pi-plugin, check-dashboard, e2e-opencode, e2e-opencode2, e2e-pi, e2e-omp, e2e-host-opencode, e2e-host-opencode2, e2e-host-pi, e2e-host-omp, rust-clippy, rust-lib-archive, rust-mc-module-lib, rust-mc-module-parity, rust-mc-module-integration, rust-workspace-crates, rust-crates, e2e-rust-hermetic-build, e2e-rust-hermetic]
1174+
needs: [preflight, check-docs-layout, check-plugin, check-pi-plugin, check-plugin-compat, unit-plugin, unit-pi-plugin, check-dashboard, e2e-opencode, e2e-opencode2, e2e-pi, e2e-omp, e2e-host-opencode, e2e-host-opencode2, e2e-host-pi, e2e-host-omp, rust-clippy, rust-lib-archive, rust-mc-module-lib, rust-mc-module-parity, rust-mc-module-integration, rust-workspace-crates, rust-crates, e2e-rust-hermetic-build, e2e-rust-hermetic, rust-raw-replay-review]
11061175
steps:
11071176
- uses: actions/checkout@v5
11081177
- uses: oven-sh/setup-bun@v2

‎packages/e2e-tests/scripts/ci-workflow.test.ts‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,33 @@ test("remaining Rust crates retain Bun migration oracles and hermetic archives r
104104
expect(unpack).toContain("ckdev-mc-e2e ckdev-mc-e2e-drive-fault ckdev-subc");
105105
expect(unpack).toContain('test -x "packages/e2e-tests/.cache/prebuilt/$binary"');
106106
});
107+
108+
test("raw replay review reuses the hermetic driver and requires the pinned live witnesses", async () => {
109+
const producer = workflow.jobs["e2e-rust-hermetic-build"]!;
110+
const build = producer.steps.find((step) => step.name === "Build hermetic binary pair (including fault-injection variant)")!;
111+
expect(build.run).toContain("--example transform_replay");
112+
expect(build.run).toContain("rust-raw-replay-probe");
113+
114+
const review = workflow.jobs["rust-raw-replay-review"]!;
115+
expect(review.name).toBe("Rust (raw replay review)");
116+
expect(review.needs).toEqual(["e2e-rust-hermetic-build"]);
117+
expect(review["timeout-minutes"]).toBe(120);
118+
const wait = review.steps.find((step) => step.uses === "./.github/actions/wait-ci-artifact")!;
119+
expect(wait.with!.artifact).toBe("rust-hermetic-binaries-${{ github.sha }}");
120+
121+
const fetch = review.steps.find((step) => step.name === "Fetch pinned master baseline")!;
122+
expect(fetch.run).toContain("scripts/rust-raw-replay-baseline.commit");
123+
expect(fetch.run).toContain("git fetch --no-tags --depth=1 origin");
124+
const pin = await Bun.file(resolve(root, "scripts/rust-raw-replay-baseline.commit")).text();
125+
expect(pin.trim()).toMatch(/^[0-9a-f]{40}$/);
126+
127+
const run = review.steps.find((step) => step.name === "Run staged raw replay witnesses")!;
128+
expect(run.env!.MC_RAW_REPLAY_REQUIRED).toBe("1");
129+
expect(run.env!.MC_REVIEW_ASSERT_LIVENESS).toBe("1");
130+
expect(run.run).toContain("rust-cache-review.test.ts");
131+
expect(run.run).toContain("rust-cache-r2.test.ts");
132+
expect(run.run).toContain("rust-cache-restart.test.ts");
133+
expect(run.run).toContain("release recovery census");
134+
expect(run.run).toContain("no-freeze SQL");
135+
expect(run.run).toContain("seed (a19f37|badc0de|c001d00d|92e41b|67d5a3)");
136+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"root": false,
3+
"extends": ["../biome.json"],
4+
"files": { "includes": ["**/*.ts", "**/*.json"] },
5+
"overrides": [
6+
{
7+
"includes": ["**/*.ts"],
8+
"linter": { "rules": { "style": { "noNonNullAssertion": "off" } } }
9+
}
10+
]
11+
}

‎packages/plugin/review-tests/rust-cache-harness.ts‎

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ export function scratch(prefix: string): string {
4040
cleanups.push(() => rmSync(path, { recursive: true, force: true }));
4141
return path;
4242
}
43-
export function database(): ContextDatabase {
44-
const db = new Database(":memory:") as ContextDatabase;
43+
export function database(path = ":memory:"): ContextDatabase {
44+
const db = new Database(path) as ContextDatabase;
4545
initializeDatabase(db);
4646
runMigrations(db);
4747
cleanups.push(() => db.close());
@@ -125,7 +125,7 @@ export function probe(binary: string, directory: string, body: unknown): Record<
125125
const result = spawnSync(binary, [directory], {
126126
input: JSON.stringify(body),
127127
encoding: "utf8",
128-
timeout: 30_000,
128+
timeout: 120_000,
129129
});
130130
if (result.status !== 0)
131131
throw new Error(`Rust review probe failed: ${result.stderr} ${result.error ?? ""}`);
@@ -137,9 +137,10 @@ export async function hostFixture(
137137
factory = createRustModeTransform,
138138
baselineRoot?: string,
139139
midTurn = false,
140+
persistent: { directory: string; dbPath: string } | undefined = undefined,
140141
) {
141-
const directory = scratch("independent-module-");
142-
const db = database();
142+
const directory = persistent?.directory ?? scratch("independent-module-");
143+
const db = database(persistent?.dbPath);
143144
let input: MessageLike[] = [];
144145
const provider = {
145146
readMessages: () =>
@@ -188,10 +189,12 @@ export async function hostFixture(
188189
cleanups.push(baselineRaw.setRawMessageProvider(sid, provider));
189190
}
190191
let response: Record<string, unknown> = {};
192+
let request: Record<string, unknown> = {};
191193
let mutate: (value: Record<string, unknown>) => void = () => {};
192194
const client: RustModeModuleClient = {
193195
call: async ({ method, body }) => {
194196
if (method !== "transform") return { ok: true };
197+
request = body as Record<string, unknown>;
195198
response = probe(binary, directory, {
196199
...(body as Record<string, unknown>),
197200
...(midTurn ? { mid_turn: true } : {}),
@@ -248,6 +251,10 @@ export async function hostFixture(
248251
db,
249252
sid,
250253
deps,
254+
directory,
255+
get request() {
256+
return request;
257+
},
251258
get response() {
252259
return response;
253260
},

0 commit comments

Comments
 (0)