@chrono-meta/fh-gate 1.4.86 → 1.4.88

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39,6 +39,7 @@ if command -v gh >/dev/null 2>&1; then
39
39
  # `[{"number":227},{"number":226},{"number":225}]` → old 1, new 3; `[]` → 0 both ways — which is
40
40
  # why an environment with zero open PRs can never surface this, and why the 0-case alone is not a
41
41
  # calibration. Observed live the same day: 2 open PRs reported as 1.
42
+ # Anchored by scripts/test_session_close_chain_lanes.sh lane ①-b-P3.
42
43
  PRS=$(gh pr list --author "@me" --state open --json number 2>/dev/null | grep -o '"number"' | wc -l | tr -d ' ' || true)
43
44
  [ "${PRS:-0}" -gt 0 ] && echo "⚠️ ①-b $PRS open PR(s) by you — classify: self-mergeable vs awaiting-external"
44
45
  fi
@@ -72,11 +73,16 @@ if [ "${FH_CHANGED:-0}" -gt 0 ]; then
72
73
  fi
73
74
  fi
74
75
 
75
- # real-time completion log — required whenever any commit landed today
76
+ # ④-log real-time completion log — required whenever any commit landed today.
77
+ # NAMING: this block does NOT implement CLAUDE.md's ④ (memory hygiene). It implements the
78
+ # "Real-time completion tracking" paragraph that sits above the chain. Labelling it ④ meant a green
79
+ # "④" told a reader memory hygiene had been verified when nothing had checked it — false coverage,
80
+ # the same class as a summary line that prints PASS while the exit code refuses. The label now says
81
+ # what it checks. (Numbering mismatch found 2026-08-02 by the lane-writing pass.)
76
82
  COMMITS_TODAY=$(git -C "$FH" log --since="today 00:00" --oneline 2>/dev/null | wc -l | tr -d ' ')
77
83
  FC="$FH/tracks/_meta/fh_completed_${TODAY}.md"
78
84
  if [ "$COMMITS_TODAY" -gt 0 ] && [ ! -f "$FC" ]; then
79
- echo "❌ commits landed today but tracks/_meta/fh_completed_${TODAY}.md is missing"
85
+ echo "❌ ④-log commits landed today but tracks/_meta/fh_completed_${TODAY}.md is missing"
80
86
  FAIL=1
81
87
  fi
82
88
 
@@ -150,12 +156,20 @@ if [ "$HOOK_OK" -eq 0 ]; then
150
156
  echo " (that file is gitignored, so a fresh clone has none). An unmeasured dispatch count is"
151
157
  echo " NOT a count of zero. Install: templates/subagent-tally-hook.json → .claude/settings.json"
152
158
  fi
153
- DISPATCHED=$(grep -c "^$TODAY$" "$TALLY" 2>/dev/null | tr -d ' ' || echo 0)
159
+ # `grep -c` PRINTS 0 and EXITS 1 when the count is zero. Under `set -o pipefail` (line 16) that
160
+ # makes the pipeline fail, `|| echo 0` appends a SECOND line, and the value becomes "0\n0" — which
161
+ # `[ -eq ]` rejects as a bash error, so the branch it guards is skipped. The guard was therefore
162
+ # disarmed in EXACTLY the case it exists for (zero log entries). Measured 2026-08-04 during a close:
163
+ # the check printed ✅ while `[: 0\n0: integer expression expected` went to stderr.
164
+ # Fix = no `|| echo`, plus integer sanitation. This is the documented prescription for this class
165
+ # ([[feedback_pipefail_fallback_disarms_guard]]) applied to the checker that cited it.
166
+ _int() { case "${1:-}" in (''|*[!0-9]*) echo 0 ;; (*) echo "$1" ;; esac; }
167
+ DISPATCHED=$(_int "$(grep -c "^$TODAY$" "$TALLY" 2>/dev/null | tr -d ' ' || true)")
154
168
  # Both quotings, because the file carries both: hand-written entries use `- date: 2026-08-02`
155
169
  # while anything appended via yaml.dump renders `- date: '"'"'2026-08-02'"'"'`. Matching one form counted
156
170
  # half the entries as absent — a divergent-normalizer miss inside the check that exists to catch
157
171
  # missing records. Known-pair calibrated below in test_dispatch_log_lanes.sh.
