AI Code Generation, AST Validation, and Safe Code Transformation Architecture
AI Code Generation, AST Validation, and Safe Code Transformation Architecture
Real-World Problem Context
AI code generation tools — GitHub Copilot, ChatGPT, Cursor, Cody — generate code from natural language prompts. But generated code can contain syntax errors, use deprecated APIs, introduce security vulnerabilities (SQL injection, XSS), violate project coding standards, import nonexistent modules, or produce type errors. Blindly pasting AI-generated code into a codebase is risky. The solution: validate and transform AI output using ASTs (Abstract Syntax Trees) before it reaches the codebase. This includes parsing the generated code, checking it against lint rules, verifying type correctness, detecting security anti-patterns, ensuring import compatibility, and even auto-fixing common issues. This post covers the architecture of AI code validation pipelines: how AST parsing works, how to build validation rules, how to integrate with language servers (LSP), and how to create safe auto-apply workflows that transform AI suggestions before they land in code.
Problem Statements
-
Syntactic and Semantic Validation: How do you parse AI-generated code into an AST, detect syntax errors, verify that referenced variables/functions exist in scope, and check type correctness — all before the code is inserted into the project?
-
Security and Quality Gates: How do you automatically detect security vulnerabilities (XSS, injection, hardcoded secrets), coding standard violations, and deprecated API usage in AI-generated code, using AST-based pattern matching?
-
Safe Transformation Pipeline: How do you build an automated pipeline that receives AI-generated code, validates it, transforms it (auto-fix lint issues, add missing imports, adjust formatting), and produces a safe-to-apply result — with clear feedback on what was changed and why?
Deep Dive: Internal Mechanisms
1. AST Parsing Fundamentals
/*
* An AST (Abstract Syntax Tree) is the structured representation
* of source code that compilers and tools work with.
*
* Source code:
* const x = 1 + 2;
*
* Token stream (lexing):
* [CONST, IDENT("x"), EQUALS, NUMBER(1), PLUS, NUMBER(2), SEMICOLON]
*
* AST (parsing):
* VariableDeclaration {
* kind: "const"
* declarations: [
* VariableDeclarator {
* id: Identifier { name: "x" }
* init: BinaryExpression {
* operator: "+"
* left: Literal { value: 1 }
* right: Literal { value: 2 }
* }
* }
* ]
* }
*
* Tools use the AST to analyze, transform, and validate code
* without working with raw text (which is fragile and error-prone).
*/
// Parsing AI-generated JavaScript/TypeScript:
import * as parser from '@babel/parser';
import traverse from '@babel/traverse';
import generate from '@babel/generator';
import * as t from '@babel/types';
function parseAICode(code, language = 'typescript') {
try {
const ast = parser.parse(code, {
sourceType: 'module',
plugins: [
'typescript',
'jsx',
'decorators-legacy',
'classProperties',
'optionalChaining',
'nullishCoalescingOperator',
],
});
return { success: true, ast, errors: [] };
} catch (error) {
return {
success: false,
ast: null,
errors: [{
type: 'SyntaxError',
message: error.message,
line: error.loc?.line,
column: error.loc?.column,
}],
};
}
}
// For Python:
// import ast
// tree = ast.parse(ai_generated_code)
//
// For Rust:
// use syn::parse_str;
// let ast: syn::File = syn::parse_str(&code)?;
2. Security Pattern Detection
/*
* AI-generated code may contain security vulnerabilities:
*
* 1. SQL injection (string concatenation in queries)
* 2. XSS (dangerouslySetInnerHTML, innerHTML)
* 3. Hardcoded secrets (API keys, passwords)
* 4. Path traversal (unsanitized file paths)
* 5. Eval usage (eval(), new Function())
* 6. Prototype pollution
* 7. Command injection (child_process with user input)
* 8. Insecure randomness (Math.random() for security)
*
* AST-based detection is more reliable than regex
* because it understands code structure, not just text.
*/
const securityRules = [
{
name: 'no-eval',
severity: 'error',
detect(path) {
// Detect: eval(...), new Function(...)
if (path.isCallExpression()) {
const callee = path.get('callee');
if (callee.isIdentifier({ name: 'eval' })) {
return 'eval() is a code injection risk. Use JSON.parse() for data or a sandboxed interpreter.';
}
}
if (path.isNewExpression()) {
const callee = path.get('callee');
if (callee.isIdentifier({ name: 'Function' })) {
return 'new Function() is equivalent to eval(). Avoid dynamic code generation.';
}
}
},
},
{
name: 'no-innerhtml',
severity: 'error',
detect(path) {
// Detect: element.innerHTML = userInput
if (path.isAssignmentExpression()) {
const left = path.get('left');
if (left.isMemberExpression()) {
const prop = left.get('property');
if (prop.isIdentifier({ name: 'innerHTML' })) {
return 'innerHTML assignment is an XSS risk. Use textContent or a sanitizer like DOMPurify.';
}
}
}
// Detect: dangerouslySetInnerHTML in JSX
if (path.isJSXAttribute()) {
if (path.node.name.name === 'dangerouslySetInnerHTML') {
return 'dangerouslySetInnerHTML is an XSS risk. Sanitize HTML with DOMPurify before rendering.';
}
}
},
},
{
name: 'no-hardcoded-secrets',
severity: 'error',
detect(path) {
if (path.isVariableDeclarator()) {
const id = path.get('id');
const init = path.get('init');
if (id.isIdentifier() && init.isStringLiteral()) {
const name = id.node.name.toLowerCase();
const value = init.node.value;
const secretPatterns = [
/api[_-]?key/i, /secret/i, /password/i,
/token/i, /private[_-]?key/i,
];
const looksLikeSecret = secretPatterns.some(p => p.test(name))
&& value.length > 8
&& !value.startsWith('process.env');
if (looksLikeSecret) {
return `Hardcoded secret in "${id.node.name}". Use environment variables instead.`;
}
}
}
},
},
{
name: 'no-sql-concat',
severity: 'error',
detect(path) {
// Detect: `SELECT * FROM users WHERE id = ${userId}`
if (path.isTemplateLiteral()) {
const raw = path.get('quasis').map(q => q.node.value.raw).join('');
if (/\b(SELECT|INSERT|UPDATE|DELETE|DROP)\b/i.test(raw)) {
return 'SQL query with template literals is an injection risk. Use parameterized queries.';
}
}
},
},
];
// Run security analysis:
function runSecurityAnalysis(ast) {
const findings = [];
traverse(ast, {
enter(path) {
for (const rule of securityRules) {
const message = rule.detect(path);
if (message) {
findings.push({
rule: rule.name,
severity: rule.severity,
message,
line: path.node.loc?.start.line,
column: path.node.loc?.start.column,
});
}
}
},
});
return findings;
}
3. Import Validation
/*
* AI often generates code with wrong imports:
* - Importing from packages not in package.json
* - Importing nonexistent local files
* - Using named exports that don't exist
* - Mixing up default vs named imports
* - Using outdated import paths
*/
function validateImports(ast, projectContext) {
const issues = [];
traverse(ast, {
ImportDeclaration(path) {
const source = path.node.source.value;
// Check if it's a local import:
if (source.startsWith('.') || source.startsWith('/')) {
// Verify the file exists:
const resolvedPath = resolveImportPath(source, projectContext.currentFile);
if (!projectContext.fileExists(resolvedPath)) {
issues.push({
type: 'missing-local-import',
message: `Local module "${source}" does not exist`,
line: path.node.loc?.start.line,
suggestion: findSimilarFile(source, projectContext),
});
}
} else {
// Package import — check package.json:
const packageName = source.startsWith('@')
? source.split('/').slice(0, 2).join('/')
: source.split('/')[0];
if (!projectContext.dependencies[packageName] &&
!projectContext.devDependencies[packageName]) {
issues.push({
type: 'missing-package',
message: `Package "${packageName}" not in dependencies`,
line: path.node.loc?.start.line,
autofix: `npm install ${packageName}`,
});
}
}
// Check named imports against exports:
for (const specifier of path.node.specifiers) {
if (t.isImportSpecifier(specifier)) {
const importedName = specifier.imported.name;
const moduleExports = projectContext.getModuleExports(source);
if (moduleExports && !moduleExports.includes(importedName)) {
issues.push({
type: 'nonexistent-export',
message: `"${importedName}" is not exported from "${source}"`,
line: path.node.loc?.start.line,
available: moduleExports,
});
}
}
}
},
});
return issues;
}
4. Type Checking AI Output
/*
* AI-generated TypeScript may have type errors:
* - Wrong argument types passed to functions
* - Missing required properties in objects
* - Incorrect return types
* - Using any/unknown without assertion
*
* Approach: programmatic TypeScript compiler API
*/
import ts from 'typescript';
function typeCheckCode(code, projectContext) {
// Create a virtual file system for the compiler:
const fileName = 'ai-generated.ts';
const compilerOptions = {
target: ts.ScriptTarget.ES2022,
module: ts.ModuleKind.ESNext,
strict: true,
noEmit: true,
jsx: ts.JsxEmit.ReactJSX,
};
// Create a compiler host with virtual files:
const host = ts.createCompilerHost(compilerOptions);
const originalGetSourceFile = host.getSourceFile;
host.getSourceFile = (name, languageVersion) => {
if (name === fileName) {
return ts.createSourceFile(
name, code, languageVersion, true
);
}
// Fall back to real files for project context:
return originalGetSourceFile.call(host, name, languageVersion);
};
const program = ts.createProgram([fileName], compilerOptions, host);
const diagnostics = ts.getPreEmitDiagnostics(program);
return diagnostics.map(d => ({
type: 'type-error',
message: ts.flattenDiagnosticMessageText(d.messageText, '\n'),
line: d.file && d.start !== undefined
? d.file.getLineAndCharacterOfPosition(d.start).line + 1
: undefined,
severity: d.category === ts.DiagnosticCategory.Error ? 'error' : 'warning',
code: d.code,
}));
}
/*
* For faster type checking of small snippets,
* use the TypeScript Language Service API
* which supports incremental compilation.
*
* Even faster: use isolated declarations mode
* or type inference from the surrounding context.
*/
5. AST-Based Auto-Fixing
/*
* Auto-fix common issues in AI-generated code:
* - Add missing semicolons
* - Fix import ordering
* - Apply consistent formatting
* - Convert var → const/let
* - Add missing return types
* - Remove unused variables
*/
function autoFixAST(ast) {
const fixes = [];
traverse(ast, {
// Fix 1: var → const/let
VariableDeclaration(path) {
if (path.node.kind === 'var') {
// Check if the variable is reassigned:
const binding = path.scope.getBinding(
path.node.declarations[0].id.name
);
if (binding && !binding.constantViolations.length) {
path.node.kind = 'const';
fixes.push({
rule: 'prefer-const',
message: `Changed var to const`,
line: path.node.loc?.start.line,
});
} else {
path.node.kind = 'let';
fixes.push({
rule: 'no-var',
message: `Changed var to let`,
line: path.node.loc?.start.line,
});
}
}
},
// Fix 2: console.log removal:
CallExpression(path) {
if (
path.get('callee').isMemberExpression() &&
path.get('callee.object').isIdentifier({ name: 'console' }) &&
path.get('callee.property').isIdentifier({ name: 'log' })
) {
path.remove();
fixes.push({
rule: 'no-console',
message: 'Removed console.log',
line: path.node.loc?.start.line,
});
}
},
// Fix 3: == to ===:
BinaryExpression(path) {
if (path.node.operator === '==') {
path.node.operator = '===';
fixes.push({
rule: 'eqeqeq',
message: 'Changed == to ===',
line: path.node.loc?.start.line,
});
}
if (path.node.operator === '!=') {
path.node.operator = '!==';
fixes.push({
rule: 'eqeqeq',
message: 'Changed != to !==',
line: path.node.loc?.start.line,
});
}
},
});
// Generate fixed code from modified AST:
const output = generate(ast, {
retainLines: true,
concise: false,
});
return { code: output.code, fixes };
}
6. Validation Pipeline Architecture
/*
* Full validation pipeline for AI-generated code:
*
* AI Output (raw code string)
* │
* ▼
* ┌─────────────────┐
* │ 1. Parse (AST) │ → Syntax errors?
* └────────┬────────┘
* ▼
* ┌─────────────────┐
* │ 2. Security │ → Vulnerabilities?
* │ Analysis │
* └────────┬────────┘
* ▼
* ┌─────────────────┐
* │ 3. Import │ → Missing packages?
* │ Validation │ Wrong paths?
* └────────┬────────┘
* ▼
* ┌─────────────────┐
* │ 4. Type Check │ → Type errors?
* └────────┬────────┘
* ▼
* ┌─────────────────┐
* │ 5. Lint Rules │ → Style violations?
* └────────┬────────┘
* ▼
* ┌─────────────────┐
* │ 6. Auto-Fix │ → Apply safe fixes
* └────────┬────────┘
* ▼
* ┌─────────────────┐
* │ 7. Format │ → Prettier/dprint
* └────────┬────────┘
* ▼
* Validated + Fixed Code
* + Report of issues found/fixed
*/
class AICodeValidator {
constructor(projectContext) {
this.context = projectContext;
this.stages = [];
}
addStage(name, fn, options = {}) {
this.stages.push({ name, fn, ...options });
return this;
}
async validate(code, metadata = {}) {
const report = {
input: code,
output: code,
stages: [],
errors: [],
warnings: [],
fixes: [],
safe: true,
};
let currentCode = code;
let currentAST = null;
for (const stage of this.stages) {
const stageResult = {
name: stage.name,
startTime: performance.now(),
issues: [],
};
try {
const result = await stage.fn(currentCode, currentAST, this.context, metadata);
if (result.ast) currentAST = result.ast;
if (result.code) currentCode = result.code;
if (result.errors?.length) {
report.errors.push(...result.errors);
stageResult.issues.push(...result.errors);
// If stage is blocking and has errors, stop:
if (stage.blocking && result.errors.some(e => e.severity === 'error')) {
report.safe = false;
stageResult.blocked = true;
report.stages.push(stageResult);
break;
}
}
if (result.warnings?.length) {
report.warnings.push(...result.warnings);
}
if (result.fixes?.length) {
report.fixes.push(...result.fixes);
}
} catch (error) {
stageResult.error = error.message;
report.safe = false;
}
stageResult.duration = performance.now() - stageResult.startTime;
report.stages.push(stageResult);
}
report.output = currentCode;
return report;
}
}
// Configure the pipeline:
const validator = new AICodeValidator(projectContext)
.addStage('parse', parseStage, { blocking: true })
.addStage('security', securityStage, { blocking: true })
.addStage('imports', importValidationStage)
.addStage('types', typeCheckStage)
.addStage('lint', lintStage)
.addStage('autofix', autofixStage)
.addStage('format', formatStage);
const result = await validator.validate(aiGeneratedCode);
7. Language Server Protocol Integration
/*
* LSP (Language Server Protocol) provides rich code intelligence.
* Instead of building validation from scratch, leverage LSP:
*
* LSP capabilities useful for AI code validation:
* - textDocument/diagnostic → All errors/warnings
* - textDocument/completion → Verify referenced symbols exist
* - textDocument/codeAction → Auto-fixes from the language server
* - textDocument/hover → Type information
* - textDocument/formatting → Format to project standards
*
* Architecture:
*
* AI Code → Create virtual document → Send to LSP
* │ │
* │ ▼
* │ LSP returns diagnostics
* │ │
* ▼ ▼
* Apply auto-fixes from LSP code actions
*/
// Programmatic LSP client for validation:
class LSPValidator {
constructor(languageServer) {
this.server = languageServer;
this.docVersion = 0;
}
async validateCode(code, filePath) {
// Open a virtual document in the language server:
const uri = `file://${filePath}`;
await this.server.sendNotification('textDocument/didOpen', {
textDocument: {
uri,
languageId: 'typescript',
version: ++this.docVersion,
text: code,
},
});
// Wait for diagnostics:
const diagnostics = await this.waitForDiagnostics(uri);
// Request auto-fixes for each diagnostic:
const fixes = [];
for (const diagnostic of diagnostics) {
const actions = await this.server.sendRequest(
'textDocument/codeAction',
{
textDocument: { uri },
range: diagnostic.range,
context: { diagnostics: [diagnostic] },
}
);
if (actions?.length) {
fixes.push({
diagnostic,
actions: actions.filter(a => a.isPreferred),
});
}
}
// Close the virtual document:
await this.server.sendNotification('textDocument/didClose', {
textDocument: { uri },
});
return { diagnostics, fixes };
}
}
8. Context-Aware Validation
/*
* AI code doesn't exist in isolation — it's inserted into
* a specific file, function, or component context.
* Validation must consider the surrounding code.
*/
function buildValidationContext(targetFile, insertionPoint) {
return {
// File-level context:
existingImports: extractImports(targetFile),
existingExports: extractExports(targetFile),
existingVariables: extractTopLevelBindings(targetFile),
// Scope context at insertion point:
scopeVariables: getScopeVariables(targetFile, insertionPoint),
scopeTypes: getScopeTypes(targetFile, insertionPoint),
// Project context:
tsConfig: loadTsConfig(),
eslintConfig: loadESLintConfig(),
packageJson: loadPackageJson(),
// Style context (for consistency):
useSemicolons: detectSemicolonUsage(targetFile),
quoteStyle: detectQuoteStyle(targetFile),
indentStyle: detectIndentation(targetFile),
};
}
// Validate that AI code is compatible with its insertion context:
function validateContextCompatibility(aiCode, context) {
const issues = [];
const aiAST = parseAICode(aiCode);
// Check for variable name conflicts:
traverse(aiAST.ast, {
VariableDeclaration(path) {
for (const decl of path.node.declarations) {
if (t.isIdentifier(decl.id)) {
if (context.scopeVariables.includes(decl.id.name)) {
issues.push({
type: 'name-conflict',
message: `Variable "${decl.id.name}" already exists in scope`,
suggestion: `Rename to "${decl.id.name}2" or use a more descriptive name`,
});
}
}
}
},
});
// Check for duplicate imports:
traverse(aiAST.ast, {
ImportDeclaration(path) {
const source = path.node.source.value;
const existing = context.existingImports.find(
i => i.source === source
);
if (existing) {
// Merge imports instead of duplicating:
issues.push({
type: 'duplicate-import',
message: `Import from "${source}" already exists`,
autofix: 'merge', // Merge new specifiers into existing
existingImport: existing,
newSpecifiers: path.node.specifiers,
});
}
},
});
return issues;
}
9. Diff and Review Interface
/*
* After validation and fixes, present the changes clearly:
*
* ┌───────────────────────────────────────────┐
* │ AI Code Review │
* ├───────────────────────────────────────────┤
* │ ✅ Syntax: Valid │
* │ ⚠️ Security: 1 warning │
* │ └─ Line 12: eval() detected (auto-fix) │
* │ ✅ Types: No errors │
* │ 🔧 Auto-fixed: 3 issues │
* │ ├─ var → const (3 occurrences) │
* │ ├─ == → === (1 occurrence) │
* │ └─ Added missing semicolons (2) │
* │ │
* │ Diff: │
* │ - var result = fetch(url) │
* │ + const result = fetch(url) │
* │ │
* │ [Apply] [Apply with Changes] [Reject] │
* └───────────────────────────────────────────┘
*/
function generateValidationReport(result) {
const sections = [];
// Summary:
sections.push({
type: 'summary',
safe: result.safe,
errorCount: result.errors.length,
warningCount: result.warnings.length,
fixCount: result.fixes.length,
});
// Errors (blocking):
if (result.errors.length) {
sections.push({
type: 'errors',
title: 'Errors (must fix)',
items: result.errors.map(e => ({
line: e.line,
message: e.message,
rule: e.rule,
autoFixAvailable: !!e.autofix,
})),
});
}
// Warnings:
if (result.warnings.length) {
sections.push({
type: 'warnings',
title: 'Warnings',
items: result.warnings,
});
}
// Applied fixes:
if (result.fixes.length) {
sections.push({
type: 'fixes',
title: 'Auto-applied fixes',
items: result.fixes.map(f => ({
rule: f.rule,
message: f.message,
line: f.line,
})),
});
}
// Diff:
if (result.input !== result.output) {
sections.push({
type: 'diff',
original: result.input,
modified: result.output,
});
}
return sections;
}
10. Production Integration Patterns
/*
* Integration with AI coding tools:
*
* Pattern 1: Post-generation validation (editor plugin)
* AI generates code → Plugin validates AST → Show issues inline
*
* Pattern 2: Pre-apply gate (CI/CD)
* AI PR bot generates code → CI validates → Block merge if issues
*
* Pattern 3: Real-time validation (streaming)
* AI streams tokens → Incremental parse → Live error indicators
*
* Pattern 4: Training feedback loop
* Validation failures → Log patterns → Improve prompt engineering
*/
// Pattern 1: VS Code extension integration:
class AICodeValidationExtension {
constructor(context) {
this.diagnosticCollection =
vscode.languages.createDiagnosticCollection('ai-validation');
}
async onAICodeInserted(document, range, aiCode) {
// Run validation pipeline:
const result = await validator.validate(aiCode, {
filePath: document.uri.fsPath,
language: document.languageId,
});
// Show diagnostics in editor:
const diagnostics = result.errors.map(error => {
const line = range.start.line + (error.line || 0);
return new vscode.Diagnostic(
new vscode.Range(line, 0, line, 100),
`[AI] ${error.message}`,
error.severity === 'error'
? vscode.DiagnosticSeverity.Error
: vscode.DiagnosticSeverity.Warning
);
});
this.diagnosticCollection.set(document.uri, diagnostics);
// Offer auto-fixes as code actions:
if (result.fixes.length) {
// Register code action provider for AI fixes
}
}
}
// Pattern 4: Feedback loop for prompt improvement
function logValidationFailures(prompt, generatedCode, validationResult) {
if (validationResult.errors.length > 0) {
const patterns = validationResult.errors.map(e => e.rule);
// Track common failure patterns:
analytics.track('ai_code_validation_failure', {
errorPatterns: patterns,
promptLength: prompt.length,
codeLength: generatedCode.length,
language: validationResult.language,
});
// Use failures to improve system prompts:
// "The AI commonly generates eval(). Add instruction:
// 'Never use eval() or new Function()'"
}
}
/*
* Performance considerations:
*
* Parsing: ~5-20ms for typical file
* Security analysis: ~10-50ms (AST traversal)
* Type checking: ~100-500ms (TypeScript compiler)
* Lint: ~50-200ms (ESLint)
* Format: ~20-100ms (Prettier)
*
* Total: ~200-900ms — acceptable for post-generation validation
* For real-time: skip type checking, use incremental parsing
*/
Trade-offs & Considerations
| Aspect | Regex Validation | AST Validation | LSP Validation | Type Checker |
|---|---|---|---|---|
| Accuracy | Low (false positives) | High (structural) | Very high | Highest |
| Speed | <1ms | 5-50ms | 100-500ms | 100-500ms |
| Setup | None | Parser dependency | Running server | Compiler API |
| Cross-language | Limited | Per-parser | Per-server | Per-language |
| Auto-fix | Text replacement | AST transform | Code actions | N/A |
| Scope analysis | No | Yes | Yes | Yes |
| Type awareness | No | No | Yes | Yes |
Best Practices
-
Always parse AI output into an AST before insertion — never rely solely on text-based validation — regex-based checks miss structural issues (is that string inside a comment? Is that
evalactually a variable name?); AST parsing catches syntax errors immediately and enables structural pattern matching for security and quality rules. -
Run security checks as a blocking gate before any other validation — AI models can generate code with SQL injection, XSS, eval, hardcoded secrets, and command injection patterns; detect these via AST pattern matching and block insertion until they're resolved; this is the highest-priority validation stage.
-
Use the project's existing ESLint/Prettier configuration for AI code — AI-generated code should match the project's coding standards; run the project's actual lint and format tools (not a generic configuration) to ensure consistency; auto-fix what's safe to fix and flag the rest.
-
Validate imports against the project's actual dependency graph — AI often hallucinates package names or import paths; check that package imports exist in
package.jsonand local imports resolve to actual files; offer to install missing packages or suggest similar existing imports. -
Build a feedback loop from validation failures to prompt engineering — track which validation rules AI code most commonly fails (e.g., "frequently uses
varinstead ofconst", "often omits error handling"); use these patterns to improve system prompts, few-shot examples, or fine-tuning data to reduce failure rates over time.
Conclusion
Validating AI-generated code before it enters a codebase requires a multi-stage pipeline built on AST analysis. The pipeline starts with parsing: converting raw code text into an Abstract Syntax Tree using tools like Babel (JavaScript/TypeScript), ast (Python), or syn (Rust) — this immediately catches syntax errors. Security analysis traverses the AST to detect dangerous patterns: eval(), innerHTML assignments, SQL string concatenation, hardcoded secrets, and dangerouslySetInnerHTML. Import validation checks that referenced packages exist in package.json and local imports resolve to real files. Type checking via the TypeScript Compiler API catches type mismatches, missing properties, and incorrect function signatures. Auto-fixing transforms the AST to resolve safe issues: var → const, == → ===, removing console.log. Formatting applies the project's Prettier/ESLint configuration for consistency. The pipeline can integrate with the Language Server Protocol for rich diagnostics and code actions from the actual language server. Context-aware validation checks that AI code doesn't conflict with existing variables, duplicate imports, or violate the surrounding scope's contracts. The entire pipeline runs in 200-900ms — fast enough for post-generation validation in editors, CI gates, and code review workflows. By tracking validation failures, teams can improve their AI prompting strategies, creating a feedback loop that reduces future validation errors.
What did you think?