Date: 2026-06-23
- This plan executes without user intervention (mode-auto active).
- All questions resolved: proceed even though the test's start-time
light test-validator --stopwill kill any user-run prover on :3001 (the test starts its own stack). - Use subagents for parallel porting (max 5 concurrent), spawned so they never prompt.
- If blocked, find an alternative approach; do not stop. Skip-and-note only as last resort.
- Keep working until ALL examples are ported, compiled, and (Phase 2) tested green.
- User instruction: "Plan to port all portable ones, use subagents to do it in parallel up to 5 at a time."
- Batching is explicitly ALLOWED for this plan (user requested parallel subagents). This overrides the default no-batching rule.
- Split the work into todos; work through them one wave at a time; do not collapse waves.
- Use subagents to do the per-example porting in parallel, max 5 concurrent.
- If a subagent gets stuck or starts doing random things, it must stop and report rather than improvise; escalate research to another subagent.
- Test EARLY: subagents must get each example to compile before reporting done.
- Hard constraint — validator is a singleton: the rewritten tests spawn
light test-validator(ports 8899/8784/3001) and each run calls--stopfirst. Therefore only one example's test may run at a time. Subagents do PORT + COMPILE only (parallel-safe). Test EXECUTION is serialized in a separate phase. - Do not add Claude as a commit co-author. No emojis. Never index slices with
[i]in any Rust we write.
Switch every portable example from released light-* crates + light-program-test to:
- local
~/dev/light/light-sdkspath deps via[patch.crates-io], light-client+ a reallight test-validatorfor integration tests,- the anchor-1.0.2 / solana-4.0 dependency stack.
Pinocchio examples (counter/pinocchio) are OUT until light-sdk-pinocchio is added to the local workspace.
Add:
[patch.crates-io]
light-sdk = { path = "/Users/jorrit/dev/light/light-sdks/sdk" }
light-hasher = { path = "/Users/jorrit/dev/light/light-sdks/hasher" }
light-client = { path = "/Users/jorrit/dev/light/light-sdks/client" }(Patching these three pulls the whole transitive light-* graph from the local workspace.)
anchor-lang = "1.0.2"(anchor examples only; was 0.31.1).- Enable
keccakon the SDK so address derivation works at runtime:light-sdk = { version = "0.23.0", features = ["anchor", "cpi-context", "keccak"] }(native examples: same but without"anchor"). [dev-dependencies]: removelight-program-test; remove thesolana-sdkumbrella; use split crates matching the local stack:Keep any other existing dev-deps the test genuinely uses.light-client = "0.23.0" solana-instruction = "3.4" solana-keypair = "3.1.2" solana-pubkey = { version = "4.2", features = ["curve25519", "sha2"] } solana-signature = "3.4" solana-signer = "3.0" # solana-keypair -> five8 1.0 -> five8_core 0.1.2 gates `impl Error for DecodeError` behind `std` five8_core = { version = "0.1.2", features = ["std"] } tokio = "1.49.0"
- anchor 1.0 collapsed
Contextto one lifetime:Context<'_, '_, '_, 'info, T>->Context<'info, T>(replace all). - Fix any other anchor-1.0 / borsh-1.x breaks the compiler surfaces (iterate
cargo check).
- Swap imports: drop
light_program_test::*and thesolana_sdkumbrella. Uselight_client::{rpc::{LightClient, LightClientConfig, Rpc, RpcError}, indexer::{Indexer, AddressWithTree, CompressedAccount, TreeInfo, ...}}and the split solana crates (solana_instruction::Instruction,solana_keypair::Keypair,solana_signature::Signature,solana_signer::Signer). - Replace
LightProgramTest::new(...)setup with astart_validator_and_connect()helper that:- builds the
.sopath:format!("{}/../../target/deploy/<prog>.so", env!("CARGO_MANIFEST_DIR"))(adjust../..to the workspace target dir for that example), - runs
light test-validator --stop(cleanup), then spawnslight test-validator --sbf-program <ID> <so_path>, - connects
LightClient::new(LightClientConfig::local()), pollsget_slot()until ready, then sleeps ~10s.
- builds the
- Fund a fresh payer:
rpc.airdrop_lamports(&payer.pubkey(), 10_000_000_000).await. - Mark every test
#[tokio::test(flavor = "multi_thread", worker_threads = 4)](LightClient wraps the blocking RPC client). - Do NOT add a
--stopat the END of the test (it kills the test's own process group -> SIGKILL). Rely on the next run's start-time--stop. - Helper fns generic over
R: Rpc + Indexerneed no body changes.
counter/anchor is the worked example. Subagents should read these files as the template:
counter/anchor/Cargo.toml(patch block)counter/anchor/programs/counter/Cargo.toml(deps)counter/anchor/programs/counter/src/lib.rs(Context fix)counter/anchor/programs/counter/tests/test.rs(validator+client test)
basic-operations/anchor/createbasic-operations/anchor/updatebasic-operations/anchor/burnbasic-operations/anchor/closebasic-operations/anchor/reinitread-onlyaccount-comparisoncreate-and-updatezk/nullifierzk/zk-id(also has a Noir circuit testtests/circuit.rs— leave circuit alone, only port the light test)
counter/nativeairdrop-implementations/simple-claimbasic-operations/native/createbasic-operations/native/updatebasic-operations/native/burnbasic-operations/native/closebasic-operations/native/reinit
Native specifics: no anchor migration; bump solana-program to the local 4.0-compatible set; tests build instructions manually (no anchor InstructionData/ToAccountMetas); a src/test_helpers.rs also imports light_program_test and must be ported too.
- Apply the
keccakfeature fix tocounter/anchor/programs/counter/Cargo.toml. - Run
cargo test-sbffor counter against the validator; confirmtest_counter ... ok. - Lock the recipe text above against whatever the run reveals. Only after green do we fan out.
Each subagent ports ONE example per the recipe and must achieve BOTH:
-
cargo check --manifest-path <program Cargo.toml> --tests --features test-sbfgreen (anchor) / appropriate features (native), -
cargo build-sbf --manifest-path <program Cargo.toml>green. Subagents MUST NOT runcargo test-sbf/ spawn a validator (port singleton). Report deviations from the recipe. -
Wave A (5): basic-operations/anchor/{create, update, burn, close, reinit}
-
Wave B (5): read-only, account-comparison, create-and-update, zk/nullifier, counter/native
-
Wave C (5): airdrop-implementations/simple-claim, basic-operations/native/{create, update, burn, close}
-
Wave D (2): basic-operations/native/reinit, zk/zk-id
For each ported example, run cargo test-sbf ... -- --nocapture, confirm the test passes, fix runtime issues (most likely the same keccak/feature class). Never two validators at once.
- Every listed example: program lib + tests compile against local path deps;
cargo build-sbfproduces a.so. - Each example's integration test passes against
light test-validator(Phase 2). - No example still references
light-program-testor thesolana-sdkumbrella. [patch.crates-io]points only at local paths; no releasedlight-*crates cross an API boundary.- Pinocchio examples untouched (documented as blocked).
Phase 0: DONE — counter/anchor ported + test_counter PASSED with keccak fix. Recipe locked.
Phase 1 (port + compile): DONE for all portable examples. cargo check --tests + cargo build-sbf green for:
- counter/anchor (also test-passed)
- basic-operations/anchor/{create,update,burn,close,reinit}
- account-comparison (test_solana_account.rs kept on LiteSVM, bumped to 0.12), create-and-update
- read-only, zk/nullifier, zk/zk-id (vendored
read_state_merkle_tree_root; circuit.rs imports redirected) - counter/native, basic-operations/native/{create,update,close,reinit,burn}
BLOCKED / not ported:
- airdrop-implementations/simple-claim — depends on the compressed-TOKEN stack (light-token, light-compressed-token-sdk, light-token-types, light-token-interface) which is NOT in the local workspace, and local light-client dropped
get_compressed_token_accounts_by_owner. REVERTED to released deps (left working). - counter/pinocchio — needs light-sdk-pinocchio (not local). Untouched.
Phase 2 (run tests): DONE — user authorized; prover killed first. 16 integration-test crates PASS against light test-validator:
- counter/anchor (test_counter)
- basic-operations/anchor/{create,update,burn,close,reinit}
- read-only, account-comparison (LightClient + LiteSVM), create-and-update (both files)
- zk/nullifier (2 tests — after adapting the duplicate-rejection check: the real indexer rejects a non-inclusion proof for an existing address at proof-build time)
- counter/native, basic-operations/native/{create,update,close,reinit,burn}
Phase 2 fixes applied:
- native update/close/reinit/burn:
test-sbf -> test-helpersdragged host-onlylight-client/solana-keypair(getrandom 0.2, no solana target) into the SBF build. Moved the 4 test-helpers optional deps under[target.'cfg(not(target_os = "solana"))'.dependencies]and gatedpub mod test_helperswithnot(target_os = "solana"). - zk/nullifier: adapted duplicate-rejection assertion (see above).
zk/zk-id: Rust port migrated and cargo check --tests GREEN. Its integration test cannot LINK without the native libcircuit produced by the example's own scripts/setup.sh (node/npm/circom ZK toolchain) — a pre-existing requirement independent of the SDK migration (the released-deps version needs it too). Not run.
- Requires the local
~/dev/light/light-sdksworkspace to stay buildable (it is). - Requires
lightCLI + the locally-builtphoton(0.51.2 w/--prover-url) + a prover. User is managing a prover server; Phase 2 must coordinate with whatever prover/validator is running (the test's--stopwill kill a user-run prover on :3001). - Each example is its own cargo workspace (separate Cargo.lock/target) — parallel edits touch disjoint paths, so no git/worktree isolation needed.
zk/zk-idis highest-risk (circuit deps); scheduled last/alone.