Skip to content

feat(bin): add fseventsd footprint early warning to the fleet watcher - #73

Merged
withally merged 8 commits into
mainfrom
fm/fm-fseventsd-early-warning-w1
Aug 24, 2026
Merged

feat(bin): add fseventsd footprint early warning to the fleet watcher#73
withally merged 8 commits into
mainfrom
fm/fm-fseventsd-early-warning-w1

Conversation

@withally

Copy link
Copy Markdown
Owner

Intent

Add an fseventsd footprint early-warning to fleet monitoring exactly per the accepted review at /Users/ivan/Projects/firstmate/data/fseventsd-growth-review-r4/report.md. Section 7 owns the thresholds and design, and the whole report must be read first. This changes Firstmate shared tracked material and must follow the firstmate-coding-guidelines skill. Scope is only the monitoring and early-warning integration recommended by section 7: threshold checks on fseventsd footprint growth surfaced through the fleet existing monitoring path, with colocated behavioral regression tests, and nothing beyond that recommendation. The fm-pending-reply, fm-tool-update-check, and fm-wake-queue regression failures were each reproduced with these changes absent on clean current main at 6545983; they are pre-existing baseline failures with fixes already in flight in other tasks, so do not modify those unrelated subsystems in this task and use the validation gate documented baseline-failure path or wait for those fixes if the pipeline cannot proceed.

What Changed

  • bin/fm-telemetry.sh gains an fseventsd-check subcommand that samples the host's fseventsd memory footprint via top, plus memory-pressure level and swap usage via sysctl, keeps a bounded three-hour sample history under state/telemetry/fseventsd-samples, and emits a WARNING/ACTION/EMERGENCY line only when a newly higher level is reached (tracked in fseventsd-alert), so a level that merely stays true does not re-alert. Thresholds follow the accepted review: 512 MiB twice or >256 MiB/hour over two hours for warning; >2 GiB, an hourly doubling above the warning floor, or a warning combined with warning-or-worse memory pressure or >8 GiB swap for action; sustained growth from the action range with memory pressure at warning-or-worse for emergency. Every condition that holds is reported in the reason string, and a sample whose history cannot be persisted still alerts, with the persistence failure logged as a diagnostic.
  • bin/fm-watch.sh invokes the check on its existing bounded slow-check cadence via run_check_capture, and surfaces any output as a check: fseventsd: <out> wake through the normal wake-append path.
  • Adds behavioral regression tests: 13 cases in tests/fm-telemetry.test.sh covering cadence, each threshold, the kernel pressure encoding, irregular sample spacing, and the disable seam, plus two in tests/fm-watcher-lock.test.sh for the watcher wake path and for proving watcher tests never sample the live host; tests/lib.sh exports FM_TELEMETRY_FSEVENTSD_DISABLE=1 so unrelated watcher suites stay isolated from the real fseventsd. Docs in docs/architecture.md and docs/scripts.md were updated to describe the new wake reason and subcommand.

Risk Assessment

✅ Low: The change is purely additive to bin/fm-telemetry.sh (no removed lines), gated behind a bounded 300s cadence and a dedupe file, wired into the watcher through the existing run_check_capture path whose failure modes cannot kill the watcher, and every accepted section-7 threshold plus each fix round's claim traces correctly against concrete sample sequences and is covered by behavioral regression tests.

Testing

Ran the two colocated suites (fm-telemetry and fm-watcher-lock) and both pass in full, then went past pass/fail to produce the actual operator surface: a walkthrough that seeds the durable fseventsd sample history and runs the unmodified fleet watcher and wake-drain with only pgrep/top/sysctl faked, capturing the exact wake text for every section-7 tier from healthy silence through WARNING, both ACTION forms and EMERGENCY, plus the persisted samples and alert files behind each. A final unfaked run against this Mac's real fseventsd confirmed the healthy daemon stays silent and its 5904K footprint is parsed correctly. No failures, no flakiness, and the unrelated pre-existing baseline failures were deliberately not exercised.

Evidence: End-to-end fleet walkthrough transcript (all six alert tiers through the real watcher + wake drain)

Source: End-to-end fleet walkthrough transcript (all six alert tiers through the real watcher + wake drain)

=== 2. WARNING - two consecutive samples above 512 MiB === $ bin/fm-watch.sh check: fseventsd: WARNING: fseventsd MEM=640.00 MiB; two consecutive samples above 512 MiB $ bin/fm-wake-drain.sh 1787577141\t1\tcheck\tfseventsd\tcheck: fseventsd: WARNING: fseventsd MEM=640.00 MiB; two consecutive samples above 512 MiB === 4. ACTION - past 2 GiB, doubled within the hour, yellow memory pressure === check: fseventsd: ACTION: fseventsd MEM=2500.00 MiB; two consecutive samples above 512 MiB; MEM above 2 GiB; doubling within one hour; warning plus yellow memory pressure; pressure_level=2 swap_used=0.00 GiB === 6. EMERGENCY - sustained climb toward 4 GiB with worsening pressure === check: fseventsd: EMERGENCY: fseventsd MEM=3500.00 MiB; two consecutive samples above 512 MiB; MEM above 2 GiB; warning plus red critical memory pressure; warning plus more than 8 GiB swap; sustained growth toward 4 GiB plus worsening memory pressure; pressure_level=4 swap_used=8.79 GiB; stop launching new work, save state, and reduce workload before the machine wedges

