doctor + sweep

the health check and the housekeeping cron that neither v1 nor v2 ever had
$ ujima doctor — validates the whole workspace, exits 1 on problems
ujima doctor - 2026-07-18T09:12:03Z
  agents: 9 | problems: 1 | warnings: 2
  [FAIL] SECRET (Slack token) in agents/dex/notes/NOTES-2026-07-11.md
         - remove it and rotate the credential
  [warn] agents/koda missing heartbeat.md
  [warn] stale convening wave-1-security (phase EXECUTE,
         no transition in 9d) - initiatives/wave-1-security/convene.json

# checks doctor actually runs:
  structure       — required top-level dirs present
  agents          — agent.json name match, secrets_env is
                    NAMES only (never values), 7 subdirs present
  messages        — every .md header parses, no duplicate ids
  events          — every JSONL line parses (bad lines counted,
                    not fatal — readers skip them)
  encoding        — UTF-8 BOM, mojibake signs (â€, é, �)
  secrets         — 7 regex patterns: Slack, Anthropic, OpenAI,
                    GitHub, Google, Asana PAT, PEM private key
  caps            — notes >8,000 chars, checkpoints >4,000
  generated views — STATUS.md / LEDGER.md still carry the
                    GENERATED banner (hand-edited otherwise)
  stale convenings — non-CLOSED, no transition in 7 days
$ ujima sweep — run from Task Scheduler / cron / Heimdall
Sweep complete. 3 action(s).
  - expired stale lock: shared__WORKING-AGREEMENTS.md
  - escalated unread M-20260718T... (to koda)
  - escalated stale status: cindra

# sweep's four jobs, in order:
1. expire stale locks       TTL 600s (10 min)
2. unread-message escalation urgent >30m, normal >4h
3. stale-status escalation    WORKING/BLOCKED/WAITING
                             unchanged >24h (once/agent/day)
4. checkpoint-due nudge       >=50 ledger notes since
                             the last checkpoint

Every escalation is dedup'd by a stable key (e.g.
unread:M-20260718T083200Z-k7m2) so re-running sweep
never double-raises the same concern — and every raise is
also an events/ line, so the ledger of "who got escalated,
when, why" survives even after the human deletes the file
under escalations/ to resolve it.