
Harness Engineering
- 173 installs
- 431 repo stars
- Updated July 22, 2026
- kangarooking/kangarooking-skills
Design evaluation and test harnesses that systematically exercise agents, APIs, and workflows with fixtures, graders, regressions, and reproducible pass-fail signals before release.
About
harness-engineering guides Claude to build robust test and evaluation harnesses for agents and services: curated scenarios, automated graders, baseline comparisons, and CI-friendly reporting so teams ship with measurable quality bars instead of ad-hoc manual checks.
- Fixture and scenario design
- Deterministic grading rubrics
- Regression suite orchestration
- CI integration for harness runs
- Flake detection and reporting
Harness Engineering by the numbers
- 173 all-time installs (skills.sh)
- +4 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #842 of 2,153 Testing & QA skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/kangarooking/kangarooking-skills --skill harness-engineeringAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 173 |
|---|---|
| repo stars | ★ 431 |
| Last updated | July 22, 2026 |
| Repository | kangarooking/kangarooking-skills ↗ |
What it does
Design evaluation and test harnesses that systematically exercise agents, APIs, and workflows with fixtures, graders, regressions, and reproducible pass-fail signals before release.
Files
Harness Engineering Framework
One-click initialization of a complete Harness Engineering framework in any project directory.
Based on insights from OpenAI (Codex), Anthropic (3-agent GAN architecture), and LangChain (self-verify loops), this skill sets up:
- 3-agent architecture: Planner (spec), Generator (build), Evaluator (test)
- Sprint contracts: Machine-verifiable "done" criteria before coding
- Quality hooks: Loop detection, pre-completion checklist, context injection
- Slash commands:
/plan,/build,/qa,/sprint - Golden principles: 10 non-negotiable rules enforced across all agents
When to Use
- Starting a new project and want structured AI-assisted development
- Want to set up Plan-Build-Verify-Fix workflow in current project
- User says "harness", "init harness", "setup framework", or similar
Initialization Process
Step 1: Gather Project Info
Before generating files, ask the user:
1. Project name (or detect from current directory name) 2. Tech stack (optional, e.g. "React + Node.js", "Python FastAPI", "Go microservice") 3. Project type (web app, API service, CLI tool, library, etc.)
If the user provides a description with /harness <description>, extract the info from context.
Step 2: Generate Framework Files
Execute the scaffold script:
python3 {{SKILL_PATH}}/scripts/scaffold.py --project-name "<PROJECT_NAME>" --tech-stack "<TECH_STACK>" --project-type "<PROJECT_TYPE>" --target-dir "<CURRENT_PROJECT_DIR>"This generates the following structure in the current project:
<project>/
CLAUDE.md # Project map (<80 lines)
.claude/
agents/
planner.md # Spec creation agent
generator.md # Implementation agent
evaluator.md # Testing/grading agent
doc-gardener.md # Doc freshness agent
commands/
plan.md # /plan command
build.md # /build command
qa.md # /qa command
sprint.md # /sprint command
hooks/
loop-detector.py # File edit loop detection
pre-completion-check.py # Task completion checklist
context-injector.py # Session context middleware
docs/
architecture.md # System design
golden-principles.md # Non-negotiable rules
sprint-workflow.md # Sprint process
contracts/
TEMPLATE.md # Sprint contract template
specs/ # (populated by planner)
plans/ # (populated by planner)Step 3: Configure Hooks
After generating files, merge hook configuration into the project's .claude/settings.json (or create it):
python3 {{SKILL_PATH}}/scripts/merge_settings.py --target-dir "<CURRENT_PROJECT_DIR>"This adds hook definitions without overwriting existing settings.
Step 4: Verify Installation
Confirm all files were created:
ls -la CLAUDE.md .claude/agents/ .claude/commands/ .claude/hooks/ docs/Report to the user what was created and how to start using it.
Usage After Initialization
| Command | Purpose |
|---|---|
/plan <description> | Create a feature specification from 1-4 sentences |
/build | Build the most recent spec using sprint workflow |
/qa | Run evaluator against current code |
/sprint <description> | Full Plan-Build-Verify cycle from scratch |
Key Principles
The framework enforces these rules (see docs/golden-principles.md after install):
1. Spec before code - No implementation without a written spec 2. Testable criteria - Every feature has machine-verifiable acceptance criteria 3. Self-verify first - Generator must self-check before evaluator runs 4. Loop awareness - Editing same file 5+ times triggers a stop-and-reassess 5. Contract-driven - Sprint contracts define "done" before coding begins
Architecture
For full details, read the generated docs/architecture.md after installation. Key flow:
User Prompt → Planner (spec) → Generator + Evaluator negotiate contract
→ Generator builds → Evaluator tests → Fix loop (max 3x) → Complete#!/usr/bin/env python3
"""
Harness Engineering Framework - Scaffold Generator
Generates the complete harness framework structure in a target project directory.
Handles template variable substitution and existing file detection.
"""
import argparse
import json
import os
import shutil
import sys
from pathlib import Path
def parse_args():
parser = argparse.ArgumentParser(description='Initialize Harness Engineering Framework')
parser.add_argument('--project-name', required=True, help='Name of the project')
parser.add_argument('--tech-stack', default='', help='Technology stack (e.g., "React + Node.js")')
parser.add_argument('--project-type', default='', help='Project type (e.g., "web app", "API service")')
parser.add_argument('--target-dir', required=True, help='Target project directory')
return parser.parse_args()
def get_skill_dir():
"""Get the directory where this script lives (the skill root)."""
return Path(__file__).resolve().parent.parent
def substitute(template: str, project_name: str, tech_stack: str, project_type: str) -> str:
"""Replace template variables."""
return (template
.replace('{{PROJECT_NAME}}', project_name)
.replace('{{TECH_STACK}}', tech_stack or 'Not specified')
.replace('{{PROJECT_TYPE}}', project_type or 'General')
.replace('{{DATE}}', __import__('datetime').date.today().isoformat()))
def generate_file(template_path: Path, target_path: Path, project_name: str,
tech_stack: str, project_type: str, force: bool = False):
"""Generate a single file from a template."""
if target_path.exists() and not force:
print(f" SKIP (exists): {target_path}")
return False
target_path.parent.mkdir(parents=True, exist_ok=True)
if template_path.suffix in ('.md', '.txt', '.json'):
content = template_path.read_text(encoding='utf-8')
content = substitute(content, project_name, tech_stack, project_type)
target_path.write_text(content, encoding='utf-8')
else:
# Binary or script files - copy as-is
shutil.copy2(template_path, target_path)
if template_path.suffix == '.py':
target_path.chmod(target_path.stat().st_mode | 0o755)
print(f" CREATE: {target_path}")
return True
def merge_settings(target_dir: Path):
"""Merge hook settings into existing .claude/settings.json."""
settings_path = target_dir / '.claude' / 'settings.json'
skill_dir = get_skill_dir()
template_settings = skill_dir / 'templates' / 'settings.json'
if not template_settings.exists():
print(" SKIP: No settings template found")
return
template_content = json.loads(template_settings.read_text(encoding='utf-8'))
if settings_path.exists():
existing = json.loads(settings_path.read_text(encoding='utf-8'))
# Merge hooks
if 'hooks' in template_content:
if 'hooks' not in existing:
existing['hooks'] = template_content['hooks']
else:
for hook_type in template_content['hooks']:
if hook_type not in existing['hooks']:
existing['hooks'][hook_type] = template_content['hooks'][hook_type]
else:
# Append new hooks, avoiding duplicates
existing_matchers = {h.get('matcher', '') for h in existing['hooks'][hook_type]}
for new_entry in template_content['hooks'][hook_type]:
new_matcher = new_entry.get('matcher', '')
if new_matcher not in existing_matchers:
existing['hooks'][hook_type].append(new_entry)
# Merge permissions
if 'permissions' in template_content:
if 'permissions' not in existing:
existing['permissions'] = template_content['permissions']
else:
for perm_type in ('allow', 'deny'):
if perm_type in template_content['permissions']:
if perm_type not in existing['permissions']:
existing['permissions'][perm_type] = template_content['permissions'][perm_type]
else:
existing_set = set(existing['permissions'][perm_type])
for item in template_content['permissions'][perm_type]:
if item not in existing_set:
existing['permissions'][perm_type].append(item)
settings_path.write_text(json.dumps(existing, indent=2, ensure_ascii=False), encoding='utf-8')
print(f" MERGE: {settings_path}")
else:
settings_path.parent.mkdir(parents=True, exist_ok=True)
settings_path.write_text(json.dumps(template_content, indent=2, ensure_ascii=False), encoding='utf-8')
print(f" CREATE: {settings_path}")
def main():
args = parse_args()
target_dir = Path(args.target_dir).resolve()
skill_dir = get_skill_dir()
templates_dir = skill_dir / 'templates'
if not target_dir.exists():
print(f"Error: Target directory does not exist: {target_dir}")
sys.exit(1)
print(f"\nInitializing Harness Engineering Framework in: {target_dir}")
print(f"Project: {args.project_name}")
print(f"Tech stack: {args.tech_stack or 'Not specified'}")
print(f"Project type: {args.project_type or 'General'}")
print()
# Define generation order (paths relative to templates/ and target/)
file_mappings = [
# Phase 1: Root
('CLAUDE.md', 'CLAUDE.md'),
# Phase 2: Documentation
('docs/architecture.md', 'docs/architecture.md'),
('docs/golden-principles.md', 'docs/golden-principles.md'),
('docs/sprint-workflow.md', 'docs/sprint-workflow.md'),
('docs/contracts/TEMPLATE.md', 'docs/contracts/TEMPLATE.md'),
# Phase 3: Agents
('agents/planner.md', '.claude/agents/planner.md'),
('agents/generator.md', '.claude/agents/generator.md'),
('agents/evaluator.md', '.claude/agents/evaluator.md'),
('agents/doc-gardener.md', '.claude/agents/doc-gardener.md'),
# Phase 4: Commands
('commands/plan.md', '.claude/commands/plan.md'),
('commands/build.md', '.claude/commands/build.md'),
('commands/qa.md', '.claude/commands/qa.md'),
('commands/sprint.md', '.claude/commands/sprint.md'),
# Phase 5: Hooks
('hooks/loop-detector.py', '.claude/hooks/loop-detector.py'),
('hooks/pre-completion-check.py', '.claude/hooks/pre-completion-check.py'),
('hooks/context-injector.py', '.claude/hooks/context-injector.py'),
]
created = 0
skipped = 0
for template_rel, target_rel in file_mappings:
template_path = templates_dir / template_rel
target_path = target_dir / target_rel
if not template_path.exists():
print(f" WARN: Template not found: {template_path}")
continue
if generate_file(template_path, target_path, args.project_name,
args.tech_stack, args.project_type):
created += 1
else:
skipped += 1
# Ensure empty dirs exist
for empty_dir in ('docs/specs', 'docs/plans'):
(target_dir / empty_dir).mkdir(parents=True, exist_ok=True)
# Merge settings
merge_settings(target_dir)
print(f"\nDone! Created {created} files, skipped {skipped} existing files.")
print(f"\nNext steps:")
print(f" 1. Review CLAUDE.md for project overview")
print(f" 2. Run /plan <feature-description> to create your first spec")
print(f" 3. Run /sprint <feature-description> for a full cycle")
if __name__ == '__main__':
main()
Doc Gardener Agent
You are a technical writer who keeps documentation in sync with code. You audit documentation freshness and fix or report discrepancies.
Input
No specific input required. You audit the entire docs/ directory against the current code state.
Process
1. Freshness Audit
For each file in docs/:
- Reference check: For every code path, function name, or file path mentioned in docs, verify it still exists using Glob and Grep
- Example check: If the doc contains code examples, verify they're syntactically correct and reference current APIs
- Version check: If the doc mentions specific versions or dependencies, verify they're current
2. Coverage Audit
For each source code module:
- Check if there's corresponding documentation in
docs/ - Flag significant modules without documentation
- Note: Not every file needs docs — focus on public APIs, configuration, and architectural decisions
3. Classification
Classify each issue as:
| Severity | Description | Action |
|---|---|---|
| broken | Referenced path/file no longer exists | Fix immediately |
| stale | Content doesn't reflect current behavior | Fix immediately |
| missing | New feature/module has no documentation | Report to user |
| minor | Typos, formatting, minor inaccuracies | Fix immediately |
| structural | Doc organization needs rethinking | Report to user |
4. Auto-Fix
For broken, stale, and minor issues:
- Fix directly using the Edit tool
- Update file paths, function signatures, or descriptions to match current code
- Fix typos and formatting issues
5. Report
Produce a freshness report:
## Doc Freshness Report
**Date**: <today>
**Files audited**: X
**Issues found**: Y
### Fixed
- [x] docs/architecture.md: Updated path src/users.js -> src/services/users.js
- [x] docs/api.md: Fixed endpoint /v1/auth -> /v2/auth
### Reported (needs user decision)
- [ ] docs/specs/feature-x.md: No corresponding implementation found. Spec may be outdated.
- [ ] src/services/payments.js: No documentation exists for this module.Constraints
- Only fix documentation files, never source code
- When uncertain about intent, report rather than guess
- Don't create new documentation — only update or flag missing docs
- Keep changes minimal — fix the specific issue, don't rewrite entire docs
Evaluator Agent
You are a QA lead with deep testing expertise. You test implementations against contracts and produce structured grade reports. You NEVER modify code — only test and report.
Input
- A sprint contract in
docs/contracts/<feature-name>.md - A specification in
docs/specs/<feature-name>.md - The running application or code to evaluate
Evaluation Process
1. Read Contract
Read the sprint contract to understand:
- What acceptance criteria must be met
- What verification methods are specified
- What the pass threshold is (default: 80/100)
2. Read Spec
Read the spec for context on what the feature should do. Use this to understand intent, but grade against the contract's acceptance criteria.
3. Four-Layer Testing Strategy
Execute tests in order of increasing depth:
Layer 1: Unit Level (Read code)
- Read the implementation source code
- Verify logic correctness against spec requirements
- Check error handling
- Look for edge cases that aren't handled
Layer 2: Build Level (Run build)
- Verify the code compiles/builds successfully
- Run any existing test suites
- Check for type errors, linting issues
Layer 3: Integration Level (Playwright MCP) If the feature has a UI or API:
- Use
browser_navigateto open the application - Use
browser_snapshotto inspect the page structure - Use
browser_click,browser_fill,browser_typeto interact - Test each acceptance criterion as a user would
- Use
browser_network_requeststo check API calls - Use
browser_console_messagesto check for errors
Layer 4: Visual Level (zai-mcp-server / screenshots) If the feature has visual components:
- Take a screenshot with
browser_take_screenshot - Use
analyze_imageto verify visual layout - Check for UI consistency, proper rendering, responsive behavior
4. Grade Each Criterion
For each acceptance criterion in the contract:
| Status | Meaning |
|---|---|
| PASS | Criterion fully met, evidence documented |
| FAIL | Criterion not met, specific failure details provided |
| PARTIAL | Criterion partially met, gap described |
For each FAIL, you must provide: 1. Which criterion failed (reference by number) 2. Expected behavior (from spec/contract) 3. Actual behavior (what you observed) 4. Reproduction steps (how to reproduce the failure) 5. Suggested fix (specific, actionable)
5. Calculate Score
Score = (PASS criteria / Total criteria) * 100PARTIAL counts as 0.5 PASS.
6. Produce Report
Write a structured evaluation report:
## Evaluation Report: <Feature Name>
**Date**: <today>
**Score**: X/100
**Threshold**: 80/100
**Result**: PASS / FAIL
### Criterion Results
| # | Criterion | Status | Notes |
|---|-----------|--------|-------|
| 1 | ... | PASS | ... |
| 2 | ... | FAIL | ... |
### Failures Detail
#### Criterion 2: <description>
- **Expected**: ...
- **Actual**: ...
- **Reproduction**: 1. ... 2. ... 3. ...
- **Suggested Fix**: ...
### Summary
<overall assessment>Decision Logic
- Score >= 80: Sprint passes. Report success.
- Score < 80: Sprint fails. Return failure report to Generator for fix loop.
- After 3 failed iterations: Recommend escalation to user.
Constraints
- NEVER modify code. You are an evaluator, not a builder.
- Be skeptical, not generous. Models tend to grade their own work leniently. You grade OTHER agents' work — be critical.
- Test thoroughly, not superficially. Don't just check "happy path" — probe edge cases.
- Be specific in failures. "It doesn't work" is not actionable. Provide exact reproduction steps.
- Don't move the goalposts. Grade against the contract, not against your own expectations.
Generator Agent
You are a senior engineer who implements features incrementally with self-verification discipline. You build, verify, and iterate.
Input
- A spec file in
docs/specs/<feature-name>.md - A sprint contract in
docs/contracts/<feature-name>.md
Process
1. Read Inputs
- Read the spec file to understand what to build
- Read the sprint contract to understand what "done" looks like
- Read relevant existing code (only what you need — context budget)
- Do NOT read the entire codebase
2. Implement
Implement ONE sprint's worth of work:
- Follow the contract's scope (In Scope items only)
- Follow existing code patterns and conventions
- Write clean, testable code with clear input/output boundaries
- Add tests as you go when possible
3. Self-Verify (CRITICAL)
Before reporting completion, you MUST:
1. Re-read your code: Does it do what the spec asks? 2. Build check: Does it compile/build without errors? 3. Run tests: Do existing tests still pass? Do new tests pass? 4. Check acceptance criteria: Go through each criterion in the contract. Can you verify each one? 5. Check for debug artifacts: Remove any console.log/print statements, TODO comments, or temporary code
Do NOT skip this step. Models that skip self-verification produce worse results than those that verify.
4. Sprint Report
Write a brief report (in your response, not a file) covering:
- What was implemented
- What was self-verified and how
- What needs evaluator attention
- Any deviations from the spec (and why)
5. Fix Loop
If the evaluator returns failures: 1. Read each failure carefully — understand the root cause 2. Fix the specific issue, not symptoms 3. Re-verify after each fix 4. Track iterations — if you've been fixing the same file 3+ times, reconsider your approach entirely
Loop Awareness
If you've edited the same file more than 3 times in a sprint, STOP and report:
- What you've tried
- Why it's not working
- What alternative approach you'd like to try
- Whether the spec or contract needs clarification
The loop-detector hook will also enforce this, but proactive awareness is better.
Context Budget
- Read only the spec, contract, and relevant existing code
- Do NOT read unrelated modules
- Do NOT read test files unless debugging
- If you need to understand a dependency, read its interface/API, not its implementation
Constraints
- Follow the contract scope exactly — no scope creep
- If the spec is unclear, ask for clarification rather than guessing
- Do not modify files outside the sprint scope without explicit approval
- Do not add features "just in case" — implement what the contract specifies
Planner Agent
You are a senior product engineer who writes precise, machine-verifiable specifications. You NEVER write implementation code — only specifications and plans.
Input
A brief feature description (1-4 sentences) from the user or /plan command.
Process
1. Clarify: If the description is ambiguous, ask the user questions before proceeding. Do not make assumptions about requirements.
2. Research: Read relevant existing code to understand patterns, conventions, and integration points. Use Glob and Grep to find related files. Only read what you need (context budget).
3. Spec: Write a structured specification to docs/specs/<feature-name>.md containing:
- Problem Statement: What problem does this feature solve?
- User Stories: "As a [user], I want to [action], so that [benefit]"
- Acceptance Criteria: Each must be machine-verifiable (see below)
- Component Boundaries: What modules/files are involved? What's the scope?
- Data Flow: How does data move through the system?
- Error Handling: What can go wrong and how to handle it?
- Edge Cases: At minimum 3 edge cases that must be handled
- Dependencies: External dependencies, other features, libraries needed
- Performance Constraints: Any latency, memory, or throughput requirements
4. Plan: Create sprint tasks using TaskCreate with clear dependencies between tasks.
Acceptance Criteria Rules
Every acceptance criterion must:
- Be testable by a machine — no subjective criteria like "looks good" or "feels right"
- Have a clear pass/fail state — no partial credit
- Specify the verification method: unit, playwright, devtools, visual, build, or manual
- Be specific — "User can submit a form" not "Form works"
Good examples:
- "POST /api/users returns 201 with valid user object when fields are valid"
- "Clicking 'Submit' button navigates to /success page within 2 seconds"
- "Login form shows error message when password is incorrect"
Bad examples:
- "Form works correctly"
- "UI looks professional"
- "Performance is good"
Constraints
- NEVER write implementation code. You are a planner, not a builder.
- NEVER skip acceptance criteria. They are the contract between Generator and Evaluator.
- NEVER assume context. If you're unsure about something, ask the user.
- Always identify at least 3 edge cases per feature.
- Keep the spec focused. One feature per spec file. Complex features should be decomposed into multiple specs.
Output
Write the spec file to docs/specs/<feature-name>.md. Create tasks via TaskCreate for the sprint plan. Report to the user what was created and ask for approval before proceeding to build.
{{PROJECT_NAME}}
Harness Engineering Framework activated. Plan-Build-Verify workflow enabled.
Quick Reference
| Command | Purpose |
|---|---|
/plan <description> | Create a feature spec from 1-4 sentences |
/build | Build the latest spec via sprint workflow |
/qa | Run evaluator against current code |
/sprint <description> | Full Plan-Build-Verify cycle |
Architecture
See docs/architecture.md for system design.
Golden Principles
See docs/golden-principles.md — these are non-negotiable.
Agents
| Agent | Role | Trigger |
|---|---|---|
| planner | Expand brief prompts into specs | /plan |
| generator | Implement features in sprints | /build |
| evaluator | Test and grade implementations | /qa |
| doc-gardener | Maintain documentation freshness | Sprint complete |
Sprint Workflow
1. Plan: Planner creates spec in docs/specs/ 2. Contract: Generator + Evaluator agree on "done" criteria 3. Build: Generator implements one sprint 4. Verify: Evaluator tests against contract (threshold: 80/100) 5. Fix: If score < 80, Generator fixes (max 3 iterations) 6. Complete: Update docs, run doc-gardener
See docs/sprint-workflow.md for full process.
Project Structure
docs/
architecture.md -- System design
golden-principles.md -- Core rules
sprint-workflow.md -- Sprint process
contracts/ -- Sprint "done" definitions
specs/ -- Feature specifications
plans/ -- Implementation plans
.claude/
agents/ -- Agent definitions
commands/ -- Slash commands
hooks/ -- Automated guardsHooks Active
- Loop detection: Blocks after 5 edits to same file
- Pre-completion checklist: Verifies before marking done
- Context injection: Adds environment info at session start
Tech Stack
{{TECH_STACK}}
Build Feature
Implement a feature using the Generator-Evaluator cycle.
Target: $ARGUMENTS (if empty, use the most recent spec in docs/specs/)
Process
1. Read Spec
Read the target specification from docs/specs/. If no argument provided, find the most recently modified spec file.
2. Sprint Contract
Check if a contract exists in docs/contracts/ for this feature:
- If exists: Read it and proceed to build
- If not exists: Create a contract by negotiating between Generator and Evaluator perspectives:
- What specific items are in scope for this sprint?
- What acceptance criteria will be tested?
- What verification methods will be used?
3. Build Cycle
Repeat up to 3 times:
1. Build: Invoke the generator agent to implement the sprint 2. Self-verify: Generator verifies its own work 3. Evaluate: Invoke the evaluator agent to test against the contract 4. Grade: Check if score >= 80/100
- If PASS: Sprint complete
- If FAIL: Feed failures back to generator for fixing
4. Max Iterations
If 3 iterations fail:
- Pause and report accumulated failures to user
- Suggest: simplify scope, break into smaller sprints, or manual intervention
- Do NOT continue iterating
5. Complete
If sprint passes:
- Update the contract's sprint log
- Ask user if they want to run the doc-gardener agent for freshness check
Plan Feature
Create a feature specification from: $ARGUMENTS
Process
1. Invoke the planner agent with the description above 2. The planner will:
- Ask clarifying questions if the description is ambiguous
- Research existing codebase patterns
- Produce a structured spec in
docs/specs/<feature-name>.md - Create sprint tasks with dependencies via TaskCreate
3. Present the spec to the user for review 4. If approved, the user can proceed with /build or /sprint
Do NOT begin implementation. This command only produces a plan and specification.
Spec Structure
The spec will include:
- Problem statement
- User stories
- Machine-verifiable acceptance criteria
- Component boundaries
- Data flow
- Error handling
- Edge cases (minimum 3)
- Dependencies
Quality Assurance
Run a full evaluation cycle against the current implementation.
Target: $ARGUMENTS (if empty, evaluate against all contracts in docs/contracts/)
Process
1. Select Target
- If argument provided: Evaluate against the specified contract/spec
- If no argument: List all contracts in
docs/contracts/and ask user to choose, or evaluate the most recent one
2. Invoke Evaluator
Launch the evaluator agent, which will: 1. Read the sprint contract criteria 2. Test with Playwright MCP (E2E browser testing) 3. Inspect with Chrome DevTools (console errors, network, performance) 4. Visual check with screenshots (if UI is involved) 5. Read and verify source code logic
3. Grade Report
The evaluator produces a structured report with:
- Per-criterion pass/fail status
- Overall score (0-100)
- Specific failure details with reproduction steps and suggested fixes
4. Next Steps
If score < 80:
- Offer to invoke
/buildto enter the fix loop - Show the specific failures that need addressing
If score >= 80:
- Report success
- Offer to run doc-gardener for freshness check
Full Sprint
Execute the complete Plan -> Build -> Verify cycle for: $ARGUMENTS
Sprint Phases
Phase 1: Plan
Invoke the planner agent with the description. The planner will:
- Clarify ambiguities (ask the user questions first)
- Research the codebase
- Produce a spec in
docs/specs/<feature-name>.md - Create sprint tasks
Gate: User must approve the spec before proceeding.
Phase 2: Contract Negotiation
Generate a sprint contract in docs/contracts/<feature-name>.md:
- Extract acceptance criteria from the spec
- Define scope (In Scope / Out of Scope)
- Specify verification methods for each criterion
- Set threshold: 80/100
- Set max iterations: 3
Present the contract to the user for approval.
Gate: User must approve the contract before proceeding.
Phase 3: Build
Invoke the generator agent:
- Reads spec + contract
- Implements one sprint
- Self-verifies (re-read code, check build, verify against criteria)
Phase 4: Verify
Invoke the evaluator agent:
- Reads contract
- Tests via four-layer strategy (unit, integration, visual, console)
- Grades each acceptance criterion
- Calculates score
Phase 5: Fix Loop
If score < threshold (max 3 iterations): 1. Feed evaluator's failure report to generator 2. Generator analyzes root cause and fixes 3. Generator self-verifies fixes 4. Return to Phase 4
If 3 iterations fail:
- STOP. Do not continue.
- Report all accumulated failures to user
- Suggest: simplify scope, break into smaller sprints, manual intervention
- The context is likely polluted — a fresh session may help
Phase 6: Complete
If sprint passes: 1. Update contract sprint log with final results 2. Mark tasks as completed 3. Invoke doc-gardener agent for freshness check 4. Produce sprint summary:
- Feature built
- Final evaluator score
- Iterations used
- Remaining issues or tech debt
- Time to next sprint recommendation
Architecture
System design for the Harness Engineering Framework.
---
System Diagram
User
|
v
[/sprint <desc>] -----> [Planner Agent]
|
v
docs/specs/<name>.md
|
v
[Contract Negotiation]
/ \
[Generator] <-------> [Evaluator]
| |
v v
Source Code Test Results + Grade
| |
+-------- fix --------+
|
v
[Score >= 80?] --No--> Fix Loop (max 3x)
|
Yes
v
[Doc Gardener] --> docs/ updated
|
v
Sprint CompleteAgent Relationships
Planner ---> produces spec ---> Generator reads spec
Generator --> proposes contract --> Evaluator reviews
Evaluator --> grades output --> Generator fixes (if needed)
Generator --> completes sprint --> Doc Gardener auditsHook Injection Points
UserPromptSubmit --> [context-injector.py] --> adds project state to context
|
v
User prompt processed by Claude
|
v
PreToolUse (Edit/Write) --> [loop-detector.py] --> blocks if file edited 5x
|
v
Tool execution
|
v
PostToolUse (TaskUpdate) --> [pre-completion-check.py] --> reminds to verifyComponent Inventory
| Component | File | Role |
|---|---|---|
| Project Map | CLAUDE.md | Entry point, progressive disclosure hub |
| Planner Agent | .claude/agents/planner.md | Expands prompts into specs |
| Generator Agent | .claude/agents/generator.md | Implements features |
| Evaluator Agent | .claude/agents/evaluator.md | Tests and grades |
| Doc Gardener | .claude/agents/doc-gardener.md | Maintains doc freshness |
| /plan command | .claude/commands/plan.md | Triggers planner |
| /build command | .claude/commands/build.md | Triggers generator+evaluator |
| /qa command | .claude/commands/qa.md | Triggers standalone evaluation |
| /sprint command | .claude/commands/sprint.md | Triggers full cycle |
| Loop Detector | .claude/hooks/loop-detector.py | Prevents edit doom loops |
| Pre-Completion | .claude/hooks/pre-completion-check.py | Quality gate before done |
| Context Injector | .claude/hooks/context-injector.py | Session context awareness |
| Golden Principles | docs/golden-principles.md | Non-negotiable rules |
| Sprint Workflow | docs/sprint-workflow.md | Process documentation |
| Contract Template | docs/contracts/TEMPLATE.md | Sprint contract structure |
MCP Tool Mapping
| MCP Tool | Used By | Purpose |
|---|---|---|
| Playwright | Evaluator | E2E testing, browser automation |
| Chrome DevTools | Evaluator | Console errors, network, performance |
| zai-mcp-server | Evaluator | Visual verification of UI output |
| web_reader | Planner | Fetch external documentation |
Sprint Lifecycle
State: IDLE
|
[/plan or /sprint]
v
State: PLANNING (Planner produces spec)
|
[spec approved]
v
State: CONTRACTING (Generator + Evaluator negotiate)
|
[contract agreed]
v
State: BUILDING (Generator implements)
|
[self-verify passed]
v
State: VERIFYING (Evaluator tests)
|
[score >= 80]
v
State: COMPLETE (docs updated, sprint log recorded)
|
[score < 80 & iterations < 3]
v
State: FIXING (Generator addresses failures)
|
[return to VERIFYING]Extension Guide
Adding a New Agent
1. Create .claude/agents/<name>.md with frontmatter (name, description, tools) 2. Define the agent's role, constraints, and output format 3. Reference the agent in CLAUDE.md's agent table 4. Create a corresponding command in .claude/commands/<name>.md if needed
Adding a New Hook
1. Create the hook script in .claude/hooks/<name>.py 2. Register it in .claude/settings.json under the appropriate hook type 3. The hook must output valid JSON: {} to allow, {"decision": "block", "reason": "..."} to block, or {"systemMessage": "..."} to inject context
Adding a New Command
1. Create .claude/commands/<name>.md with frontmatter (description, argument-hint) 2. Define the workflow steps in the body 3. Reference existing agents via the Agent tool
Sprint Contract: {{FEATURE_NAME}}
Meta
- Spec:
docs/specs/{{FEATURE_NAME}}.md - Created: {{DATE}}
- Status: draft
- Max Iterations: 3
- Pass Threshold: 80/100
Scope
In Scope
-
Out of Scope
-
Acceptance Criteria
Each criterion must be machine-verifiable.
| # | Criterion | Verification Method | Status |
|---|---|---|---|
| 1 | [ ] | ||
| 2 | [ ] | ||
| 3 | [ ] | ||
| 4 | [ ] | ||
| 5 | [ ] |
Verification Methods
- unit: Read code and verify logic
- playwright: E2E test via Playwright MCP
- devtools: Inspect via Chrome DevTools (console errors, network)
- visual: Screenshot comparison via zai-mcp-server
- build: Compile/build succeeds
- manual: User must manually verify
Sprint Log
| Iteration | Generator Output | Evaluator Score | Issues |
|---|---|---|---|
Sign-off
- Generator: [ ] Agrees to criteria and scope
- Evaluator: [ ] Agrees to verification methods
- User: [ ] Approves scope and criteria
Golden Principles
Non-negotiable rules enforced across all agents and workflows. These encode the core insights from OpenAI (Codex), Anthropic (3-agent architecture), and LangChain (self-verify loops).
---
1. Spec Before Code
No implementation without a written specification in docs/specs/.
Why: Models lose coherence without a clear target. A spec anchors the Generator and gives the Evaluator something concrete to test against. (OpenAI)
How to apply: The Planner agent always writes a spec before any code is written. If you're tempted to "just start coding," write the spec first.
2. Testable Acceptance Criteria
Every feature must have machine-verifiable pass/fail criteria.
Why: Subjective criteria like "looks good" lead to the evaluator grading generously. Machine-verifiable criteria force precision. (Anthropic)
How to apply: Each acceptance criterion in a sprint contract must specify a verification method: unit test, Playwright E2E, console check, visual comparison, or build success.
3. One Sprint at a Time
The Generator implements one feature, then the Evaluator tests before moving on.
Why: Long-running tasks cause context drift and "context anxiety" (premature wrap-up). Decomposing into sprints keeps the agent coherent. (Anthropic)
How to apply: Each sprint contract defines a bounded scope. The Generator completes one sprint, the Evaluator grades it, then the next sprint begins.
4. Self-Verify First
The Generator must self-verify before the Evaluator runs.
Why: Models are biased toward their first plausible solution. Forcing a self-check catches obvious issues before wasting evaluator cycles. (LangChain)
How to apply: After implementing, the Generator must: (1) re-read the code, (2) verify it compiles/builds, (3) check against the spec, (4) run tests if available.
5. Loop Awareness
If a file is edited 5+ times without progress, stop and reassess.
Why: Models can enter "doom loops" — making small variations to the same broken approach. The loop detection hook prevents this waste. (LangChain)
How to apply: The loop-detector hook blocks edits after 5 attempts. When blocked, reconsider the approach entirely rather than tweaking the same code.
6. Context Budget
Agents read only what they need, not the entire codebase.
Why: Context is a scarce resource. A massive instruction file挤掉任务空间, causing the agent to miss key constraints or optimize for the wrong things. (OpenAI)
How to apply: The Planner reads only relevant existing code. The Generator reads only the spec and contract. The Evaluator reads only the contract and relevant source files.
7. Progressive Disclosure
Documentation follows a 3-level pattern: summary → details → references.
Why: This is the "map, not encyclopedia" principle. A small entry point with pointers to deeper sources keeps context lean while preserving access to depth. (OpenAI)
How to apply: CLAUDE.md is the map (<80 lines). docs/ contains the details. Specs and contracts contain task-specific depth.
8. Doc Freshness
After each sprint, verify documentation matches code.
Why: Stale docs are worse than no docs — they mislead agents into implementing against wrong assumptions. (OpenAI "doc gardening")
How to apply: The doc-gardener agent runs after sprint completion. It checks that code paths referenced in docs still exist, examples still compile, and new features have documentation.
9. Fail Loudly
Hooks and agents report problems explicitly rather than silently degrading.
Why: Silent failures accumulate into technical debt that's expensive to fix later. Better to surface issues immediately. (OpenAI)
How to apply: Hooks block with clear reasons. Agents produce structured failure reports. No silent fallbacks or "it probably works" assumptions.
10. Contract-Driven
Sprint contracts define "done" before implementation begins.
Why: Without agreed-upon completion criteria, the Generator and Evaluator have different definitions of success. Contracts align expectations before code is written. (Anthropic)
How to apply: Before each sprint, the Generator proposes what it will build and how success is verified. The Evaluator reviews the proposal. Both agree before any code is written.
Sprint Workflow
Detailed description of the Plan-Build-Verify-Fix-Complete cycle.
---
Overview
Each feature goes through a 6-phase sprint cycle. The cycle enforces the golden principles: spec before code, testable criteria, self-verify first, and contract-driven development.
Phase 1: Plan
Agent: Planner Input: 1-4 sentence description from user Output: docs/specs/<feature-name>.md
The Planner agent: 1. Reads the brief description 2. Asks clarifying questions if ambiguous 3. Researches existing codebase patterns 4. Produces a structured spec containing:
- Problem statement
- User stories
- Acceptance criteria (each must be machine-verifiable)
- Component boundaries
- Data flow
- Error handling
- Edge cases (minimum 3)
- Dependencies
Transition criteria: Spec file written to docs/specs/ and reviewed by user.
Phase 2: Contract Negotiation
Agents: Generator + Evaluator Output: docs/contracts/<feature-name>.md
Before any code is written: 1. The Generator proposes what it will build and how success will be verified 2. The Evaluator reviews the proposal to ensure:
- Criteria are truly testable (not subjective)
- Scope is bounded (not open-ended)
- Verification methods are specified for each criterion
3. Both iterate until agreement 4. User approves the final contract
Transition criteria: Contract agreed by Generator, Evaluator, and User.
Phase 3: Build
Agent: Generator Input: Spec + Contract Output: Source code changes + sprint report
The Generator: 1. Reads only the spec file and relevant existing code (context budget) 2. Implements ONE sprint (bounded by contract scope) 3. After implementing, runs self-verification:
- Re-read the code
- Verify it compiles/builds
- Check against acceptance criteria
- Run existing tests
4. Writes a brief sprint report (what was done, what was verified, what needs attention)
Transition criteria: Generator declares sprint complete with self-verification results.
Phase 4: Verify
Agent: Evaluator Input: Contract + Source code + Running application Output: Grade report (0-100 scale)
The Evaluator uses a four-layer testing strategy: 1. Unit level: Read source code, verify logic correctness 2. Integration level: Use Playwright MCP for E2E testing 3. Visual level: Use zai-mcp-server for UI screenshot comparison (if applicable) 4. Console level: Use Chrome DevTools for errors, warnings, network issues
For each acceptance criterion:
- PASS: Criterion met, evidence documented
- FAIL: Criterion not met, specific failure details, reproduction steps, suggested fix
Scoring: Score = (passed criteria / total criteria) 100 Threshold*: 80/100
Transition criteria:
- Score >= 80: Proceed to Phase 6 (Complete)
- Score < 80: Proceed to Phase 5 (Fix)
Phase 5: Fix Loop
Agent: Generator (with Evaluator feedback) Max iterations: 3
1. Evaluator's failure report is fed to the Generator 2. Generator analyzes each failure, identifies root cause 3. Generator implements fixes 4. Generator self-verifies fixes 5. Return to Phase 4 (Verify)
If 3 iterations fail:
- Pause the sprint
- Report accumulated failures to user
- Suggest: (a) simplify the scope, (b) break into smaller sprints, (c) manual intervention
- Do NOT continue iterating — context is likely polluted
Context reset guidance: If the Generator has been working for a long time and shows signs of context anxiety (premature wrap-up, repetitive mistakes), consider:
- Summarizing progress to a file
- Starting a fresh session
- Having the new session read the spec, contract, and sprint log from files
Phase 6: Complete
Agent: Doc Gardener (automatic) Output: Updated documentation
1. Doc Gardener audits documentation freshness:
- Code paths in docs still exist
- New code has corresponding docs
- Examples are current
2. Sprint log is recorded in the contract file 3. Tasks are marked complete 4. Sprint summary is produced:
- What was built
- Final evaluator score
- Iterations used
- Any remaining issues or tech debt noted
Context Reset vs Compaction
| Strategy | When to Use | Pros | Cons |
|---|---|---|---|
| Compaction | Normal operation, short sprints | Preserves continuity | Doesn't eliminate context anxiety |
| Context Reset | Long sessions, 3+ fix iterations, context anxiety | Clean slate, fresh attention | Requires handoff artifact to be thorough |
Handoff artifact (written to file before reset):
- Current sprint contract status
- What's been implemented
- What's still failing
- Next steps
- Key decisions made
The handoff artifact IS the spec + contract + sprint log files — this is why we persist them to disk rather than keeping them only in context.
#!/usr/bin/env python3
"""
Context Injector Hook for Harness Engineering Framework.
UserPromptSubmit hook that injects project state context at the start of each session.
This implements the "Context Budget" and "Environment Context Injection" principles
from LangChain's research: agents perform better when they know about their environment,
current state, and active tasks.
Hook output format:
- {"systemMessage": "..."} to inject context without blocking
"""
import json
import os
import subprocess
import sys
from pathlib import Path
def get_git_info():
"""Get current git branch and recent commits."""
info = {}
try:
branch = subprocess.check_output(
['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
stderr=subprocess.DEVNULL, timeout=5
).decode().strip()
info['branch'] = branch
except (subprocess.CalledProcessError, FileNotFoundError):
info['branch'] = 'unknown (not a git repo)'
try:
recent = subprocess.check_output(
['git', 'log', '--oneline', '-5'],
stderr=subprocess.DEVNULL, timeout=5
).decode().strip()
info['recent_commits'] = recent
except (subprocess.CalledProcessError, FileNotFoundError):
info['recent_commits'] = ''
return info
def get_project_state():
"""Scan for project state indicators."""
state = {}
cwd = Path.cwd()
# Check for active specs
specs_dir = cwd / 'docs' / 'specs'
if specs_dir.exists():
specs = list(specs_dir.glob('*.md'))
if specs:
state['active_specs'] = [s.name for s in sorted(specs, key=lambda x: x.stat().st_mtime, reverse=True)[:5]]
# Check for active contracts
contracts_dir = cwd / 'docs' / 'contracts'
if contracts_dir.exists():
contracts = [c for c in contracts_dir.glob('*.md') if c.name != 'TEMPLATE.md']
if contracts:
state['active_contracts'] = [c.name for c in sorted(contracts, key=lambda x: x.stat().st_mtime, reverse=True)[:5]]
# Check for active plans
plans_dir = cwd / 'docs' / 'plans'
if plans_dir.exists():
plans = list(plans_dir.glob('*.md'))
if plans:
state['active_plans'] = [p.name for p in sorted(plans, key=lambda x: x.stat().st_mtime, reverse=True)[:5]]
return state
def main():
parts = []
# Git info
git_info = get_git_info()
if git_info.get('branch'):
parts.append(f"Git branch: {git_info['branch']}")
if git_info.get('recent_commits'):
parts.append(f"Recent commits:\n{git_info['recent_commits']}")
# Project state
state = get_project_state()
if state.get('active_specs'):
parts.append(f"Active specs: {', '.join(state['active_specs'])}")
if state.get('active_contracts'):
parts.append(f"Active contracts: {', '.join(state['active_contracts'])}")
if state.get('active_plans'):
parts.append(f"Active plans: {', '.join(state['active_plans'])}")
if not parts:
print(json.dumps({}))
return
message = (
"Project context:\n"
+ "\n".join(parts)
+ "\n\nUse this context to understand the current state. "
+ "Read relevant specs/contracts/plans as needed (context budget: only read what you need)."
)
print(json.dumps({"systemMessage": message}))
if __name__ == '__main__':
main()
#!/usr/bin/env python3
"""
Loop Detection Hook for Harness Engineering Framework.
PreToolUse hook that tracks per-file edit counts.
Blocks edits to the same file after N attempts (default: 5).
This implements the "Loop Awareness" golden principle from LangChain's research:
models can enter "doom loops" — making small variations to the same broken approach.
Tracking edit counts per file prevents this waste.
Hook output format:
- {} to allow the tool call
- {"decision": "block", "reason": "..."} to block
"""
import json
import sys
import time
from pathlib import Path
def get_state_path():
"""Get the path to the edit counts state file."""
# Store in .claude/hooks/ relative to current project
return Path.cwd() / '.claude' / 'hooks' / '.edit-counts.json'
def load_state(state_path):
"""Load edit counts from state file."""
if state_path.exists():
try:
return json.loads(state_path.read_text())
except (json.JSONDecodeError, ValueError):
return {}
return {}
def save_state(state_path, state):
"""Save edit counts to state file."""
state_path.parent.mkdir(parents=True, exist_ok=True)
state_path.write_text(json.dumps(state, indent=2))
def is_stale(state):
"""Check if the state is from a previous session (>24h old)."""
if '_timestamp' not in state:
return True
age = time.time() - state.get('_timestamp', 0)
return age > 86400 # 24 hours
def main():
MAX_EDITS = int(json.loads(Path(__file__).read_text().split('MAX_EDITS = ')[1].split('\n')[0]) if 'MAX_EDITS' in Path(__file__).read_text() else 5)
MAX_EDITS = 5 # Default threshold
# Read hook input from stdin
try:
input_data = json.loads(sys.stdin.read())
except (json.JSONDecodeError, ValueError):
# If we can't parse input, allow the edit
print(json.dumps({}))
return
# Extract file path from tool input
tool_input = input_data.get('tool_input', {})
file_path = tool_input.get('file_path', '') or tool_input.get('path', '')
if not file_path:
# No file path detected, allow
print(json.dumps({}))
return
state_path = get_state_path()
state = load_state(state_path)
# Reset if state is stale (new session)
if is_stale(state):
state = {'_timestamp': time.time()}
# Normalize file path
try:
normalized = str(Path(file_path).resolve().relative_to(Path.cwd()))
except ValueError:
normalized = file_path
# Increment edit count
current_count = state.get(normalized, 0) + 1
state[normalized] = current_count
if current_count >= MAX_EDITS:
save_state(state_path, state)
reason = (
f"Loop detected: {normalized} has been edited {current_count} times. "
f"This likely indicates a doom loop. "
f"Consider: (1) Reassessing your approach entirely, "
f"(2) Breaking the problem into smaller steps, "
f"(3) Asking the user for guidance."
)
print(json.dumps({"decision": "block", "reason": reason}))
else:
save_state(state_path, state)
# Add a gentle reminder when approaching the limit
if current_count >= MAX_EDITS - 2:
remaining = MAX_EDITS - current_count
print(json.dumps({
"systemMessage": (
f"Warning: {normalized} has been edited {current_count} times. "
f"{remaining} edits remaining before loop detection triggers. "
f"Consider whether your current approach is working."
)
}))
else:
print(json.dumps({}))
if __name__ == '__main__':
main()
#!/usr/bin/env python3
"""
Pre-Completion Checklist Hook for Harness Engineering Framework.
PostToolUse hook that injects a verification reminder when a task is marked complete.
This implements the "Self-Verify First" golden principle from LangChain's research:
models are biased toward their first plausible solution. Forcing a verification
checklist before marking work done catches issues that would otherwise slip through.
Hook output format:
- {"systemMessage": "..."} to inject context without blocking
"""
import json
import sys
CHECKLIST = [
"1. Code compiles/builds without errors?",
"2. All tests pass (both existing and new)?",
"3. All acceptance criteria from the contract are met?",
"4. No debug artifacts left (console.log, TODO, temporary code)?",
"5. Documentation updated if behavior changed?",
]
def main():
# Read hook input from stdin
try:
input_data = json.loads(sys.stdin.read())
except (json.JSONDecodeError, ValueError):
print(json.dumps({}))
return
# Check if this is a TaskUpdate with status=completed
tool_name = input_data.get('tool_name', '')
tool_input = input_data.get('tool_input', {})
if tool_name != 'TaskUpdate':
print(json.dumps({}))
return
status = tool_input.get('status', '')
if status != 'completed':
print(json.dumps({}))
return
# Inject pre-completion checklist
message = (
"Pre-completion checklist — verify ALL items before confirming done:\n"
+ "\n".join(CHECKLIST)
+ "\n\nIf any item fails, fix it before marking the task complete."
)
print(json.dumps({"systemMessage": message}))
if __name__ == '__main__':
main()
{
"permissions": {
"allow": [
"Bash(python3:*)",
"Bash(npm:*)",
"Bash(npx:*)",
"Bash(node:*)",
"Bash(git:*)",
"Bash(ls:*)",
"Bash(mkdir:*)",
"Bash(mv:*)",
"Bash(cp:*)",
"Bash(wc:*)",
"Bash(diff:*)"
],
"deny": [
"Bash(rm -rf:*)"
]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{
"type": "command",
"command": "python3 .claude/hooks/loop-detector.py",
"timeout": 5
}
]
}
],
"PostToolUse": [
{
"matcher": "TaskUpdate",
"hooks": [
{
"type": "command",
"command": "python3 .claude/hooks/pre-completion-check.py",
"timeout": 5
}
]
}
],
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "python3 .claude/hooks/context-injector.py",
"timeout": 5
}
]
}
]
}
}