fseventsd footprint early warning - end-to-end fleet walkthrough
repo:    /Users/ivan/.no-mistakes/worktrees/37852af5566c/01M0SQSCDK191W0FGA39ASB6G1
real:    bin/fm-watch.sh (fleet watcher) + bin/fm-wake-drain.sh (wake delivery)
faked:   pgrep / top / sysctl only
clock:   real; sample history is pre-seeded with recent epochs
accepted thresholds: warning >512 MiB twice or >256 MiB/h for 2h; action >2 GiB,
                     doubling within 1h, or warning + pressure/swap; emergency is
                     sustained growth toward 4 GiB with worsening pressure.

================================================================
1. HEALTHY - the ~5 MiB daemon this Mac actually runs right now
================================================================
live sample the watcher will take: fseventsd MEM=5M, kern pressure level=1, swap used=0.00M
$ cat state/telemetry/fseventsd-samples   # history already on disk (epoch mem_bytes pressure swap_bytes)
  1787576438 5242880 1 0
  1787576798 5242880 1 0
$ bin/fm-watch.sh
  (watcher raised no fseventsd wake)
$ cat state/telemetry/fseventsd-samples   # history after this cycle
  1787576438 5242880 1 0
  1787576798 5242880 1 0
  1787577158 5242880 1 0
$ cat state/telemetry/fseventsd-alert     # (absent: no episode open)

================================================================
2. WARNING - two consecutive samples above 512 MiB
================================================================
live sample the watcher will take: fseventsd MEM=640M, kern pressure level=1, swap used=0.00M
$ cat state/telemetry/fseventsd-samples   # history already on disk (epoch mem_bytes pressure swap_bytes)
  1787576798 629145600 1 0
$ bin/fm-watch.sh
  check: fseventsd: WARNING: fseventsd MEM=640.00 MiB; two consecutive samples above 512 MiB
$ bin/fm-wake-drain.sh   # what the fleet operator/agent is actually handed
  1787577186	1	check	fseventsd	check: fseventsd: WARNING: fseventsd MEM=640.00 MiB; two consecutive samples above 512 MiB
$ cat state/telemetry/fseventsd-samples   # history after this cycle
  1787576798 629145600 1 0
  1787577186 671088640 1 0
$ cat state/telemetry/fseventsd-alert     # highest level of the live episode
  warning

================================================================
3. WARNING - growth above 256 MiB/hour sustained for two hours
================================================================
live sample the watcher will take: fseventsd MEM=800M, kern pressure level=1, swap used=0.00M
$ cat state/telemetry/fseventsd-samples   # history already on disk (epoch mem_bytes pressure swap_bytes)
  1787568158 104857600 1 0
  1787576798 828375040 1 0
$ bin/fm-watch.sh
  check: fseventsd: WARNING: fseventsd MEM=800.00 MiB; two consecutive samples above 512 MiB; growth 279.10 MiB/hour for 2h
$ bin/fm-wake-drain.sh   # what the fleet operator/agent is actually handed
  1787577188	1	check	fseventsd	check: fseventsd: WARNING: fseventsd MEM=800.00 MiB; two consecutive samples above 512 MiB; growth 279.10 MiB/hour for 2h
$ cat state/telemetry/fseventsd-samples   # history after this cycle
  1787568158 104857600 1 0
  1787576798 828375040 1 0
  1787577187 838860800 1 0
$ cat state/telemetry/fseventsd-alert     # highest level of the live episode
  warning

================================================================
4. ACTION - past 2 GiB, doubled within the hour, yellow memory pressure
================================================================
live sample the watcher will take: fseventsd MEM=2500M, kern pressure level=2, swap used=0.00M
$ cat state/telemetry/fseventsd-samples   # history already on disk (epoch mem_bytes pressure swap_bytes)
  1787576798 629145600 1 0
$ bin/fm-watch.sh
  check: fseventsd: ACTION: fseventsd MEM=2500.00 MiB; two consecutive samples above 512 MiB; MEM above 2 GiB; doubling within one hour; warning plus yellow memory pressure; pressure_level=2 swap_used=0.00 GiB
$ bin/fm-wake-drain.sh   # what the fleet operator/agent is actually handed
  1787577190	1	check	fseventsd	check: fseventsd: ACTION: fseventsd MEM=2500.00 MiB; two consecutive samples above 512 MiB; MEM above 2 GiB; doubling within one hour; warning plus yellow memory pressure; pressure_level=2 swap_used=0.00 GiB
$ cat state/telemetry/fseventsd-samples   # history after this cycle
  1787576798 629145600 1 0
  1787577189 2621440000 2 0
$ cat state/telemetry/fseventsd-alert     # highest level of the live episode
  action

