---
name: lfo-gap-watch
description: Pattern-match drafted prompts and worker output against gaps-pattern-library.md. Append corrections to prompts before relay. Surface to Rocky if mitigation requires Rocky decision.
model: claude-sonnet-4-6
phase: all
invoked_by: lfo-prompt-pricing (every drafted prompt), and on-demand on worker output review
preconditions:
  - gaps-pattern-library.md read
  - intake_envelope sealed
inputs:
  - target_text (drafted prompt OR worker output OR Rocky intent statement)
  - source_phase (1-7)
outputs:
  - findings list (rule_id + matched + suggested correction)
  - corrected_text (input + appended corrections)
side_effects:
  - log every fired rule to `_Internal/orchestrator-sessions/session-{uuid}.md`
authored_by: claude-code-orchestrator
created: 2026-05-08
---

# lfo-gap-watch

Run the gap-pattern-library against a drafted prompt or worker output. Append corrections inline. Log fires to session.

## Procedure

1. **Read library.** Open `standards/gaps-pattern-library.md`. Hold all rules in head (currently G1–G5; will grow).

2. **Match each rule against `target_text` + intake envelope + session phase:**
   - Rule format = "Detect when" condition + "What goes wrong" + "Mitigation"
   - Use exact-keyword + structural-position matching (not LLM-vibes — pattern detection should be reproducible)

3. **For each fired rule:** apply mitigation:
   - **G1 (Standard Exclusions not applied):** if target prompt is `first-pass` and `_Internal/exclusions/exclusions-{job}.md` doesn't exist → block. Surface: "Phase 2 (`lfo-standard-exclusions-apply`) hasn't run. Run before first-pass."
   - **G2 (Site exposure inferred from drawing-spec):** if target text contains "tidal" / "saltwater" / "marine immersion" AND envelope `site_exposure ≠ tidal/immersed` → strip the inference; append correction: "site_exposure is `<envelope>` per intake. Strip saltwater +5% uplift, strip tidal-window assumption."
   - **G3 (Labour rate from methodology default):** if target text contains "$85" / "rate-card-v1.7-day" / "labour-rate-baseline-fr-85" AND envelope `labour.blended_rate_aud_per_hr ≠ 85` → append correction: "Use $<envelope>/hr blended per intake. Do not default to $85 base."
   - **G4 (Margin/contingency formula or ordering wrong):** if target text contains "1.165" or "16.5%" alone (without × 1.10 multiplier) → append: "Wrong multiplier. Apply locked formula: labour × 1.10 cont × 1.15 margin = 1.265; materials × 1.15 margin × 1.10 cont = 1.265. Per-category ordering preserved."
   - **G5 (Override missing reason_codes):** if target output contains override row WITHOUT reason_codes filled → re-prompt: "Override row {id} missing reason_codes. Re-prompt for 1-3 multi-tag from 9 domains."

4. **Output findings + corrected_text.** Format:
   ```yaml
   findings:
     - rule_id: G2
       matched: "tidal-window scheduling assumed"
       severity: block
       correction: "site_exposure is coastal-air per intake. Strip tidal-window."
   corrected_text: |
     <original prompt with corrections appended at bottom under "GAP-WATCH CORRECTIONS:" heading>
   ```

5. **Log fires.** Append to `_Internal/orchestrator-sessions/session-{uuid}.md` under `## Gap-watch fires`:
   ```
   - <timestamp> rule G2 fired on phase 5 first-pass prompt — corrected before relay
   ```

6. **Severity routing:**
   - `block` (G1): orchestrator does NOT relay until Rocky resolves
   - `correct` (G2/G3/G4): orchestrator appends correction inline, relays
   - `re-prompt` (G5): orchestrator drafts a follow-up prompt to worker after observing the gap in output

7. **Three-strike escalation.** If the same rule fires THREE times in the same session on the same locked-envelope field (e.g. G3 fires on $85 in worker output 3× in a row despite envelope $95):
   - Strike 1: append correction inline, relay
   - Strike 2: append correction inline + add prefix "REPEAT — strict envelope compliance required", relay
   - Strike 3: STOP. Surface to Rocky: "rule {id} has fired 3× this session on the same field. Worker not honouring envelope. Likely methodology row stuck or worker confusion. How should I proceed?"
   - Reset strike counter on session close OR on Rocky-confirmed root-cause fix.

## Pattern detection — implementation notes

- Use plain-text keyword search + structural rules (e.g. "drafted-prompt-step == first-pass AND envelope.site_exposure == coastal-air AND target contains 'tidal'"). Avoid LLM judgment on whether a rule fires — too much variance. Each rule should have a reproducible boolean predicate.
- If a rule's predicate is genuinely model-judgment (e.g. "is this scope item shown in drawings?"): require Rocky pre-confirmation; don't auto-fire.

## When new patterns surface

During lessons-write Phase 7 (`lfo-lessons-write`), Rocky may flag a missed gap. The orchestrator drafts a new pattern (G6+) into `gaps-pattern-library.md` per the format-template at the bottom of that file. Rocky confirms before merge.

## Failure modes

- A rule's predicate produces false positive (fires on output that's actually correct) → log to session as `false-positive`; tune the predicate in `gaps-pattern-library.md` (Rocky-confirmed) or remove the rule.
- A genuine gap occurs but no rule matches → catch in Phase 7 lessons-write; new rule G6+ proposed.
- Pattern library grows beyond ~30 rules → consider splitting into category sub-libraries (gaps-pattern-library-intake.md, gaps-pattern-library-pricing.md, etc.) for sanity. Only when needed.
