You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Stage 5d-next-5 / Step 7 housekeeping follow-ups: HTTP errors + CI coverage + CI cyclic-tests + doc fold
Self-contained brief for a fresh Claude session. Do not assume any prior context — read the linked PRs and source paths fresh before proposing changes. Modeled on the structure of the now-closed #25 (also a self-contained brief).
TL;DR
After PR #23 (Stage 5d-next-5 Phases 1+2a+2b+3) and PR #26 (send_coins switched to in-circuit _and_sources API + off-circuit shim retained as defense-in-depth) landed, four housekeeping follow-ups were deliberately deferred. They're separable: each can be its own PR; none of them blocks the others. List in priority order:
HTTP error-mapping — replace the current 200 OK + {success: false} failure path of /api/send + /api/mint with proper 4xx + structured error body. (Medium-High risk: this is an API-contract change that wallet clients consume.)
CI coverage exclusions drop — remove account_server.rs + server.rs from the --ignore-filename-regex set in .github/workflows/ci.yaml. (Medium risk: the --fail-under-lines 100 gate must actually hit 100% for those files; needs local cargo llvm-cov verification.)
CI cyclic-tests inclusion — remove --skip stage_5d --skip stage_5e from .github/workflows/ci.yaml and bump timeout-minutes from 30 to ~120. (High risk: GH ubuntu-latest has 7 GB RAM and ~2 GB per cyclic test; only verifiable via an actual CI run.)
Doc fold — move program-plonky2/STAGE_5D_NEXT_5_AGGREGATOR.md into program-plonky2/MIGRATION_RESEARCH.md §7.22 and update cross-references. (Low risk: pure documentation movement.)
Working tree: ~/Documents/GitHub/zkcoins/server-claude-5d5/ — this is the Claude-owned clone per memory reference_zkcoins_claude_workdir. Do NOT touch the user's separate server/ clone.
Each carries a different risk profile that warrants its own decision-pass instead of expanding PR #26's scope. The defense-in-depth comment block at server/src/account_server.rs:377-389 already records the rationale for the off-circuit shim, and PR #26's body explicitly lists these four items under "Out of scope".
Replace the current 200 OK + {success: false} failure path of the HTTP handlers with proper status codes and a structured error body, so wallet clients can distinguish "user error" (4xx) from "server error" (5xx) and surface specific error strings.
Current state
server/src/server.rs::send_coin_handler (line ~370): on send_coins error, returns (StatusCode::OK, Json(SendCoinResponse { success: false, … })) (lines 495-503). The actual error string from send_coins is eprintln!-logged but never reaches the client.
server/src/server.rs::mint_handler (line ~507, feature-gated faucet): same pattern.
server/src/account_server.rs::send_coins returns Result<Vec<CoinProof>, &'static str> with these failure strings:
"Unknown account address"
"Insufficient funds"
"Coin is missing commitment"
"Should provide an inclusion proof"
"Coin should not exist in coin history tree"
"Coin should not exist in tree yet"
"In-coin not present in source's output_coins_root" (Phase 2b defense-in-depth shim)
"Source commitment not present in history MMR" (Phase 2b defense-in-depth shim)
"Too many in-coins for one transition"
"Too many out-coins for one transition"
"prev_commitment_pubkey required for account update"
Map error strings to status codes in the HTTP handler:
fnmap_send_coins_error(err:&str) -> (StatusCode,&str){match err {"Unknown account address" => (StatusCode::NOT_FOUND, err),"Insufficient funds" => (StatusCode::UNPROCESSABLE_ENTITY, err),// Defense-in-depth shim rejections — caller witness is malformed"In-coin not present in source's output_coins_root"
| "Source commitment not present in history MMR"
| "Coin is missing commitment"
| "Coin should not exist in coin history tree"
| "Coin should not exist in tree yet"
| "Should provide an inclusion proof"
| "Too many in-coins for one transition"
| "Too many out-coins for one transition" => (StatusCode::UNPROCESSABLE_ENTITY, err),"prev_commitment_pubkey required for account update" => (StatusCode::BAD_REQUEST, err),
s if s.ends_with("failed") => (StatusCode::INTERNAL_SERVER_ERROR,"prove failed"),
_ => (StatusCode::INTERNAL_SERVER_ERROR,"internal error"),}}
Extend SendCoinResponse with error: Option<String>:
Update the Err(_) => … branches of send_coin_handler + mint_handler to use the map.
Option B — Proper error enum (cleaner, bigger change)
Refactor account_server::send_coins to return Result<Vec<CoinProof>, SendCoinsError> where SendCoinsError is an enum. Each variant maps to a status code via impl IntoResponse. Tests gain compiler-checked exhaustiveness instead of string-matching.
Memory feedback_threat_model_over_checklist: the threat model is "user sent malformed witness" vs "server crashed". Both options achieve the same external behavior; Option A is the minimum viable change. Option B is the right long-term shape but slips scope. Recommend Option A unless the user explicitly asks for B.
Tests
For each error string, a handler-level test that asserts the status code + body. Tests live in server/src/server_tests.rs. Pattern:
#[test]fntest_send_coin_handler_maps_insufficient_funds_to_422(){// Spin up state with mint account at balance 0; client sends 1.// Assert response.status() == StatusCode::UNPROCESSABLE_ENTITY// Assert body.error == Some("Insufficient funds")}
Cover at least: Unknown account address → 404, Insufficient funds → 422, the two Phase 2b shim rejections → 422, the prove_*_failed paths → 500.
Verification
cargo test -p server --release --all-features -- --test-threads=1 adds ~8-10 new tests, runtime impact minimal (handler tests are non-cyclic and fast).
Wallet client (in zk-coins/app) needs awareness — but this issue is about the server side; the client update is its own follow-up.
Acceptance criteria
SendCoinResponse includes an error: Option<String> field; on success it is None.
Each documented send_coins error string maps to a deterministic (StatusCode, error_body) via map_send_coins_error (or the enum equivalent under Option B).
New handler-level tests cover at minimum the 5 cases listed above.
The defense-in-depth shim rejection paths surface specific 422 + the error string within milliseconds (as the shim already runs in microseconds; the HTTP layer doesn't add latency).
cargo test -p server --release --all-features -- --test-threads=1 green.
Risks
API-contract change: wallet clients consuming the API today see 200 OK + success:false. After this change they see 4xx + body.error. If wallet clients hard-code the 200 check, they break. Mitigation: coordinate with zk-coins/app maintainer (= user); explicitly call out the contract change in the PR title + body.
5xx leakage: prove_*_failed is the catch-all for cyclic-recursion prover failure. If the body echoes the raw error string, an attacker could probe for prover internal state. Mitigation: collapse all *_failed strings to a generic "prove failed" body (per the Option A snippet above).
Item 2 — CI coverage exclusions drop (Medium risk; needs local llvm-cov verification)
Goal
Remove the temporary account_server.rs + server.rs exclusions from the cargo llvm-cov invocation in .github/workflows/ci.yaml. Those files were excluded while the in-circuit send_coins wiring was a Step 7 follow-up (their test modules were disabled at the include-point). PR #23 + PR #26 restored those tests; the gate can now run against the full surface.
Read the --show-missing-lines output for each unreached line/function in account_server.rs + server.rs.
For each uncovered region:
If a real test path can exercise it (likely most error branches): add the test. Pattern matches server/src/account_server_tests.rs.
If the region is genuinely unreachable in normal operation (e.g. a Mutex::PoisonError recovery branch, a logging fallback): annotate with #[cfg_attr(coverage_nightly, coverage(off))] on the function or // LCOV_EXCL_LINE on the line. Mention WHY in the comment so future readers don't think it's slop.
.github/workflows/ci.yaml--ignore-filename-regex argument no longer contains account_server\.rs or server\.rs.
Any coverage(off) / LCOV_EXCL annotations added in step 3 are justified with an in-source comment explaining why the region is unreachable.
Risks
Coverage gap: if some lines/functions can't reasonably be tested without major refactoring, the gate may resist 100%. Mitigation: tactical coverage(off) annotations on the truly-unreachable regions (e.g. unwrap_or_else(PoisonError::into_inner) fallbacks, unreachable!() arms). Memory feedback_no_fallbacks: don't use coverage(off) as a "fallback" for real coverage gaps — only for genuinely unreachable code.
Local run cost: the 30-60 min llvm-cov run is non-trivial wall time. Plan to run it once, fix all gaps in a single pass, then re-run for confirmation.
Item 3 — CI cyclic-tests inclusion + timeout bump (High risk; only verifiable via CI run)
Goal
Make the GitHub-hosted CI actually run the Stage 5d / Stage 5e cyclic-recursion tests in program-plonky2, instead of --skip-ing them. Bump the tests job's timeout-minutes so the longer wall fits.
Local single-threaded wall time anchors for the cyclic tests at the current production parameters (MAX_IN_COINS = MAX_OUT_COINS = 8, INNER_PAD_BITS_STAGE_5D_NEXT_5 = 15):
Test class
Per-test wall (M3)
stage_5c_plus_* smokes
~40-55 s
stage_5d_next_3_* integration
~45-80 s
stage_5d_next_5_phase_2b_* positives
~99-154 s
stage_5d_next_5_phase_3_* negatives
~35-55 s
Aggregator unit tests
~25 s each
Full program-plonky2 lib sweep ~42 min wall on M3 with --test-threads=2. Single-threaded on ubuntu-latest is ~80-120 min (CPU is ~2.5× slower; no GPU acceleration available).
Implementation plan
Bump timeout-minutes from 30 → 120 for the tests job (line 81).
Remove --skip stage_5b --skip stage_5c --skip stage_5d --skip stage_5e (or keep --skip stage_5b --skip stage_5c if those are deprecated; verify with cargo test ... -- --list).
Push to a branch targeting develop (the workflow trigger is pull_request: branches: [develop] — feat/plonky2-migration-targeted PRs do NOT trigger CI). The cleanest path: rebase this fix onto develop-after-feat/plonky2-migration-merges, or use a temporary branch off develop for verification.
Watch the CI run; if it OOMs, drop the cyclic-test count or bump runner size; if it times out at 120 min, bump further.
Verification
CI run on a develop-targeted PR completes within 120 min with all cyclic tests green.
A real CI run on a develop-targeted PR completes green within the bumped timeout.
Risks
OOM-kill: GH ubuntu-latest has 7 GB RAM. Each cyclic StateTransitionCircuit is ~2 GB resident; --test-threads=1 keeps that at one circuit at a time, but the cargo build itself + the test runner overhead can push memory close to the edge. Mitigation: keep --test-threads=1; consider larger GH-hosted runner via runs-on: ubuntu-latest-large if standard tier OOMs.
Fold the standalone aggregator architecture write-up into the MIGRATION_RESEARCH.md §7.x lessons-learned series (where it logically belongs as "§7.22 — Stage 5d-next-5 source-side verification: aggregator-pattern + ConstantGate + pad-bits"), then delete the standalone file and update cross-references.
Current state
program-plonky2/STAGE_5D_NEXT_5_AGGREGATOR.md — 612 lines, full Phase 2a/2b architecture, empirical insights (ConstantGate::new(2) injection + helper_degree = pad_bits + 1), test coverage matrix, "How to verify Phase 2a from scratch" runbook.
program-plonky2/MIGRATION_RESEARCH.md — has §7.0 through §7.21 sections capturing lessons from each migration phase. §7.21 is "Stage 5d-next-4 source-side verification: blocked on Plonky2 1.1.0, deferred to Stage 5d-next-5 (post-MVP)". §7.22 would naturally be the resolution.
Cross-references to STAGE_5D_NEXT_5_AGGREGATOR.md exist in:
program-plonky2/SESSION_STATE.md
program-plonky2/STEP7_PREP.md
ROADMAP.md row 5 (Step 5 status footnote)
server/src/account_server.rs (the defense-in-depth comment block)
Status snapshot table from STAGE_5D_NEXT_5_AGGREGATOR.md (Phases 1/2a/2b/3 all done).
Architecture summary — copy the ASCII box diagram.
Two empirical insights — copy the ConstantGate::new(2) injection and helper_degree = pad_bits + 1 sections with their probe-verified tables. These are the lessons the MIGRATION_RESEARCH doc is meant to capture.
Test coverage matrix — Phase 3 positives + negatives.
Benchmark table.
Verification runbook.
Update cross-references. Replace each occurrence of STAGE_5D_NEXT_5_AGGREGATOR.md with MIGRATION_RESEARCH.md#722-stage-5d-next-5-source-side-verification-via-aggregator-pattern-resolves-721 (or whatever exact GitHub-anchor heading you write).
program-plonky2/MIGRATION_RESEARCH.md §7.22 contains the architecture summary, two empirical insights, test coverage matrix, benchmark, and verification runbook.
cargo doc succeeds with no broken intra-doc links.
Risks
Lost content: the standalone doc is 612 lines; the §7.22 section may need to be more compressed than a verbatim copy to fit the MIGRATION_RESEARCH.md style (which is "lessons" not "tutorials"). The full architecture / runbook content could land in §7.22 verbatim, but if §7.x sections are normally tighter, abbreviate and link to PR Stage 5d-next-5: Phases 2a + 2b + 3 LANDED (refs #19; closes the source-verification work) #23 for full detail. Mitigation: read §7.18 / §7.19 / §7.21 first to calibrate the section-length norm.
Acceptance criteria (issue-level)
A reviewer should be able to verify, in order:
Each of the four items above lands in its own PR (or, if the user prefers, two PRs grouped by risk: item 4 in one, items 1+2+3 in another).
cargo check --workspace --all-targets + cargo fmt --all --check + cargo clippy --workspace --all-targets --all-features -- -D warnings green after each PR.
cargo test -p server --release --all-features -- --test-threads=1 green after Item 1 (with the new handler-mapping tests) and after Item 2 (with any added coverage-gap tests).
cargo llvm-cov -p server --show-missing-lines --fail-under-lines 100 --fail-under-functions 100 -- --test-threads=1 green at the relaxed exclusion list after Item 2.
A CI run on a develop-targeted PR completes within 120 min after Item 3.
git grep STAGE_5D_NEXT_5_AGGREGATOR.md returns 0 hits outside git history after Item 4.
House rules (from caller's memories — read before pushing)
feedback_zkcoins_direct_develop: zkCoins repos push directly to develop, no PRs targeting develop directly. But this work is on top of feat/plonky2-migration (which is itself en route to develop via PR feat: Plonky2 / Poseidon proof-system migration #17). Add commits to a follow-up branch off feat/plonky2-migration AND open a Draft PR targeting feat/plonky2-migration. Exception for Item 3 verification: that one needs a develop-targeted PR because CI only triggers there.
feedback_pr_as_draft + feedback_gh_pr_draft_status: PRs always Draft; after every gh pr edit or push, re-verify isDraft: true via gh pr view N --json isDraft because the GH CLI sometimes flips it back to ready.
feedback_no_force_push: never git push --force except after a rebase.
feedback_no_squash: don't squash commits on the feature branch; the user squashes on merge.
feedback_local_tests_before_push: run cargo fmt --all --check, cargo clippy --workspace --all-targets --all-features -- -D warnings, and the relevant cargo test lane locally before every push.
feedback_ci_monitor_after_push: after every push, monitor CI; if any job is red, intervene. NOTE: PR-targeting feat/plonky2-migration doesn't trigger CI; only develop-targeted PRs do. Items 1/2/4 only get CI feedback once the parent PR feat: Plonky2 / Poseidon proof-system migration #17 merges to develop and these changes land there.
feedback_merge_self: never merge a PR yourself — the user does that.
Commit-message style: no Claude / Anthropic / AI / Generated with … / Co-Authored-By: Claude footers. Plain human-developer style. Match the existing commit-message tone in the repo (git log --oneline).
feedback_no_fallbacks: no ?? default, || default, empty catch, default parameters added without explicit instruction. Applies especially to Item 2's coverage-gap fixes (don't coverage(off) real gaps; only annotate genuinely unreachable regions).
feedback_threat_model_over_checklist: findings against the real threat model, not generic hardening. Applies to Item 1's status-code mapping (the threat is "client sends malformed witness" → 4xx vs "server crashes during prove" → 5xx; don't invent new 4xx codes for hypothetical attacks).
Reference reading order
program-plonky2/STAGE_5D_NEXT_5_AGGREGATOR.md (will be folded by Item 4, but it's the canonical architecture write-up — read before doing anything).
server/src/account_server.rs lines 278-534 (send_coins function — the off-circuit shim at 377-401, the sources Vec build at 425-445, the prover calls at 478-501).
Stage 5d-next-5 / Step 7 housekeeping follow-ups: HTTP errors + CI coverage + CI cyclic-tests + doc fold
TL;DR
After PR #23 (Stage 5d-next-5 Phases 1+2a+2b+3) and PR #26 (
send_coinsswitched to in-circuit_and_sourcesAPI + off-circuit shim retained as defense-in-depth) landed, four housekeeping follow-ups were deliberately deferred. They're separable: each can be its own PR; none of them blocks the others. List in priority order:200 OK + {success: false}failure path of/api/send+/api/mintwith proper4xx + structured error body. (Medium-High risk: this is an API-contract change that wallet clients consume.)account_server.rs+server.rsfrom the--ignore-filename-regexset in.github/workflows/ci.yaml. (Medium risk: the--fail-under-lines 100gate must actually hit 100% for those files; needs localcargo llvm-covverification.)--skip stage_5d --skip stage_5efrom.github/workflows/ci.yamland bumptimeout-minutesfrom 30 to ~120. (High risk: GHubuntu-latesthas 7 GB RAM and ~2 GB per cyclic test; only verifiable via an actual CI run.)program-plonky2/STAGE_5D_NEXT_5_AGGREGATOR.mdintoprogram-plonky2/MIGRATION_RESEARCH.md §7.22and update cross-references. (Low risk: pure documentation movement.)Working environment
zk-coins/serverfeat/plonky2-migration(HEAD includes PR Stage 5d-next-5: Phases 2a + 2b + 3 LANDED (refs #19; closes the source-verification work) #23 already; PR Stage 5d-next-5 follow-ups: in-circuit send_coins (defense-in-depth) + doc parity (closes #25) #26 is onfeat/plonky2-5d-next-5-followupwaiting to be merged intofeat/plonky2-migration). The user merges PRs; do not merge yourself.~/Documents/GitHub/zkcoins/server-claude-5d5/— this is the Claude-owned clone per memoryreference_zkcoins_claude_workdir. Do NOT touch the user's separateserver/clone.rust-toolchainpins nightly; workspace isprogram-plonky2/+script-plonky2/+server/+shared/.Context (must read)
What's already landed
feat/plonky2-5d-next-5-phase2→feat/plonky2-migration, merged): Stage 5d-next-5 Phases 1+2a+2b+3. Aggregator pattern, per-slot SMT inclusion, SPEC §8 (c)(d)(e) chain for source, active-bit binding, 3 §13 source-side negatives. Full architecture write-up inprogram-plonky2/STAGE_5D_NEXT_5_AGGREGATOR.md.feat/plonky2-5d-next-5-followup→feat/plonky2-migration, currently Draft awaiting merge):send_coinsswitched toprove_*_and_sources. Off-circuit pre-check loop retained as defense-in-depth (per the user's chosen Option A in Wire in-circuit source-side verification into account_server::send_coins (Stage 5d-next-5 Phase 2 follow-on) #25). One new negative test:test_send_coins_rejects_tampered_source_proof_inclusion. Doc parity inROADMAP.md+SESSION_STATE.md+STEP7_PREP.md+script-plonky2/src/lib.rsProver docstrings.Why these were deferred from #25 / PR #26
Each carries a different risk profile that warrants its own decision-pass instead of expanding PR #26's scope. The defense-in-depth comment block at
server/src/account_server.rs:377-389already records the rationale for the off-circuit shim, and PR #26's body explicitly lists these four items under "Out of scope".Item 1 — HTTP error mapping (Medium-High risk; API-contract change)
Goal
Replace the current
200 OK + {success: false}failure path of the HTTP handlers with proper status codes and a structured error body, so wallet clients can distinguish "user error" (4xx) from "server error" (5xx) and surface specific error strings.Current state
server/src/server.rs::send_coin_handler(line ~370): onsend_coinserror, returns(StatusCode::OK, Json(SendCoinResponse { success: false, … }))(lines 495-503). The actual error string fromsend_coinsiseprintln!-logged but never reaches the client.server/src/server.rs::mint_handler(line ~507, feature-gatedfaucet): same pattern.server/src/account_server.rs::send_coinsreturnsResult<Vec<CoinProof>, &'static str>with these failure strings:"Unknown account address""Insufficient funds""Coin is missing commitment""Should provide an inclusion proof""Coin should not exist in coin history tree""Coin should not exist in tree yet""In-coin not present in source's output_coins_root"(Phase 2b defense-in-depth shim)"Source commitment not present in history MMR"(Phase 2b defense-in-depth shim)"Too many in-coins for one transition""Too many out-coins for one transition""prev_commitment_pubkey required for account update""prove_initial_with_in_and_out_coins_and_sources failed""prove_account_update_with_in_and_out_coins_and_sources failed"Implementation plan
Option A — String-based mapping (smaller change)
Map error strings to status codes in the HTTP handler:
Extend
SendCoinResponsewitherror: Option<String>:Update the
Err(_) => …branches ofsend_coin_handler+mint_handlerto use the map.Option B — Proper error enum (cleaner, bigger change)
Refactor
account_server::send_coinsto returnResult<Vec<CoinProof>, SendCoinsError>whereSendCoinsErroris an enum. Each variant maps to a status code viaimpl IntoResponse. Tests gain compiler-checked exhaustiveness instead of string-matching.Memory
feedback_threat_model_over_checklist: the threat model is "user sent malformed witness" vs "server crashed". Both options achieve the same external behavior; Option A is the minimum viable change. Option B is the right long-term shape but slips scope. Recommend Option A unless the user explicitly asks for B.Tests
For each error string, a handler-level test that asserts the status code + body. Tests live in
server/src/server_tests.rs. Pattern:Cover at least:
Unknown account address→ 404,Insufficient funds→ 422, the two Phase 2b shim rejections → 422, theprove_*_failedpaths → 500.Verification
cargo test -p server --release --all-features -- --test-threads=1adds ~8-10 new tests, runtime impact minimal (handler tests are non-cyclic and fast).zk-coins/app) needs awareness — but this issue is about the server side; the client update is its own follow-up.Acceptance criteria
SendCoinResponseincludes anerror: Option<String>field; on success it isNone.send_coinserror string maps to a deterministic(StatusCode, error_body)viamap_send_coins_error(or the enum equivalent under Option B).cargo test -p server --release --all-features -- --test-threads=1green.Risks
200 OK + success:false. After this change they see4xx + body.error. If wallet clients hard-code the 200 check, they break. Mitigation: coordinate withzk-coins/appmaintainer (= user); explicitly call out the contract change in the PR title + body.prove_*_failedis the catch-all for cyclic-recursion prover failure. If the body echoes the raw error string, an attacker could probe for prover internal state. Mitigation: collapse all*_failedstrings to a generic"prove failed"body (per the Option A snippet above).Item 2 — CI coverage exclusions drop (Medium risk; needs local llvm-cov verification)
Goal
Remove the temporary
account_server.rs+server.rsexclusions from thecargo llvm-covinvocation in.github/workflows/ci.yaml. Those files were excluded while the in-circuitsend_coinswiring was a Step 7 follow-up (their test modules were disabled at the include-point). PR #23 + PR #26 restored those tests; the gate can now run against the full surface.Current state
.github/workflows/ci.yamlline 177:Target shape:
--ignore-filename-regex 'main\.rs|publisher\.rs|server_runtime\.rs|scanner_runtime\.rs|_tests\.rs$' \Implementation plan
cargo llvm-cov -p server --show-missing-lines \ --ignore-filename-regex 'main\.rs|publisher\.rs|server_runtime\.rs|scanner_runtime\.rs|_tests\.rs$' \ --fail-under-lines 100 \ --fail-under-functions 100 \ -- --test-threads=1--show-missing-linesoutput for each unreached line/function inaccount_server.rs+server.rs.server/src/account_server_tests.rs.Mutex::PoisonErrorrecovery branch, a logging fallback): annotate with#[cfg_attr(coverage_nightly, coverage(off))]on the function or// LCOV_EXCL_LINEon the line. Mention WHY in the comment so future readers don't think it's slop.cargo llvm-cov ... --fail-under-lines 100 --fail-under-functions 100returns 0..github/workflows/ci.yamlto drop the exclusions.Verification
cargo llvm-covpass at 100% lines / 100% functions.develop(because the workflow only triggers there) confirms the gate still passes.Acceptance criteria
cargo llvm-cov -p server --show-missing-lines --ignore-filename-regex 'main\.rs|publisher\.rs|server_runtime\.rs|scanner_runtime\.rs|_tests\.rs$' --fail-under-lines 100 --fail-under-functions 100 -- --test-threads=1passes locally..github/workflows/ci.yaml--ignore-filename-regexargument no longer containsaccount_server\.rsorserver\.rs.coverage(off)/LCOV_EXCLannotations added in step 3 are justified with an in-source comment explaining why the region is unreachable.Risks
coverage(off)annotations on the truly-unreachable regions (e.g.unwrap_or_else(PoisonError::into_inner)fallbacks,unreachable!()arms). Memoryfeedback_no_fallbacks: don't usecoverage(off)as a "fallback" for real coverage gaps — only for genuinely unreachable code.Item 3 — CI cyclic-tests inclusion + timeout bump (High risk; only verifiable via CI run)
Goal
Make the GitHub-hosted CI actually run the Stage 5d / Stage 5e cyclic-recursion tests in
program-plonky2, instead of--skip-ing them. Bump thetestsjob'stimeout-minutesso the longer wall fits.Current state
.github/workflows/ci.yamllines 121-124:And line 81:
Local single-threaded wall time anchors for the cyclic tests at the current production parameters (
MAX_IN_COINS = MAX_OUT_COINS = 8,INNER_PAD_BITS_STAGE_5D_NEXT_5 = 15):stage_5c_plus_*smokesstage_5d_next_3_*integrationstage_5d_next_5_phase_2b_*positivesstage_5d_next_5_phase_3_*negativesFull
program-plonky2lib sweep ~42 min wall on M3 with--test-threads=2. Single-threaded onubuntu-latestis ~80-120 min (CPU is ~2.5× slower; no GPU acceleration available).Implementation plan
timeout-minutesfrom30→120for thetestsjob (line 81).--skip stage_5b --skip stage_5c --skip stage_5d --skip stage_5e(or keep--skip stage_5b --skip stage_5cif those are deprecated; verify withcargo test ... -- --list).develop(the workflow trigger ispull_request: branches: [develop]—feat/plonky2-migration-targeted PRs do NOT trigger CI). The cleanest path: rebase this fix ontodevelop-after-feat/plonky2-migration-merges, or use a temporary branch offdevelopfor verification.Verification
develop-targeted PR completes within 120 min with all cyclic tests green.Acceptance criteria
.github/workflows/ci.yamltestsjob no longer--skipsstage_5d/stage_5e(and optionallystage_5b/stage_5c).testsjobtimeout-minutesis ≥ 120.Risks
ubuntu-latesthas 7 GB RAM. Each cyclicStateTransitionCircuitis ~2 GB resident;--test-threads=1keeps that at one circuit at a time, but the cargo build itself + the test runner overhead can push memory close to the edge. Mitigation: keep--test-threads=1; considerlargerGH-hosted runner viaruns-on: ubuntu-latest-largeif standard tier OOMs.feat/plonky2-migrationwhich doesn't trigger the workflow. This work needs a separate verification PR againstdevelop.Item 4 — Doc fold:
STAGE_5D_NEXT_5_AGGREGATOR.md→MIGRATION_RESEARCH.md §7.22(Low risk; pure doc move)Goal
Fold the standalone aggregator architecture write-up into the
MIGRATION_RESEARCH.md §7.xlessons-learned series (where it logically belongs as "§7.22 — Stage 5d-next-5 source-side verification: aggregator-pattern + ConstantGate + pad-bits"), then delete the standalone file and update cross-references.Current state
program-plonky2/STAGE_5D_NEXT_5_AGGREGATOR.md— 612 lines, full Phase 2a/2b architecture, empirical insights (ConstantGate::new(2)injection +helper_degree = pad_bits + 1), test coverage matrix, "How to verify Phase 2a from scratch" runbook.program-plonky2/MIGRATION_RESEARCH.md— has §7.0 through §7.21 sections capturing lessons from each migration phase. §7.21 is "Stage 5d-next-4 source-side verification: blocked on Plonky2 1.1.0, deferred to Stage 5d-next-5 (post-MVP)". §7.22 would naturally be the resolution.STAGE_5D_NEXT_5_AGGREGATOR.mdexist in:program-plonky2/SESSION_STATE.mdprogram-plonky2/STEP7_PREP.mdROADMAP.mdrow 5 (Step 5 status footnote)server/src/account_server.rs(the defense-in-depth comment block)program-plonky2/src/circuit/main.rs(multiple doc-comment links)program-plonky2/src/circuit/source_aggregator.rsprogram-plonky2/src/circuit/recursion_shape_probe.rsImplementation plan
program-plonky2/MIGRATION_RESEARCH.md, find the §7.21 section, append §7.22 below it with:## 7.22 Stage 5d-next-5: source-side verification via aggregator pattern (resolves §7.21)STAGE_5D_NEXT_5_AGGREGATOR.md(Phases 1/2a/2b/3 all done).ConstantGate::new(2)injection andhelper_degree = pad_bits + 1sections with their probe-verified tables. These are the lessons the MIGRATION_RESEARCH doc is meant to capture.STAGE_5D_NEXT_5_AGGREGATOR.mdwithMIGRATION_RESEARCH.md#722-stage-5d-next-5-source-side-verification-via-aggregator-pattern-resolves-721(or whatever exact GitHub-anchor heading you write).program-plonky2/STAGE_5D_NEXT_5_AGGREGATOR.md.Verification
Acceptance criteria
program-plonky2/STAGE_5D_NEXT_5_AGGREGATOR.mddeleted.program-plonky2/MIGRATION_RESEARCH.md §7.22contains the architecture summary, two empirical insights, test coverage matrix, benchmark, and verification runbook.git grep STAGE_5D_NEXT_5_AGGREGATOR.mdoutsidegit logreturns 0 results.cargo docsucceeds with no broken intra-doc links.Risks
Acceptance criteria (issue-level)
A reviewer should be able to verify, in order:
cargo check --workspace --all-targets+cargo fmt --all --check+cargo clippy --workspace --all-targets --all-features -- -D warningsgreen after each PR.cargo test -p server --release --all-features -- --test-threads=1green after Item 1 (with the new handler-mapping tests) and after Item 2 (with any added coverage-gap tests).cargo llvm-cov -p server --show-missing-lines --fail-under-lines 100 --fail-under-functions 100 -- --test-threads=1green at the relaxed exclusion list after Item 2.develop-targeted PR completes within 120 min after Item 3.git grep STAGE_5D_NEXT_5_AGGREGATOR.mdreturns 0 hits outside git history after Item 4.House rules (from caller's memories — read before pushing)
feedback_zkcoins_direct_develop: zkCoins repos push directly todevelop, no PRs targetingdevelopdirectly. But this work is on top offeat/plonky2-migration(which is itself en route todevelopvia PR feat: Plonky2 / Poseidon proof-system migration #17). Add commits to a follow-up branch offfeat/plonky2-migrationAND open a Draft PR targetingfeat/plonky2-migration. Exception for Item 3 verification: that one needs adevelop-targeted PR because CI only triggers there.feedback_pr_as_draft+feedback_gh_pr_draft_status: PRs always Draft; after everygh pr editor push, re-verifyisDraft: trueviagh pr view N --json isDraftbecause the GH CLI sometimes flips it back to ready.feedback_no_force_push: nevergit push --forceexcept after a rebase.feedback_no_squash: don't squash commits on the feature branch; the user squashes on merge.feedback_local_tests_before_push: runcargo fmt --all --check,cargo clippy --workspace --all-targets --all-features -- -D warnings, and the relevantcargo testlane locally before every push.feedback_ci_monitor_after_push: after every push, monitor CI; if any job is red, intervene. NOTE: PR-targetingfeat/plonky2-migrationdoesn't trigger CI; onlydevelop-targeted PRs do. Items 1/2/4 only get CI feedback once the parent PR feat: Plonky2 / Poseidon proof-system migration #17 merges todevelopand these changes land there.feedback_merge_self: never merge a PR yourself — the user does that.Claude/Anthropic/AI/Generated with …/Co-Authored-By: Claudefooters. Plain human-developer style. Match the existing commit-message tone in the repo (git log --oneline).feedback_no_fallbacks: no?? default,|| default, empty catch, default parameters added without explicit instruction. Applies especially to Item 2's coverage-gap fixes (don'tcoverage(off)real gaps; only annotate genuinely unreachable regions).feedback_threat_model_over_checklist: findings against the real threat model, not generic hardening. Applies to Item 1's status-code mapping (the threat is "client sends malformed witness" → 4xx vs "server crashes during prove" → 5xx; don't invent new 4xx codes for hypothetical attacks).Reference reading order
program-plonky2/STAGE_5D_NEXT_5_AGGREGATOR.md(will be folded by Item 4, but it's the canonical architecture write-up — read before doing anything).server/src/account_server.rslines 278-534 (send_coinsfunction — the off-circuit shim at 377-401, thesourcesVec build at 425-445, the prover calls at 478-501).server/src/server.rslines 370-505 (send_coin_handler— Item 1's target)..github/workflows/ci.yaml(Items 2 + 3).program-plonky2/MIGRATION_RESEARCH.md§7.x sections (Item 4's target shape — read §7.18 + §7.19 + §7.21 to calibrate the section style).