158
- LOGGED=$(grep -cE "^- date: *'?$TODAY'?" "$LOG" 2>/dev/null | tr -d ' ' || echo 0)
172
+ LOGGED=$(_int "$(grep -cE "^- date: *'?$TODAY'?" "$LOG" 2>/dev/null | tr -d ' ' || true)")
159
173
  if [ "${DISPATCHED:-0}" -gt 0 ] && [ "${LOGGED:-0}" -eq 0 ]; then
160
174
  echo "❌ ④-e $DISPATCHED sub-agent dispatch(es) today and ZERO invocation-log entries — the 60/40"
161
175
  echo " promotion gate and the UAP loop both read that file; an unlogged session is invisible to"
@@ -163,6 +163,15 @@ cat > "$TMP/s5_positive.sh" <<'EOF'
163
163
  set -uo pipefail
164
164
  N=$(find /nope . -maxdepth 1 2>/dev/null | grep -c . || echo 0)
165
165
  M=$(git log --oneline 2>/dev/null | wc -l || echo 0)
166
+ # WIDENED 2026-08-04. Every line below was INVISIBLE to the narrowed rule, and each was verified to
167
+ # actually produce "0\n0" before being pinned here (line count measured, not assumed):
168
+ P=$(cat /etc/hosts | grep -c . | tr -d ' ' || echo 0) # transparent filter after the counter
169
+ Q=$(grep -c "^nosuchline$" /etc/hosts 2>/dev/null | tr -d ' ' || echo 0) # the PR #251 shape
170
+ R=$(grep -Ec "^nosuchline$" /etc/hosts || echo 0) # combined flag cluster -Ec
171
+ S=$(grep --count "^nosuchline$" /etc/hosts || echo 0) # long option
172
+ T=$(grep -Fcx "nosuchline" /etc/hosts || echo 0) # -Fcx
173
+ U=$(false | grep -c . | cat || echo 0) # trailing stage that always emits
174
+ V=$(false | grep -c . | grep -v nosuch || echo 0) # trailing grep whose pattern misses the "0"
166
175
  EOF
167
176
  cat > "$TMP/s5_negative.sh" <<'EOF'
168
177
  #!/usr/bin/env bash
@@ -174,8 +183,8 @@ J=$(printf '%s' "$x" | jq -r '.a // 0' 2>/dev/null || echo 0)
174
183
  # A comment describing the defect must not be scored as the defect: cmd | grep -c . || echo 0
175
184
  EOF
176
185
  n=$(s_hits "$TMP/s5_positive.sh")
177
- [ "$n" -eq 2 ] && ok "S5 known-positive: counter-stage fallbacks (grep -c, wc) detected 2/2" \
178
- || bad "S5 known-positive: expected 2 S-hits, got $n — the pipefail disarm is invisible"
186
+ [ "$n" -eq 9 ] && ok "S5 known-positive: 9/9 — incl. -Ec/--count/-Fcx flag clusters, trailing \`| cat\`/\`| grep -v\`, and the no-upstream-pipe form" \
187
+ || bad "S5 known-positive: expected 9 S-hits, got $n — 2 = the pre-2026-08-04 rule (pipe-presence anchor); 4 = the first widening, which still missed every combined flag cluster (\`-Ec\`, \`--count\`, \`-Fcx\`) and any trailing stage outside a hardcoded name list"
179
188
 
180
189
  n=$(s_hits "$TMP/s5_negative.sh")
181
190
  [ "$n" -eq 0 ] && ok "S5 known-negative: \`||\` chains, empty-on-failure pipelines and comments stay silent" \
@@ -75,7 +75,14 @@ PY
75
75
  [ "$ok" -eq 0 ] ; chk $? "hook ends in exit 0 — a non-zero hook exit discards its stdout SILENTLY"
76
76
  # run it against a scratch HUB and confirm it appends today's date
77
77
  CLAUDE_PROJECT_DIR="$T/hub" bash -c "$cmd" >/dev/null 2>&1
