# 6. Argument pattern extraction via AST node classification **Date:** 2026-07-03 **Status:** Accepted ## Context A behavioral token like `assertEquals` tells the LLM that the function is called, but not *how*. Two codebases both use `assertEquals` — one writes `assertEquals(expected, actual)` and the other writes `assertEquals(actual, expected)` with swapped argument order. An LLM guessing the wrong order writes broken tests. The highlights.scm captures tell us *that* a function is called. We need the argument *structure* — number of arguments, their types, and the common patterns. ## Decision For each behavioral capture node, walk up to its parent `call_expression` (or equivalent), find the argument list node, and classify each argument by structural role. Argument classification is language-agnostic: | Classification | Matches | |---|---| | `lit` | string, number, boolean, null | | `var` | identifiers, names | | `call` | nested call expressions, method invocations | | `lambda` | lambda expressions, blocks, do-blocks | | `kwarg` | keyword/named arguments | | `expr` | binary/unary/ternary/operator expressions | | `template` | string interpolation, template literals | | `other` | anything else (fallback) | Argument list node detection uses a tiered approach: 1. `child_by_field_name("arguments")` — works for Python, JS, TS, Java, Go, Ruby, Rust. 2. Fallback: scan children for `argument_list`, `arguments`, `template_string` (JS tagged templates). The argument iterator is a simple generic traversal: yield all named children of the arglist node. No per-language special cases. This works for positional args, keyword args, lambdas inside argument lists, and template expressions. Results are aggregated per cluster into a summary showing min/max/common arg counts and the top argument-type patterns. ## Consequences **Positive:** - Reveals argument ordering conventions: `assertEquals: n=2 [lit,var]` means expected-first. - Reveals calling convention variance: `verify: n=0 [] | n=1 [lambda] | n=1 [var]` means three styles coexist. - Zero per-language branches — generic tiered detection and iteration handles all 10 grammars. **Negative:** - `kwarg` detection only covers named arguments, not default values or spread operators. - Nested destructuring patterns fall into `other` bucket — no granularity for complex argument shapes. - `other` is a catch-all that can hide meaningful distinctions we haven't classified yet. ## Alternatives Considered - **Extract raw argument text**: Language-agnostic but fragile — variable names change per test, producing high variance and low signal. - **No argument extraction**: The LLM sees `assertEquals` but doesn't know argument order. Leads to wrong code. - **Per-language argument extractors**: Would be more precise but violate the zero-adapters constraint.