PCL Invalidation Deep Dive
When to Use
- A user reports a new PCL/Credible Layer invalidation and needs to know what the blocked transaction attempted.
- You need production-style agentic triage for a dropped transaction: transaction summary, assertion reason, verdict, next action, improved traces, and value/exposure.
- You need to estimate actual loss, unique protected value, repeated blocked attempt volume, or unpriced failed-trace gaps from platform invalidation data.
- You are investigating suspicious PCL incidents, failed traces, protected value, blocked exploit attempts, false positives, assertion misconfigurations, allowance drains, NFT or ERC1155 transfers, privileged calls, bridge/router/vault flows, accounting bugs, or "we got hacked" requests.
When NOT to Use
- You are designing or implementing a new assertion. Use
designing-assertions,mapping-invariants,implementing-assertions, orpcl-assertion-workflow. - You are testing assertion behavior in a local project. Use
testing-assertionsorbacktesting-assertions. - You only need generic EVM incident analysis that is unrelated to PCL invalidation records.
- You do not have access to PCL incident data and cannot provide a tx hash, incident id, project id, or exported trace artifact.
Rationalizations to Reject
- "The list row is enough." Fetch incident detail and per-transaction trace records before reasoning.
- "The transaction hash is the row key." Key evidence by
(incident_id, invalidating_transaction.id)because repeated simulations can reuse a hash across windows. - "The sender is the victim." Extract source owners from
transferFromcalls and events;tx.from, recipient, source owner, and adopter can all differ. - "The repeated total is the loss." Separate actual landed loss, unique protected value, repeated blocked volume, and unpriced gaps.
- "Labels are enough for RCA." Important code-bearing contracts need verified source, Sourcify data, decompiled output, or an explicit unresolved-source gap.
- "This is just an allowance drain." Treat allowance abuse as one mechanism, not the default. Check the assertion logic, protocol state, privileged roles, callbacks, oracle/accounting paths, bridges, vaults, NFTs/ERC1155s, and native value where the trace points there.
- "
castis optional."castis required for professional selector decoding, calldata decoding, RPC reads, balance/allowance/storage checks, and replay probes unless the user explicitly accepts a degraded analysis. - "External API keys are mandatory." Probe configured/private endpoints first, tell the operator what is available, then proceed with the best mode. Treat keyed archive RPC and explorer APIs as confidence/speed upgrades, not default blockers; fall back to public RPC, Sourcify, 4byte/cast, and Heimdall when private endpoints are absent.
Operating Standard
Treat PCL as the primary incident index and chain evidence as the source of truth. Start with the platform's invalidation record, then verify the attempted state/value change with transaction traces, receipts, logs, calldata, and token flows.
Stay mechanism-agnostic until the evidence narrows the case. A protected loss can be a fungible token transfer, NFT/ERC1155 movement, native value movement, mint/burn, vault share accounting change, privileged state mutation, bridge message, oracle/accounting update, or protocol-specific invariant break.
Always separate:
- Actual landed loss: value that really moved on-chain.
- Unique protected value: lower-bound unique source balances that would have been drained if the first blocked attempt succeeded.
- Repeated blocked attempt volume: sum of all invalidated attempts, including retries against the same balances.
- Unpriced or unavailable traces: failed/pending trace gaps; never silently fold these into exact totals.
- Non-fungible or state-only protection: asset ids, ownership/state changes, or protocol risk that cannot be honestly reduced to a USD number without pricing evidence.
This skill is the local version of the production "Agentic triage" flow: it should work from PCL invalidation records and local/API evidence, without requiring the dApp backend to precompute the report.
Keyless mode means no third-party RPC, explorer, or source API keys. Code recovery is local-only through Heimdall-rs; if Heimdall is unavailable, list that as a source gap. Live incident discovery still needs pcl platform auth unless the user provides exported PCL incident/trace artifacts or a prebuilt evidence packet.
Read references/etl-pipeline.md when you need the full local ETL checklist, data-source matrix, context packet, or value-accounting rules.
Fast Feature-Parity Mode
When the user asks for production-style agentic triage, optimize for the same user outcome as the Notion spec: a fast answer that explains what the tx attempted, why the assertion stopped it, whether there are red flags, and the next action.
For one to five invalidating transactions:
- Fetch detail, trace, contract context, previous transaction, asset/protocol metadata, and block-pinned state reads once, then build a compact evidence packet before writing the full report.
- If a report agent or spawned agent will write the final output, hand it the compact packet first. Do not ask it to rediscover PCL list/detail/trace data unless the packet is missing or internally inconsistent.
For larger incident windows:
- Build an index first, then group by route/signature before fetching or loading every raw trace into the model.
- Fully deep-dive one representative completed trace per group and any materially different failed/no-trace rows.
- Keep raw artifacts on disk and summarize the rest in tables. Do not paste large traces into the prompt or final answer.
- Preserve complete row coverage through
(incident_id, pcl_tx_id, hash, block, trace_status)tables, even when only representative traces are rendered.
If a multi-agent runner is available and the user asks for a deep pass, split the work into three artifact-sharing phases:
- Prefetch/RCA phase: PCL context, normalized trace, source/decompiler context, previous txs, and trace-backed mechanism.
- Replay phase: RPC/cast checks for calldata, selectors, receipts/logs, balances, allowances, owners, protocol storage, and replayability.
- Packet phase: run
{baseDir}/scripts/build_evidence_packet.pyover the saved artifacts and append any replay/state-read outputs as auxiliary files. - Report phase: final invalidation-detail artifact using the compact evidence packet first, raw artifacts only for verification, and explicit gaps.
If no runner is available, execute the same phases sequentially. Do not let the report phase invent missing source, replay, or previous-transaction evidence.
Fast Packet-Only Report Mode
Use this mode whenever an evidence_packet.md or equivalent prefetched packet is provided. This is the default for spawned report agents.
- Prefer
{baseDir}/scripts/render_fast_report.py --packet <evidence_packet.md> --run-dir <run-dir> --out <final_report.md>to create the first report draft, then review/edit only if the deterministic draft misses an important packet fact. - Target under 90 seconds from packet read to saved report for one completed trace.
- Read the evidence packet first, then only the listed auxiliary state/receipt/price/previous-transaction files.
- Do not rerun PCL, source collection, decompilation, Homebrew formula checks, selector lookup, RPC, or explorer calls unless the packet has a blocking contradiction or the user explicitly asks for a deeper pass.
- Do not open the raw trace JSON if the packet plus normalized trace already provide the call order, movement rows, assertion check, and revert reason. If exact order is still needed, open only the raw trace artifact and cite that as the reason.
- Keep the main report to about 1,200-1,800 words for one invalidating tx. Use concise tables and a 10-18 step
Full Improved Trace; list deeper source/decompiler details as artifact links or gaps. - If a desired verification is missing from the packet, report it as a gap instead of spending minutes fetching it.
- Report wall time and either token usage or concrete byte counts. Do not perform local tooling checks solely to fill runtime metadata.
Quick Workflow
Scope the incident
- Resolve the project or incident from the user's words with
pcl search --query <name> --toon,pcl incidents --project-id <id-or-slug> --toon, orpcl incidents --incident-id <id> --toon. - Record exact UTC snapshot time, project id/slug, chain id/name, assertion title/id, adopter address, incident id, PCL transaction ids, hashes, and block numbers.
- State whether the user asked for latest, a date range, all history, or a specific incident. Use exact dates.
- For "recent" or "latest", include at least one latest failed/no-trace incident and at least two completed traces when available, so both paths are exercised.
- Resolve the project or incident from the user's words with
Use the right PCL binary
- For end-user investigations, prefer the Homebrew CLI on PATH:
command -v pclandpcl --version. - On macOS, the Phylax formula is
phylaxsystems/pcl/phylax; Homebrew corepclis the unrelated Point Cloud Library. - If the user asks for latest Brew PCL, verify
brew info phylaxsystems/pcl/phylax, thenbrew upgrade phylaxsystems/pcl/phylaxif permitted. - If working inside a PCL checkout for CLI development, rebuild and run
./target/debug/pcl; do not mix checkout and Brew outputs in one report.
- For end-user investigations, prefer the Homebrew CLI on PATH:
Extract PCL data
- Run
pcl doctor --toon,pcl auth ensure --toon, andpcl workflows show incident-investigation --toonbefore authenticated project incident queries. - Use
--toonfor agent-facing inspection and--jsononly when strict JSON parsing is needed. - Pull incident lists with explicit filters:
--project-id,--environment production,--from-date,--to-date,--all,--limit,--toonor--json. - Use
pcl export incidents --project-id <id> --environment production --out incidents.jsonl --errors errors.jsonl --checkpoint checkpoint.json --resume --continue-on-error --toonfor resumable list artifacts. Treat the export as an index; still fetch detail and trace records separately. - When using
pcl incidents ... --output <file> --json, verify that the file exists and inspect its shape. List outputs may contain only thedatapayload, not the full envelope, and detail/trace commands may need stdout capture instead. - For each incident, pull detail with
pcl incidents --incident-id <id> --toon. - For each invalidating transaction, pull trace with
pcl incidents --incident-id <id> --tx-id <pcl-transaction-id> --toon, even when the list row says no traces completed. A failed trace still returns the transaction object, block env, calldata, and debug trace status. - For multi-incident work, save trace JSON artifacts with both ids in the filename, such as
trace_<incident-id>_<pcl-tx-id>.json; trace-only responses may not repeat the incident id. - After fetching traces, run
{baseDir}/scripts/normalize_pcl_trace.py --pretty trace_*.json > normalized_traces.jsonto extract non-delegatecall token calls, ERC20/ERC721/ERC1155/WETH/ERC4626 events, event-level deltas, and allowance checks. Use the normalized rows as a parsing aid, then spot-check against raw trace text before final accounting. - Track
transaction_count, completed/pending/failed trace counts from each transaction'sdebug_traces[].status,landed_on_chain, revert reason, and request ids from the response envelope orpcl requests list --limit 20 --toon. - Key rows by
(incident_id, invalidating_transaction.id). Do not dedupe incident coverage by transaction hash alone; repeated simulations can reuse a hash while differing by incident window, block number, PCL tx id, or trace status.
- Run
Assemble the local triage context
- Start with automatic capability discovery:
{baseDir}/scripts/check_triage_requirements.py --chain-id <chain-id> --json. Add--require-decompilerwhen unverified code, transient contracts, or source gaps must be resolved for the answer. - Read
capability_selection. Before deeper context collection, tell the operator one concise capability note: recommended mode, whether configured/private RPC is available, whether keyed explorer access is available, whether public RPC is being used, and what gaps that creates. - If
capability_selection.modeisprivate-or-mixed, use configured/private RPC and explorer sources first, with public/Sourcify/Heimdall fallback. - If it is
keyless-public, proceed without waiting for keys, but label archive/debug/account-history limits. - If the user explicitly wants a no-key proof, rerun with
{baseDir}/scripts/check_triage_requirements.py --chain-id <chain-id> --no-api-keys. - If the requirements preflight exits non-zero and the mode is
blocked, stop and surface its output verbatim. Do not continue to a root-cause report until the missing RPC/decompiler capability is configured, unless the user explicitly accepts a degraded report. - Build a local evidence packet from PCL plus RPC/explorer data: transaction object, transaction execution trace, assertion execution trace, previous transaction from the sender, all touched contract addresses, created contracts, ABIs/source when available, asset/protocol metadata, receipts/logs, and relevant state reads.
- Fetch the previous transaction from the same sender before the invalidation block/hash using explorer account history or an equivalent RPC/indexer source. Save it as
previous_tx_<sender>.json; if unavailable, list it as a gap because it can distinguish benign user flow, preparatory approvals, and multi-block exploit setup. - Treat contract source context as a required stage before root-cause analysis. Extract every address from transaction traces, assertion traces, transaction objects, created-contract lines, token proxy/implementation delegatecalls, and assertion/runtime helper calls.
- Run
{baseDir}/scripts/collect_contract_context.py --chain-id <chain-id> --out-dir contract_context trace_*.jsonafter traces are saved when private-or-mixed mode is available. Provide--rpc-urlexplicitly or rely on current env/provider/public fallback. It fetches bytecode, Etherscan source when keyed, Sourcify source, and emitscontract_context_manifest.jsonwith unverified decompiler targets. - Run
{baseDir}/scripts/collect_contract_context.py --chain-id <chain-id> --no-api-keys --out-dir contract_context trace_*.jsonfor keyless-public mode or explicit no-key proof. It ignores provider/explorer keys and uses explicit/env/public RPC plus Sourcify. - When
contract_context_manifest.jsonhas bytecode-backeddecompiler_targets, run{baseDir}/scripts/run_heimdall_decompiler.py contract_context/contract_context_manifest.json --out-dir decompiled --require-success. Heimdall-rs is the single supported local decompiler/disassembler path for this skill. Installheimdallon PATH or setHEIMDALL_BIN=/absolute/path/to/heimdall. - After normalization, contract context, decompilation, and targeted replay/state reads, run
{baseDir}/scripts/build_evidence_packet.py --run-dir <run-dir> --incident-json <incident.json> --trace-json <trace.json> --normalized-json <normalized.json> --contract-context <manifest.json> --decompilation-manifest <heimdall_manifest.json> --aux-file "capability preflight=capability_preflight.json" --aux-file "state reads=state_reads.json" --aux-file "previous sender txs=previous_tx.json" --out evidence_packet.md. Use the packet as the report agent's primary input. - For a fast single-trace report, run
{baseDir}/scripts/render_fast_report.py --packet evidence_packet.md --run-dir <run-dir> --out final_report.mdbefore any free-form writing. Review the draft for obvious errors and only then add concise human improvements. - If an evidence packet is already provided, read it first and treat listed artifacts as prefetched. Do not rerun
pcl search, incident list/detail, trace fetch, source collection, or decompilation unless a listed artifact is missing, stale, or inconsistent with the request. - The contract-context helper tries explicit/env RPC, derived provider RPC when allowed, and configured public RPC fallbacks. It exits with the exact missing JSON-RPC requirement when none works. Use
--allow-missing-rpconly when explicitly accepting a degraded source-only packet, then list that as a confidence gap. - For contracts created inside a non-landed PCL simulation,
eth_getCode(latest)may return no code. Treat these as transient created-contract gaps, then recover init/runtime bytecode from trace output, calldata, replay, or decompiler tooling before relying on that route for RCA. - For every code-bearing address, attach one of: verified source/ABI, Sourcify contract data, decompiled output, or a specific unresolved-source gap. Do not do root-cause analysis from labels alone when source/decompiled context is missing for an important touched contract.
- Store large JSON artifacts under
/tmpor the current workspace and summarize from files instead of pasting huge traces into the final answer. - If any context is unavailable, keep an explicit gap list.
- Start with automatic capability discovery:
Improve trace readability
- Strip ANSI before parsing or summarizing trace text.
- If
transaction_trace_contentandassertion_trace_contentare null, split combinedtrace_contenton theTransaction TraceandAssertion Traceheadings. - Resolve contract and asset names from explorer source/ABI, token metadata, NFT metadata, or labels before explaining the trace.
- Decode ERC20
Transfer/Approval, ERC721Transfer, ERC1155TransferSingle/TransferBatch, WETHDeposit/Withdrawal, ERC4626Deposit/Withdraw, and protocol-specific events when present. - Convert decoded asset events into movement rows: asset contract, standard, address or owner, raw delta or token id, human amount where applicable, source event, and whether the event came from a delegate context.
- Pretty-print fungible amounts with decimals and symbols; cap display precision at 6 decimals unless the dust amount or precision matters. For NFTs and state changes, preserve ids and raw state keys.
- Present raw addresses next to names the first time they appear.
Decode attempted action
- Use completed PCL traces first. Extract real asset movements and protocol state changes from non-delegatecall calls, emitted logs, and assertion reads.
- Separate roles from trace evidence: transaction sender, outer call target, source owner, recipient/beneficiary, asset contract, protocol account, and assertion adopter can all be different addresses. Do not assume
tx.fromis the victim/source owner. - Decode selectors with
cast 4byte, verified ABIs, or explorer ABIs. Decode calldata only as supporting evidence unless trace execution is unavailable. - For failed traces, inspect calldata to identify candidate tokens/routes, but report value as a lower bound unless the calldata decodes cleanly into actual attempted transfer amounts.
- Preserve the exact assertion failure reason. Do not paraphrase it into an allowance or transfer story unless the trace supports that mechanism.
- For proxy tokens, avoid counting both the proxy call and implementation delegatecall. Count one attempted token movement at the proxy token address, using the event or non-delegatecall line as the canonical row.
Replay and verify on chain
- Use public RPC first for keyless basic reads (
eth_chainId,eth_getCode, transaction/receipt checks, latest reads, and block-pinned reads when supported). Label public RPC as rate-limited and non-archive/non-debug unless verified otherwise. - Use Alchemy or another archive RPC for stronger
eth_getTransactionByHash, receipts, logs, balances, allowances, storage reads, anddebug_traceTransactionortrace_callwhen keyless RPC cannot answer the needed method or block. - If chain-specific RPC env vars are missing but
ALCHEMY_API_KEYis set, derive the chain RPC URL only when the chain is known to the helper. Do not print secrets. - RPC discovery order:
--rpc-url, explicit chain RPC env var, generic chain-id env patterns, genericRPC_URL, derived Alchemy URL fromALCHEMY_API_KEYunless--no-api-keysis set, then public RPC fallback for supported chains. Label public-RPC results as non-archive/non-debug unless verified otherwise. - Record whether each RPC or explorer data source was environment-provided, derived, or public fallback. Do not say an env var is missing unless you checked it in the current shell.
- Use
cast 4byte,cast calldata-decode,cast call,cast run,cast rpc, and block-pinned state reads to decode, replay, or sanity-check the invalidating transaction where possible. - Fetch the sender's previous transaction with Etherscan v2 account history when available:
module=account&action=txlist&address=<sender>&endblock=<block-1>&sort=desc&page=1&offset=3&chainid=<chain_id>. If explorer history is unavailable in keyless mode, use an equivalent no-key indexer when available, a bounded block/RPC scan only when it is cheap, or record previous-transaction history as a gap. Do not block the whole report on this gap when the PCL trace already proves the attempted movement. - For suspicious sequences, run a bounded related-transaction lookback around previous blocks for the same sender, source owner/account, recipient/beneficiary, asset, spender/operator/adopter, and outer route. Keep the expansion bounded, save the query, and report if the analysis depends on it.
- Use Sourcify first for no-key verified source/ABI. Use Etherscan v2 or chain explorers for verified source/ABI, asset transfers, contract labels, creation txs, and public links when keys are configured or keyless source is insufficient.
- Use Sourcify as a no-key verified-source fallback with
/server/v2/contract/<chain-id>/<address>?fields=all. - For code-bearing addresses without verified source, use Heimdall-rs via
{baseDir}/scripts/run_heimdall_decompiler.py. If Heimdall is unavailable, store runtime bytecode and list the address as a source/decompiler gap. - Label all decompiled output as approximate. Use it to understand control flow, selectors, storage, and call routing; do not treat it as verified source.
- Decode unknown selectors with
cast 4byte <selector>; if unresolved, include the selector in open gaps instead of inventing a function name. - If PCL says
landed_on_chain=false, treat it as a blocked simulation/invalidation unless chain evidence proves otherwise. - If any transaction landed, split the analysis into blocked value vs actual loss.
- Run exposure reads against trace source owners, asset owners, protocol accounts, spenders, operators, and adopter addresses, not just the invalidating transaction sender.
- Use public RPC first for keyless basic reads (
Compute value
- Price by asset address and chain. DeFiLlama coin prices are acceptable for quick current marks; use block-time prices when the user asks for historical accounting or public incident numbers.
- Normalize fungible decimals from token metadata, not assumptions. For NFTs/ERC1155s, preserve token ids and use collection/floor or explicit valuation only when sourced.
- Repeated volume: sum every confirmed fungible attempted transfer; for non-fungible/state-only attempts, count repeated attempted asset ids or state changes separately.
- Unique protected value: dedupe by
(chain, asset, source owner, token id when applicable)and usually take the max observed fungible attempted amount per source balance. - Do not double count delegatecalls, downstream consolidation transfers, swap outputs, approvals, or the same balance retried by multiple bots.
Root-cause, verdict, and exposure
- Classify the mechanism: allowance abuse, Permit2/AllowanceHolder path, router/path abuse, compromised key, privileged function, oracle/accounting bug, bridge route issue, NFT/operator approval abuse, vault share/accounting issue, liquidation path issue, or assertion false positive.
- Identify victim/source owners or accounts, attacker/recipient, spender/operator/adopter, asset/protocol contracts, routers, pools, bridges, and any temporary contracts.
- Decide whether the transaction looks malicious, benign/expected, misconfigured, or inconclusive. Support the verdict with trace evidence.
- Check whether risk remains: outstanding approvals, same route still callable, same adopter/spender still approved, funds still in source wallets, and whether the attacker can retry.
- Recommend one concrete next step: do nothing, revoke/modify approvals, adjust/reset an assertion/circuit breaker, investigate further, pause a route, or escalate.
Report
- Default to the production invalidation-detail shape, not a terse incident summary.
- First render an above-the-fold executive summary that breaks down the transaction, the assertion that invalidated it, an evidence-backed verdict, and one recommended next step.
- Then render a detailed triage report with transaction explanation, mermaid diagram, root-cause analysis, transaction object, full improved trace, value accounting, exposure, and data gaps.
- The trace section must be one ordered Full Improved Trace, not separate transaction/assertion fragments. Start at the outer transaction call, include intermediate contract calls, decoded movements/state reads, assertion inspection calls, state checks, and the final invalidation/revert reason in execution order.
- In the full improved trace, include decoded movement rows from ERC20/ERC721/ERC1155/ERC4626/native/protocol events when present. These are the easiest rows for operators to verify against the raw trace.
- For repeated invalidations, group by route/signature and render one representative full improved trace per group, plus a complete transaction-object table for every
(incident_id, pcl_tx_id). Do not paste six near-identical traces when a grouped summary is clearer. - Use a Mermaid
sequenceDiagramas the primary diagram for step-by-step transaction execution. Useflowchartonly as a secondary actor/topology/value-flow view. - Include a warning that agentic triage can be wrong when the report will be shown directly to users or operators.
- Give exact counts: incidents, invalidating txs, completed traces, failed/pending traces, landed txs.
- Give three numbers when applicable: actual loss, unique protected value, repeated blocked attempt volume.
- Provide hashes/addresses for evidence, plus explorer links when possible.
Mermaid Diagram Rules
Use the diagram type that matches the question:
- Sequence diagram for ordered execution: who called whom, in what order, where the assertion checked state, and where the transaction stopped. This is the default for invalidation triage.
- Flowchart for static relationships: actor topology, value-flow overview, component ownership, or multiple independent routes. Do not use a flowchart as the only diagram when the key question is step-by-step execution.
For production triage sequence diagrams:
- Start with
sequenceDiagramandautonumberso each step can be referenced in the text. - Declare participants explicitly and in left-to-right execution order. Use aliases for readable labels and line breaks.
- Keep participant labels short: role + short address, not full paragraphs.
- Use message labels that name the exact function or check:
deploy,execute,transferFrom,ownerOf,safeTransferFrom,getLogs,allowance,storage read,revert. - Put token amounts in message labels only when they are central to the incident.
- Use
Note overfor context such as "source owner is not tx sender" or "delegatecall mirror ignored". - Use
loopfor repeated attempts or repeated source-owner drains. - Use
alt/elsefor completed-trace vs failed-trace branches, or allowed vs invalidated paths. - Mark the assertion stop point explicitly with a final
-->>or--x-style message label such asrevert: <reason>. - Avoid labels that contain lowercase
endas a standalone word in flowcharts; it can break Mermaid parsing. Keep node ids stable and labels quoted when using punctuation. - In flowcharts, avoid edge targets that start with lowercase
oorximmediately after an edge marker such as---; add a space or capitalize the node id/label so Mermaid does not parse a circle or cross edge accidentally.
Value Language
Use wording that cannot be misread:
- "Blocked
$Xin repeated drain attempts" for summed retries. - "Protected about
$Yin unique user funds" for deduped source balances. - "Trace-backed lower bound" when any trace failed, is pending, or calldata-only inference is required.
- "Unverified calldata estimate" for decoded routes without execution traces.
Do not turn a repeated-attempt figure into a real-loss figure. Do not call failed-trace calldata "protected value" unless the attempted token transfer is decoded and verified.
Data Access Checklist
Expect to need:
- PCL CLI auth and platform incident APIs.
- Chain RPC. Public RPC is acceptable for keyless basic reads when it works; archive/debug RPC is a high-confidence upgrade.
- Sourcify for no-key verified contract lookup where supported.
- 4byte/cast for no-key selector and event signature lookup.
- Heimdall-rs for local unverified contract decompilation/disassembly. Install
heimdallon PATH or setHEIMDALL_BIN=/absolute/path/to/heimdall. castfor selectors, calldata, storage, balances, and ad hoc ABI calls.- Asset metadata and prices from chain calls, DeFiLlama, CoinGecko, NFT/indexer APIs, or a verified token list.
- Optional keyed upgrade: Etherscan v2 or chain-specific explorer API keys for faster tx history, source, ABI, labels, and logs.
- Optional: Tenderly/Phalcon for visual traces when RPC debug traces are unavailable.
Minimum useful environment:
pclauthenticated against the platform.castfrom Foundry.- One working RPC source: explicit/env RPC, derived provider RPC, or a configured public RPC fallback for the chain.
- Sourcify reachable, plus Heimdall when important touched contracts are unverified.
- Optional high-fidelity upgrades: archive/debug RPC and explorer API access for account history, labels, source/ABI acceleration, and trace replay.
Before RCA, run the requirement gate. Use auto-discovery by default; use keyless mode when private endpoints are absent or the user explicitly asks for no keys:
{baseDir}/scripts/check_triage_requirements.py --chain-id <chain-id> --json
{baseDir}/scripts/check_triage_requirements.py --chain-id <chain-id> --no-api-keys
{baseDir}/scripts/check_triage_requirements.py --chain-id <chain-id> --no-api-keys --require-decompiler
{baseDir}/scripts/check_triage_requirements.py --chain-id <chain-id> --require-explorer
The gate reports which capability is missing, why it is required, which env var, local tool, public RPC fallback, or flag can satisfy it, and capability_selection.mode. Surface a concise capability note before proceeding instead of silently choosing keyed or keyless data sources.
Final Answer Shape
When the user asks to see the agentic triage output, produce a production-style invalidation detail artifact. Do not collapse it into a short incident brief.
Use this order:
Executive Summary
- Transaction: one paragraph on what the invalidated transaction attempted.
- Assertion: assertion title/id, adopter, and exact invalidation reason.
- Verdict: malicious/benign/misconfiguration/inconclusive plus the evidence for that verdict.
- Recommended next step: one actionable operator/user action.
- Agent warning: "This triage was generated by an agent and can be wrong. Verify critical conclusions against the raw transaction, trace, and assertion evidence before taking irreversible action."
Triage Report
- Scope and data freshness: snapshot time, project, chain, date range or incident ids, PCL version, trace counts, request ids when relevant.
- Data access mode: capability note from preflight, selected RPC/explorer/source/decompiler mode, and any keyless limitations.
- Detailed transaction explanation: actors, contracts, route, created contracts, source owners/accounts, recipient/beneficiary, asset movements or state changes, landed/not landed status.
- Mermaid diagram: prefer a numbered
sequenceDiagramthat shows each transaction/assertion step and the assertion stop point. Add a flowchart only if a separate topology/value-flow view is useful. - Root cause analysis: evidence-backed mechanism and why it was invalidated.
- Source/decompiler context: source coverage for all touched code-bearing contracts, plus unresolved or decompiled-only gaps.
- Transaction object: concise key fields (
hash, PCL tx id, from, to, value, block, calldata selectors, landed status). - Full improved trace: one ordered trace that combines the transaction execution and assertion evaluation. Include formatted contract names, decoded asset/protocol events, human-readable amounts or ids, delegatecall notes, adopter reads, logs/call inputs inspected, state checks, and the final revert reason.
- Value and exposure: actual loss, unique protected value, repeated blocked attempt volume, unverified estimates, remaining balances/allowances/ownership/state exposure.
- Open gaps and confidence: failed/pending traces, missing source/ABI, unpriced assets, and what would improve confidence.
- Runtime and usage: report wall-clock time. If token usage is unavailable, report compact packet size, key artifact bytes loaded or referenced, and output size instead of guessing.
In fast packet-only mode, keep the same section order but compress aggressively. Preserve the Full Improved Trace and value/exposure sections; shorten source/decompiler and previous-transaction discussion when they are not decisive.
Avoid generic security advice unless the user asks. Prioritize the concrete mechanism, value, what happened, why it was stopped, and exactly what to check or revoke now.