| name | test-fix-round |
| description | Run an ultrawork test-fix loop: read the ledger, select the highest-priority unresolved failure, diagnose and fix it, verify it, update the ledger, and continue until all entries are fixed or a blocking escalation stops the run. Use when you need to keep fixing test failures from a ledger. Triggers on: test-fix-round, test-fix-loop, fix test ledger, fix next test, ultrawork test fix. |
| user-invocable | true |
Test Fix Loop
Run an /ultrawork-style loop over the ledger. Keep selecting unresolved entries, fixing them, verifying them, updating the ledger, and continuing until the ledger is fully resolved or a blocking escalation stops the run.
Arguments
| Argument | Required | Default | Description |
|---|
--ledger | Yes | — | Path to the test-fix ledger JSON file (must conform to .claude/schemas/test-fix-ledger.schema.json) |
Run the loop
- Read the ledger from
--ledger.
- Repeatedly select the highest-priority unresolved entry.
- Checkpoint the ledger as soon as an entry is claimed.
- Diagnose the failure, apply the minimal fix, and run build/typecheck plus targeted verification.
- Update the ledger entry, add regressions back into the ledger, and continue to the next unresolved entry.
- Finish only when every ledger entry is
fixed.
- If any entry becomes
escalated, treat that as a blocking condition. Stop the loop and report the ledger as blocked, not successful.
The loop is successful only when there are zero unresolved entries and zero escalated entries.
Read the ledger
- Read the JSON file at the
--ledger path.
- Validate that it has the expected top-level fields:
session_id, created_at, updated_at, initial_failure_count, and entries.
- If the file is missing or invalid, abort with
ERROR: Ledger file not found or invalid at <path>.
- Treat
status: "discovered" and status: "attempted" as unresolved work.
Select the next entry
- Filter entries to unresolved items.
- Sort by
priority ascending, then by attempt_count ascending.
- Select the first entry from the sorted list.
- Set that entry's
status to "attempted".
- Immediately write the ledger to disk as a checkpoint before doing diagnosis or edits.
- If no unresolved entries remain, move to the final completion check instead of doing another iteration.
This checkpoint lets the outer ultrawork driver resume cleanly if the run is interrupted.
Diagnose the failure
Follow the existing ledger discipline for each selected entry.
Check .test-failures.json
- If
packages/opencode/.test-failures.json exists, read it first.
- Match the current entry by
file and inspect the error field.
- Use that raw bun output to anchor the current failure before reading source.
Read the failing test
- Open the test file from the entry's
file field.
- Read imports plus any
beforeAll, beforeEach, afterEach, or afterAll blocks.
- Find the exact
describe / test / it block for the entry's test name.
- Read the assertions and summarize the test's intent in one sentence.
Locate the source
- If
source_file and source_line exist, read the surrounding function there.
- Otherwise, use the first non-test, non-library frame from
stack_trace.
- If neither exists, infer the target module from the test imports and read the function under test.
- Read one level of related internal calls when needed.
Compare expected and actual
- Extract the actual behavior from
error_message or the raw test output.
- Trace the code path for the test inputs.
- Identify where behavior diverges from the expectation.
- Choose one root cause branch: source bug, API change, test bug, or environment issue.
Record the diagnosis
- Write a 1-2 sentence diagnosis for the ledger.
- State both the root cause category and the concrete problem.
Apply the fix
- Make the smallest change that addresses the diagnosed failure.
- Follow repo conventions strictly when editing source or tests.
- Avoid unrelated refactors, broad cleanup, or speculative improvements.
- Track every modified file for the ledger's
modified_files field.
- Record a short
fix_applied note describing what changed.
When the root cause is a test bug, add a // Fixed: <reason> comment on the corrected line.
Verify the change
Use targeted verification inside the loop. Do not run a full-suite sweep after every single iteration unless the current situation clearly requires it.
Run build and typecheck
- Invoke
/build-verify --scope typecheck first.
- If it fails, treat the attempt as a failed fix and update the ledger accordingly.
Run the targeted test
- Invoke
/test-analyze --file <test-file-path> for the current entry's file.
- Confirm whether the target test still appears in the failure table.
- If the target test passes but other tests in the same file fail, count the original entry as verified and handle the other failures as regressions.
Collect modified files
- Keep the list of every file changed during the iteration.
- Store paths relative to the project root.
Update the ledger
Mark a verified fix
If the target test now passes:
- Set the entry's
status to "fixed".
- Save
diagnosis, fix_applied, and modified_files.
- Refresh the top-level
updated_at timestamp.
Handle a failed attempt
If the target test still fails or verification cannot run cleanly:
- Increment
attempt_count.
- Preserve the attempted
diagnosis.
- Prefix
fix_applied with [FAILED] .
- If
attempt_count >= max_attempts, set status to "escalated" and record the most specific escalation_reason.
- If attempts remain, set
status back to "discovered" and revert the failed code changes before continuing.
- Refresh the top-level
updated_at timestamp.
An escalated entry is not a win condition. It blocks final success and should stop the ultrawork loop for human review unless the caller explicitly wants to continue collecting more blocked items.
Check regressions
- Build a targeted regression list from
modified_files plus the current entry's test file.
- Run
/test-analyze --file <path> for those files.
- Compare new failures against existing ledger entries by both
file and test.
- Add unmatched failures as new
discovered entries with fresh IDs.
- If a previously
fixed entry breaks again, move it back to "discovered", increment its attempt_count, and note the regression source.
Targeted regression checks happen during the loop. Use a broader sweep near the end of the ultrawork run, or when targeted checks suggest wider fallout.
Continue iterating
After each iteration:
- Validate the ledger structure and write it back to
--ledger with 2-space indentation.
- Print a short iteration summary with the entry ID, outcome, regressions added, and remaining unresolved count.
- Re-read the ledger state if needed, then select the next unresolved entry.
- Keep looping until one of the end conditions is reached.
Do not treat a single fixed entry as completion. The skill owns the whole ledger until it is resolved or blocked.
Finish correctly
Before exiting the ultrawork loop, evaluate the whole ledger.
Succeed
Complete successfully only when all entries are fixed.
- Confirm there are no
discovered entries.
- Confirm there are no
attempted entries.
- Confirm there are no
escalated entries.
- Run a broader verification sweep if the loop has accumulated enough changes that targeted checks are no longer sufficient.
Block
Stop as blocked when any entry is escalated.
- Leave the escalated entry in the ledger.
- Explain why it is blocked and cite the
escalation_reason.
- Report that the ledger is not fully resolved.
- Do not claim success just because no more safe iterations remain.
Checklist