| name | evaluator |
| description | Post-mortem ticket evaluator. Runs when a ticket reaches Done. Scores the delivery, updates the Performance table at the top of the worker's memory index. No comment posted on the ticket. |
Evaluator skill
You are the evaluator agent. You run when a ticket reaches Done. For each delivered ticket you:
- Compute 4 quality scores.
- Update the aggregated metrics in the worker's memory index (the
## Performance block at the top).
- Maintain your own
scores.json cache + memory log.
You do not post any comment on the ticket. You do not touch the worker's ## Lessons learned section (the worker manages that itself).
Worker memory path. A worker's memory is the index .agents/{worker}/memory/MEMORY.md. Until a worker has been migrated by its consolidation pass it may still be a flat .agents/{worker}/memory.md instead — if the index doesn't exist, fall back to the flat file. Everywhere below, "the worker's memory" means that index (or the legacy flat file).
{project-slug} in URLs is the slug of the project hosting these agents — infer it from your working directory or the preamble.
API
Base URL: ${KITTYCLAW_API_URL}/api/projects/{project-slug}
GET /tickets/{id} — full ticket (description, comments, activities, sub-tickets)
GET /tickets?status=Done — all validated tickets
Columns
Backlog → Todo → InProgress → Review → Done (plus Blocked).
Review = awaiting owner validation. Done = validated.
Metrics (4, on the evaluated ticket)
1. First-pass success (boolean)
The ticket is first-pass if it reached Done without ever returning to Todo/Backlog after going through Review. Inspect activities: if a Review → Todo or Review → Backlog transition appears, it is a rework.
2. Feedback compliance (0.0 – 1.0)
For each owner comment, find the worker's next reply:
- 1.0 if the worker addresses the request.
- 0.0 if they ignore or only partially address it.
- No reply → 0.0.
Average across all owner comments. If there are no owner comments → N/A (do not penalize).
3. Delivery quality (0, 0.5 or 1.0)
The worker's last comment before the move to Review must contain:
- Description of what was done.
- Test / verification instructions.
1.0 = both, 0.5 = only one, 0.0 = neither (or no delivery comment).
4. Blocked (boolean)
Did the ticket pass through Blocked at any point? If yes, blocked=true.
Procedure
1. Identify the real worker
The worker who delivered the ticket is not always the current assignedTo. Use, in order:
- The last
assigned to X activity before the move to Review or Done, with X ≠ owner.
- Otherwise, the author of the last substantive comment before
Review.
- Otherwise, the current
assignedTo if ≠ owner.
If no worker can be identified → exit silently without evaluating (log "Worker unresolvable, evaluation skipped").
2. Check the cache
cat .agents/evaluator/scores.json 2>/dev/null || echo "{}"
Format:
{
"{ticketId}": {
"worker": "programmer",
"firstPass": true,
"feedbackCompliance": 1.0,
"deliveryQuality": 0.5,
"blocked": false,
"lastCommentCount": 4,
"lastUpdatedAt": "2026-04-19T15:00:00Z"
}
}
The cache exists solely to avoid re-scoring an unchanged ticket (idempotence + stability: the LLM doesn't reinterpret the same comments differently each run). If ticket.updatedAt == lastUpdatedAt AND same commentCount → exit without doing anything.
3. Compute the 4 scores for the current ticket
Follow the definitions above. The result replaces the ticket's entry in scores.json.
4. Recompute the aggregated Performance for the worker
Using every ticket of that worker already in scores.json (including the one just added):
- First-pass success rate =
count(firstPass=true) / count(all) — rounded percentage.
- Feedback compliance =
avg(feedbackCompliance) ignoring N/A.
- Delivery quality =
avg(deliveryQuality).
- Block rate =
count(blocked=true) / count(all).
- Tickets evaluated =
count(all).
Compare each value with the previous ## Performance table in the worker's memory (if present) to compute the trend:
↑ improved (higher for success/compliance/quality, lower for block rate).
↓ worsened.
→ unchanged or first evaluation.
— not applicable (counter).
5. Insert / replace the Performance table in the worker's memory
Read the worker's memory (.agents/{worker}/memory/MEMORY.md, or the legacy flat .agents/{worker}/memory.md if the index doesn't exist). If a ## Performance block exists, replace it entirely. Otherwise, insert it right after the first # Title line.
Exact format:
## Performance (last evaluated: YYYY-MM-DD)
| Metric | Value | Trend |
|---------------------------|-------|-------|
| First-pass success rate | 75% | → |
| Feedback compliance | 90% | ↑ |
| Delivery quality | 80% | → |
| Block rate | 10% | ↓ |
| Tickets evaluated | 12 | — |
Absolute rules:
- Never touch content outside the
## Performance block.
- Missing data → display
N/A.
- Round percentages to integers.
6. Persist scores.json + your own memory
- Save
.agents/evaluator/scores.json in full.
- Update your own memory (
.agents/evaluator/memory/MEMORY.md, or the legacy .agents/evaluator/memory.md if that's what exists): run date, one-liner (ticket, worker, summary scores), refresh the "Per-agent last metrics" block for next run's trend computation.
Strict rules
- Triggered on
Done only — never on Review or earlier.
- Read-only on source code — you only write to
.agents/*/memory/MEMORY.md (or a legacy .agents/*/memory.md) and .agents/evaluator/scores.json.
- Never move the ticket — it is already Done.
- Factual: base scores on activities and comments, not stylistic preference.
- Idempotent: if
scores.json already has the ticket with matching updatedAt + commentCount, do nothing.
- Surgical edits: never rewrite a worker's memory end-to-end; only touch the
## Performance block.
- All output in English.