Skip to content

Incident loop, Phase 4.9 Troubleshoot-to-Guardrail

Forge accumulates lessons faster than it accumulates prevention. Phase 4.9 closes that gap. Use this when a bug surfaces in a live session.

Four steps before moving on

  1. Root cause one line. Format cause was X because Y. Not "I fixed it." If you cannot state the cause cleanly you have not understood the bug yet.
  2. Log the lesson. Run forge/scripts/forge_incident_log (a thin shim into forge_memory_auto_capture.log_incident(), the single owner of LESSONS.md writes). Atomic tempfile plus rename. Upserts on incident_id: existing entries get seen_count bumped, last_seen updated, the date appended under ### Recurrences.
  3. Decide guard tier (seen-twice rule). A single low-blast occurrence logs only. Two or more occurrences, or one with high blast radius (data loss, security, silent corruption, customer-visible regression with no quick rollback), require a concrete guard the same session: hook, eval check, doctrine clause, boundary sanitizer.
  4. Link forward. Lesson's guard: field references the guard's file path or doctrine section. The guard's comment references the incident_id. Future audits trace the chain in either direction.

CLI

forge_incident_log \
  --id "kebab-case-stable-id" \
  --title "one-line title" \
  --blast low|medium|high \
  --doctrine "Section X (rule)" or "n/a" \
  --eval-check "check-name" or "none" \
  --root-cause "cause was X because Y" \
  --fix "what changed in code" \
  --guard "path/to/guard.py:LN" or "doctrine:Section-X" \
  --guard-status shipped|proposed|not-needed|overdue \
  --recurrence-prevention "what stops this from happening again"

Only --id and --title are required; missing fields render as tbd or sensible defaults. Output is JSON {action, incident_id, seen_count, last_seen} to stdout.

Schema (LESSONS.md, new entries only)

## YYYY-MM-DDTHH:MM [incident_id] one-line title

- **doctrine:** Section X (rule name) | n/a
- **eval_check:** check-name | none
- **incident_id:** kebab-case-stable-key
- **seen_count:** N
- **first_seen:** YYYY-MM-DD
- **last_seen:** YYYY-MM-DD
- **blast_radius:** low | medium | high
- **guard:** path/to/guard.py:LN | doctrine:Section-X | none (single-occurrence)
- **guard_status:** shipped | proposed | not-needed | overdue

### Root cause
One line.

### Fix
What changed.

### Recurrence prevention
What stops this from happening again, or the proposed mechanism if guard_status is proposed.

Existing pre-4.9 free-form entries are grandfathered. New entries follow the schema.

Eval harness

Two checks registered in forge/eval.json under doctrine_section 10.4, both warning severity (initial; tighten to error after one clean week per the same policy as no-em-dashes):

  • lessons-md-schema-conformance (forge_eval_check_lessons_md_schema.py): warns when a Phase 4.9 incident block (kebab-tagged header plus declared incident_id) omits required fields.
  • lessons-orphan-recurrence (forge_eval_check_lessons_orphan_recurrence.py): warns when seen_count >= 2, guard_status is proposed or none, and last_seen is 7+ days old.

Auto-dream nag (Sunday)

forge_memory_auto_dream.lessons_recurrence_scan() runs as Pass E during the nightly consolidation, but only fires on Sunday. Compiles backlog of seen_count >= 2 entries with guard_status in (proposed, none) and routes through forge_notify.sh warning. The bot does not auto-build guards: silent automated guard-building violates the seen-twice judgment requirement, so the human keeps the call.

CLI smoke test: forge_memory_auto_dream.py --recurrence-scan --force-recurrence --dry-run.

Doctrine

FORGE-DOCTRINE.md Section 10.4. Sub-clause of self-iteration; the contract that says "when X breaks twice, a guard ships."

Files

  • forge/scripts/forge_memory_auto_capture.py — owns log_incident(), single writer of LESSONS.md
  • forge/scripts/forge_incident_log — thin CLI shim
  • forge/scripts/forge_memory_auto_dream.pylessons_recurrence_scan() (Pass E)
  • forge/scripts/forge_eval_check_lessons_md_schema.py
  • forge/scripts/forge_eval_check_lessons_orphan_recurrence.py
  • forge/eval.json — registers both checks
  • forge/FORGE-DOCTRINE.md — Section 10.4
  • forge/LESSONS.md — single store

[phoenix-4-9 worker]