commit dd762f7bcff6e02254e6291317a16575a282b0e0 (tree)
parent 3e75fb41a9176df8234fea8731506f0b4d697951
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date: Wed, 18 Feb 2026 21:10:42 +0000
skills: replace port-astgen + fix-stages with unified enable-tests
Combine two specialized skills (port-astgen for astgen_test.zig,
fix-stages for stages_test.zig) into a single parameterized skill
that takes any test file as input. Improvements over the originals:
- Config table maps test files to modifiable/reference files
- Flush accumulated passing tests before dispatching worker
- Fast test-zig0 for per-iteration verification, valgrind only in final check
- Worker budget (~15 cycles) to avoid context exhaustion
- Guard rail: skip test after 2 consecutive no-progress
- Worker truncates output (tail -50) and never reads whole reference files
- Handles all failure types: parser, AstGen, and sema
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
7 files changed, 358 insertions(+), 582 deletions(-)
diff --git a/stage0/.claude/skills/enable-tests/SKILL.md b/stage0/.claude/skills/enable-tests/SKILL.md
@@ -0,0 +1,211 @@
+---
+name: enable-tests
+description: Iteratively enable disabled tests in a given zig test file, fix divergences by mechanical porting from upstream, and commit.
+allowed-tools: Read, Write, Edit, Bash, Grep, Glob, Task
+disable-model-invocation: true
+---
+
+# Enable Tests — Orchestrator
+
+You manage the iterative loop of enabling disabled tests in a Zig test file.
+For each iteration you enable a test, run it, and either batch-commit
+passing tests or dispatch a worker to fix failures.
+
+**Input:** The test file is provided as the skill argument (e.g.,
+`stage0/astgen_test.zig`). If no argument is given, ask the user.
+
+**You do NOT**: analyze test output in detail, compare Zig/C code, or
+edit C source files. The worker handles all of that.
+
+**CRITICAL RULES:**
+1. **NEVER stop early.** Do not pause to ask the user if you should continue.
+ Do not summarize remaining work and wait. Keep looping until every disabled
+ test is enabled and passing.
+2. **ALWAYS run the test command yourself before committing.** Never trust
+ the worker's claim that tests pass — verify it.
+3. **Batch passing tests.** When multiple consecutive tests pass without code
+ changes, commit them together in one commit.
+
+## File Configuration
+
+Based on the test file argument, look up which files are in scope:
+
+| Test file | Modifiable files | Git-add list | Key reference files |
+|---|---|---|---|
+| `astgen_test.zig` | `astgen.c`, `astgen_test.zig` | `stage0/astgen.c stage0/astgen_test.zig` | `lib/std/zig/AstGen.zig`, `lib/std/zig/Ast.zig`, `lib/std/zig/Zir.zig` |
+| `stages_test.zig` | `astgen.c`, `parser.c`, `sema.c`, `stages_test.zig` | `stage0/astgen.c stage0/parser.c stage0/sema.c stage0/stages_test.zig` | `lib/std/zig/AstGen.zig`, `lib/std/zig/Parse.zig`, `lib/std/zig/Ast.zig`, `lib/std/zig/Zir.zig`, `src/Sema.zig` |
+| `sema_test.zig` | `sema.c`, `intern_pool.c`, `sema_test.zig` | `stage0/sema.c stage0/intern_pool.c stage0/sema_test.zig` | `src/Sema.zig`, `src/InternPool.zig`, `src/Air.zig` |
+
+All C source paths are under `stage0/`. Reference paths are relative to the
+repository root.
+
+If the test file is not in the table, infer by inspecting the file's imports
+and the types of failures it can produce. To add a new test file, add a row
+with its modifiable C sources, git-add list, and upstream reference files.
+
+## Phase 0: Verify baseline is green
+
+Run the full test suite:
+
+```sh
+cd ~/code/zig
+./zig-out/bin/zig build test-zig0 -Dzig0-cc=tcc 2>&1 | tail -5
+```
+
+If non-zero exit or failures: dispatch a worker (Step 4) with the failure
+context. After the worker returns, follow Steps 5-7. Re-run Phase 0 until
+clean.
+
+If clean: proceed to the main loop.
+
+## Main Loop
+
+Repeat until no disabled tests remain in the test file.
+
+### Step 1: Find the next disabled test
+
+Search the test file in priority order:
+
+**Priority A -- SkipZigTest lines:**
+Search for lines matching:
+```
+if (true) return error.SkipZigTest
+```
+Pick the first one. Note the test name (the `test "..."` header above
+the skip line) and the line number.
+
+**Priority B -- Commented corpus entries:**
+Search for lines matching `//"..` inside the `corpus_files` tuple
+(between `const corpus_files = .{` and `};`). Pick the first one.
+Note the file name and line number.
+
+If neither is found, all tests are enabled -- go to Final Check.
+
+### Step 2: Enable the test
+
+**For SkipZigTest:** Comment out the `if (true) return error.SkipZigTest;`
+line to enable the test.
+
+**For commented corpus entry:** Uncomment the line (remove `//` prefix).
+
+### Step 3: Smoke test
+
+```sh
+./zig-out/bin/zig build test-zig0 -Dzig0-cc=tcc 2>&1 | tail -10
+```
+
+### Step 4: Evaluate and dispatch
+
+**If it passes** -- go back to Step 1 and enable the next test. Accumulate
+passing tests without committing yet.
+
+**If it fails** -- first, flush any accumulated passing tests. If there
+are previously-enabled tests that passed without code changes, commit them
+now before dispatching the worker. This isolates the worker's code changes
+from the batch of trivially-passing tests:
+
+```sh
+git add <git-add-list>
+git commit -m "<test_file>: enable <accumulated list>
+
+Co-Authored-By: <model name>"
+```
+
+Then dispatch a worker:
+
+1. Read `.claude/skills/enable-tests/worker-prompt.md`.
+2. Replace `{{TEST_CONTEXT}}` with:
+ - Test file name
+ - What was enabled: test name or corpus file path + line number
+ - First ~20 lines of test output from Step 3 (use `tail -20`)
+ - Modifiable C files (from the configuration table above)
+ - Key reference files (from the configuration table above)
+3. Launch via the Task tool:
+ ```
+ subagent_type=general-purpose
+ prompt=<the worker prompt with context filled in>
+ ```
+
+### Step 5: Parse worker response
+
+Extract:
+- `STATUS`: one of `pass`, `progress`, or `no-progress`
+- `COMMIT_MSG`: a descriptive commit message
+
+If the worker response does not end with the expected STATUS format,
+treat it as `no-progress` and verify the test file was reverted.
+
+### Step 6: Handle by status
+
+**If STATUS is `pass` or `progress`:**
+
+Verify before committing (use the fast test command, not valgrind):
+
+```sh
+./zig-out/bin/zig build test-zig0 -Dzig0-cc=tcc 2>&1 | tail -10 ; echo "EXIT: $?"
+```
+
+Check: (1) EXIT is 0, (2) no test failures. If either fails, dispatch
+another worker to fix.
+
+Only if clean, commit all accumulated changes:
+
+```sh
+git add <git-add-list from configuration table>
+git commit -m "<COMMIT_MSG>
+
+Co-Authored-By: <model name>"
+```
+
+If `progress` (not `pass`), the worker re-disabled the test. Commit
+anyway -- the passing tests + partial fix are still valuable.
+
+**If STATUS is `no-progress`:**
+
+Worker should have re-disabled the test. Verify:
+
+```sh
+git diff stage0/
+```
+
+If there are accumulated passing tests, commit those:
+
+```sh
+git add <git-add-list>
+git commit -m "<test_file_name>: enable <list of tests>
+
+Co-Authored-By: <model name>"
+```
+
+If nothing to commit, revert and continue:
+
+```sh
+git checkout -- <git-add-list>
+```
+
+### Step 7: Repeat
+
+Go back to Step 1. **Never stop early** -- continue until all disabled
+tests are enabled.
+
+**Guard rail:** If the same test receives `no-progress` twice in a row,
+skip it permanently (leave it disabled) and move to the next test. Log
+which test was skipped.
+
+## Commit message conventions
+
+- Batch of passing tests: `"<test_file>: enable <file1>, <file2>, ..."`
+- Worker fix + passing tests: use the worker's COMMIT_MSG
+- If the list is too long: `"<test_file>: enable N files from <dir>/"`
+
+## Final Check
+
+When no disabled tests remain (or all remaining are permanently skipped),
+run the full suite with valgrind (this is the only place where valgrind
+is used -- per-iteration verification uses the fast `test-zig0` command):
+
+```sh
+./zig-out/bin/zig build all-zig0 -Dvalgrind |& grep -v Warning | head -10 ; echo "EXIT: $?"
+```
+
+Must exit 0 with no unexpected output.
diff --git a/stage0/.claude/skills/enable-tests/worker-prompt.md b/stage0/.claude/skills/enable-tests/worker-prompt.md
@@ -0,0 +1,145 @@
+# Enable Tests -- Worker (single iteration)
+
+You are a worker agent fixing a test failure. The orchestrator has enabled a
+test for you (either by uncommenting a corpus entry or by commenting out a
+`SkipZigTest` line). Your job: diagnose the failure, port the fix from
+upstream Zig to C, clean up, and return a result.
+
+This is a **mechanical translation** -- no creativity, no invention. When the
+C code differs from Zig, copy the Zig structure into C.
+
+## Context from orchestrator
+
+{{TEST_CONTEXT}}
+
+## Workflow
+
+### Step 1: Run the test
+
+```sh
+cd ~/code/zig
+./zig-out/bin/zig build test-zig0 -Dzig0-cc=tcc 2>&1 | tail -50
+```
+
+Capture the last ~50 lines. If tests pass, skip to Step 5.
+
+### Step 2: Determine failure stage
+
+From the output, identify the failure type:
+
+**Parser failure** (stack trace through `parser_test` or `expectAstConsistent`):
+- AST node/token mismatch between C and Zig parsers
+- Rendered output doesn't match source (canonical form)
+- Fix target: `stage0/parser.c`, reference: `lib/std/zig/Parse.zig`
+
+**AstGen failure** (stack trace through `astgen_test` or `expectEqualZir`):
+- `has_compile_errors` -- C AstGen emitted compile errors
+- `expectEqualZir` -- ZIR instruction/extra/string mismatch
+- `unhandled tag N` -- missing tag in `expectEqualData`/`dataMatches`
+- Fix target: `stage0/astgen.c` (or test zig for unhandled tags),
+ reference: `lib/std/zig/AstGen.zig`
+
+**Sema failure** (stack trace through `sema` or Air checks):
+- `has_compile_errors` -- C Sema emitted compile errors
+- Air array null checks failed
+- Fix target: `stage0/sema.c`, reference: `src/Sema.zig`
+
+### Step 3: Analyze the failure
+
+**For `has_compile_errors`:** Temporarily add `#include <stdio.h>` and
+`fprintf(stderr, ...)` to `setCompileError()` (or the sema equivalent)
+to find which error fires. Run the test again and note the function
+and line.
+
+**For ZIR mismatch:** Note `inst_len`, `extra_len`, `string_bytes_len`
+diffs and the first tag mismatch position.
+
+**For `unhandled tag N`:** Add the missing tag to `expectEqualData` and
+`dataMatches` switch statements in the test file.
+
+**For parser failures:** Note the AST node index, expected vs actual
+tag/token, and surrounding source context.
+
+### Step 4: Compare and port
+
+Find the upstream Zig function that corresponds to the failing code path.
+Use the Task tool with `subagent_type=general-purpose` to search for and
+read **only the specific function** in both implementations (C and Zig).
+Do NOT read entire files -- request just the relevant function(s).
+
+Enumerate differences that affect output:
+- Extra data written (field order, conditional fields, body lengths)
+- Instruction tags emitted
+- String table entries
+- Break payload values
+- AST node tags and token associations
+
+Apply the minimal mechanical change to match the upstream. Make ONE change
+at a time. After each change, run:
+
+```sh
+./zig-out/bin/zig build test-zig0 -Dzig0-cc=tcc 2>&1 | tail -20
+```
+
+Check that no previously-passing tests broke.
+
+**Progress** means any of:
+- `inst_len` / `extra_len` / `string_bytes_len` diff decreased
+- First tag mismatch position moved later
+- Compile errors resolved (even if other mismatch remains)
+- AST mismatch moved to a later node
+
+### Step 5: Clean up
+
+1. Remove ALL `fprintf`/`printf` debug statements from C files.
+2. Remove `#include <stdio.h>` if it was added for debugging.
+3. Verify with: `grep -n 'fprintf\|printf' stage0/astgen.c stage0/parser.c stage0/sema.c`
+4. If the test still fails:
+ - **For SkipZigTest:** re-add the `if (true) return error.SkipZigTest;`
+ line with a TODO comment describing the remaining diff.
+ - **For corpus entry:** re-comment the line (add `//` prefix back) and
+ add a TODO comment above it describing the remaining diff.
+5. Final verification -- this must exit 0:
+
+```sh
+./zig-out/bin/zig build test-zig0 -Dzig0-cc=tcc 2>&1 | tail -5
+```
+
+### Step 6: Return result
+
+You MUST end your response with exactly this format:
+
+```
+STATUS: pass | progress | no-progress
+COMMIT_MSG: <one-line descriptive message about what was ported/fixed>
+```
+
+- `pass` -- the enabled test now passes (entry remains enabled)
+- `progress` -- partial progress was made (entry was re-disabled with TODO)
+- `no-progress` -- no measurable improvement (entry was re-disabled)
+
+## 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. Before returning, grep for `fprintf|printf` in all C files
+ and remove any you find.
+- **Do NOT commit.** The orchestrator handles all commits.
+- **Functions must appear in the same order as in the upstream Zig file.**
+- **Prefer finding systematic differences** instead of debugging by bisection.
+ Zig code is bug-free for the purposes of porting. When test cases fail, it
+ means the C implementation differs from the Zig one. Making implementations
+ consistent is the correct approach.
+- From `./zig-out/bin/zig build` commands, use **only** `./zig-out/bin/zig build *-zig0`.
+ Other targets may build/test zig itself, which takes ages and is unnecessary.
+- When reading upstream reference files, **never read the whole file**.
+ Use Grep to find the function, then Read with offset/limit to get only the
+ relevant section. AstGen.zig is 568K, Sema.zig is 1.6M -- reading them
+ whole will blow your context.
+- **Budget your iterations.** If after ~15 test-fix cycles you have made
+ progress but cannot fully fix the test, stop iterating. Clean up (Step 5)
+ and report `progress`. The orchestrator will commit partial work and can
+ dispatch you again later. Do not exhaust your context chasing diminishing
+ returns.
diff --git a/stage0/.claude/skills/fix-stages/SKILL.md b/stage0/.claude/skills/fix-stages/SKILL.md
@@ -1,150 +0,0 @@
----
-name: fix-stages
-description: Iteratively enable commented-out corpus entries in stages_test.zig, fix parser/AstGen divergences, and commit.
-allowed-tools: Read, Write, Edit, Bash, Grep, Glob, Task
-disable-model-invocation: true
----
-
-# Fix Stages — Orchestrator
-
-You manage the iterative loop of enabling corpus entries in `stages_test.zig`.
-For each iteration you uncomment entries, test them, and either batch-commit
-passing files or dispatch a worker to fix failures.
-
-**You do NOT**: analyze test output in detail, compare Zig/C code, or
-edit `astgen.c`/`parser.c`. The worker handles all of that.
-
-**CRITICAL RULES:**
-1. **NEVER stop early.** Do not pause to ask the user if you should continue.
- Do not summarize remaining work and wait. Keep looping until every corpus
- entry is uncommented and passing.
-2. **ALWAYS run the test command yourself before committing.** Never trust
- the worker's claim that tests pass — verify it.
-3. **Batch passing files.** When multiple consecutive files pass without code
- changes, commit them together in one commit.
-
-## Phase 0: Verify baseline is green
-
-Run the full test suite:
-
-```sh
-cd ~/code/zig
-./zig-out/bin/zig build test-zig0 -Dzig0-cc=tcc 2>&1 | tail -5
-```
-
-If non-zero exit or failures: dispatch a worker (Step 4) with the failure
-context. After the worker returns, follow Steps 5–7. Re-run Phase 0 until
-clean.
-
-If clean: proceed to the main loop.
-
-## Main Loop
-
-Repeat until no commented-out entries (`//"..`) remain in `stages_test.zig`
-`corpus_files`.
-
-### Step 1: Uncomment the next entry
-
-Search `stage0/stages_test.zig` for lines matching `//"..` inside the
-`corpus_files` tuple. Pick the first one. Uncomment it (remove `//` prefix).
-
-### Step 2: Smoke test
-
-```sh
-./zig-out/bin/zig build test-zig0 -Dzig0-cc=tcc 2>&1 | tail -10
-```
-
-### Step 3: Evaluate result
-
-**If it passes** — go back to Step 1 and uncomment the next entry. Accumulate
-passing files without committing yet.
-
-**If it fails** — proceed to Step 4 for the failing file.
-
-### Step 4: Dispatch worker
-
-1. Read `.claude/skills/fix-stages/worker-prompt.md`.
-2. Replace `{{TEST_CONTEXT}}` with:
- - File path that failed
- - First ~20 lines of test output from Step 2
-3. Launch via the Task tool:
- ```
- subagent_type=general-purpose
- prompt=<the worker prompt with context filled in>
- ```
-
-### Step 5: Parse worker response
-
-Extract:
-- `STATUS`: one of `pass`, `progress`, or `no-progress`
-- `COMMIT_MSG`: a descriptive commit message
-
-### Step 6: Handle by status
-
-**If STATUS is `pass` or `progress`:**
-
-Verify before committing:
-
-```sh
-./zig-out/bin/zig build all-zig0 -Dvalgrind |& grep -v Warning | head -10 ; echo "EXIT: $?"
-```
-
-Check: (1) EXIT is 0, (2) no unexpected errors. If either fails, dispatch
-another worker to fix.
-
-Only if clean, commit all accumulated changes:
-
-```sh
-git add stage0/astgen.c stage0/parser.c stage0/stages_test.zig
-git commit -m "<COMMIT_MSG>
-
-Co-Authored-By: <model name>"
-```
-
-If `progress` (not `pass`), the worker re-commented the failing entry. Commit
-anyway — the passing files + partial fix are still valuable.
-
-**If STATUS is `no-progress`:**
-
-Worker should have re-commented the entry. Verify:
-
-```sh
-git diff stage0/stages_test.zig
-```
-
-If there are accumulated passing files (from previous Step 3a iterations),
-commit those:
-
-```sh
-git add stage0/stages_test.zig
-git commit -m "stages_test: enable <list of files>
-
-Co-Authored-By: <model name>"
-```
-
-If nothing to commit, revert and continue:
-
-```sh
-git checkout -- stage0/astgen.c stage0/parser.c stage0/stages_test.zig
-```
-
-### Step 7: Repeat
-
-Go back to Step 1. **Never stop early** — continue until all corpus entries
-are uncommented.
-
-## Commit message conventions
-
-- Batch of passing files: `"stages_test: enable <file1>, <file2>, ..."`
-- Worker fix + passing files: use the worker's COMMIT_MSG
-- If the list of files is too long, use `"stages_test: enable N files from <dir>/"`
-
-## Final Check
-
-When no commented-out entries remain, run:
-
-```sh
-./zig-out/bin/zig build all-zig0 -Dvalgrind |& grep -v Warning | head -10 ; echo "EXIT: $?"
-```
-
-Must exit 0 with no output.
diff --git a/stage0/.claude/skills/fix-stages/worker-prompt.md b/stage0/.claude/skills/fix-stages/worker-prompt.md
@@ -1,135 +0,0 @@
-# Fix Stages — Worker (single iteration)
-
-You are a worker agent fixing a corpus test failure in `stages_test.zig`.
-The file fails either at the **parser stage** (C parser vs Zig parser) or the
-**AstGen stage** (C AstGen vs Zig AstGen). Your job: diagnose the failure,
-port the fix from upstream Zig to C, clean up, and return a result.
-
-This is a **mechanical translation** — no creativity, no invention. When the
-C code differs from Zig, copy the Zig structure into C.
-
-## Context from orchestrator
-
-{{TEST_CONTEXT}}
-
-## Key files
-
-- `stage0/astgen.c` — C AstGen implementation (modify if AstGen failure)
-- `stage0/parser.c` — C parser implementation (modify if parser failure)
-- `stage0/stages_test.zig` — corpus test (re-comment entry if can't fix)
-- `stage0/parser_test.zig` — parser test infrastructure (comparators)
-- `stage0/astgen_test.zig` — AstGen test infrastructure (comparators)
-- `lib/std/zig/AstGen.zig` — upstream AstGen reference (~14k lines)
-- `lib/std/zig/Parse.zig` — upstream parser reference
-- `lib/std/zig/Ast.zig` — AST node accessors
-- `lib/std/zig/Zir.zig` — ZIR instruction definitions
-
-## Workflow
-
-### Step 1: Run the full test
-
-```sh
-cd ~/code/zig
-./zig-out/bin/zig build test-zig0 -Dzig0-cc=tcc 2>&1
-```
-
-Record the full output. If tests pass, skip to Step 5.
-
-### Step 2: Determine failure stage
-
-From the stack trace, identify which stage failed:
-
-- **Parser failure**: stack trace goes through `parser_test.corpusCheck`
- - `expectAstConsistent` — AST node/token mismatch between C and Zig parsers
- - `expectEqualStrings` — rendered output doesn't match source (canonical form)
-
-- **AstGen failure**: stack trace goes through `astgen_test.corpusCheck`
- - `has_compile_errors` — C AstGen emitted compile errors
- - `expectEqualZir` — ZIR instruction/extra/string mismatch
- - `expectEqualData` — specific data field mismatch
-
-### Step 3: Analyze the failure
-
-**For AstGen `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.
-
-**For AstGen `zir mismatch`:** Note `inst_len`, `extra_len`,
-`string_bytes_len` diffs and the first tag mismatch position.
-
-**For parser failures:** Note the AST node index, expected vs actual tag/token,
-and surrounding source context.
-
-### Step 4: Compare and port
-
-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
-- AST node tags and token associations
-
-Apply the minimal mechanical change to match the upstream. Make ONE change at
-a time. After each change, run:
-
-```sh
-./zig-out/bin/zig build test-zig0 -Dzig0-cc=tcc 2>&1 | head -20
-```
-
-Check that no previously-passing tests broke.
-
-**Progress** means any of:
-- `inst_len` diff decreased
-- `extra_len` diff decreased
-- `string_bytes_len` diff decreased
-- First tag mismatch position moved later
-- Compile errors resolved (even if ZIR mismatch remains)
-- AST mismatch moved to a later node
-
-### Step 5: Clean up
-
-1. Remove ALL `fprintf`/`printf` debug statements from `astgen.c`/`parser.c`.
-2. Remove `#include <stdio.h>` if it was added for debugging.
-3. If the test still fails:
- - Re-comment the entry in `stages_test.zig` (add `//` prefix back).
- - Add a TODO comment above it describing the remaining diff.
-4. Final verification — this must exit 0:
-
-```sh
-./zig-out/bin/zig build test-zig0 -Dzig0-cc=tcc 2>&1 | tail -5
-```
-
-### Step 6: Return result
-
-You MUST end your response with exactly this format:
-
-```
-STATUS: pass | progress | no-progress
-COMMIT_MSG: <one-line descriptive message about what was ported/fixed>
-```
-
-- `pass` — the enabled test now passes (entry remains uncommented)
-- `progress` — partial progress was made (entry was re-commented with TODO)
-- `no-progress` — no measurable improvement (entry was re-commented)
-
-## 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. Before returning, grep for `fprintf\|printf` in
- `astgen.c`/`parser.c` and remove any you find.
-- **Do NOT commit.** The orchestrator handles all commits.
-- **Functions must appear in the same order as in the upstream Zig file.**
-- **Prefer finding systematic differences** instead of debugging and hunting
- for bugs. Zig code is bug-free for the purposes of porting. When test cases
- fail, it means the C implementation differs from the Zig one. Making
- implementations consistent is the correct approach.
-- From `./zig-out/bin/zig build` commands, use *only* `./zig-out/bin/zig build *-zig0`. Other
- `./zig-out/bin/zig build` commands may start building/testing zig itself, which takes
- ages and is wholly unnecessary.
diff --git a/stage0/.claude/skills/port-astgen/SKILL.md b/stage0/.claude/skills/port-astgen/SKILL.md
@@ -1,163 +0,0 @@
----
-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 — Orchestrator
-
-You manage the iterative porting loop. For each iteration you either fix
-existing failures or enable a new disabled test, dispatch a worker agent,
-then verify and commit.
-
-**You do NOT**: analyze test output in detail, compare Zig/C code, or
-edit `astgen.c`. The worker handles all of that.
-
-**CRITICAL RULES:**
-1. **NEVER stop early.** Do not pause to ask the user if you should continue.
- Do not summarize remaining work and wait. Keep looping until every test is
- enabled and passing.
-2. **ALWAYS run the test command (in Phase 0, below) yourself before
- committing.** Never trust the worker's claim that tests pass — verify it. If
- the test run fails (non-zero exit) or it emits any output, do NOT commit and
- let the worker fix it.
-
-## Phase 0: Check for leftovers
-
-Before enabling anything new, run the **full** test suite:
-
-```sh
-cd ~/code/zig
-./zig-out/bin/zig build all-zig0 -Dvalgrind > /dev/null 2>&1 ; echo "EXIT: $?"
-```
-
-Also check for failures:
-
-```sh
-./zig-out/bin/zig build all-zig0 -Dvalgrind 2>&1 | grep -iE 'FAIL|error:' | head -5
-```
-
-If EXIT is non-zero or any FAIL/error lines appear: dispatch a worker
-(Step 4 below) with context describing the failure — no test was
-"enabled", just paste the output and tell the worker existing tests are
-failing. After the worker returns, follow Steps 5–7 as normal. Re-run
-Phase 0 until the full test suite passes before proceeding to the main
-loop.
-
-If EXIT is 0 with no failures: proceed to the main loop.
-
-## Main Loop
-
-Repeat until no `SkipZigTest` lines AND no commented corpus entries
-remain in `astgen_test.zig`.
-
-### Step 1: Find the next disabled test
-
-Search `stage0/astgen_test.zig` in priority order:
-
-**Priority A — SkipZigTest lines:**
-Search for lines matching:
-```
-if (true) return error.SkipZigTest
-```
-Pick the first one. Note the test name (the `test "..."` header above
-the skip line) and the line number.
-
-**Priority B — Commented corpus entries:**
-If no SkipZigTest lines exist, search for lines matching `//"..\` inside
-the `corpus_files` tuple (between `const corpus_files = .{` and `};`).
-Pick the first one. Note the file name and line number.
-
-If neither is found, all tests are enabled — go to the final check.
-
-### Step 2: Enable the test
-
-**For SkipZigTest:** Comment out the `if (true) return error.SkipZigTest;`
-line to enable the test.
-
-**For commented corpus entry:** Uncomment the line (remove the leading
-`//`).
-
-### Step 3: Quick smoke test
-
-Run:
-```sh
-./zig-out/bin/zig build test-zig0 -Dzig0-cc=tcc 2>&1 | head -10
-```
-Capture only the first ~10 lines. This tells you pass/fail at a glance
-without bloating your context. The worker will run the full test itself.
-
-### Step 4: Dispatch worker
-
-1. Read the file `.claude/skills/port-astgen/worker-prompt.md`.
-2. Replace the placeholder `{{TEST_CONTEXT}}` with the actual context:
- - What was enabled: test name + line number (SkipZigTest) or
- corpus file name + line number (corpus entry)
- - Whether this is a SkipZigTest or a corpus entry
- - The first ~10 lines of test output from Step 3
-3. Launch via the Task tool:
- ```
- subagent_type=general-purpose
- prompt=<the worker prompt with context filled in>
- ```
-
-### Step 5: Parse worker response
-
-The worker returns a structured result. Extract:
-- `STATUS`: one of `pass`, `progress`, or `no-progress`
-- `COMMIT_MSG`: a descriptive commit message
-
-### Step 6: Handle by status
-
-**If STATUS is `pass` or `progress`:**
-
-**You MUST verify before committing.** Run:
-
-```sh
-./zig-out/bin/zig build all-zig0 -Dvalgrind |& grep -v Warning | head -10 ; echo "EXIT: $?"
-```
-
-Check two things only: (1) EXIT is 0, (2) the ~10 lines of output
-contain no unexpected errors. If either check fails, do NOT commit —
-dispatch another worker to fix.
-
-Only if clean, commit:
-
-```sh
-git add stage0/astgen.c stage0/astgen_test.zig
-git commit -m "<COMMIT_MSG from worker>
-
-Co-Authored-By: <whatever model is running this>"
-```
-
-**If STATUS is `no-progress`:**
-
-Do NOT commit. The worker should have reverted the test file (re-added
-SkipZigTest or re-commented the corpus entry). Verify with:
-
-```sh
-git diff stage0/astgen_test.zig
-```
-
-If there are leftover changes (the entry is still enabled), re-comment
-the corpus entry or re-add the SkipZigTest yourself. Then:
-
-```sh
-git checkout -- stage0/astgen.c stage0/astgen_test.zig
-```
-
-Log that this test was skipped due to no progress and continue.
-
-### Step 7: Repeat
-
-Go back to Step 1. **Never stop early** — continue until all
-`SkipZigTest` lines are gone AND all corpus entries are uncommented.
-
-## Final Check
-
-When no disabled tests remain, run:
-
- ./zig-out/bin/zig build all-zig0 -Dvalgrind
-
-If that fails, go back to Phase 0 (treat it as a leftover).
diff --git a/stage0/.claude/skills/port-astgen/worker-prompt.md b/stage0/.claude/skills/port-astgen/worker-prompt.md
@@ -1,132 +0,0 @@
-# Port AstGen — Worker (single iteration)
-
-You are a worker agent 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.
-
-The orchestrator has enabled a test for you (either by uncommenting a
-corpus entry in `corpus_files` or by commenting out a `SkipZigTest`
-line). Your job: diagnose any failure, port the fix, clean up, and
-return a result.
-
-## Context from orchestrator
-
-{{TEST_CONTEXT}}
-
-## 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
-
-## Workflow
-
-### Step 1: Run the full test
-
-```sh
-cd ~/code/zig
-./zig-out/bin/zig build test-zig0 -Dzig0-cc=tcc 2>&1
-```
-
-Record the full output. If tests pass, skip to Step 5.
-
-### Step 2: 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 3: 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 4: Port the fix
-
-Apply the minimal mechanical change to `astgen.c` to match the upstream.
-Make ONE change at a time. After each change, run:
-
-```sh
-./zig-out/bin/zig build test-zig0 -Dzig0-cc=tcc 2>&1 | head -20
-```
-
-Check that no previously-passing tests broke. If your change causes
-regressions (new FAILs or crashes that weren't there before), revert it
-immediately.
-
-**Progress** means any of:
-- `inst_len` diff decreased
-- `extra_len` diff decreased
-- `string_bytes_len` diff decreased
-- First tag mismatch position moved later
-
-You should mark progress marker (whatever it is from the above) *in/near
-the disabled test entry* (as a TODO comment on the SkipZigTest line or
-above the commented corpus entry).
-
-### Step 5: Clean up
-
-1. Remove ALL `fprintf`/`printf` debug statements from `astgen.c`.
-2. Remove `#include <stdio.h>` if it was added for debugging.
-3. If the test still fails:
- - **For a SkipZigTest test:** re-add the `if (true) return error.SkipZigTest;`
- line with a TODO comment describing the remaining diff.
- - **For a corpus entry:** re-comment the line in `corpus_files` (add `//`
- prefix back) and add a TODO comment above it describing the remaining diff.
-4. Final verification — this must exit 0 with no output:
-
- ./zig-out/bin/zig build all-zig0 -Dvalgrind |& grep -v Warning | head -100
-
-### Step 6: Return result
-
-You MUST end your response with exactly this format:
-
-```
-STATUS: pass | progress | no-progress
-COMMIT_MSG: <one-line descriptive message about what was ported/fixed>
-```
-
-- `pass` — the enabled test now passes (SkipZigTest was NOT re-added /
- corpus entry remains uncommented)
-- `progress` — partial progress was made (SkipZigTest was re-added with
- TODO / corpus entry was re-commented with TODO above it)
-- `no-progress` — no measurable improvement (SkipZigTest was re-added /
- corpus entry was re-commented)
-
-## 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. Before returning, grep for `fprintf\|printf` in
- `astgen.c` and remove any you find.
-- **Do NOT commit.** The orchestrator handles all commits.
-- **Functions must appear in the same order as in the upstream Zig file.**
-- **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.
-- From `./zig-out/bin/zig build` commands, use *only* `./zig-out/bin/zig build *-zig0`. Other `./zig-out/bin/zig build`
- commands may start building/testing zig itself, which takes ages and is
- wholly unnecessary.
diff --git a/stage0/CLAUDE.md b/stage0/CLAUDE.md
@@ -20,6 +20,6 @@
- no `cppcheck` suppressions. They are here for a reason. If it is complaining
about automatic variables, make it non-automatic. I.e. find a way to satisfy
the linter, do not suppress it.
-- if you are in the middle of porting AstGen, load up the skill
- .claude/skills/port-astgen/SKILL.md and proceed with it.
+- if you are in the middle of enabling tests, load up the skill
+ .claude/skills/enable-tests/SKILL.md and proceed with it.
- remember: **mechanical copy** when porting existing stuff, no new creativity.