Skip to content
fitme·story
Case studies
v7.9.1 · 3 min read · 2026-06-04

F2 Phase 0 Reality-Check — Mechanical Defense Against Post-Squash-Merge State Drift (v7.9.1)

The post-squash-merge state-drift pattern repeated 5 times in 4 days (2026-06-01 → 2026-06-04). Each time, state.json::tasks said pending but file changes were already on main. The next Phase 0 then scheduled "new work" that was in fact already done. F2 adds a mandatory Phase 0.1 sub-step to /pm-workflow — cross-checks each pending task against the last 30 days of git log + merged PR titles + Tier 2.2 log events. If 2+ evidence items match, the advisory "this task may already be done" surfaces BEFORE scheduling. Wall- clock under 0.1 seconds. 16 tests pass in 0.15s. Operator decides; never blocking.

5 in 4 days
post-squash-merge state-drift instances (2026-06-01 → 2026-06-04) — the calibration baseline that justified the gate
Each time, state.json::tasks still said pending while the changes were already on main, costing a 3-phase re-plan. F2's Phase 0.1 sub-step cross-checks each pending task against 30 days of git log + merged PR titles + Tier 2.2 events; ≥2 evidence items surface a "may already be done" advisory. Runs in 0.09s; never blocking.
What do T1 / T2 / T3 mean?
T1Instrumented — from a ledger/commit
T2Declared — stated, not measured
T3Narrative — estimate from memory

F2 Phase 0 Reality-Check Sub-step

The post-squash-merge state-drift pattern repeated 5 times in 4 days.

F2 is the mechanical defense.

The drift loop

  1. Feature work ships via squash-merged PR; main has the changes
  2. state.json::tasks still say pending because the operator merged via web UI (which doesn't update state.json) or gh pr merge (which also doesn't)
  3. Next session opens Phase 0 on the same feature; agent reads state.json and sees pending tasks
  4. Agent schedules "new work" that is in fact already done
  5. Operator catches the drift — usually 2-3 phases later — and runs make close-feature to reconcile

Cost: 3-phase re-plan per drift instance.

What F2 does

Before Phase 0 schedules anything, run:

make phase-0-reality-check FEATURE=<name>

The check reads state.json::tasks and cross-checks each pending / in_progress / open task against the last 30 days of:

  • Git log subjects — has anyone shipped a matching commit?
  • Merged PR titles (FT2 + fitme-story) — has either repo merged a matching PR?
  • Tier 2.2 log events in .claude/logs/<feature>.log.json — has anyone logged an implementation event mentioning the task ID or keywords?

If 2+ distinct evidence items hit a pending task's keywords, the advisory "this task may already be done" surfaces.

Threshold mechanics

  • Noise-token filter: 50+ common English words + framework-specific noise ("the", "test", "implement", "framework", "scripts", "case-study", "v7", "v8", etc.) drop out before matching. Keeps "BRANCH_ISOLATION_VIOLATION" or "REPO_ROOT_OVERRIDE" or "T4a" as signal.
  • Per-item: ≥2 distinct keywords — single-keyword matches don't count.
  • Across-sources: ≥2 total evidence items — combining git + PR + log adds up.

The strict thresholds keep false-positive rate manageable.

Sequencing with F16 + F17

F2 ships same-day as F16 (try-repo harness) and F17 (last_fired_at index). All three are independent v7.9.1 candidates:

  • F16 validates that gates ARE wired correctly via subprocess integration testing
  • F17 materializes "when did each gate last fire?" so meta-checks stay O(1)
  • F2 catches state-drift BEFORE the next Phase 0 schedules duplicate work

F1 (STATE_TASKS_FILESYSTEM_DRIFT advisory, queued) addresses the same pattern at the cycle-time gate level. F2 addresses it at the workflow level.

Shipped via

  • PR #618 — script + 16 tests + SKILL.md Phase 0.1 + Makefile target + docs + case study

Where to read more