================================================================
5. ACTION - warning episode plus more than 8 GiB of swap in use
================================================================
live sample the watcher will take: fseventsd MEM=700M, kern pressure level=1, swap used=9000M
$ cat state/telemetry/fseventsd-samples   # history already on disk (epoch mem_bytes pressure swap_bytes)
  1787576798 629145600 1 0
$ bin/fm-watch.sh
  check: fseventsd: ACTION: fseventsd MEM=700.00 MiB; two consecutive samples above 512 MiB; warning plus more than 8 GiB swap; pressure_level=1 swap_used=8.79 GiB
$ bin/fm-wake-drain.sh   # what the fleet operator/agent is actually handed
  1787577192	1	check	fseventsd	check: fseventsd: ACTION: fseventsd MEM=700.00 MiB; two consecutive samples above 512 MiB; warning plus more than 8 GiB swap; pressure_level=1 swap_used=8.79 GiB
$ cat state/telemetry/fseventsd-samples   # history after this cycle
  1787576798 629145600 1 0
  1787577191 734003200 1 9437184000
$ cat state/telemetry/fseventsd-alert     # highest level of the live episode
  action

================================================================
6. EMERGENCY - sustained climb toward 4 GiB with worsening pressure
================================================================
live sample the watcher will take: fseventsd MEM=3500M, kern pressure level=4, swap used=9000M
$ cat state/telemetry/fseventsd-samples   # history already on disk (epoch mem_bytes pressure swap_bytes)
  1787576468 2726297600 1 0
  1787576813 3145728000 2 0
$ bin/fm-watch.sh
  check: fseventsd: EMERGENCY: fseventsd MEM=3500.00 MiB; two consecutive samples above 512 MiB; MEM above 2 GiB; warning plus red critical memory pressure; warning plus more than 8 GiB swap; sustained growth toward 4 GiB plus worsening memory pressure; pressure_level=4 swap_used=8.79 GiB; stop launching new work, save state, and reduce workload before the machine wedges
$ bin/fm-wake-drain.sh   # what the fleet operator/agent is actually handed
  1787577193	1	check	fseventsd	check: fseventsd: EMERGENCY: fseventsd MEM=3500.00 MiB; two consecutive samples above 512 MiB; MEM above 2 GiB; warning plus red critical memory pressure; warning plus more than 8 GiB swap; sustained growth toward 4 GiB plus worsening memory pressure; pressure_level=4 swap_used=8.79 GiB; stop launching new work, save state, and reduce workload before the machine wedges
$ cat state/telemetry/fseventsd-samples   # history after this cycle
  1787576468 2726297600 1 0
  1787576813 3145728000 2 0
  1787577193 3670016000 4 9437184000
$ cat state/telemetry/fseventsd-alert     # highest level of the live episode
  emergency
Evidence: Reproducible E2E walkthrough script

Source: Reproducible E2E walkthrough script

#!/usr/bin/env bash
# End-to-end operator walkthrough of the fseventsd footprint early warning.
#
# Each scenario stands up a fresh Firstmate home, seeds the durable sample
# history under state/telemetry/ as if the fleet had already been sampling for
# the last few minutes, then runs the REAL bin/fm-watch.sh once and drains the
# wake with the REAL bin/fm-wake-drain.sh. Only the host samplers
# (pgrep/top/sysctl) are faked, so an fseventsd footprint that would take hours
# to leak can be presented to the unmodified watcher path in one run.
set -u
ROOT=${FM_EVIDENCE_ROOT:?set FM_EVIDENCE_ROOT to the repo root}
# shellcheck source=/dev/null
. "$ROOT/tests/wake-helpers.sh"
WATCH="$ROOT/bin/fm-watch.sh"
DRAIN="$ROOT/bin/fm-wake-drain.sh"
TMP_ROOT=$(fm_test_tmproot fm-fseventsd-evidence)
NOW=$(date '+%s')
MIB=1048576

scenario() { # <label> <mem-top-string> <pressure> <swap-top-string> <seed-lines...>
  local label=$1 mem=$2 pressure=$3 swap=$4
  shift 4
  local dir state fake out line
  dir=$(make_case "$(printf '%s' "$label" | tr -cs 'A-Za-z0-9' '-')")
  state="$dir/state"
  fake="$dir/fakebin"
  out="$dir/watch.out"
  printf 'fm-pr-check-migration-scan-v1\n' > "$state/.pr-check-migration-scan-v1"
  printf 'fm-pr-check-migration-v1\n' > "$state/.pr-check-migration-v1"
  chmod 0600 "$state"/.pr-check-migration*

  cat > "$fake/pgrep" <<'SH'
#!/usr/bin/env bash
[ "${1:-}" = -x ] && [ "${2:-}" = fseventsd ] || exit 1
printf '342\n'
SH
  cat > "$fake/top" <<SH
#!/usr/bin/env bash
printf 'PID COMMAND MEM RPRVT CMPRS %%CPU TIME #TH\n'
printf '342 fseventsd $mem $mem 2M 0.4 00:31.00 17\n'
SH
  cat > "$fake/sysctl" <<SH
#!/usr/bin/env bash
case "\$*" in
  '-n kern.memorystatus_vm_pressure_level') printf '$pressure\n' ;;
  '-n vm.swapusage') printf 'total = 16384.00M  used = $swap  free = 8192.00M  (encrypted)\n' ;;
  *) exit 1 ;;