78
- [ "$(grep -c "$(date +%Y-%m-%d)" "$T/hub/tracks/_meta/.subagent_dispatch_tally" 2>/dev/null || echo 0)" -ge 1 ]
78
+ # Split + sanitize, not `grep -c || echo 0`: on no-match `grep -c` PRINTS "0" and exits 1, so
79
+ # the fallback appends a SECOND line and `[ -ge ]` dies with "integer expression expected".
80
+ # Here that error happens to land on the FAIL branch — honest scope: this was never a live
81
+ # fail-open, it was a verdict reached by a bash error instead of a comparison, one refactor
82
+ # away from flipping. Found by the S5 sweep after the rule was widened (2026-08-04).
83
+ _tally_n=$(grep -c "$(date +%Y-%m-%d)" "$T/hub/tracks/_meta/.subagent_dispatch_tally" 2>/dev/null); _tally_n=${_tally_n:-0}
84
+ case "$_tally_n" in (*[!0-9]*|'') _tally_n=0 ;; esac
85
+ [ "$_tally_n" -ge 1 ]
79
86
  chk $? "hook actually appends a dated line when run (not merely present)"
80
87
  fi
81
88
  else
@@ -93,6 +100,39 @@ hookless_verdict() { # $1=hook_configured -> MEASURED | NOT-MEASURED
93
100
  grep -q "NOT MEASURED" "$CHECK" ; chk $? "the subject actually carries that branch (not just this lane)"
94
101
  [ -f "$SCRIPT_DIR/../templates/subagent-tally-hook.json" ] ; chk $? "installable snippet exists for a clone that has no settings.json"
95
102
 
103
+ echo "── pipefail disarm (the guard was dead in EXACTLY its target case) ──"
104
+ # `grep -c` PRINTS 0 and EXITS 1 on a zero count. Under `set -o pipefail` the old
105
+ # `grep -c … | tr -d ' ' || echo 0` therefore produced "0\n0", `[ -eq ]` threw, and the ❌ branch
106
+ # was skipped — so ④-e reported ✅ whenever the log had ZERO entries, which is the one situation it
107
+ # exists to block. Measured live 2026-08-04 during a session close.
108
+ # Lane 1: reproduce the OLD form and assert it is broken (a control — if this ever passes, the
109
+ # premise changed and the fix below is measuring nothing).
110
+ old_form=$(bash -c 'set -uo pipefail; grep -c "^NOSUCHDATE$" '"$CHECK"' 2>/dev/null | tr -d " " || echo 0')
111
+ [ "$(printf %s "$old_form" | grep -c "")" -eq 2 ] ; chk $? "control: the old \`|| echo 0\` form really does yield TWO lines under pipefail"
112
+ # Lane 2: the shipped form yields a single sanitised integer.
113
+ new_form=$(bash -c 'set -uo pipefail; _int() { case "${1:-}" in (""|*[!0-9]*) echo 0 ;; (*) echo "$1" ;; esac; }; _int "$(grep -c "^NOSUCHDATE$" '"$CHECK"' 2>/dev/null | tr -d " " || true)"')
114
+ [ "$new_form" = "0" ] ; chk $? "shipped form yields a single sanitised 0"
115
+ ( set -uo pipefail; [ "$new_form" -eq 0 ] ) 2>/dev/null ; chk $? "and \`[ -eq ]\` accepts it (the old form threw here)"
116
+ # Lane 3-5: BEHAVIOUR, not spelling. The first draft of these lanes string-matched the old form and
117
+ # went GREEN on a revert — the needle did not match `tr -d ' '` (quotes), so the anchor was
118
+ # decorative in the way this repo already named ([[feedback_anchor_can_be_decorative]], cause:
119
+ # "needle matching unrelated text"). Extract the subject's OWN assignment lines and evaluate them.
120
+ _eval_subject_assign() { # $1 = variable name; echoes what the SUBJECT computes for a zero match
121
+ local var="$1" line
122
+ line=$(grep -E "^${var}=" "$CHECK" | head -1)
123
+ [ -n "$line" ] || { echo "NOLINE"; return; }
124
+ bash -c "set -uo pipefail
125
+ _int() { case \"\${1:-}\" in (''|*[!0-9]*) echo 0 ;; (*) echo \"\$1\" ;; esac; }
126
+ TODAY=NOSUCHDATE; TALLY='$CHECK'; LOG='$CHECK'
127
+ $line
128
+ printf %s \"\$$var\"" 2>/dev/null
129
+ }
130
+ for _v in DISPATCHED LOGGED; do
131
+ _got=$(_eval_subject_assign "$_v")
132
+ [ "$(printf %s "$_got" | grep -c '')" -le 1 ] ; chk $? "subject's \$$_v is ONE line on a zero match (was two: the disarm)"
133
+ ( set -uo pipefail; [ "${_got:-x}" -eq 0 ] ) 2>/dev/null ; chk $? "subject's \$$_v survives \`[ -eq 0 ]\` (the disarm threw here)"
134
+ done
135
+
96
136
  echo ""
97
137
  if [ "$FAILED" -ne 0 ]; then echo "DISPATCH-LOG LANES: FAIL"; exit 1; fi
98
138
  echo "DISPATCH-LOG LANES: PASS ($PASS/$PASS)"
@@ -0,0 +1,211 @@
1
+ #!/usr/bin/env bash
2
+ # test_node_infra_delta_lanes.sh — lanes for the INFRA-DELTA branch of scripts/fh_node_check.sh.
3
+ #
4
+ # WHY A SEPARATE FILE FROM test_node_check_lanes.sh: that suite covers the floor probes (git hooks,
5
+ # Mode D companion detection, emission model) and never enters the infra-delta branch — every one of
6
+ # its fixtures runs against a state file whose PREV_HEAD is either empty (first session) or equal to
7
+ # HEAD. The branch that reacts to `git pull` moving install-relevant files was unexercised, which is
8
+ # how the four items below survived. Keeping it separate also keeps the two suites' fixture families
9
+ # from colliding: this one must CHANGE HEAD between runs, which breaks the "event once then silent"
10
+ # lane in the other file.
11
+ #
12
+ # CALIBRATION RULE OBSERVED THROUGHOUT: every lane asserting an ABSENCE names its known-positive in
13
+ # the lane title and builds it from the SAME fixture family in the SAME run. Exit codes are read
14
+ # DIRECTLY off the subject, never through a pipe.
15
+ #
16
+ # ⓘ GAP lanes pin behaviour believed WRONG without failing the suite; if the behaviour changes they
17
+ # announce "GAP CLOSED" so the lane gets promoted instead of silently rotting.
18
+ #
19
+ # Exit 0 = every asserting lane calibrated · 1 = the instrument is wrong.
20
+
21
+ set -uo pipefail
22
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
23
+ CHECK="$SCRIPT_DIR/fh_node_check.sh"
24
+ [ -f "$CHECK" ] || { echo "FAIL subject missing: $CHECK"; exit 1; }
25
+
26
+ TMPROOT=$(mktemp -d "${TMPDIR:-/tmp}/fh_infra_lanes.XXXXXX")
27
+ trap 'rm -rf "$TMPROOT"' EXIT
28
+ FAILED=0; PASSED=0; GAPS=0
29
+
30
+ _pass() { echo "✅ $1"; PASSED=$((PASSED+1)); }
31
+ _fail() { echo "❌ $1"; FAILED=1; }
32
+ _line() { # $1=name $2=pattern $3=expect(0/1) $4=output
33
+ local hit=0
34
+ printf '%s\n' "$4" | grep -q -- "$2" && hit=1
35
+ if [ "$hit" = "$3" ]; then _pass "$1 (hit=$hit, expected=$3)"
36
+ else _fail "$1 — hit=$hit, expected=$3"; printf '%s\n' "$4" | sed 's/^/ │ /'; fi
37
+ }
38
+ _rc() { if [ "$2" = "$3" ]; then _pass "$1 (exit=$2, expected=$3)"; else _fail "$1 — exit=$2, expected=$3"; fi; }
39
+ _gap() { # $1=name $2=observed(1=still open) $3=note
40
+ if [ "$2" = "1" ]; then echo "ⓘ GAP (still open) $1"; echo " ↳ $3"; GAPS=$((GAPS+1))
41
+ else echo "🎉 GAP CLOSED — $1 : behaviour changed, promote this lane to an asserting one"; fi
42
+ }
43
+
44
+ # ── fixtures ────────────────────────────────────────────────────────────────────
45
+ # A hub with FH's own gate sentinel in both hooks, so MISS is empty and any output is attributable
46
+ # to the infra-delta branch alone (instrument-discrimination: a lane that cannot tell which leg
47
+ # fired is not measuring that leg).
48
+ _hub() { # $1=name → echoes path
49
+ local d="$TMPROOT/$1"
50
+ mkdir -p "$d/.git" || true
51
+ git -C "$d" init -q 2>/dev/null || { mkdir -p "$d"; git -C "$d" init -q; }
52
+ git -C "$d" config user.email anchor@local; git -C "$d" config user.name anchor
53
+ mkdir -p "$d/.git/hooks"
54
+ printf '#!/bin/sh\n# FH 4-Axis Gate\nexit 0\n' > "$d/.git/hooks/pre-commit"
55
+ cp "$d/.git/hooks/pre-commit" "$d/.git/hooks/pre-push"
56
+ chmod +x "$d/.git/hooks/pre-commit" "$d/.git/hooks/pre-push"
57
+ echo seed > "$d/seed.txt"
58
+ git -C "$d" add -A >/dev/null 2>&1
59
+ git -C "$d" -c commit.gpgsign=false commit -qm seed >/dev/null 2>&1
60
+ printf '%s' "$d"
61
+ }
62
+ _commit() { # $1=hub $2..=paths to create
63
+ local d="$1" p; shift
64
+ for p in "$@"; do mkdir -p "$d/$(dirname "$p")"; echo changed >> "$d/$p"; done
65
+ git -C "$d" add -A >/dev/null 2>&1
66
+ git -C "$d" -c commit.gpgsign=false commit -qm change >/dev/null 2>&1
67
+ }
68
+ _run() { # $1=hub $2=statefile ; sets OUT/RC (RC read directly, no pipe)
69
+ OUT=$(HUB_DIR="$1" FH_NODE_STATE="$2" FH_MACHINE_ID=lanebox bash "$CHECK" 2>&1); RC=$?
70
+ }
71
+ # seed the state so PREV_HEAD is the pre-change commit (this is the ONLY way into the branch)
72
+ _seed_state() { _run "$1" "$2" >/dev/null 2>&1; }
73
+
74
+ NEW='🆕 Install-relevant assets changed'
75
+ HDR='🖥️ \[node\]'
76
+
77
+ echo "══ infra delta: fires on an install-relevant change, silent otherwise ══"
78
+ H=$(_hub p_pos); _seed_state "$H" "$TMPROOT/st_pos"
79
+ _commit "$H" scripts/fh_env_delta_scan.sh
80
+ _run "$H" "$TMPROOT/st_pos"
81
+ _line "ID-P install-relevant file changed → 🆕 block fires" "$NEW" 1 "$OUT"
82
+ _line "ID-P → names the changed path" 'scripts/fh_env_delta_scan.sh' 1 "$OUT"
83
+ _line "ID-P → routes to the wizard" 'Re-run /install-wizard' 1 "$OUT"
84
+ _rc "ID-P detector never gates (exit 0)" "$RC" 0
85
+
86
+ # CONTROL for ID-P, same fixture family, same run-shape: HEAD advances by exactly one commit, but
87
+ # the changed path is outside the install surface. Silence here is only meaningful BECAUSE ID-P
88
+ # above spoke on an otherwise identical fixture.
89
+ H=$(_hub p_ctl); _seed_state "$H" "$TMPROOT/st_ctl"
90
+ _commit "$H" README.md
91
+ _run "$H" "$TMPROOT/st_ctl"
92
+ _line "ID-C non-install path changed → no 🆕 block (over-fire ctl for ID-P)" "$NEW" 0 "$OUT"
93
+ _rc "ID-C → exit 0" "$RC" 0
94
+
95
+ echo
96
+ echo "══ one fixture PER regex alternative (an untested alternative is an untested branch) ══"
97
+ # Deliberately one path per alternative: a single fixture touching several at once would let any
98
+ # alternative be deleted with the suite staying green — the lane4c lesson from test_node_check_lanes.
99
+ i=0
100
+ for p in "templates/.git-hooks/pre-push" \
101
+ "templates/settings.SessionStart.snippet.json" \
102
+ "scripts/fh_session_load.sh" \
103
+ "plugins/fh-meta/skills/install-wizard/SKILL.md" \
104
+ "plugins/fh-meta/skills/install-doctor/SKILL.md"; do
105
+ i=$((i+1))
106
+ H=$(_hub "alt$i"); _seed_state "$H" "$TMPROOT/st_alt$i"
107
+ _commit "$H" "$p"
108
+ _run "$H" "$TMPROOT/st_alt$i"
109
+ _line "ID-R$i alternative covered: $p" "$NEW" 1 "$OUT"
110
+ done
111
+
112
+ # NEGATIVE for the regex: install-adjacent-LOOKING paths that the pattern deliberately does not
113
+ # cover. Without this leg "the regex matches everything" would also pass ID-R1..5.
114
+ H=$(_hub alt_neg); _seed_state "$H" "$TMPROOT/st_altneg"
115
+ _commit "$H" "plugins/fh-meta/skills/harvest-loop/SKILL.md"
116
+ _run "$H" "$TMPROOT/st_altneg"
117
+ _line "ID-Rn non-install skill path → not treated as infra (paired with ID-R4/5)" "$NEW" 0 "$OUT"
118
+
119
+ echo
120
+ echo "══ unreachable PREV_HEAD: UNMEASURED, not silence ══"
121
+ H=$(_hub p_unreach); _commit "$H" scripts/fh_env_delta_scan.sh
122
+ printf 'lanebox|1700000000|deadbee' > "$TMPROOT/st_unreach"
123
+ _run "$H" "$TMPROOT/st_unreach"
124
+ _line "ID-U gc/rebase/shallow → UNMEASURED is stated" 'UNMEASURED' 1 "$OUT"
125
+ _line "ID-U → does NOT claim the 🆕 delta (not found ≠ 0)" "$NEW" 0 "$OUT"
126
+ _rc "ID-U → exit 0" "$RC" 0
127
+
128
+ echo
129
+ echo "══ co-reporting: a missing floor does not suppress the infra delta ══"
130
+ H=$(_hub p_both); _seed_state "$H" "$TMPROOT/st_both"
131
+ rm -f "$H/.git/hooks/pre-commit"
132
+ _commit "$H" scripts/fh_node_check.sh
133
+ _run "$H" "$TMPROOT/st_both"
134
+ _line "ID-B missing floor reported" 'Missing mechanical floor' 1 "$OUT"
135
+ _line "ID-B AND infra delta reported (paired)" "$NEW" 1 "$OUT"
136
+
137
+ echo
138
+ echo "══ ⓘ GAP lanes — believed-wrong behaviour, pinned not asserted ══"
139
+
140
+ # GAP 1 — attribution. Every other emission path prints the 🖥️ [node] header; the infra-only and
141
+ # UNMEASURED-only paths print 4-space-indented CONTINUATION lines under a header that was never
142
+ # printed. This is not cosmetic in situ: SessionStart hooks on one matcher have their outputs
143
+ # delivered as separate, unordered, unlabelled blocks (measured in
144
+ # scripts/test_sessionstart_multihook_lanes.sh), so an unattributed indented block genuinely cannot
145
+ # be traced to the hook that emitted it.
146
+ H=$(_hub g_hdr); _seed_state "$H" "$TMPROOT/st_hdr"
147
+ _commit "$H" scripts/fh_env_delta_scan.sh
148
+ _run "$H" "$TMPROOT/st_hdr"
149
+ _g=0
150
+ { printf '%s\n' "$OUT" | grep -q "$NEW"; } && ! printf '%s\n' "$OUT" | grep -q "$HDR" && _g=1
151
+ _gap "infra-only emission has NO 🖥️ [node] header" "$_g" \
152
+ "Output is 4-space-indented continuation lines with no parent line. MISS and IDENTITY both print a \
153
+ header; the infra and UNMEASURED paths do not, because they hang off an if/elif that both fail."
154
+
155
+ # GAP 2 — silent truncation. `head -6` with no count. The script's own header argues 'not found ≠ 0'
156
+ # and 'distinguish no-change from could-not-measure'; an omitted-3-of-9 list is the same class of
157
+ # hidden omission, one layer in.
158
+ H=$(_hub g_trunc); _seed_state "$H" "$TMPROOT/st_trunc"
159
+ _commit "$H" scripts/fh_a.sh scripts/fh_b.sh scripts/fh_c.sh scripts/fh_d.sh \
160
+ scripts/fh_e.sh scripts/fh_f.sh scripts/fh_g.sh scripts/fh_h.sh scripts/fh_i.sh
161
+ _run "$H" "$TMPROOT/st_trunc"
162
+ LISTED=$(printf '%s\n' "$OUT" | grep -c '^ - ')
163
+ _g=0; { [ "$LISTED" = "6" ] && ! printf '%s\n' "$OUT" | grep -qiE 'more|[0-9]+ of [0-9]+|truncat'; } && _g=1
164
+ _gap "9 install-relevant files changed → 6 listed, 3 dropped with no count" "$_g" \
165
+ "listed=$LISTED, and no '…and N more'. A big pull (exactly when re-running the wizard matters most) \
166
+ silently hides the tail. Cheap fix: append the residual count."
167
+
168
+ # GAP 3 — the one-shot consumes an UNRESOLVED unknown. State is written UNCONDITIONALLY to HEAD_NOW,
169
+ # including on the run that could not compute the delta. Next session PREV_HEAD == HEAD, so the
170
+ # delta across that pull is never attempted again and never mentioned again. The script's header
171
+ # says events report once and CONDITIONS report until they stop being true — an un-computed delta is
172
+ # an unresolved condition, and it is being treated as a discharged event.
173
+ H=$(_hub g_once); _commit "$H" scripts/fh_env_delta_scan.sh
174
+ printf 'lanebox|1700000000|deadbee' > "$TMPROOT/st_once"
175
+ SPOKE=0
176
+ for _i in 1 2 3; do
177
+ _run "$H" "$TMPROOT/st_once"
178
+ printf '%s\n' "$OUT" | grep -q 'UNMEASURED' && SPOKE=$((SPOKE+1))
179
+ done
180
+ _g=0; [ "$SPOKE" = "1" ] && _g=1
181
+ _gap "UNMEASURED spoken once, then permanently forgotten (state advanced anyway)" "$_g" \
182
+ "spoke on $SPOKE of 3 consecutive runs. Same shape as the failure the state-based design was \
183
+ introduced to fix ('a broken machine reported once then went quiet forever') — the fix reached MISS \
184
+ but not the infra branch. CODE-vs-DOC: fh_node_check.sh's own header claims it distinguishes \
185
+ 'no change' from 'could not measure', yet after run 1 the two are indistinguishable forever."
186
+
187
+ # GAP 4 — the event is consumed even when nobody saw it. State advances before/independently of
188
+ # whether the emission reached anyone. Pairs directly with the F11 measurement: a SessionStart hook
189
+ # whose sibling ordering, delivery, or exit handling drops its output loses the notice permanently.
190
+ H=$(_hub g_unseen); _seed_state "$H" "$TMPROOT/st_unseen"
191
+ _commit "$H" scripts/fh_env_delta_scan.sh
192
+ HUB_DIR="$H" FH_NODE_STATE="$TMPROOT/st_unseen" FH_MACHINE_ID=lanebox bash "$CHECK" >/dev/null 2>&1
193
+ _run "$H" "$TMPROOT/st_unseen"
194
+ _g=0; printf '%s\n' "$OUT" | grep -q "$NEW" || _g=1
195
+ _gap "infra event is consumed by a run whose output nobody received" "$_g" \
196
+ "One discarded-stdout run and the notice is gone for good. The write is deliberately unconditional \
197
+ (so the timestamp does not mean 'last time it spoke'), but that argument covers the TIMESTAMP, not \
198
+ the HEAD field. Recording HEAD only when the delta was successfully computed AND emitted would keep \
199
+ the timestamp honest and stop discharging unseen events."
200
+
201
+ echo
202
+ echo "──────────────────────────────────────────────"
203
+ if [ "$FAILED" -ne 0 ]; then
204
+ echo "NODE INFRA-DELTA LANES: FAIL — instrument miscalibrated (do not trust its verdict)"
205
+ echo " asserting lanes passed: $PASSED · open gaps: $GAPS"
206
+ exit 1
207
+ fi
208
+ echo "NODE INFRA-DELTA LANES: PASS ($PASSED asserting lanes) · $GAPS KNOWN GAP(S) open (see ⓘ above)"
209
+ echo " Green covers: fires/does-not-fire, per-alternative regex coverage, UNMEASURED, co-reporting."
210
+ echo " Green does NOT cover the four gaps above."
211
+ exit 0