ADR-031: The executor grammar as a facts-driven Plan walker — per-protocol hop facts + mechanics, derived enclosure¶
Status: accepted; implemented (A1–A3 of epic 62V6Q5 complete) and D6 realized (epic 6SU5LM).
D6 realization (post re-drive + enclosure-derivation migration), as corrected 2026-08-16 (arch review epic
PZBGP7, taskSCMSTK): all 35build_*_walkfamily producers route to a singlederive_planpost-refactor, and the hand-written per-familyderive_2hop_*/derive_3hop_*/derive_all_v2bodies are eliminated. But the earlier “enclosure is derived fromRepay/OutDesttags, NOT prot-tuple match arms” claim overstated what shipped. The dispatch is facts-keyed — a(len, repay-sequence)partition — but the enclosure bodies are hand-authored per shape: ≈24facts[i].prot == Prot::…if-branches dispatch between shapes’ internal variants (including the 3-hop block’s ~17-arm per-family enumeration). Only the single-V4-middle residual is a genuineRepay/OutDest-tag partition. V4 hops do carryRepay::NetZero(viav4_hop_facts_netzero) and that fact is load-bearing for the residual partition. The misreading came from the citation of a source scan that counted literalmatcharms while the same dispatch existed as if/else chains — a spelling check, not a structural one.Structural correction (same epic): the fused ≈3,849-line
derive_planis now six shape modules undergrammar_walker/shapes/(all_v2_chain,two_hop_seed_v4,two_hop_v4_led,three_hop,two_hop_uniswap_only,tag_residual) behind a ≈29-line dispatcher on(len, repay-sequence)— one module per enclosure block, dispatcher keyed on exactly what the blocks already key on.Consequence of the correction: enclosure ordering defects are caught, not unrepresentable — correctness rests on the revm contract matrix + golden suites (per ADR-029 D5, the designated source of truth) plus
LedgerValidator’s always-fatal Reject (ADR-030). Whether theRepay/OutDesttag vocabulary can genuinely absorb the enumerated shapes (i.e. make D6’s “derived” claim literally true) is spikeRQQIUK; until it reports, treat “tag-derived enclosure” as aspiration for the enumerated shapes, not fact.Resolution (same epic, tasks T5+T6 — the aspiration is now realized): tags alone could not absorb the shapes (spike
RQQIUK, negative on the merged-pair holdout), but the vocabulary could: theterminal_formaxis merged the “blocked”v3v4pair, and the topology-rule analysis found 21/23 arms derivable from a 3-rule debt-flow set, with the last 2 unlocked by therepay_mechanism+seed_deliveryfacts. (The design docs behind both —docs/plans/pzbgp7-terminal-form-axis-draft.mdanddocs/spikes/t6-topology-rules-analysis.md— were removed in the stale-docs cleanup71ec78b2; their findings survive in this paragraph, theCONTEXT.mdwalker glossary, and the rule walkers ingrammar_walker/shapes/three_hop.rs.) All 23 3-hop bodies are deleted; three rule walkers (rule_walk_v2v3,rule_walk_v4_led,rule_walk_v2v3_v4_mixedingrammar_walker/shapes/three_hop.rs) derive the enclosures from the facts, byte-identical (golden suites + revm matrix green; the shadow-walk pin tests caught three rule corrections pre-cutover). D6’s “enclosure is derived from facts, NOT chosen” claim is now literal.
Context¶
grammar_shape.rs is a 7,013-line monolith of 30 hand-written per-family Plan
producers (build_*_plan), dispatched by the 30-row build_for/AxisSupport
table. Each re-derives the ordering invariants by hand, so correctness is gated
only post-hoc by the LedgerValidator; the D0 defect class (V4-take-before-
credit, terminal-V2 1-wei overdraw) escaped the hand-authored producers and was
caught only by the revm matrix. ADR-029 D4 chose per-family Plan authoring as
the interim mechanism and deferred a generic walker (6ZIE5X a-branch); CM5V3X
costed it. The corpus — 30 builders + AxisSupport + validator + the
25-family revm matrix — now exists to generalize over and regress against.
Decision¶
Adopt the hybrid deepening: the grammar becomes per-protocol hop facts
(data) + per-protocol mechanics (code) + one generic walker that derives
enclosure and emits a single Plan. The encoder (plan_to_bytes) and the
validator gate (plan_to_ledger_ops + LedgerValidator) are reused
unchanged — both are pure functions of the Plan, so the walker’s only output
contract is “a Plan”. build_for/AxisSupport dissolve into hop facts
(family axis-support becomes a fact, not 30 rows). Most per-protocol mechanics
already exist as shared, byte-identical helpers (v4_scaffold_table,
v4_bridge_steps, v4_terminal_capture_steps, funding_branch, enc_v*).
Landing was feature-flag parallel (A1/A2, --features walk) gated by byte-
identity to the hand-written producers on every family, then a hard cutover
(A3). The cutover is complete: the walker is now the sole producer — the 30
build_*_plan bodies, build_all_v2_chain, and the build_for/AxisSupport
rows are deleted, and family_axis_support is facts-derived from the
hop-protocol patterns rather than a 30-row table. Correctness is gated by the
revm contract-matrix (execution against the on-chain cmd_executor, per
ADR-029 D5 — not byte-parity against the suspect producers, which no longer
exist), plus the golden-byte corpora and honesty invariant. A validator
Reject remains always-fatal (ADR-030).
Considered options¶
Fully per-family Plan authoring (status quo, D4 interim): keeps ordering hand-reasoned per family — the adversarial surface this epic deletes.
Per-family declarative trace tables: data, but still ~30 rows, one per family — doesn’t kill the combinatorial fan-out D6 targets.
Walker without a mechanics seam (all data): blurs D4’s data-vs-code split and can’t express imperative Solidity callback wiring. Rejected.
Consequences¶
A new protocol is one hop-facts descriptor + one mechanics module (D6 additive proof), never a per-family body.
Enclosure ordering defects are caught, not unrepresentable: the validator gate + revm matrix are the enforcement (see the header’s record correction — the per-shape bodies are hand-authored code; the “derived” claim applies fully only to the residual tag partition).
grammar_shape.rsshrank from 7,013 lines to ~1,600 (the shared mechanics helpers + dispatch + derive seam + tests); the per-protocol facts table and walkers live ingrammar_walker.rs.Rejectstays reachable (amounts from solver inputs + hand-authored facts can still err), so the validator and ADR-030’s fatal-Reject remain load-bearing.
Addendum¶
The mechanics unification + 2-hop walks (epic 6SWFBS, 2026-08-18)¶
At the D6 cutover the six per-shape modules existed, but two of them still
hand-built per-family PlanStep bodies: the 2-hop seed→V4 shape
(v2v4/v3v4 — 60 literal sites) and the 2-hop V4-led shape
(v4v2/v4v3/v4v4 — 49 literal sites). Epic 6SWFBS removed the last
hand-built Plan body:
T1 (gates before code): per-shape golden stream pins (families × base/batch/erc6909 amount sets) + a RED probe counting
PlanStep::literals in each walk region. Probes flip GREEN per file and are retained as honesty invariants (D6 precedent).T2 — folded the
v3_flash/v3_flash_topair into the singlemechanics::v3_flash: the recipient triple may be omitted and is then derived fromfacts.out_dest(Executor→SELF, PoolManager→PM);Some((idx, pool, repays))makes it explicit. All flash-bearing sites in the three-hop, tag-residual, and uniswap-only modules route through the one primitive.T3/T4 — the two 2-hop shapes are now one arm function each, composed purely of
mechanicsprimitives (v4_swap,v4_unlock,v4_take_compact/v4_take_compact_at,v4_take_delta,v4_settle/v4_settle_delta/v4_settle_all,v4_sync,erc20_transfer,native_transfer,self_fund,weth_deposit/weth_withdraw,v2_flash,v3_flash,v2_swap,v4_batch/v4_batch_entry) + the sharedv4_terminal_capture_steps/v4_bridge_stepshelpers. The v2↔v3 delta within each 2-hop shape collapsed to the lead protocol — plus, once,weth_depositstanding in forself_fund.T5 — deduped the five
facts_of_*producers onto the shared per-protocol builders. Exposed that the shared defaults are not the per-family defaults: the v3v4v3 terminal needsrepay: Offstream(builder default isSelfRefund), and fee overflow must decline the path (the builder default zeros the fee, which would mis-encode; a latentTakeBeforeCreditvalidator reject, caught by the byte pin before commit).
Byte-transparency proof: zero golden-hash edits across all per-shape
pin tables and glopcn_bytepin (every family × amount set × both entry
points); the executor suite ends all-green (118 lib tests) with no RED
probes remaining. Net across T2–T5: ~1,100 lines removed.
Corrections to the main text: (a) “the per-shape bodies are
hand-authored code” — as of 6SWFBS, no shape module contains a single
PlanStep literal; all step construction lives in mod mechanics plus
the two shared grammar_shape helpers, and the D4 data/code split is
complete for every family. (b) The D4 additive claim (“a new protocol is
one hop-facts descriptor + one mechanics module”) now holds for the
walked 2-hop arms too: walking six families cost ten small primitives
and one arm function per shape.