esac
SH
  chmod +x "$fake/pgrep" "$fake/top" "$fake/sysctl"

  mkdir -p "$state/telemetry"
  : > "$state/telemetry/fseventsd-samples"
  for line in "$@"; do
    printf '%s\n' "$line" >> "$state/telemetry/fseventsd-samples"
  done

  printf '\n================================================================\n'
  printf '%s\n' "$label"
  printf '================================================================\n'
  printf 'live sample the watcher will take: fseventsd MEM=%s, kern pressure level=%s, swap used=%s\n' \
    "$mem" "$pressure" "$swap"
  printf '$ cat state/telemetry/fseventsd-samples   # history already on disk (epoch mem_bytes pressure swap_bytes)\n'
  sed "s/^/  /" "$state/telemetry/fseventsd-samples"
  printf '$ bin/fm-watch.sh\n'
  PATH="$fake:$PATH" FM_HOME="$dir" FM_STATE_OVERRIDE="$state" \
    FM_TELEMETRY_FSEVENTSD_DISABLE=0 \
    FM_POLL=5 FM_SIGNAL_GRACE=1 FM_CHECK_INTERVAL=0 FM_HEARTBEAT=999999 \
    "$WATCH" > "$out" 2>&1 &
  local wpid=$! i=0
  while [ "$i" -lt 150 ]; do
    grep -q fseventsd "$out" 2>/dev/null && break
    kill -0 "$wpid" 2>/dev/null || break
    sleep 0.1
    i=$((i + 1))
  done
  kill "$wpid" 2>/dev/null || true
  wait "$wpid" 2>/dev/null || true
  if grep -q fseventsd "$out"; then
    grep -F fseventsd "$out" | sed 's/^/  /'
    printf '$ bin/fm-wake-drain.sh   # what the fleet operator/agent is actually handed\n'
    FM_STATE_OVERRIDE="$state" "$DRAIN" 2>/dev/null | grep -F fseventsd | sed 's/^/  /'
  else
    printf '  (watcher raised no fseventsd wake)\n'
  fi
  printf '$ cat state/telemetry/fseventsd-samples   # history after this cycle\n'
  sed 's/^/  /' "$state/telemetry/fseventsd-samples"
  if [ -f "$state/telemetry/fseventsd-alert" ]; then
    printf '$ cat state/telemetry/fseventsd-alert     # highest level of the live episode\n'
    sed 's/^/  /' "$state/telemetry/fseventsd-alert"
  else
    printf '$ cat state/telemetry/fseventsd-alert     # (absent: no episode open)\n'
  fi
}

printf 'fseventsd footprint early warning - end-to-end fleet walkthrough\n'
printf 'repo:    %s\n' "$ROOT"
printf 'real:    bin/fm-watch.sh (fleet watcher) + bin/fm-wake-drain.sh (wake delivery)\n'
printf 'faked:   pgrep / top / sysctl only\n'
printf 'clock:   real; sample history is pre-seeded with recent epochs\n'
printf 'accepted thresholds: warning >512 MiB twice or >256 MiB/h for 2h; action >2 GiB,\n'
printf '                     doubling within 1h, or warning + pressure/swap; emergency is\n'
printf '                     sustained growth toward 4 GiB with worsening pressure.\n'

scenario "1. HEALTHY - the ~5 MiB daemon this Mac actually runs right now" \
  5M 1 0.00M \
  "$((NOW - 720)) $((5 * MIB)) 1 0" \
  "$((NOW - 360)) $((5 * MIB)) 1 0"

scenario "2. WARNING - two consecutive samples above 512 MiB" \
  640M 1 0.00M \
  "$((NOW - 360)) $((600 * MIB)) 1 0"

scenario "3. WARNING - growth above 256 MiB/hour sustained for two hours" \
  800M 1 0.00M \
  "$((NOW - 9000)) $((100 * MIB)) 1 0" \
  "$((NOW - 360)) $((790 * MIB)) 1 0"

scenario "4. ACTION - past 2 GiB, doubled within the hour, yellow memory pressure" \
  2500M 2 0.00M \
  "$((NOW - 360)) $((600 * MIB)) 1 0"

scenario "5. ACTION - warning episode plus more than 8 GiB of swap in use" \
  700M 1 9000M \
  "$((NOW - 360)) $((600 * MIB)) 1 0"

scenario "6. EMERGENCY - sustained climb toward 4 GiB with worsening pressure" \
  3500M 4 9000M \
  "$((NOW - 690)) $((2600 * MIB)) 1 0" \
  "$((NOW - 345)) $((3000 * MIB)) 2 0"
Evidence: Live-host check against the real fseventsd daemon (PID 342, 5904K) — healthy, stays silent

