refactor: VisualizerContext backed by transaction wire data, eliminate instruction skipping (#228)#255
Conversation
Add SignablePayloadFieldDiagnostic struct and Diagnostic variant to the SignablePayloadField enum. Diagnostics carry structured lint data (rule, domain, level, message, instruction_index) and are attested alongside display fields in the signed payload. Custom Serialize impl ensures alphabetical field ordering for deterministic serialization. Follows the same pattern as AmountV2. Part of #228. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Builder function for constructing Diagnostic fields with rule, domain,
level, message, and optional instruction_index. Sets fallback_text to
"{level}: {message}" and label to the rule ID for backwards compat.
Part of #228.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Alphabetical key ordering at top-level and nested Diagnostic object - Optional InstructionIndex omitted when None - JSON serialize/deserialize roundtrip - Diagnostic field in SignablePayload passes deterministic ordering Part of #228. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace silent filter_map drops with Diagnostic field emission when legacy transaction instructions have out-of-bounds program_id_index or account indices. Two rules: - transaction::oob_program_id: instruction skipped entirely - transaction::oob_account_index: individual accounts omitted Diagnostics are appended to the instruction fields vec, so they appear in the signed payload alongside display fields. Part of #228. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests for decode_instructions with manually crafted transactions: - OOB program_id_index emits transaction::oob_program_id diagnostic - OOB account index emits transaction::oob_account_index diagnostic - Valid transactions produce no diagnostics Part of #228. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mirror the legacy transaction diagnostic emission for v0 transactions. Diagnostic messages note that OOB indices reference lookup table accounts that are unresolvable without on-chain data, distinguishing from corruption in legacy transactions. Also moves the empty_account_keys check before the instruction loop so it fails early. Part of #228. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…dling - Add LintConfig with configurable severity overrides and report_all_rules flag for boot-metric-style attestation - Every rule always reports (ok or warn), so the attester can verify which rules ran and what they found - decode_instructions and decode_v0_instructions never panic or abort: - Data quality issues (OOB indices, empty accounts) become diagnostics - Per-instruction visualizer failures collected in errors vec - Function always returns DecodeInstructionsResult - empty_account_keys converted from Err to diagnostic - Replaced panic! in visualizer lookup with VisualSignError::DecodeError - Severity::Ok replaces "pass" for unambiguous naming - report_all_rules replaces emit_pass_diagnostics - Updated all fixtures and integration tests Part of #228. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- field-types.mdx: document the diagnostic field type for wallet integrators, including properties, handling, and current rules - contributor-guides/lint-diagnostics.mdx: guide for contributors adding lint rules -- architecture, builder usage, LintConfig, naming conventions, testing patterns - docs.json: add lint-diagnostics page to Chain Development nav Part of #228. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…e lint rule Add a new `transaction::oob_account_index_in_skipped_instruction` rule that scans account indices in instructions already being skipped due to an OOB `program_id_index`. This ensures: - The `oob_account_index` ok diagnostic accurately reflects only the instructions that were actually processed (instructions.len() instead of message.instructions.len()) - The new rule provides boot-metric attestation that even skipped instructions were examined for OOB account indices Applied to both legacy (instructions.rs) and v0 (txtypes/v0.rs) paths. Updated swig_wallet tests (5->6, 7->8 fields) and added new tests covering the combined OOB case in both paths. Agent-Logs-Url: https://github.com/anchorageoss/visualsign-parser/sessions/2bb47e94-7f50-403f-a026-d71a37c4fd5f Co-authored-by: shahan-khatchadourian-anchorage <263420032+shahan-khatchadourian-anchorage@users.noreply.github.com>
- Check account indices on all instructions, including those with OOB program_id_index, so ok-level diagnostics are accurate - Preserve original instruction indices through the visualizer loop for consistent labeling and error reporting - Surface per-instruction visualizer errors as decode::visualizer_error diagnostics instead of silently discarding them - Fix lint.rs doc comment: "ok" not "pass" - Update design spec to reflect v0 coverage, LintConfig, correct return types, and error categorization Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
oob_account_index fires only for processed instructions (valid program_id), while oob_account_index_in_skipped_instruction fires for skipped instructions. This prevents double-counting when an instruction has both OOB program_id and OOB account indices. Also regenerates CLI fixtures and updates integration test expected output for the third rule. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Document the steps contributors must follow when adding new rules: regenerate CLI fixtures, update integration test JSON, update field count assertions, and run fuzz/proptest. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Rename solana-json.expected to solana-json.display.expected (contains display fields only, no diagnostics) - Add solana-json.diagnostics.expected with compact rule/level pairs - Delete text fixtures (solana-text.expected, solana-text.input) - CLI test splits output: display fields compared against .display.expected, diagnostics validated by rule/level against .diagnostics.expected - Integration test filters diagnostics before display comparison, validates diagnostics separately by rule/level - Swig wallet tests count display fields only (excludes diagnostics) Adding a new lint rule now requires only adding a line to .diagnostics.expected -- display fixtures are unchanged. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add tracing to visualsign core: create_diagnostic_field now emits tracing::warn! for warn/error-level diagnostics, giving operators production log visibility without per-chain boilerplate - Fix doc comments: replace "pass" with "ok" to match actual level strings in lint.rs, instructions.rs, and v0.rs - Update design spec: add oob_account_index_in_skipped_instruction rule to the rules table - Rename ethereum-json fixture to .display.expected to match the refactored test convention - Fix clippy needless_borrow warning in cli_test.rs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add oob_account_index_in_skipped_instruction rule to contributor guide and field-types.mdx - Document tracing::warn! behavior in create_diagnostic_field across design spec and contributor guide - Rename test functions from "pass" to "ok" terminology in lint.rs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ProgramRef and AccountRef enums for typed resolution status. VisualizerContext holds references to the transaction's wire data instead of an index into a pre-resolved Vec<Instruction>. Resolution of compiled instruction indices to pubkeys happens lazily via helper methods (program_id, account, data). SolanaIntegrationConfig::can_handle simplified to (program_id: &str). InstructionVisualizer::can_handle uses ProgramRef pattern matching. The crate does not compile at this commit — downstream presets and instruction processing still reference the old API. Next commits update them.
All 8 visualizer presets updated: - system, compute_budget, associated_token_account, stakepool: use context.data(), context.program_id(), context.account(n) - jupiter_swap: builds Vec<String> accounts from context for parser - token_2022, swig_wallet: builds Vec<AccountMeta> shim from context - unknown_program: overrides can_handle to catch unresolved program_ids, uses context methods for IDL parsing and account resolution ProgramRef and AccountRef enums used explicitly at match sites. Instruction labels use operation names instead of instruction_index. Per-preset resolve helpers kept where they reduce repetition. Crate does not compile at this commit — instructions.rs and v0.rs still use the old VisualizerContext constructor.
No instructions are ever skipped. VisualizerContext is created for every compiled instruction. Diagnostics for inaccessible indices are emitted by scan_instruction_diagnostics (shared between legacy and v0). Eliminates: filtered instruction vec, oob_account_index_in_skipped_instruction rule, code duplication between legacy and v0 diagnostic logic. Tests not yet updated — 9 failures from changed diagnostic model and label format.
No instructions are ever skipped. Unified diagnostic scan with two rules (oob_program_id, oob_account_index) replaces the old three-rule model. shared scan_instruction_diagnostics function used by both legacy and v0. Test updates: - Remove oob_account_index_in_skipped_instruction assertions - Update ok-diagnostic counts from 3 to 2 - Update label assertions (operation names instead of "Instruction N") - Update fixture test helpers for new VisualizerContext constructor 70/70 lib tests pass. Pipeline integration tests need fixture updates.
Update instruction_fields() helper to identify instruction fields by excluding known non-instruction labels (Network, Accounts) rather than matching "Instruction N" prefix. All solana crate tests pass: lib (70), pipeline (9), fuzz, semantic.
- Replace .unwrap() in Diagnostic serialize impl with direct serialize_entry calls (review #7) - Accept Severity enum in create_diagnostic_field instead of &str, preventing arbitrary strings in attested payload (review #8) - Thread LintConfig through convert functions from single construction point in to_visual_sign_payload (review #11) - Document decode::visualizer_error as intentionally always-on, not routed through LintConfig (review #9) - Update spec to reflect three rules (removed oob_account_index_in_skipped_instruction), document ProgramRef/AccountRef types and no-skip behavior
- Integration test: update expected label and diagnostic rules - CLI fixtures: regenerate solana-json display expected output - CLI fixtures: update diagnostics to 2 rules (removed oob_account_index_in_skipped_instruction) - Fix clippy len_zero warning in test All tests pass: fmt, clippy, full test suite.
- Remove oob_account_index_in_skipped_instruction from rule lists - Add decode::visualizer_error as always-on rule - Update create_diagnostic_field examples to use Severity enum - Update diagnostic message examples (no more "skipped" wording) - Update field-types.mdx and contributor guide
…tic assertions - Restore solana-text.input and solana-text.display.expected fixtures - CLI test loop handles non-JSON output (text/human) with string comparison - JSON fixtures use membership checking (assert_json_contains) instead of byte-for-byte string comparison, avoiding false failures from key ordering - Diagnostic assertions check rule, level, and instruction_index - Regenerate solana-json and ethereum-json display fixtures
|
Preview deployment for your docs. Learn more about Mintlify Previews.
|
- Remove docs/superpowers/plans/2026-04-13-pr230-review-fixes.md (Claude internal planning artifact, +1362 lines, ~36% of PR additions) and add docs/superpowers/ to .gitignore so it doesn't recur. - Add TODO on Severity/LintConfig in src/visualsign/src/lint.rs to implement DeterministicOrdering / deterministic Serialize when lint config gets folded into metadata_digest. - Add explicit assert_deterministic_ordering(&diag) for SignablePayloadFieldDiagnostic next to siblings in the compile-time ordering test, so it has the same anchor as other field types. Feature-gating diagnostics (the second blocking concern) is split out to a follow-up.
Addresses the second blocking concern in PR review: parser_app and parser_grpc_server now build without the diagnostics machinery, so wallets/HSMs hashing the SignablePayload see the same shape as before the lint refactor. - visualsign: cfg-gate `lint` module, `Diagnostic` variant on `SignablePayloadField`, `SignablePayloadFieldDiagnostic`, and `create_diagnostic_field` behind `feature = "diagnostics"`. - visualsign-solana: same feature is propagated. Provide two `decode_instructions` / `decode_v0_instructions` impls -- the diagnostics-on variant collects errors and emits diagnostic fields; the diagnostics-off variant returns `Result<Vec<_>, VisualSignError>` and propagates empty-account-keys + visualizer errors as `Err` (the pre-refactor abort-on-bad-tx semantics). - parser_cli: enables `diagnostics` by default; uses the weak `?` syntax on `visualsign-solana?/diagnostics` so non-default builds still work. - parser/app, grpc-server: no `diagnostics` feature -- production stays on the stable shape. - Makefile: split workspace builds/tests/clippy so feature unification can't sneak diagnostics on for parser_app via `cargo build --all`. Adds explicit feature-on test+clippy passes for the gated lib code. - Integration test now strictly asserts parser_app emits no diagnostic fields, validating the gate. Per-site treatment of the `.filter(field_type != "diagnostic")` workarounds the reviewer flagged: in cli_test.rs they're cfg-gated so the OFF build is filter-free; in swig_wallet tests they remain as a harmless no-op when the feature is off (the assertion counts still hold). True elimination would require moving diagnostics out of `payload.fields` into a separate top-level array -- noted as a possible follow-up rather than done here.
…-diagnostics-refactor
…ntext API main moved forward with 14 new IDL-based Solana presets while this PR was in review. All of them were authored against the pre-#228 VisualizerContext that exposed `current_instruction()` and `instruction_index()`. This PR's wire-data refactor removed those, so the merged tree fails to compile. Changes: - VisualizerContext now carries `instruction_index: usize` (cheap, threaded from the dispatcher's `enumerate()`). New presets use it to label rows like "Instruction N". - Two new convenience methods on VisualizerContext: - `resolve_program_id()` — returns Err(Decode) on out-of-bounds. - `resolve_accounts()` — returns Err(Decode) on the first unresolved index. Both keep IDL-based presets one-liner-clean while preserving the explicit error path the reviewer asked for in #230. - Doc comment on VisualizerContext contrasts the all-or-nothing pattern (these helpers) with partial rendering (the unknown_program preset's `unresolved(N)` placeholders), pointing readers at a real example. - All 14 new presets ported: dflow_aggregator, jupiter_borrow, jupiter_earn, jupiter_perps, kamino_borrow, kamino_farms, kamino_vault, metadao_conditional_vault, metadao_futarchy, meteora_damm_v2, meteora_dlmm, neutral_trade, onre_app, orca_whirlpool. Same shape: pull program_id, data, and accounts from the context up front, then use them throughout. - jupiter_swap signature drift: `create_jupiter_swap_expanded_fields` now takes &VisualizerContext (not separate program_id+data); test updated. - meteora_dlmm and neutral_trade `build_named_accounts` updated to take data + accounts separately instead of a constructed Instruction. - Drop redundant `Some(hex::encode(...))` second arg on every `create_raw_data_field` call site -- the helper's `None` branch already emits the same lowercase hex via `default_hex_representation`. Touching every preset in this commit anyway, so cleaner to land together. - Rename ethereum-from-file fixture to *.display.expected to match the *.display.expected / *.diagnostics.expected scheme this PR introduced. Verified: make build, make lint, make test all green in both feature configs (diagnostics on for parser_cli, off for parser_app/grpc-server).
|
@prasanna-anchorage status of the items from your review: Blocking:
Non-blocking nits:
Per-site filter treatment: with the gate in place, filters are either compiled out (cli_test diagnostic-fixture branch is Other: merge from Stacked follow-up #281 refreshes the |
Code-review feedback on the feature gate: the OFF-path implementations of `decode_instructions` and `decode_v0_instructions` had zero direct unit-test coverage because both test modules were entirely gated `#[cfg(all(test, feature = "diagnostics"))]`. Their semantics differ from the ON path (errors propagate as Err instead of being collected as diagnostics), so they need explicit coverage. - New `mod off_tests` in `core/instructions.rs` and `core/txtypes/v0.rs`, gated `#[cfg(all(test, not(feature = "diagnostics")))]`. Covers: - empty `account_keys` -> `Err(VisualSignError::DecodeError)` - OOB program_id flows through to `unknown_program` as `Ok` - OOB account index flows through as `Ok` - Makefile `make test` now invokes `cargo test -p visualsign-solana --no-default-features --lib` so the new OFF-path tests actually run. - Make the `Severity` match in `create_diagnostic_field` exhaustive (`Severity::Ok | Severity::Allow` instead of `_`) so the compiler flags missing arms when a new variant is added. - Document the `diagnostics` Cargo feature in `docs/contributor-guides/lint-diagnostics.mdx`: which artifacts ship with it, how the OFF path differs, and how the Makefile defeats Cargo feature unification.
Security review
The reviewer agent examined the diagnostic feature gate, dual Key checks that passed:
|
|
@prasanna-anchorage ready for re-review. |
Addresses #230 review comment from @prasanna-anchorage on the original swig_wallet/mod.rs:934. Replaces the |-grouped fallback (`ListLayout | Divider | Unknown`) with one arm per variant in enum declaration order, matching the style used in src/visualsign/src/lib.rs. Eliminates the ordering ambiguity that prompted the review comment. Behavior is unchanged -- all four arms call fallback_summary(common).
1b8e15a to
3f28e5d
Compare
| common, | ||
| preview_layout, | ||
| } => { | ||
| assert_eq!(common.label, "Instruction 1"); |
There was a problem hiding this comment.
this is unexpected for this PR, can we keep user visible fields same instead of changing the behavior in a refactor PR?
There was a problem hiding this comment.
Addressed in 2ee2ee7 + 5d767d4 + 664682e: top-level common.label reverted to format!("Instruction {N}") across all 11 affected presets (swig_wallet, compute_budget, system ×3, token_2022, unknown_program ×2, associated_token_account, jupiter_swap, stakepool); "Instruction Decoding Note" TextV2 fallback restored on the diagnostics-OFF path (this PR had silently dropped it from convert_v0_to_visual_sign_payload's error branch); matching unit-test + CLI fixture + integration-test assertions flipped back. title.text, condensed, and expanded slots are unchanged — the descriptive operation summaries were already in title.text on main and remain there. 6b91873 is the main merge; the new exponent_finance and kamino_limit presets are ported in the same form ("Instruction N").
Addresses @prasanna-anchorage review comment on PR #255 (#255 (comment)): keep user-visible fields the same as main in a refactor PR. The PR had repurposed common.label from the ordinal "Instruction N" form to descriptive operation summaries (e.g., "Swig: Create wallet (Ed25519)", "Set Compute Unit Limit: 10000000 units", "Token 2022: <op>", etc.) across 9 preset code paths. Those descriptive strings already live in preview_layout.title.text on both main and this branch, so reverting the label slot loses no information for wallet rendering. Reverts label in: - swig_wallet, compute_budget, system (transfer/create_account/assign), token_2022, unknown_program (IDL hit + catch-all), associated_token_account, jupiter_swap, stakepool Test assertions in swig_wallet/mod.rs flipped back to "Instruction 1/2/3" to match. Title, condensed, and expanded slots unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ailure Addresses a second user-visible regression on PR #255: in the diagnostics-OFF (production parser_app) path, a v0 transaction whose instructions failed to decode used to render a TextV2 field labeled "Instruction Decoding Note" with the decoder error inline. After the diagnostics refactor, that path propagated the Err via `?`, turning a soft failure (partial payload + note) into a hard conversion failure for wallets. Restores main's behavior: catch the Err in convert_v0_to_visual_sign_payload's diagnostics-OFF branch and push a TextV2 note instead of bubbling. Mirrors the sibling "Transfer Decoding Note" pattern immediately below in the same function. Diagnostics-ON path is unchanged (its decode_v0_instructions variant always succeeds and emits diagnostic-typed fields). Adds a positive test (test_v0_decode_failure_emits_instruction_decoding_note) gated on `#[cfg(not(feature = "diagnostics"))]` that constructs a v0 message with empty account_keys, exercises convert_v0_to_visual_sign_payload directly, and asserts the payload contains a TextV2 field with the expected label and the underlying error text. This contract was previously untested on both main and PR #255 -- which is why the original drop in PR #255 didn't trip any existing test. Locks the contract going forward. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merges origin/main (3bbf4e7) into the PR #255 branch. Resolves three content conflicts and ports two new presets from main to the wire-data VisualizerContext API introduced by PR #255. Conflicts resolved: - .gitignore: keep both `docs/superpowers/` (PR #255) and `.surfpool/` (main, from #294). - src/chain_parsers/visualsign-solana/src/presets/dflow_aggregator/mod.rs: take main's #286 changes (remaining-accounts surfacing, nested-arg flattening, `mut expanded_fields` + warn-on-parse-fail) on top of PR #255's wire-data API. The raw-data field is now pushed in place via `expanded_fields.push(create_raw_data_field(data, ...))` using `context.data()` instead of the removed `instruction.data`. - src/chain_parsers/visualsign-solana/src/presets/unknown_program/mod.rs: combine main's #288 determinism fix (locally-built `BTreeMap<String, String>` returned alongside the parsed payload, iterated at render time instead of `parsed.named_accounts`'s upstream `HashMap`) with PR #255's wire-data API (`context.program_id()`, `context.data()`, `context.num_accounts()`, `resolve_account_str(context, i)`). Imports remain `BTreeMap`; `HashMap` is no longer referenced. Two new presets ported to the wire-data API (same pattern as the existing `80b076b` port commit): - exponent_finance (from main #275): `current_instruction()` / `instruction.{program_id,data,accounts}` replaced by `context.resolve_program_id()?`, `context.resolve_accounts()?`, `context.data()`. Inline `expanded_fields.push(create_raw_data_field(...))` pattern; the unused `append_raw_data` helper deleted. - kamino_limit (from main #284): same API port. Function helpers (`build_named_accounts`, `build_parsed_fields`, `build_fallback_fields`, `append_raw_data`) retained -- they take `data: &[u8]` and `accounts: &[AccountMeta]` and remain useful. Both new presets' `config.rs` switched from `std::collections::HashMap` to `std::collections::BTreeMap` to satisfy main's #288 disallowed-types lint and the `SolanaIntegrationConfigData.programs: BTreeMap<...>` field shape. Verified: - cargo fmt clean - cargo check --workspace --exclude parser_cli clean - cargo check -p parser_cli clean - cargo test -p visualsign-solana --lib: 148 passed, 0 failed Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Heads up: after the rebase the only remaining CI failure is one stale integration-test assertion that the
The System Transfer preset code at One-line fix in {
"FallbackText": "Program ID: 11111111111111111111111111111111\nData: 0200000000ca9a3b00000000",
- "Label": "Transfer: 1000000000 lamports",
+ "Label": "Instruction 1",
"PreviewLayout": {That matches the same line on Everything else looks good:
After this lands, the PR should be ready for final review. |
Commit 2ee2ee7 reverted the per-instruction `common.label` from descriptive operation summaries back to the ordinal "Instruction N" form across the solana presets, and flipped the matching unit-test assertions in swig_wallet/mod.rs. Three snapshot fixtures and one integration test still held the descriptive form and tripped on PR #255's CI ubuntu job: - src/integration/tests/parser.rs: `parser_solana_native_transfer_e2e` expected the system-transfer instruction's PreviewLayout label to be "Transfer: 1000000000 lamports"; now expects "Instruction 1". - src/parser/cli/tests/fixtures/solana-json.display.expected and the parallel .text fixture: 6 top-level instruction labels flipped in instruction order ("Instruction 1" .. "Instruction 6"): * "Transfer: 10000000000 lamports" -> "Instruction 1" * "Create Associated Token Account (Idempotent)" -> "Instruction 2" * "Stake Pool Instruction: Deposit SOL" -> "Instruction 3" * "Set Compute Unit Limit: 400000 units" -> "Instruction 4" * "Set Compute Unit Price: 50000 micro-lamports per compute unit" -> "Instruction 5" * "Transfer: 10000 lamports" -> "Instruction 6" Inner field labels ("Instruction", "Program ID", "Compute Unit Limit", "Stake Pool Instruction", "Transfer Amount", etc.) and the title/condensed text content are unchanged -- only the top-level PreviewLayout `common.label` was affected by 2ee2ee7's revert. Verified locally: - cargo fmt --check: clean - cargo clippy -D warnings (workspace, parser_cli, diagnostics-feature): clean - make -C src test: rc=0 (full pipeline incl. binaries + integration) - cargo test -p integration --test parser: 9/9 passed (parser_solana_native_transfer_e2e ok) - cargo test -p parser_cli: 8/8 passed (fixture snapshots match) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Thanks @prasanna-anchorage for the reviews. Ready for review. |
Summary
Addresses all review feedback from #230 by refactoring
VisualizerContextto work directly with transaction wire data (&CompiledInstruction+&[Pubkey]), eliminating instruction skipping/filtering, and simplifying the diagnostic model.Supersedes #230 — that PR has the original review discussion and should be closed once this merges.
What changed
VisualizerContextholds&CompiledInstruction+&[Pubkey]instead of an index into a pre-resolvedVec<Instruction>. Resolution of indices to pubkeys happens lazily viaProgramRef/AccountRefenums that distinguish resolved from unresolved.unknown_programcatches instructions with unresolvable program IDs.oob_program_id,oob_account_index) replace the old 3-rule model.oob_account_index_in_skipped_instructionis eliminated — there is no concept of "skipped instruction."scan_instruction_diagnosticsfunction used by both legacy and v0 paths, eliminating code duplication.context.data(),context.program_id(),context.account(n)) — noInstructionstruct is constructed.Review comments addressed (#230)
Review comments addressed (this PR, @prasanna-anchorage)
docs/superpowers/plans/2026-04-13-pr230-review-fixes.md(1362-line internal planning artifact)docs/superpowers/to.gitignorediagnosticsfeature onvisualsign/visualsign-solana/parser_cli;parser_appandgrpc-serverbuild without it. CI/Makefile split per-package to avoid Cargo feature unification across the workspace. Integration test now strictly assertsparser_appemits nodiagnosticfields.LintConfig/SeveritylackDeterministicOrderinglint.rsfor when lint config lands inmetadata_digestassert_deterministic_ordering(&diag)next to siblingsBranch synced with main (commit 80b076b)
mainadvanced with 14 new IDL-based Solana presets (Jupiter Borrow/Earn/Perpetuals, Kamino Borrow/Vault/Farms, MetaDAO Conditional Vault/Futarchy, Meteora DAMM V2/DLMM, Neutral Trade, Onre App, Orca Whirlpool, DFlow Aggregator) authored against the pre-#228current_instruction()API. Migrated each to the wire-data API.instruction_index: usizetoVisualizerContext(cheap, threaded from the dispatcher'senumerate()); new presets use it for "Instruction N" labels.resolve_program_id()andresolve_accounts()(Err on first unresolved index). IDL-based presets call these; visualizers wanting partial rendering pattern-match onprogram_id()/account(n)directly. Doc comment onVisualizerContextcontrasts the two patterns and points atunknown_programas the canonical partial-rendering example.jupiter_swap'screate_jupiter_swap_expanded_fieldssignature drift (now takes&VisualizerContextinstead of separate program_id+data).Some(hex::encode(data))increate_raw_data_fieldcalls — the helper'sNonebranch already emits the same lowercase hex.Stacked follow-up
#281 — refresh the
solana-add-idlskill template to match the post-#228 API (brokensquads_multisigtemplate reference, stale imports, redundant hex encoding).Test plan
make build,make lint,make testclean in both feature configs (diagnostics on/off)parser_appstrictly emits nodiagnosticfields (locks in the production payload shape)-D warnings,cargo fmt --checkclean🤖 Generated with Claude Code