AI Code Editing via a Tight, Symmetric Normalization Layer

An elaboration of the thesis, core arguments, and proposed implementation

Thesis

An AI code-editing architecture should not try to solve the error-proneness of an LLM by making the model work natively on an AST, nor by validating raw text output after the fact. Instead, it should insert a transparent, tight, symmetric normalization layer between the model and the code.

In this design:

all without requiring full program compilability at the point of the edit.

The AST is not a representation for the model to reason about. It is an infrastructure-side shield that absorbs the model's statistical errors before they take effect.

Core Arguments

1. An LLM is a statistical prediction model, not a semantic reasoner

The decisive correction we make to the conventional framing: AI does not understand semantics better than a human does. Treating the model as if it could "work semantically" is a category error. It produces plausible token sequences; it does not comprehend structure. Any architecture must be built around this fact, not against it.

2. Both established paradigms are wrong for this reason

Both approaches misplace responsibility: they either overestimate the model or leave error handling too late.

3. Separation of concerns is the actual solution

The key insight is to split the probabilistic part from the deterministic part:

`` LLM (probabilistic) │ plain code text ▼ Parse / Normalize (deterministic) │ ▼ Canonical AST ──► semantic operation │ ▼ Generator (deterministic) │ ▼ Source code ``

This shifts reliability out of the model and into deterministic code, rather than relying on a downstream code → compiler/tests → LLM fixes it loop.

4. Tight normalization is the essential property (Python ast as the model)

The value is not in a rich representation, but in a narrow, deterministic one. Python's ast is the ideal reference: it breaks code down to instruction level and re-emits it uniformly, with no variation in representation.

"Tight" means:

The required property is symmetry / bidirectionality:

`` normalize(print(IR)) == IR normalize(code1) == normalize(code2) // for semantically equal, syntactically different inputs ``

This yields a normal form for code. We explicitly rates this as more important than a rich, generic cross-language IR — "IRs only help conditionally if they are not tight and cannot be read and written symmetrically." A general IR that is merely a good internal representation is not enough; it must be a tight, invertible normal form.

5. AST-completeness must be decoupled from compilability

Every operation must produce a structurally complete, internally consistent AST — but that AST need not compile or be semantically correct in the full project context. For example:

`` foo(unknownVariable); ``

is a perfectly valid AST node even though foo and unknownVariable may not exist. Whether they resolve is a later concern.

This gives three distinct validation levels:

  1. Syntax — "Did this become a complete AST?"
  2. Structure — "Is this AST admissible and canonical?"
  3. Compilation — "Is the whole program semantically correct?"

Only levels 1 and 2 belong in the normalization layer. Level 3 stays with the compiler. Keeping compilation out means each AI edit remains local, fast, and context-light — no need to pull in imports, type resolution, or the build system for a single block change.

Implementation

Target use case

The AI wants to change a semantic block inside a function. It generates the character sequence (error-prone). The infrastructure parses that sequence into an AST, applies the operation, and regenerates code:

``` Existing: if (x > 10) { foo(x); bar(x); }

LLM proposes (possibly malformed): foo(x); baz(x) // missing ';'

Flow: LLM text → AST(fragment) → normalized operation → transform target AST → generator → source ```

The AST thus acts as a transaction and validation boundary around each edit.

Parsing / normalization backend (Java)

Process architecture

RPC / serialization

Contract shape

An LSP-like — but custom — AST-RPC contract, e.g.:

`` service AstService { rpc Parse(ParseRequest) returns (ParseResponse); rpc Generate(GenerateRequest) returns (GenerateResponse); } ``

Overall picture

`` LLM │ plain code ┌──────▼──────┐ │ Normalizer │ parse · validate · recover └──────┬──────┘ │ canonical AST semantic operation │ ┌──────▼──────┐ │ Generator │ deterministic, variation-free └──────┬──────┘ │ source ``

The Genuinely Novel Point

The interesting engineering problem is not the AST schema itself. It is the normalization layer as a constraint- and recovery mechanism between a probabilistic model and a deterministic program representation — reconstructing as much as possible from faulty AI code text without inventing semantic decisions of its own.

In one line:

The model is allowed to be dumb and wrong; the representation is designed to absorb its errors — via a tight, symmetric, bidirectional normalization layer, decoupled from full compilation.