Source: Live-host check against the real fseventsd daemon (PID 342, 5904K) — healthy, stays silent

$ top -l 1 -pid 342 -stats pid,command,mem,rsize,compressed,cpu,time,threads PID COMMAND MEM MEM CMPRS %CPU TIME #TH 342 fseventsd 5904K 5904K 1648K 0.0 06:26.37 19 $ FM_TELEMETRY_FSEVENTSD_DISABLE=0 bin/fm-telemetry.sh fseventsd-check # real host, no fakes (exit 0, no alert text: healthy daemon stays silent) $ cat state/telemetry/fseventsd-samples 1787577203 6225920 1 8739818045

$ pgrep -x fseventsd
  342
$ top -l 1 -pid $(pgrep -x fseventsd|head -1) -stats pid,command,mem,rsize,compressed,cpu,time,threads | tail -3
  
  PID  COMMAND   MEM   MEM   CMPRS %CPU TIME     #TH
  342  fseventsd 5904K 5904K 1648K 0.0  06:26.37 19 
$ FM_TELEMETRY_FSEVENTSD_DISABLE=0 bin/fm-telemetry.sh fseventsd-check   # real host, no fakes
  (exit 0, no alert text: healthy daemon stays silent)
$ cat state/telemetry/fseventsd-samples   # epoch mem_bytes pressure_level swap_bytes
  1787577203 6225920 1 8739818045

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 info
  • 🚨 bin/fm-telemetry.sh:494 - The memory-pressure escalation treats kern.memorystatus_vm_pressure_level &gt;= 1 as "yellow memory pressure", but on macOS that sysctl uses the dispatch memory-pressure encoding NORMAL=1, WARN=2, CRITICAL=4 (see MacOSX26.5.sdk/usr/include/dispatch/source.h:254-256). Verified live on this host: the sysctl returns 1 while memory_pressure reports "System-wide memory free percentage: 50%", i.e. normal. Concrete trace: fseventsd at 600 MiB on two consecutive 5-minute samples on a completely healthy Mac sets warning=1, then line 494 sees pressure_level=1 and emits ACTION: fseventsd MEM=600.00 MiB; two consecutive samples above 512 MiB; warning plus yellow memory pressure instead of the accepted WARNING tier. Because level 1 is the steady-state value, the warning tier of section 7 is unreachable on real macOS and every 512 MiB warning is mislabeled as ACTION - exactly the collapse of the two tiers the report warns against ("Keep the previous report's 2 GiB threshold as the operational action boundary rather than the first alert"). The same off-by-one tier is in the emergency guard at line 510 and in the header comment at line 45 ("warning-or-worse memory pressure"). The colocated tests bake in the wrong encoding too: they feed FM_FAKE_PRESSURE_LEVEL=0 for "no pressure" and 3 for "critical" (tests/fm-telemetry.test.sh:80, 240, 260), values the kernel never returns, so no test would catch this. Fix: compare against >= 2 (warn) for the action escalation and the emergency guard, and use 1/2/4 in the fakes.
  • ⚠️ bin/fm-telemetry.sh:482 - The doubling rule fires on any sample that is >= 2x any sample in the previous hour, with no absolute floor. fseventsd's healthy footprint is ~5 MiB (report section 1; measured 5664K on this host right now), and it starts far smaller right after boot. Concrete trace: after a reboot the watcher samples fseventsd at 1.5 MiB, and 30 minutes later it has settled at its normal 4.8 MiB. Line 482 matches (4.8 >= 2 * 1.5), so severity becomes action and the fleet is woken with ACTION: fseventsd MEM=4.80 MiB; doubling within one hour for a completely healthy daemon. Because the alert file then holds rank 2, a genuine 512 MiB warning later in the same episode is also suppressed by fseventsd_publish_alert. Section 7 states the doubling rule without a floor, so gating it on an absolute minimum (e.g. only evaluate doubling once MEM is above the 512 MiB warning boundary) is a deliberate deviation from the accepted thresholds and needs the author's call.
  • ⚠️ bin/fm-telemetry.sh:509 - The emergency tier requires the pressure level to strictly increase between the previous sample and the current one (FSEVENTSD_PRESSURE_LEVEL -gt prev_pressure) in the same cycle where three consecutive rising samples above 2 GiB are observed. Concrete trace: the machine crosses into warn pressure at 10:02, the watcher samples at 10:00 and 10:05 (both warn), fseventsd climbs 2.6 -> 3.0 -> 3.5 GiB across 10:05/10:10/10:15 - every growth condition holds at 10:10 and 10:15 but prev_pressure == current pressure at both, so no EMERGENCY is ever emitted for a machine that is sustainedly degrading toward the wedge the report describes. Only the single sample where the transition happens to land can qualify, and at that sample the three-sample growth chain may not yet hold. Section 7 says "sustained growth toward 4 GiB plus worsening memory pressure"; reading "worsening" as sustained elevated pressure (level >= warn) rather than a per-sample delta is a threshold-semantics decision for the author.
  • ℹ️ bin/fm-telemetry.sh:469 - The two-hour growth rate is computed only against samples landing in the 300-second window [now-7500, now-7200]. Sample spacing is >= 300s by the cadence guard at line 445 and in practice slightly more (fm-watch.sh's CHECK_INTERVAL is 300s plus poll and processing time), so the spacing can exceed the window width and no sample lands inside it. Concrete case: samples at now-7150 and now-7480 with 330s spacing - neither is in [7200,7500] on some cycles, so the 256 MiB/hour rule silently evaluates to "no growth" even while growth is real. It self-corrects on a later cycle, so impact is a delayed warning rather than a lost one, but selecting the newest retained sample at least 7200s old (the history already keeps 3 hours) would make the rule deterministic.
  • ℹ️ bin/fm-watch.sh:1028 - The fseventsd check is placed first in the slow-check block and its output path calls wake, which exits the watcher process (bin/fm-push-transition-lib.sh:88-108) after touching .last-check. So the first cycle in which an fseventsd level is newly reached skips every *.check.sh sweep - including PR merge polling - and defers them by a full CHECK_INTERVAL. Alert dedupe keeps this to one cycle per escalation, so this is an accepted one-shot cost rather than a defect; noting it because the surrounding comment block explains that check ordering was chosen specifically to avoid starvation.
  • ℹ️ bin/fm-telemetry.sh:429 - FM_TELEMETRY_NOW is a new environment-controlled clock override read in production code, but unlike the sibling seam FM_TELEMETRY_RECORD_ONCE it is not listed in the script header's environment section (lines 48-58). Per the repo guideline that a script's header plus --help owns exact env mechanics, add a one-line entry describing it as a test seam.

🔧 Fix: fix fseventsd pressure encoding, doubling floor, growth window
3 infos still open:

  • ℹ️ bin/fm-telemetry.sh:517 - The emergency guard's second disjunct is unreachable. The condition already requires FSEVENTSD_PRESSURE_LEVEL -ge 2 on line 515, so in the { [ &#34;$prev_pressure&#34; -ge 2 ] || [ &#34;$FSEVENTSD_PRESSURE_LEVEL&#34; -gt &#34;$prev_pressure&#34; ]; } compound, whenever the first test is false (prev_pressure is -1, 0 or 1) the second is necessarily true (current >= 2 > prev). prev_pressure is always a numeric field read from the history file, so there is no third case. The compound is a tautology and can be deleted. The header at line 48 also documents "or is still rising" as a distinct emergency path that cannot actually decide the outcome, so it should be dropped alongside it.
  • ℹ️ tests/fm-watcher-lock.test.sh:775 - The fix round corrected the pressure fakes in tests/fm-telemetry.test.sh to the kernel's 1/2/4 encoding but left this watcher-path fake returning 0, a value kern.memorystatus_vm_pressure_level never emits. The test still passes because 0 and the real steady-state 1 both sit below the warn threshold, so it does not mask a defect, but the fake now contradicts the encoding the production code and the sibling test suite are built around and will mislead the next person editing this path. Change it to 1.
  • ℹ️ bin/fm-telemetry.sh:499 - The MEM above 2 GiB (line 496) and doubling within one hour (line 499) branches assign to reasons rather than appending, discarding any warning evidence already accumulated. Concrete trace: fseventsd samples 600 MiB then 1.3 GiB five minutes later. Line 464 sets warning=1 with reasons='two consecutive samples above 512 MiB', line 487 sets doubling=1, then line 499 overwrites reasons, so the operator sees ACTION: fseventsd MEM=1331.20 MiB; doubling within one hour; pressure_level=1 swap_used=0.00 GiB with no mention of the sustained 512 MiB breach or (in the growth case) the measured MiB/hour rate. The alert is correctly tiered but reports less than it measured; the sibling pressure/swap branches at lines 500-506 already append instead of overwrite. Since this changes user-facing alert text, confirm with the author before switching these two branches to append.

🔧 Fix: retain fseventsd action evidence, drop dead pressure clause
2 infos still open:

  • ℹ️ bin/fm-telemetry.sh:519 - The emergency branch assigns reasons=&#39;sustained growth toward 4 GiB plus worsening memory pressure&#39;, discarding every reason accumulated for the same sample, and the emergency message at line 535 also omits the pressure_level=/swap_used= suffix that the ACTION message at line 533 carries. Concrete trace (the suite's own emergency scenario, tests/fm-telemetry.test.sh:278-284: 2600M/3000M/3500M at pressure 2): line 464 sets warning=1 with reasons='two consecutive samples above 512 MiB', line 496 appends 'MEM above 2 GiB', then line 519 overwrites both, so the operator sees EMERGENCY: fseventsd MEM=3500.00 MiB; sustained growth toward 4 GiB plus worsening memory pressure; stop launching new work... with no 512 MiB breach, no measured MiB/hour rate, no pressure level and no swap figure. The highest tier therefore reports strictly less than the ACTION tier below it - the same evidence-dropping the previous round's decision ("ACTION alerts must append and retain all same-sample warning evidence ... so crash-forensics output remains diagnosable") corrected at lines 496 and 499 but did not extend here. Fix would be to append the emergency reason and add the pressure/swap suffix to the emergency message; since it changes user-facing alert text, confirm with the author.
  • ℹ️ docs/scripts.md:107 - The fm-telemetry.sh row still reads "Arm, disarm, or report the never-auto-armed detached host resource-snapshot recorder...", which no longer covers the script's second responsibility: the fseventsd-check subcommand (bin/fm-telemetry.sh:899) that the watcher now calls on its slow-check path (bin/fm-watch.sh:1028) and whose output becomes a user-visible check: fseventsd: wake reason. The script header and --help were updated; this shared index entry was not, so the repo's one-line-per-script map understates what the script owns.

🔧 Fix: retain fseventsd emergency evidence, update scripts index
2 infos still open:

  • ℹ️ bin/fm-telemetry.sh:504 - The pressure escalation branch correctly fires at warn-or-worse (FSEVENTSD_PRESSURE_LEVEL -ge 2), but its reason string is hardcoded to 'warning plus yellow memory pressure'. macOS encodes 4 as CRITICAL (red), not warning/yellow. Concrete trace, which the suite itself exercises (tests/fm-telemetry.test.sh:322-327): two consecutive 600 MiB samples with FM_FAKE_PRESSURE_LEVEL=4 emit ACTION: fseventsd MEM=600.00 MiB; two consecutive samples above 512 MiB; warning plus yellow memory pressure; pressure_level=4 swap_used=0.00 GiB. The operator is told the machine is at yellow pressure while the kernel is reporting critical. The numeric pressure_level=4 in the suffix keeps the alert diagnosable, so this is a wording defect rather than a lost signal; a level-aware phrase (e.g. 'warning plus warn-or-worse memory pressure', or yellow/red selected from the level) would match what was measured. Since this is user-facing alert text derived from the accepted report's 'yellow memory pressure' wording, confirm with the author.
  • ℹ️ bin/fm-telemetry.sh:504 - Lines 504 and 507 prepend ; unconditionally (reasons=&#34;${reasons}; warning plus ...&#34;), unlike every sibling reason append at 496, 500 and 519 which use the [ -z &#34;$reasons&#34; ] || reasons=&#34;$reasons; &#34; guard. Today this is safe only because both branches are gated on warning -eq 1, and warning is set to 1 exclusively alongside a non-empty reasons at line 471 or 485 - a non-local invariant. If a future warning condition is added that does not also write a reason, the alert emits a leading '; ' (ACTION: fseventsd MEM=600.00 MiB; ; warning plus more than 8 GiB swap; ...). Not reachable with the current code; worth aligning with the sibling pattern while the two forms sit four lines apart.

🔧 Fix: label fseventsd pressure by level, guard reason separators
1 info still open:

  • ℹ️ bin/fm-telemetry.sh:498 - The ACTION tier is an if/elif chain, so exactly one action reason is ever recorded even when several action conditions hold in the same sample. Concrete trace: fseventsd samples 600 MiB at t, then 2500 MiB at t+300 with pressure 4 and 9 GiB swap. Line 464 sets warning=1 ('two consecutive samples above 512 MiB'); line 487 sets doubling=1 (2500M >= 2*600M and above the 512 MiB floor); the pressure and swap action conditions also hold. The chain takes the first branch (line 496, 'MEM above 2 GiB') and lines 498/503/509 never execute, so the emitted alert is ACTION: fseventsd MEM=2500.00 MiB; two consecutive samples above 512 MiB; MEM above 2 GiB; pressure_level=4 swap_used=8.79 GiB. The pressure and swap evidence survives numerically in the suffix, but the measured doubling-within-one-hour signal is computed and then silently discarded - nothing in the output says the footprint doubled. That is the same evidence-dropping class the author ruled on in round 2 ('ACTION alerts must append and retain all same-sample warning evidence before adding the ACTION reason, so crash-forensics output remains diagnosable') and in round 3 for the emergency tier; the fix rounds converted assignment to append but left the four action conditions mutually exclusive. Fix would be to evaluate the four action conditions independently and append each reason that holds instead of elif-chaining them. Since it changes user-facing alert text, confirm with the author.

🔧 Fix: report every fseventsd action reason that holds
2 issues (1 warning, 1 info) still open:

  • ⚠️ bin/fm-watch.sh:1028 - The fseventsd check is unconditional at the top of the slow-check block, and age_of returns 999999 for a missing file (bin/fm-watch.sh:588-592), so .last-check being absent makes age_of &gt;= CHECK_INTERVAL true on the FIRST cycle of every watcher spawn — even for the many tests that set FM_CHECK_INTERVAL=999999 specifically to suppress the sweep. Every one of the ~90 $WATCH spawns across tests/fm-watch-triage.test.sh (56), tests/fm-watcher-lock.test.sh (14), tests/fm-pr-check-security.test.sh (9), tests/fm-wake-queue.test.sh (6) and others therefore executes the real pgrep -x fseventsd + top -l 1 -pid ... against the live host, because those tests prepend a fakebin that contains no pgrep/top (e.g. tests/fm-watch-triage.test.sh:50). Two concrete consequences. (1) Host coupling: the MEM above 2 GiB rule fires on a single sample with empty history (bin/fm-telemetry.sh:494), so on a machine where fseventsd has actually grown past 2 GiB — precisely the incident this monitors — every watcher-spawning test gets an unexpected check: fseventsd: ACTION ... wake and the watcher exits at cycle 1, failing dozens of unrelated tests for reasons invisible in their assertions. (2) Cost/timing: top -l 1 -pid 342 -stats pid,command,mem measured 0.95s wall on this host, so roughly 90 seconds is added to the suite and ~1s of unmodelled latency is inserted into the first cycle of poll-timing-sensitive triage tests. The new colocated test (tests/fm-watcher-lock.test.sh:754) correctly fakes the samplers; the other watcher tests have no such isolation. Needs an author decision on the seam (a fake-sampler default in the shared watcher test harness, or an explicit opt-out env the tests set), so flagging rather than fixing.
  • ℹ️ bin/fm-telemetry.sh:534 - fseventsd_record_sample ... || return 0 discards the computed alert whenever persisting the history sample fails. fseventsd_record_sample (bin/fm-telemetry.sh:397-412) returns non-zero on mktemp failure, on the prune awk failing, on the append printf failing, or on mv -f failing — i.e. exactly the disk-full / state-directory-unwritable conditions that correlate with a host already degrading. Concrete trace: fseventsd is at 3.5 GiB on the third consecutive rising sample with pressure level 2, severity=emergency and the full reason string are built, then the root volume is full so mktemp &#34;$TELEMETRY_DIR/.fseventsd-history.XXXXXX&#34; fails and the function returns 0 without ever calling fseventsd_publish_alert. The operator gets no EMERGENCY and no diagnostic — the watcher sees empty output and treats the cycle as healthy. Emitting the alert is strictly safer than swallowing it: fseventsd_publish_alert dedupes off $FSEVENTSD_ALERT, which is a separate file, so a failed history write cannot cause repeat alerts. Suggested fix: record the sample, and on failure still publish the alert (optionally via diagnostic for the persistence failure) instead of returning early.

🔧 Fix: isolate watcher tests from live fseventsd, alert on write failure
1 info still open:

  • ℹ️ tests/lib.sh:43 - The isolation seam is exported from tests/lib.sh, so it only reaches suites that source that library. Three opt-in live e2e suites spawn the real watcher without sourcing it — tests/fm-grok-continuity-live-e2e.test.sh:71, tests/fm-opencode-primary-live-e2e.test.sh:301 and tests/fm-pi-primary-live-e2e.test.sh:260 each cp &#34;$ROOT/bin/fm-watch-arm.sh&#34; into the fixture project and have the agent run it, and none of them set FM_TELEMETRY_FSEVENTSD_DISABLE (grep returns 0 hits for FM_TELEMETRY in all three). Those watchers therefore still execute the real pgrep/top against this host on their first cycle, and on a machine where fseventsd has actually crossed 2 GiB the single-sample MEM above 2 GiB rule (bin/fm-telemetry.sh:501) would wake and exit the watcher mid-test. The blast radius is bounded: all three are gated behind FM_GROK_LIVE_E2E / FM_OPENCODE_LIVE_E2E / FM_PI_LIVE_E2E and skip by default, and by design they already drive real agents on the real host. tests/fm-claude-stop-autoarm-live-e2e.test.sh substitutes its own fake fm-watch-arm.sh so it is unaffected. Noting this as the known limit of the round-6 seam rather than asking for the three unrelated live suites to be touched in this task.
✅ **Test** - passed

✅ No issues found.

  • bash tests/fm-telemetry.test.sh — all 28 cases pass, including the 13 new fseventsd cadence/warning/action/emergency/pressure-encoding/persistence-failure cases
  • bash tests/fm-watcher-lock.test.sh — all cases pass, including test_watcher_surfaces_fseventsd_warning_on_slow_check_path and test_watcher_tests_never_sample_the_live_fseventsd
  • Manual E2E: FM_EVIDENCE_ROOT=&lt;repo&gt; /Users/ivan/.no-mistakes/evidence/01M0SQSCDK191W0FGA39ASB6G1/e2e-fseventsd-early-warning.sh — six scenarios, each seeding durable state/telemetry/fseventsd-samples history then running the real bin/fm-watch.sh and the real bin/fm-wake-drain.sh
  • Live-host check: FM_TELEMETRY_FSEVENTSD_DISABLE=0 bin/fm-telemetry.sh fseventsd-check against the machine's actual fseventsd (PID 342), plus top -l 1 -pid 342 -stats pid,command,mem,rsize,compressed,cpu,time,threads to confirm the parsed footprint matched
  • git status --porcelain — worktree clean, no transient artifacts left
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@withally
withally merged commit c8a82e7 into main Aug 24, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant