# 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.
