| name | ct-ivt-looper |
| description | Runs a project-agnostic autonomous Implement-then-Validate-then-Test compliance loop on any git worktree. Detects the project's test framework (vitest, jest, mocha, pytest, unittest, go-test, cargo-test, rspec, phpunit, bats, or other) and iterates until the implementation satisfies its specification, recording convergence metrics to the manifest. Use when given an implementation task that must ship verified: the IVT loop is the autonomous compliance layer enforced before any release or PR. Triggers on phrases like 'implement and verify', 'run the IVT loop', 'ship this task', 'complete implementation with tests', 'verify against spec', or any implementation task with acceptance criteria. Works in any git worktree regardless of language or framework, never hardcoded to one project's tooling. |
| protocol | testing |
| loomStage | testing |
| adrRefs | ["ADR-051","ADR-061"] |
IVT Looper
Overview
Runs an autonomous Implement-then-Validate-then-Test loop against any git worktree, detects the test framework in use, and iterates until the implementation converges on its specification. This skill is the autonomous compliance layer: no task ships, no release runs, and no PR opens until the loop has recorded a converged result to the manifest.
Core Principle
The loop converges on the spec, not on 'tests pass'. Passing tests that don't cover the spec is a failure.
Immutable Constraints
| ID | Rule | Enforcement |
|---|
| IVT-001 | Test framework MUST be detected or declared before the loop starts. | validateTestingProtocol rejects entries without a framework; deducts 20 from score. |
| IVT-002 | Loop MUST cap iterations at MAX_ITERATIONS (default 5). | Hard counter in the loop; unreachable convergence forces HITL escalation. |
| IVT-003 | Every MUST requirement in the spec MUST map to at least one passing test. | Spec-to-test traceability matrix; gaps block convergence. |
| IVT-004 | Final manifest entry MUST record framework, testsRun, testsPassed, testsFailed, ivtLoopConverged, ivtLoopIterations. | validateTestingProtocol requires these fields; ivtLoopConverged: false fails validation. |
| IVT-005 | Framework detection MUST NOT be hardcoded to one project's tooling. | Detection walks the project tree; no vitest/jest-only code paths. |
| IVT-006 | Loop MUST NOT run on the main branch. | Check git branch --show-current; stop if on main/master/trunk. |
| IVT-007 | Non-convergence after MAX_ITERATIONS MUST escalate to HITL (exit code 65). | Agent stops, writes manifest with ivtLoopConverged: false, exits. |
| IVT-008 | Final manifest entry MUST set agent_type: "testing". | Validator rejects any other value. |
The IVT Loop
load_spec(task_id) # read acceptance criteria from canon
detect_framework(worktree) # see references/frameworks.md
branch_check() # abort if on main/master/trunk
iteration = 0
while iteration < MAX_ITERATIONS: # IVT-002
iteration += 1
# ----- I : Implement -----
apply_patch(current_diff) # patch generated by the implementer
ensure_provenance_tags(new_code) # IMPL-003 tags on new functions
# ----- V : Validate -----
lint_result = run_project_linter()
type_result = run_type_checker() # tsc / mypy / go vet / rustc
if lint_result.failed or type_result.failed:
regenerate_fix_for(lint_result, type_result)
continue # back to top, same iteration count
# ----- T : Test -----
test_result = run_framework_tests() # framework-specific command
trace = spec_to_test_trace(spec) # IVT-003: every MUST has a test
if test_result.all_passed and trace.complete:
write_manifest(
framework=framework,
iterations=iteration,
converged=True,
agent_type="testing",
)
return CONVERGED # exit loop, exit skill with code 0
# ----- Analyze and regenerate fix -----
failure = diagnose(test_result, trace)
current_diff = regenerate_fix_for(failure)
# MAX_ITERATIONS reached without convergence
write_manifest(
framework=framework,
iterations=MAX_ITERATIONS,
converged=False,
agent_type="testing",
)
escalate_to_hitl() # IVT-007: exit code 65
The loop is a single stage from the lifecycle's point of view. Implement, Validate, and Test are not three separate tasks — they are three phases of one autonomous run that either converges or escalates.
Out of Scope (T9675)
ct-ivt-looper operates on the testing LOOM lifecycle stage (stage 8). It performs the dynamic Implement-then-Validate-then-Test loop with framework detection and iterate-until-green convergence semantics.
This skill does NOT:
- Audit static artifacts for schema/compliance/RFC-2119 keyword usage, ADR-document structure, or JSON Schema conformance. Those belong to
ct-validator at the validation stage (stage 7). When the question is "is this document/manifest well-formed?" rather than "does this code converge on its spec?", chain to ct-validator rather than expanding scope here.
- Promote a green loop to release. Release sequencing belongs to
ct-release-orchestrator at stage 9.
Chain handoffs
| Direction | When | Handoff |
|---|
ct-ivt-looper → ct-validator | Loop converged; need to audit the resulting artifacts (e.g. final manifest, spec back-references) against schema/compliance | Emit the convergence manifest entry, then dispatch the validation stage |
ct-validator → ct-ivt-looper | Spec is valid but implementation needs dynamic verification | Receive a dispatch from the validation stage; iterate the IVT loop on the worktree |
Governance: see ADR-051 (programmatic gate integrity) which defines the evidence atoms (tool:test, test-run:<json>) the loop emits and that downstream cleo verify --gate testsPassed re-validates, and ADR-061 (project-agnostic verify tools) which defines the canonical tool-resolution layer.
Framework Detection
Framework detection is project-agnostic: the skill walks the worktree, inspects config files, and selects the correct test command. No language or framework is special-cased above another. The full detection table lives in references/frameworks.md. In summary: detection reads the project manifest (package.json, pyproject.toml, Cargo.toml, go.mod, Gemfile, composer.json, or .cleo/project-context.json#testing.command) and selects one of: vitest, jest, mocha, pytest, unittest, go-test, cargo-test, rspec, phpunit, bats, other.
Convergence Criteria
The loop has converged when all of the following hold:
- Every MUST requirement in the task's linked specification has at least one passing test (spec-to-test traceability complete).
testsFailed == 0, testsRun > 0, and testsPassed == testsRun.
- Project linter reports zero errors (warnings are allowed).
- Type checker reports zero errors.
- CI-equivalent local commands return zero (e.g.,
pnpm run build, cargo build, go build ./...).
- No new runtime errors were observed during the test run.
A loop that makes tests pass by deleting assertions, narrowing scope, or mocking the thing under test is not converged — that is a spec violation dressed up as a green run. See references/loop-anatomy.md for worked examples.
Branch Discipline
Before iteration 1, the skill MUST verify the current branch:
branch=$(git branch --show-current)
if [[ "$branch" == "main" || "$branch" == "master" || "$branch" == "trunk" ]]; then
echo "Refusing to run IVT loop on protected branch: $branch"
exit 65
fi
The loop is destructive in the sense that it rewrites code on failed iterations. It MUST run on a feature branch or worktree. If the user invoked it on a protected branch, stop and request a feature branch.
Escalation on Non-Convergence
When the loop exhausts MAX_ITERATIONS without converging, the skill MUST:
- Write the manifest entry with
ivtLoopConverged: false and the iteration count.
- Record the last diagnostic output in
key_findings.
- Exit with code 65 (
HANDOFF_REQUIRED).
- Leave the worktree in its last state — do not revert.
The human reviewer picks up the worktree, reads the diagnostics, and either raises the iteration cap, rewrites the spec, or manually corrects the implementation before rerunning the skill. The full escalation handoff protocol is in references/escalation.md.
Integration
Record the loop outcome through cleo check protocol:
cleo check protocol \
--protocolType testing \
--framework vitest \
--testsRun 142 \
--testsPassed 142 \
--testsFailed 0 \
--ivtLoopConverged true \
--ivtLoopIterations 3
cleo check protocol \
--protocolType testing \
--framework pytest \
--testsRun 87 \
--testsPassed 84 \
--testsFailed 3 \
--ivtLoopConverged false \
--ivtLoopIterations 5
Exit code 0 = loop converged and protocol is valid. Exit code 65 = HANDOFF_REQUIRED (non-convergence).
This skill MUST also chain a validation check against the spec before its own testing check:
cleo check protocol \
--protocolType validation \
--specMatchConfirmed true \
--testSuitePassed true \
--protocolComplianceChecked true
Anti-Patterns
| Pattern | Problem | Solution |
|---|
| Hardcoding vitest or jest | Violates IVT-005; breaks on any non-JS project | Walk the worktree and pick the framework per project |
| Running the loop on main/master | Violates IVT-006; pollutes shared branch | Check git branch --show-current and refuse protected branches |
| No iteration cap | Infinite loop on unreachable convergence; context burn | Enforce MAX_ITERATIONS (default 5); escalate to HITL on exhaustion |
| Deleting assertions to force green | Tests pass but the spec is not met | The skill requires spec-to-test traceability; gaps block convergence |
| Skipping the validation phase | Lint or type errors slip through | The loop MUST run lint and type check before the test phase every iteration |
| Treating testing as "just run the tests" | Misses the loop; one-shot runs are not compliant | Testing is a loop, not a single call; record ivtLoopIterations |
| Exiting without writing the manifest | Downstream skills cannot see the outcome | Always write the manifest entry — converged or not — before exiting |
| Reverting the worktree on escalation | Destroys diagnostic evidence | Leave the failed state; the human reviewer needs it |
Critical Rules Summary
- Detect the test framework from the worktree before iterating; never hardcode.
- Cap iterations at
MAX_ITERATIONS (default 5); escalate on exhaustion.
- Every MUST in the spec MUST map to a passing test before declaring convergence.
- Each iteration runs all three phases: Implement, Validate, Test.
- Never run on
main, master, or trunk — stop and request a feature branch.
- Record
framework, testsRun, testsPassed, testsFailed, ivtLoopConverged, ivtLoopIterations in the manifest.
- On non-convergence, exit 65 and leave the worktree untouched.
- Validate every run via
cleo check protocol --protocolType testing.
See also / References
This skill binds to the testing LOOM lifecycle stage. Governing ADRs:
LOOM coverage matrix: docs/skills/loom-coverage-matrix.md.