---
name: port-astgen
description: Iteratively port AstGen.zig to astgen.c by enabling skipped corpus tests, finding divergences, and mechanically copying upstream code.
allowed-tools: Read, Write, Edit, Bash, Grep, Glob, Task
disable-model-invocation: true
---

# Port AstGen — Iterative Corpus Test Loop

You are porting `AstGen.zig` to `astgen.c`. This is a **mechanical
translation** — no creativity, no invention. When the C code differs
from Zig, copy the Zig structure into C.

## Key files

- `stage0/astgen.c` — C implementation (modify this)
- `stage0/astgen_test.zig` — corpus tests (enable/skip tests here)
- `lib/std/zig/AstGen.zig` — upstream reference (~14k lines)
- `lib/std/zig/Ast.zig` — AST node accessors
- `lib/std/zig/Zir.zig` — ZIR instruction definitions

## Loop

Repeat the following steps until all corpus tests pass or you've made
3 consecutive iterations with zero progress.

### Step 1: Find the first skipped corpus test

Search `astgen_test.zig` for lines matching:
```
if (true) return error.SkipZigTest
```
Pick the first one. If none found, all corpus tests pass — stop.

### Step 2: Enable it

Remove or comment out the `if (true) return error.SkipZigTest` line.

### Step 3: Run tests

```sh
zig build test-zig0 2>&1
```

Record the output. If tests pass, go to Step 7.

### Step 4: Analyze the failure

From the test output, determine the failure type:

- **`has_compile_errors`**: Temporarily add `#include <stdio.h>` and
  `fprintf(stderr, ...)` to `setCompileError()` in `astgen.c` to find
  which `SET_ERROR` fires. Run the test again and note the function and
  line.
- **`zir mismatch`**: Note `inst_len`, `extra_len`, `string_bytes_len`
  diffs and the first tag mismatch position.
- **`unhandled tag N`**: Add the missing ZIR tag to the `expectEqualData`
  and `dataMatches` switch statements in `astgen_test.zig`.

### Step 5: Compare implementations

Find the upstream Zig function that corresponds to the failing code
path. Use the Task tool with `subagent_type=general-purpose` to read
both implementations and enumerate **every difference**.

Focus on differences that affect output:
- Extra data written (field order, conditional fields, body lengths)
- Instruction tags emitted
- String table entries
- Break payload values (operand_src_node)

Do NOT guess. Read both implementations completely and compare
mechanically.

### Step 6: Port the fix

Apply the minimal mechanical change to `astgen.c` to match the upstream.
Run `zig build test-zig0` after each change to check for progress.

**Progress** means any of:
- `inst_len` diff decreased
- `extra_len` diff decreased
- `string_bytes_len` diff decreased
- First tag mismatch position moved later

If after porting a fix the test still fails but progress was made,
continue to Step 7 (commit progress, re-skip).

### Step 7: Clean up and commit

1. If the corpus test still fails: re-add the `SkipZigTest` line with
   a TODO comment describing the remaining diff.
2. Remove ALL `fprintf`/`printf` debug statements from `astgen.c`.
3. Remove `#include <stdio.h>` if it was added for debugging.

4. Verify this exits 0 with no unexpected output:

    zig build fmt-zig0 && zig build all-zig0 -Dvalgrind |& grep -v Warning

5. Commit:
   ```sh
   git add astgen.c astgen_test.zig
   git commit -m "<descriptive message>

   Co-Authored-By: <whatever model is running this>"
   ```

### Step 8: Repeat

Go back to Step 1.

## Rules

- **Mechanical copy only.** Do not invent new approaches. If the upstream does
  X, do X in C.
- **Never remove zig-cache.**
- **Never print to stdout/stderr in committed code.** Debug prints are
  temporary only.
- **Functions must appear in the same order as in the upstream Zig file.**
- **Commit after every iteration**, even partial positive progress.
- **Prefer finding systematic differences for catching bugs** instead of
  debugging and hunting for them. Zig code is bug-free for the purposes of
  porting. When test cases fail, it means the C implementation differs from the
  Zig one, which is the source of the bug. So standard "bug hunting" methods no
  longer apply -- making implementations consistent is a much better approach
  in all ways.
