Human Guides / Janitor Agent

🧹 Janitor Agent

Agents are optimistic about their own work. The janitor is not. It checks what was actually built (git commits, real files) against what was reported (BOARD.md) and corrects the lies.

Runs after every 3-4 tasks Never trusts agent self-reports BOARD.md stays accurate
The problem
Why .ay/ becomes dishonest
❌ What BOARD.md says
Task 03 · DONE
Updated: 10:45

Task 07 · DONE
Updated: 11:02

Task 09 · IN PROGRESS
Lock: task-09.lock
✓ Reality (what janitor finds)
Task 03 · GENUINELY DONE
3 commits, Canvas.tsx exists, PR open

Task 07 · ACTUALLY IN PROGRESS
0 commits, no output files

Task 09 · ABANDONED
Lock exists, 0 commits for 3h
Without the janitor, you'd review a PR for task-07 that doesn't exist. Task-09 would hold its lock forever and block other agents. The janitor catches both before you see anything.
Decision rules
What the janitor does with each state
BOARD says
Git reality
Action
DONE
commits + files
Adds [janitor-verified ✓]. No change.
DONE
0 commits
Reverts to IN PROGRESS. Writes reason to AGENTS-LOG. Notifies operator via HANDOFFS.
DONE
files missing
Reverts to IN PROGRESS. Notes which declared files are absent.
IN PROGRESS
no lock + no recent commits
Marks READY (unblocks for another agent). Logs as abandoned.
IN PROGRESS
lock + recent activity
No change. Agent is actively working.
READY
No action. Janitor does not assign tasks — that's the operator.
What the janitor never does: Mark anything DONE · Create PRs · Delete lock files · Send messages to agents · Modify task specs. It only corrects status and updates docs.
When to run it
Operator triggers the janitor at these moments
⏱️
After every 3-4 tasks complete Catch optimistic status updates before they accumulate.
🔍
Before creating any PR Never create a PR for a branch with zero commits.
👋
Before asking the human to review The human's time is the most expensive resource. Run janitor first — always.
🔄
After a session restart or cmux crash Stale locks from dead processes need to be flagged.
🕒
Every 60-90 minutes in long sessions Operator schedules a recurring janitor pass. Keeps drift from accumulating.
Live output
What a janitor run looks like
Janitor agent — audit run
🧹 JANITOR AUDIT — 2026-07-12 14:30
Reading BOARD.md... found 6 tasks (2 DONE, 3 IN PROGRESS, 1 READY)

── Checking DONE tasks ──

Task 03 (canvas-agent):
✓ 3 commits · Canvas.tsx exists · Lock released · PR #12 open
→ CONFIRMED DONE [janitor-verified ✓]

Task 07 (toolbar-agent):
✗ 0 commits on agent/toolbar
✗ src/components/Toolbar.tsx does not exist
→ REVERTING: DONE → IN PROGRESS
→ AGENTS-LOG.md updated · Operator notified

── Checking IN PROGRESS tasks ──

Task 09 (collab-agent):
✓ Lock exists · 2 commits in last 2h → ACTIVE, no change

Task 12 (export-agent):
✗ No lock · 0 commits in last 3h
→ ABANDONED — reverting to READY

── Updating AGENTS.md ──
canvas-agent: DONE (confirmed)
toolbar-agent: IN PROGRESS (reverted)
collab-agent: WORKING (active)
export-agent: IDLE (abandoned)

DONE · Confirmed: 1 · Reverted: 1 · Reset: 1
AGENTS.md
The live roster — janitor keeps it accurate
Every agent reads this at session start to understand who else is active and what directories to avoid.
.ay/tracking/AGENTS.md — after janitor run
# Active Agent Roster
Last updated by: janitor · 2026-07-12 14:30

## Operator
Role: Conductor. Dispatches, monitors, unblocks. Does NOT write code.

## Workers

canvas-agent · surface:24 · agent/canvas · DONE [janitor-verified ✓]
Tasks: #03 (3 commits, PR #12) · Scope: src/components/canvas/
HANDOFF: Use object.id not index. Grid is layer 0, do not reorder.

toolbar-agent · surface:40 · agent/toolbar · IN PROGRESS
Tasks: #07 — reverted by janitor (0 commits found)
Scope: src/components/toolbar/ · Dependency: canvas-agent HANDOFF (done)

collab-agent · surface:41 · agent/collab · WORKING
Tasks: #09 — 2 commits this session, active · Scope: src/lib/collab.ts

export-agent · IDLE — abandoned (no activity 3h). Task #12 reset to READY.