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>
This commit is contained in:
2026-02-18 21:10:42 +00:00
parent 665ffcb149
commit 58f69f46b5
7 changed files with 358 additions and 582 deletions

View File

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

View File

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

View File

@@ -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 57. 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.

View File

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

View File

@@ -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 57 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).

View File

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

View File

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