@chrono-meta/fh-gate 1.4.97 → 1.4.99
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.
- package/.claude-plugin/marketplace.json +2 -2
- package/CATALOG.md +19 -0
- package/CHEATSHEET.md +9 -1
- package/CLAUDE.md +28 -2
- package/README.ja.md +229 -42
- package/README.ko.md +241 -45
- package/README.md +168 -31
- package/README.zh.md +219 -40
- package/docs/OUTPUT_EVIDENCE.md +21 -12
- package/docs/pillars.svg +3 -7
- package/knowledge/shared/harness-core/fh_ecosystem_positioning.md +2 -0
- package/knowledge/shared/harness-core/fh_global_positioning_and_distribution_roadmap.md +136 -0
- package/knowledge/shared/harness-core/fh_three_layer_canon.md +20 -0
- package/knowledge/shared/harness-core/field_verdict_crossfamily_gate.md +215 -2
- package/knowledge/shared/harness-core/ship_readiness_gate.md +112 -0
- package/knowledge/shared/learnings/subagent_invocations_log.yaml +65 -0
- package/package.json +5 -1
- package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-commons/skills/ko-tech-writer/SKILL.md +63 -12
- package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-meta/CHANGELOG.md +166 -0
- package/plugins/fh-meta/skills/auto-decorrelation/SKILL.md +30 -0
- package/scripts/consent_registry_check.sh +124 -1
- package/scripts/degrade_direction_scan.sh +10 -1
- package/scripts/digest_landing_check.sh +20 -4
- package/scripts/fh_node_check.sh +128 -1
- package/scripts/fh_session_load.sh +22 -2
- package/scripts/frontier_digest_autopilot.sh +229 -0
- package/scripts/lane_runner_check.sh +294 -26
- package/scripts/package_coverage_check.sh +17 -0
- package/scripts/postinstall_notice.js +34 -0
- package/scripts/selfcheck.sh +183 -5
- package/scripts/test_consent_registry.sh +99 -0
- package/scripts/test_degrade_scan_shell_probes.sh +75 -0
- package/scripts/test_field_canon_lanes.sh +29 -5
- package/scripts/test_lane_runner_lanes.sh +295 -0
- package/scripts/test_node_check_lanes.sh +217 -0
- package/scripts/test_selfcheck_state_lanes.sh +61 -0
- package/scripts/test_stale_clone_guard_lanes.sh +21 -7
- package/scripts/test_version_lockstep_lanes.sh +62 -0
- package/scripts/version_lockstep_check.sh +143 -1
- package/templates/.git-hooks/pre-commit +22 -1
- package/templates/consent_classes.yaml.example +30 -0
- package/templates/degrade_direction_scan.sh +10 -1
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_lane_runner_lanes.sh — behavioural lanes for scripts/lane_runner_check.sh
|
|
3
|
+
#
|
|
4
|
+
# Why this file exists at all: until 2026-08-13 the checker that detects unrun lane suites had
|
|
5
|
+
# **no lane suite of its own**. It guarded its own WIRING (it fails if selfcheck.sh stops calling
|
|
6
|
+
# it) but nothing measured its BEHAVIOUR. That is the same shape it exists to catch, one level up —
|
|
7
|
+
# a check whose verdicts nobody exercises is prose with an exit code.
|
|
8
|
+
#
|
|
9
|
+
# The lanes below drive it against SYNTHETIC fixture trees, not against this repo. Driving it
|
|
10
|
+
# against the live tree would make every lane depend on today's suite list, so a lane would go red
|
|
11
|
+
# for reasons that have nothing to do with the predicate under test.
|
|
12
|
+
#
|
|
13
|
+
# Script-relative, NOT `git rev-parse --show-toplevel`: in a vendored tree rev-parse returns the
|
|
14
|
+
# OUTER repo and this suite would then test someone else's checkout.
|
|
15
|
+
set -uo pipefail
|
|
16
|
+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
17
|
+
CHECK="$REPO_ROOT/scripts/lane_runner_check.sh"
|
|
18
|
+
T=$(mktemp -d); trap 'rm -rf "$T"' EXIT
|
|
19
|
+
pass=0; fail=0
|
|
20
|
+
ok() { printf ' ✅ %s\n' "$1"; pass=$((pass+1)); }
|
|
21
|
+
bad() { printf ' ❌ %s\n' "$1"; fail=$((fail+1)); }
|
|
22
|
+
|
|
23
|
+
# ── fixture: a tree with ONE unwired suite and a selfcheck.sh that calls the checker ──────────
|
|
24
|
+
# The `bash scripts/lane_runner_check.sh` line is load-bearing: the checker refuses to run in a
|
|
25
|
+
# tree whose selfcheck.sh does not invoke it (its self-reference guard).
|
|
26
|
+
mkfixture() { # $1 = dir
|
|
27
|
+
local d="$1"
|
|
28
|
+
mkdir -p "$d/scripts"
|
|
29
|
+
cp "$CHECK" "$d/scripts/lane_runner_check.sh"
|
|
30
|
+
printf '#!/usr/bin/env bash\nbash scripts/lane_runner_check.sh\n' > "$d/scripts/selfcheck.sh"
|
|
31
|
+
printf '#!/usr/bin/env bash\n: orphan lane suite\n' > "$d/scripts/test_orphan_lanes.sh"
|
|
32
|
+
}
|
|
33
|
+
run() { ( cd "$1" && bash scripts/lane_runner_check.sh 2>&1 ); }
|
|
34
|
+
rcof() { ( cd "$1" && bash scripts/lane_runner_check.sh >/dev/null 2>&1; echo $? ); }
|
|
35
|
+
|
|
36
|
+
# ── L1 known-POSITIVE: an undeclared orphan suite must FAIL ───────────────────────────────────
|
|
37
|
+
# Without this arm every lane below proves nothing: a checker that passed unconditionally would
|
|
38
|
+
# satisfy all the "declaration suppresses it" lanes.
|
|
39
|
+
D="$T/l1"; mkfixture "$D"
|
|
40
|
+
out=$(run "$D"); rc=$(rcof "$D")
|
|
41
|
+
if [ "$rc" = "1" ] && printf '%s' "$out" | grep -q 'test_orphan_lanes.sh'; then
|
|
42
|
+
ok "L1 known-positive: undeclared orphan suite → rc=1 and it is named"
|
|
43
|
+
else
|
|
44
|
+
bad "L1 known-positive did not fire (rc=$rc) — every lane below is meaningless without it"
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# ── L2 NO-OP: no declaration file → behaviour is exactly the L1 behaviour ─────────────────────
|
|
48
|
+
# This is the arm that protects THIS repo, which ships no company/ directory.
|
|
49
|
+
D="$T/l2"; mkfixture "$D"
|
|
50
|
+
out=$(run "$D")
|
|
51
|
+
if ! printf '%s' "$out" | grep -q 'lane_declarations'; then
|
|
52
|
+
ok "L2 no-op: absent declaration file is silent (no mention in output)"
|
|
53
|
+
else
|
|
54
|
+
bad "L2 no-op: output mentions the declaration file when none exists"
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# ── L3 org EXEMPT suppresses the orphan ───────────────────────────────────────────────────────
|
|
58
|
+
D="$T/l3"; mkfixture "$D"; mkdir -p "$D/company"
|
|
59
|
+
printf 'exempt:\n - test_orphan_lanes.sh # org-owned, runs in the org CI only\n' > "$D/company/lane_declarations.yaml"
|
|
60
|
+
out=$(run "$D"); rc=$(rcof "$D")
|
|
61
|
+
if [ "$rc" = "0" ] && printf '%s' "$out" | grep -q '1 exempt'; then
|
|
62
|
+
ok "L3 org exempt: declared suite no longer fails the check (rc=0, counted exempt)"
|
|
63
|
+
else
|
|
64
|
+
bad "L3 org exempt did not suppress (rc=$rc) — out: $(printf '%s' "$out" | tail -1)"
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# ── L4 org DEBT suppresses AND is counted as debt ─────────────────────────────────────────────
|
|
68
|
+
D="$T/l4"; mkfixture "$D"; mkdir -p "$D/company"
|
|
69
|
+
printf 'debt:\n - test_orphan_lanes.sh\n' > "$D/company/lane_declarations.yaml"
|
|
70
|
+
out=$(run "$D"); rc=$(rcof "$D")
|
|
71
|
+
if [ "$rc" = "0" ] && printf '%s' "$out" | grep -q 'declared debt'; then
|
|
72
|
+
ok "L4 org debt: declared suite suppressed and reported as debt"
|
|
73
|
+
else
|
|
74
|
+
bad "L4 org debt did not land (rc=$rc)"
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# ── L5 VISIBILITY: an org-declared list must never look like this repo's own zero ─────────────
|
|
78
|
+
D="$T/l5"; mkfixture "$D"; mkdir -p "$D/company"
|
|
79
|
+
printf 'debt:\n - test_orphan_lanes.sh\n' > "$D/company/lane_declarations.yaml"
|
|
80
|
+
out=$(run "$D")
|
|
81
|
+
if printf '%s' "$out" | grep -q 'org-declared'; then
|
|
82
|
+
ok "L5 visibility: output names the org file and its counts"
|
|
83
|
+
else
|
|
84
|
+
bad "L5 visibility: org declarations are invisible in the output"
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# ── L6 FAIL-CLOSED on an unparseable file ─────────────────────────────────────────────────────
|
|
88
|
+
# The load-bearing distinction. A malformed file must NOT read as "nothing declared" — that would
|
|
89
|
+
# render UNMEASURED as ZERO, and it would do so on the arm where a fork is relying on the file.
|
|
90
|
+
D="$T/l6"; mkfixture "$D"; mkdir -p "$D/company"
|
|
91
|
+
printf 'exempt:\n this line is not an entry\n' > "$D/company/lane_declarations.yaml"
|
|
92
|
+
out=$(run "$D"); rc=$(rcof "$D")
|
|
93
|
+
if [ "$rc" = "2" ] && printf '%s' "$out" | grep -q 'UNMEASURED'; then
|
|
94
|
+
ok "L6 fail-closed: unparseable declaration file → rc=2 and says UNMEASURED"
|
|
95
|
+
else
|
|
96
|
+
bad "L6 fail-closed FAILED (rc=$rc) — a malformed file was read as 'no declarations'"
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
# ── L6b control for L6: the SAME orphan tree with a WELL-FORMED file must not exit 2 ──────────
|
|
100
|
+
# Without this, L6 would also pass on a checker that exits 2 for any declaration file at all.
|
|
101
|
+
D="$T/l6b"; mkfixture "$D"; mkdir -p "$D/company"
|
|
102
|
+
printf 'exempt:\n - test_orphan_lanes.sh\n' > "$D/company/lane_declarations.yaml"
|
|
103
|
+
if [ "$(rcof "$D")" != "2" ]; then
|
|
104
|
+
ok "L6b control: a well-formed file does NOT trip the fail-closed arm"
|
|
105
|
+
else
|
|
106
|
+
bad "L6b control: every declaration file exits 2 — L6 proved nothing"
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
# ── L7 unknown section is a parse failure, not a silent skip ──────────────────────────────────
|
|
110
|
+
D="$T/l7"; mkfixture "$D"; mkdir -p "$D/company"
|
|
111
|
+
printf 'allowed:\n - test_orphan_lanes.sh\n' > "$D/company/lane_declarations.yaml"
|
|
112
|
+
out=$(run "$D")
|
|
113
|
+
if [ "$(rcof "$D")" = "2" ] && printf '%s' "$out" | grep -q "unknown section"; then
|
|
114
|
+
ok "L7 unknown section → rc=2 and names the section"
|
|
115
|
+
else
|
|
116
|
+
bad "L7 unknown section was tolerated — a typo'd header would silently declare nothing"
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
# ── L8 the same name in both sections is ambiguous, not last-wins ─────────────────────────────
|
|
120
|
+
D="$T/l8"; mkfixture "$D"; mkdir -p "$D/company"
|
|
121
|
+
printf 'exempt:\n - test_orphan_lanes.sh\ndebt:\n - test_orphan_lanes.sh\n' > "$D/company/lane_declarations.yaml"
|
|
122
|
+
out=$(run "$D")
|
|
123
|
+
if [ "$(rcof "$D")" = "2" ] && printf '%s' "$out" | grep -q "both exempt and debt"; then
|
|
124
|
+
ok "L8 same suite in both sections → rc=2 (ambiguous, not silently resolved)"
|
|
125
|
+
else
|
|
126
|
+
bad "L8 double declaration was silently resolved"
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
# ── L9 a stale entry WARNS but does not block (same voice as upstream DEBT hygiene) ───────────
|
|
130
|
+
# Deliberately advisory: making the downstream mirror stricter than the upstream rule it mirrors
|
|
131
|
+
# would train forks to delete the file, which costs more than a stale line.
|
|
132
|
+
D="$T/l9"; mkfixture "$D"; mkdir -p "$D/company"
|
|
133
|
+
printf 'exempt:\n - test_orphan_lanes.sh\n - test_deleted_lanes.sh\n' > "$D/company/lane_declarations.yaml"
|
|
134
|
+
out=$(run "$D"); rc=$(rcof "$D")
|
|
135
|
+
if [ "$rc" = "0" ] && printf '%s' "$out" | grep -q 'test_deleted_lanes.sh'; then
|
|
136
|
+
ok "L9 stale entry: warns and names it, does not block (rc=0)"
|
|
137
|
+
else
|
|
138
|
+
bad "L9 stale entry handling wrong (rc=$rc)"
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
# ── L10 comments and blank lines are not entries ──────────────────────────────────────────────
|
|
142
|
+
D="$T/l10"; mkfixture "$D"; mkdir -p "$D/company"
|
|
143
|
+
printf '# org lane declarations\n\nexempt:\n\n - test_orphan_lanes.sh # reason lives here\n' > "$D/company/lane_declarations.yaml"
|
|
144
|
+
if [ "$(rcof "$D")" = "0" ]; then
|
|
145
|
+
ok "L10 comments/blank lines tolerated (a file people can annotate)"
|
|
146
|
+
else
|
|
147
|
+
bad "L10 a commented, spaced file failed to parse"
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
# ── L11/L12 embedded --self-test subjects (found→extend, 2026-08-14) ──────────────────────────
|
|
151
|
+
# A lane suite living INSIDE its subject as a `--self-test` flag has no `test_*.sh`/`*_lanes.sh`
|
|
152
|
+
# filename, so the suites glob above cannot see it at all — measured on this repo's own
|
|
153
|
+
# lane_runner_check.sh header before this feature existed: 4 such subjects had zero callers and
|
|
154
|
+
# nothing here said so. mkfixture without the orphan file, so these lanes isolate the self-test
|
|
155
|
+
# behaviour from L1's own blocking orphan.
|
|
156
|
+
mkfixture_clean() { # $1 = dir
|
|
157
|
+
local d="$1"
|
|
158
|
+
mkdir -p "$d/scripts"
|
|
159
|
+
cp "$CHECK" "$d/scripts/lane_runner_check.sh"
|
|
160
|
+
# A wired ordinary suite so the `suites` set is non-empty — an empty tree fails EXTRACTOR_BROKE
|
|
161
|
+
# before the self-test report section runs at all, which would make L11/L12 pass vacuously.
|
|
162
|
+
printf '#!/usr/bin/env bash\n: wired lane suite\n' > "$d/scripts/test_ok_lanes.sh"
|
|
163
|
+
printf '#!/usr/bin/env bash\nbash scripts/lane_runner_check.sh\nbash scripts/test_ok_lanes.sh\n' > "$d/scripts/selfcheck.sh"
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
# L11 known-POSITIVE: an unwired --self-test subject is reported, advisory (rc stays 0).
|
|
167
|
+
D="$T/l11"; mkfixture_clean "$D"
|
|
168
|
+
printf '#!/usr/bin/env bash\n[ "${1:-}" = "--self-test" ] && { echo ok; exit 0; }\n' > "$D/scripts/orphan_probe.sh"
|
|
169
|
+
out=$(run "$D"); rc=$(rcof "$D")
|
|
170
|
+
if [ "$rc" = "0" ] && printf '%s' "$out" | grep -q 'orphan_probe'; then
|
|
171
|
+
ok "L11 known-positive: unwired --self-test subject named, advisory (rc=0)"
|
|
172
|
+
else
|
|
173
|
+
bad "L11 known-positive did not fire (rc=$rc) — self-test subject not reported"
|
|
174
|
+
printf '%s\n' "$out" | sed 's/^/ │ /'
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
# L12 known-NEGATIVE: the SAME subject, wired through selfcheck.sh's _subj for-loop idiom, is
|
|
178
|
+
# NOT reported — the exact shape scripts/selfcheck.sh:478 actually uses.
|
|
179
|
+
D="$T/l12"; mkfixture_clean "$D"
|
|
180
|
+
printf '#!/usr/bin/env bash\n[ "${1:-}" = "--self-test" ] && { echo ok; exit 0; }\n' > "$D/scripts/orphan_probe.sh"
|
|
181
|
+
cat > "$D/scripts/selfcheck.sh" <<'SC'
|
|
182
|
+
#!/usr/bin/env bash
|
|
183
|
+
bash scripts/lane_runner_check.sh
|
|
184
|
+
bash scripts/test_ok_lanes.sh
|
|
185
|
+
for _subj in orphan_probe; do
|
|
186
|
+
bash "scripts/$_subj.sh" --self-test
|
|
187
|
+
done
|
|
188
|
+
SC
|
|
189
|
+
out=$(run "$D")
|
|
190
|
+
if ! printf '%s' "$out" | grep -q 'orphan_probe'; then
|
|
191
|
+
ok "L12 known-negative: --self-test subject wired via _subj for-loop is not reported"
|
|
192
|
+
else
|
|
193
|
+
bad "L12 known-negative: wired subject still reported as unwired"
|
|
194
|
+
printf '%s\n' "$out" | sed 's/^/ │ /'
|
|
195
|
+
fi
|
|
196
|
+
|
|
197
|
+
# L13 known-NEGATIVE: the SAME subject, wired through a DIRECT dispatch line — the shape
|
|
198
|
+
# scripts/selfcheck.sh:898/933 actually use (`bash scripts/<name>.sh ... --self-test`), no
|
|
199
|
+
# for-loop at all. Cross-family review 2026-08-14 caught the first draft shipping only the
|
|
200
|
+
# for-loop branch, which meant probe_scope_check.sh and utterance_landing_check.sh — both wired
|
|
201
|
+
# directly, both real — were reported UNDECLARED. This lane pins that class so it cannot silently
|
|
202
|
+
# come back: without it, L11/L12 alone only ever exercise the for-loop shape.
|
|
203
|
+
D="$T/l13"; mkfixture_clean "$D"
|
|
204
|
+
printf '#!/usr/bin/env bash\n[ "${1:-}" = "--self-test" ] && { echo ok; exit 0; }\n' > "$D/scripts/orphan_probe.sh"
|
|
205
|
+
cat > "$D/scripts/selfcheck.sh" <<'SC'
|
|
206
|
+
#!/usr/bin/env bash
|
|
207
|
+
bash scripts/lane_runner_check.sh
|
|
208
|
+
bash scripts/test_ok_lanes.sh
|
|
209
|
+
bash scripts/orphan_probe.sh --self-test >/dev/null 2>&1
|
|
210
|
+
SC
|
|
211
|
+
out=$(run "$D")
|
|
212
|
+
if ! printf '%s' "$out" | grep -q 'orphan_probe'; then
|
|
213
|
+
ok "L13 known-negative: --self-test subject wired via direct dispatch is not reported"
|
|
214
|
+
else
|
|
215
|
+
bad "L13 known-negative: directly-wired subject still reported as unwired"
|
|
216
|
+
printf '%s\n' "$out" | sed 's/^/ │ /'
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
# ── L14/L15 the SUBJECT'S OWN documentation is not a caller (2026-08-15) ──────────────────────
|
|
220
|
+
# Measured on this repo: `self-test: 10/10 wired` while directional_diff_gate.sh had ZERO callers
|
|
221
|
+
# (`grep -rn directional_diff_gate scripts/*.sh templates/.git-hooks/* .github/workflows/*.yml`,
|
|
222
|
+
# excluding the checker and the subject itself → nothing). The subject's own usage comment
|
|
223
|
+
# (`# bash scripts/directional_diff_gate.sh --self-test`) matched the direct-dispatch regex, so
|
|
224
|
+
# the checker read the file's documentation of itself as evidence that something ran it.
|
|
225
|
+
#
|
|
226
|
+
# This is the SAME class the checker already closes one level up and, until now, only there:
|
|
227
|
+
# `runners` drops lane_runner_check.sh so the tool cannot certify its own DEBT list as done, and
|
|
228
|
+
# has_runner() skips a suite's own file, and runner_dispatches() skips comment lines. None of the
|
|
229
|
+
# three guards reached the --self-test branch — has_selftest_runner() scanned every runner
|
|
230
|
+
# including the subject, over raw text including comments. A guard that exists in one predicate
|
|
231
|
+
# and not in its sibling is not a guard, it is a coincidence.
|
|
232
|
+
#
|
|
233
|
+
# Two lanes because the two halves fail independently: self-exclusion alone still lets a COMMENT
|
|
234
|
+
# in a third file certify a subject, and comment-skipping alone still lets a subject's own
|
|
235
|
+
# non-comment self-reference do it.
|
|
236
|
+
|
|
237
|
+
# L14 known-POSITIVE: the subject's OWN usage comment must not count as a caller.
|
|
238
|
+
D="$T/l14"; mkfixture_clean "$D"
|
|
239
|
+
cat > "$D/scripts/orphan_probe.sh" <<'SUBJ'
|
|
240
|
+
#!/usr/bin/env bash
|
|
241
|
+
# Usage:
|
|
242
|
+
# bash scripts/orphan_probe.sh --self-test # known-pair calibration
|
|
243
|
+
[ "${1:-}" = "--self-test" ] && { echo ok; exit 0; }
|
|
244
|
+
SUBJ
|
|
245
|
+
out=$(run "$D"); rc=$(rcof "$D")
|
|
246
|
+
if [ "$rc" = "0" ] && printf '%s' "$out" | grep -q 'orphan_probe'; then
|
|
247
|
+
ok "L14 known-positive: subject's own usage comment is not a caller — still reported unwired"
|
|
248
|
+
else
|
|
249
|
+
bad "L14 known-positive did not fire (rc=$rc) — self-referential comment read as a dispatcher"
|
|
250
|
+
printf '%s\n' "$out" | sed 's/^/ │ /'
|
|
251
|
+
fi
|
|
252
|
+
|
|
253
|
+
# L15 known-POSITIVE: a comment in a DIFFERENT file must not count either. Same discipline
|
|
254
|
+
# runner_dispatches() already applies to ordinary suites ("a mention is not an invocation").
|
|
255
|
+
D="$T/l15"; mkfixture_clean "$D"
|
|
256
|
+
printf '#!/usr/bin/env bash\n[ "${1:-}" = "--self-test" ] && { echo ok; exit 0; }\n' > "$D/scripts/orphan_probe.sh"
|
|
257
|
+
cat > "$D/scripts/selfcheck.sh" <<'SC'
|
|
258
|
+
#!/usr/bin/env bash
|
|
259
|
+
bash scripts/lane_runner_check.sh
|
|
260
|
+
bash scripts/test_ok_lanes.sh
|
|
261
|
+
# historical note: we used to run `bash scripts/orphan_probe.sh --self-test` here, removed 2026-01-01
|
|
262
|
+
SC
|
|
263
|
+
out=$(run "$D"); rc=$(rcof "$D")
|
|
264
|
+
if [ "$rc" = "0" ] && printf '%s' "$out" | grep -q 'orphan_probe'; then
|
|
265
|
+
ok "L15 known-positive: a commented-out dispatch in another file is not a caller"
|
|
266
|
+
else
|
|
267
|
+
bad "L15 known-positive did not fire (rc=$rc) — a comment read as a live dispatcher"
|
|
268
|
+
printf '%s\n' "$out" | sed 's/^/ │ /'
|
|
269
|
+
fi
|
|
270
|
+
|
|
271
|
+
# L16 known-POSITIVE: a subject that names its own dispatch on a NON-comment line. This is the only
|
|
272
|
+
# lane that actually pins the self-exclusion guard, and it exists because a cross-family round
|
|
273
|
+
# (2026-08-15) showed L14/L15 do not: both of their fixtures put the self-reference in a `#` line,
|
|
274
|
+
# so comment-stripping alone already satisfies them and the guard could be deleted with all lanes
|
|
275
|
+
# still green — a repair with no control, which is the exact failure this file was written to name.
|
|
276
|
+
# The shape is a usage helper, because that is how a real subject names its own flag in live code
|
|
277
|
+
# rather than in a header comment.
|
|
278
|
+
D="$T/l16"; mkfixture_clean "$D"
|
|
279
|
+
cat > "$D/scripts/orphan_probe.sh" <<'SUBJ'
|
|
280
|
+
#!/usr/bin/env bash
|
|
281
|
+
usage() { echo "run: bash scripts/orphan_probe.sh --self-test"; }
|
|
282
|
+
[ "${1:-}" = "--self-test" ] && { echo ok; exit 0; }
|
|
283
|
+
usage
|
|
284
|
+
SUBJ
|
|
285
|
+
out=$(run "$D"); rc=$(rcof "$D")
|
|
286
|
+
if [ "$rc" = "0" ] && printf '%s' "$out" | grep -q 'orphan_probe'; then
|
|
287
|
+
ok "L16 known-positive: a subject's own NON-comment self-reference is not a caller"
|
|
288
|
+
else
|
|
289
|
+
bad "L16 known-positive did not fire (rc=$rc) — self-exclusion guard is unanchored"
|
|
290
|
+
printf '%s\n' "$out" | sed 's/^/ │ /'
|
|
291
|
+
fi
|
|
292
|
+
|
|
293
|
+
echo "----"
|
|
294
|
+
echo "lane-runner lanes: $pass passed, $fail failed"
|
|
295
|
+
[ "$fail" -eq 0 ] || exit 1
|
|
@@ -174,6 +174,223 @@ for h in "$FH_REPO"/templates/.git-hooks/pre-commit "$FH_REPO"/templates/.git-ho
|
|
|
174
174
|
else bad "lane9 sentinel does NOT match shipped $(basename "$h") — every FH machine would report 'not FH gate'" "$(head -3 "$h")"; fi
|
|
175
175
|
done
|
|
176
176
|
|
|
177
|
+
# ── lane10 — consent-gated auto-pull: the APPLY arm and every REFUSE arm ─────────────────────
|
|
178
|
+
# Added 2026-08-15 with the feature. These lanes exist because the feature's whole safety claim is
|
|
179
|
+
# about what it does NOT do, and "does not" is exactly what a green run cannot demonstrate by
|
|
180
|
+
# itself — every refuse arm below is paired against an apply arm in the same fixture, so a lane
|
|
181
|
+
# that goes silently inert fails the CONTROL rather than passing everything.
|
|
182
|
+
# 🟥 The load-bearing one is lane10-b: in a shared checkout a branch switch yanks the ground out
|
|
183
|
+
# from under a peer session (measured 2026-08-09, two sessions/one worktree). If this feature ever
|
|
184
|
+
# switches branches, that lane is what says so.
|
|
185
|
+
_ap_root="$(mktemp -d)"
|
|
186
|
+
(
|
|
187
|
+
cd "$_ap_root" || exit 1
|
|
188
|
+
# Pin the branch name: `git init` uses init.defaultBranch, which is main on this
|
|
189
|
+
# operator's machine and master on a stock Ubuntu (CI). Hardcoding "main" in the
|
|
190
|
+
# assertions below made lane10-b pass VACUOUSLY there (`rev-parse "$_DEF"` returned
|
|
191
|
+
# empty, and empty==empty compared true) while CONTROL's reset silently no-op'd.
|
|
192
|
+
git -c init.defaultBranch=main init -q up && cd up
|
|
193
|
+
git config user.email t@t; git config user.name t; git config commit.gpgsign false
|
|
194
|
+
mkdir -p scripts tracks/_meta
|
|
195
|
+
cp "$FH_REPO/scripts/fh_node_check.sh" "$FH_REPO/scripts/consent_registry_check.sh" scripts/
|
|
196
|
+
# Build the consent fixture from the SHIPPED TEMPLATE, never from the operator's local
|
|
197
|
+
# tracks/_meta/ — those are gitignored, so on CI (and on any fresh clone) they do not exist and
|
|
198
|
+
# the lanes would report a red FAIL for a file whose absence is correct by design. Measured on
|
|
199
|
+
# CI 2026-08-15: exactly that, "registry/UAP absent — lanes not run". Sourcing the template
|
|
200
|
+
# instead is strictly better than degrading to SKIP: the lanes then actually run everywhere, and
|
|
201
|
+
# they validate the very block a consumer is told to copy.
|
|
202
|
+
cp "$FH_REPO/templates/consent_classes.yaml.example" tracks/_meta/consent_classes.yaml
|
|
203
|
+
# The grant side has no shipped template (a UAP is per-operator by nature), so synthesize the
|
|
204
|
+
# minimum the registry check requires: the full R6 fingerprint, far-future expiry so the fixture
|
|
205
|
+
# never rots into a false refusal, joined to the template's own class name.
|
|
206
|
+
# Dates are computed at fixture-build time, not hardcoded, for two reasons that pull opposite
|
|
207
|
+
# ways: a fixed past date rots into a false REFUSE, and a far-future one is refused outright —
|
|
208
|
+
# R5 caps a lease at 365 days ("an unbounded expiry is a transfer, not a lease"), and the first
|
|
209
|
+
# version of this fixture used 2099-01-01 and was correctly rejected by the very floor the
|
|
210
|
+
# feature depends on. GNU-first with validation, same discipline as every _mtime helper here:
|
|
211
|
+
# `date -d` is GNU, `date -v` is BSD, and neither failing silently is acceptable.
|
|
212
|
+
_g="$(date +%Y-%m-%d)"
|
|
213
|
+
_e="$(date -d '+300 days' +%Y-%m-%d 2>/dev/null || date -v+300d +%Y-%m-%d 2>/dev/null || echo '')"
|
|
214
|
+
case "$_e" in
|
|
215
|
+
[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]) : ;;
|
|
216
|
+
*) bad "lane10 fixture: could not compute a lease expiry (neither GNU nor BSD date worked)" "$_e"; _e="$_g" ;;
|
|
217
|
+
esac
|
|
218
|
+
cat > tracks/_meta/user_adaptation_profile.md <<UAPEOF
|
|
219
|
+
---
|
|
220
|
+
standing_consent:
|
|
221
|
+
repo-freshness-autopull:
|
|
222
|
+
granted: $_g
|
|
223
|
+
expires: $_e
|
|
224
|
+
owner: scripts/fh_node_check.sh
|
|
225
|
+
mode: ff-only-merge-on-default-branch
|
|
226
|
+
target: this repo's own default branch, in the local clone, when it is already checked out
|
|
227
|
+
effects: [network, repo-mutation]
|
|
228
|
+
sinks: []
|
|
229
|
+
---
|
|
230
|
+
lane fixture — not a real profile
|
|
231
|
+
UAPEOF
|
|
232
|
+
echo base > f.txt; git add -A >/dev/null; git commit -qm base
|
|
233
|
+
cd ..; git clone -q up dn; cd up; echo newer > f.txt; git commit -qam ahead
|
|
234
|
+
) >/dev/null 2>&1
|
|
235
|
+
_dn="$_ap_root/dn"
|
|
236
|
+
if [ ! -d "$_dn/.git" ] || [ ! -f "$_dn/tracks/_meta/consent_classes.yaml" ]; then
|
|
237
|
+
bad "lane10 fixture: could not build the consent fixture (registry/UAP absent — lanes not run)" "$_ap_root"
|
|
238
|
+
else
|
|
239
|
+
git -C "$_dn" config user.email t@t >/dev/null 2>&1; git -C "$_dn" config user.name t >/dev/null 2>&1
|
|
240
|
+
# Derive rather than assume — belt-and-braces over the pin above, so a future git
|
|
241
|
+
# whose init behaviour changes again cannot make these lanes vacuous a second time.
|
|
242
|
+
_DEF="$(git -C "$_dn" symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null | sed 's#^origin/##')"
|
|
243
|
+
[ -n "$_DEF" ] || _DEF=main
|
|
244
|
+
if ! git -C "$_dn" show-ref --verify --quiet "refs/heads/$_DEF"; then
|
|
245
|
+
bad "lane10 fixture: default branch '$_DEF' has no local ref — assertions below would be vacuous" "$(git -C "$_dn" branch -a)"
|
|
246
|
+
fi
|
|
247
|
+
git -C "$_dn" fetch -q origin >/dev/null 2>&1
|
|
248
|
+
_h0="$(git -C "$_dn" rev-parse HEAD)"
|
|
249
|
+
run "$_dn" "$_ap_root/s_a" >/dev/null 2>&1
|
|
250
|
+
if [ "$(git -C "$_dn" rev-parse HEAD)" != "$_h0" ]; then
|
|
251
|
+
ok "lane10-a APPLY: consented + on default branch → fast-forwarded"
|
|
252
|
+
else
|
|
253
|
+
bad "lane10-a APPLY: consented + on default branch did NOT fast-forward (feature inert — every refuse lane below is then vacuous)" "$_h0"
|
|
254
|
+
fi
|
|
255
|
+
|
|
256
|
+
# b — the envelope. Never applies off the default branch, and NEVER switches branches.
|
|
257
|
+
(cd "$_ap_root/up" && echo newer2 > f.txt && git commit -qam ahead2) >/dev/null 2>&1
|
|
258
|
+
git -C "$_dn" fetch -q origin >/dev/null 2>&1
|
|
259
|
+
git -C "$_dn" switch -c feat-x -q >/dev/null 2>&1
|
|
260
|
+
_m0="$(git -C "$_dn" rev-parse "$_DEF")"
|
|
261
|
+
# ⚠️ CHECKING main AND THE BRANCH NAME IS NOT ENOUGH — measured by a revert probe 2026-08-15.
|
|
262
|
+
# With the envelope deliberately broken, `merge --ff-only origin/main` fast-forwards THE BRANCH
|
|
263
|
+
# YOU ARE ON, so feat-x moved while `main` sat still and the branch name never changed. The first
|
|
264
|
+
# version of this lane asserted only those two and stayed green against a defeated envelope —
|
|
265
|
+
# decorative. The tip that must not move is the CURRENT branch's own.
|
|
266
|
+
_f0="$(git -C "$_dn" rev-parse feat-x)"
|
|
267
|
+
run "$_dn" "$_ap_root/s_b" >/dev/null 2>&1
|
|
268
|
+
if [ "$(git -C "$_dn" rev-parse "$_DEF")" = "$_m0" ] \
|
|
269
|
+
&& [ "$(git -C "$_dn" rev-parse feat-x)" = "$_f0" ] \
|
|
270
|
+
&& [ "$(git -C "$_dn" branch --show-current)" = "feat-x" ]; then
|
|
271
|
+
ok "lane10-b ENVELOPE: off default branch → no apply on EITHER branch, no switch"
|
|
272
|
+
else
|
|
273
|
+
bad "lane10-b ENVELOPE: a branch tip moved off the default branch — the shared-checkout hazard" "main=$_m0->$(git -C "$_dn" rev-parse "$_DEF") feat-x=$_f0->$(git -C "$_dn" rev-parse feat-x) branch=$(git -C "$_dn" branch --show-current)"
|
|
274
|
+
fi
|
|
275
|
+
git -C "$_dn" switch -q "$_DEF" >/dev/null 2>&1
|
|
276
|
+
|
|
277
|
+
# c — no grant. absent ≠ granted.
|
|
278
|
+
cp "$_dn/tracks/_meta/user_adaptation_profile.md" "$_ap_root/uap.bak"
|
|
279
|
+
python3 - "$_dn/tracks/_meta/user_adaptation_profile.md" <<'PYX'
|
|
280
|
+
import re,sys
|
|
281
|
+
p=sys.argv[1]; s=open(p,encoding='utf-8').read()
|
|
282
|
+
open(p,'w',encoding='utf-8').write(re.sub(r'\nstanding_consent:\n(?: .*\n| .*\n)*', '\n', s, count=1))
|
|
283
|
+
PYX
|
|
284
|
+
_h1="$(git -C "$_dn" rev-parse HEAD)"
|
|
285
|
+
run "$_dn" "$_ap_root/s_c" >/dev/null 2>&1
|
|
286
|
+
[ "$(git -C "$_dn" rev-parse HEAD)" = "$_h1" ] \
|
|
287
|
+
&& ok "lane10-c NO-GRANT: absent standing_consent → no apply" \
|
|
288
|
+
|| bad "lane10-c NO-GRANT: applied without a grant" "$_h1"
|
|
289
|
+
|
|
290
|
+
# d — expired lease. A lease nobody enforces is not a lease.
|
|
291
|
+
cp "$_ap_root/uap.bak" "$_dn/tracks/_meta/user_adaptation_profile.md"
|
|
292
|
+
sed -i.bak2 's/expires: [0-9-]*/expires: 2020-01-01/' "$_dn/tracks/_meta/user_adaptation_profile.md"
|
|
293
|
+
_h2="$(git -C "$_dn" rev-parse HEAD)"
|
|
294
|
+
run "$_dn" "$_ap_root/s_d" >/dev/null 2>&1
|
|
295
|
+
[ "$(git -C "$_dn" rev-parse HEAD)" = "$_h2" ] \
|
|
296
|
+
&& ok "lane10-d EXPIRED: past-dated lease → no apply" \
|
|
297
|
+
|| bad "lane10-d EXPIRED: applied under an expired lease" "$_h2"
|
|
298
|
+
cp "$_ap_root/uap.bak" "$_dn/tracks/_meta/user_adaptation_profile.md"
|
|
299
|
+
|
|
300
|
+
# e — divergence. --ff-only must refuse rather than absorb, and local work must survive.
|
|
301
|
+
(cd "$_dn" && echo local-only > mine.txt && git add -A && git commit -qm diverge) >/dev/null 2>&1
|
|
302
|
+
_h3="$(git -C "$_dn" rev-parse HEAD)"
|
|
303
|
+
run "$_dn" "$_ap_root/s_e" >/dev/null 2>&1
|
|
304
|
+
if [ "$(git -C "$_dn" rev-parse HEAD)" = "$_h3" ] && [ -f "$_dn/mine.txt" ]; then
|
|
305
|
+
ok "lane10-e DIVERGED: ff refused, local commit survives"
|
|
306
|
+
else
|
|
307
|
+
bad "lane10-e DIVERGED: local history was moved or lost" "$_h3 -> $(git -C "$_dn" rev-parse HEAD)"
|
|
308
|
+
fi
|
|
309
|
+
|
|
310
|
+
# f — WRONG CLASS. The refusal that the other three arms structurally cannot produce.
|
|
311
|
+
# Arms c/d/e all refuse by breaking the FILE-WIDE verdict (grant block deleted → exit 3; lease
|
|
312
|
+
# past-dated → exit 1; divergence → git itself refuses). None of them touches the question "is
|
|
313
|
+
# THIS class granted", so a caller keyed on the file-wide 0 passed them all — measured 2026-08-15
|
|
314
|
+
# by a pre-publish security pass, with a live control: one unrelated class validly granted plus
|
|
315
|
+
# the target class named only in prose, and the merge ran while the banner claimed a standing
|
|
316
|
+
# consent that never existed. The revoke path was the broken one.
|
|
317
|
+
# The second assertion below is the load-bearing half: this arm must refuse WHILE the bare
|
|
318
|
+
# file-wide check still returns 0. Without it the lane would pass for the same reason lane10-c
|
|
319
|
+
# does, and the axis would go unexercised again.
|
|
320
|
+
# ORDER MATTERS, and getting it wrong is silent: the UAP is a TRACKED file in this fixture, so
|
|
321
|
+
# `git reset --hard` reverts any edit made to it. The first draft transformed the UAP and then
|
|
322
|
+
# reset — restoring the original grant, so the apply fired legitimately and the lane failed for a
|
|
323
|
+
# reason that looked like the defect. Reset and advance origin FIRST, edit the UAP AFTER.
|
|
324
|
+
git -C "$_dn" reset -q --hard "origin/$_DEF" >/dev/null 2>&1 # noqa: destructive-op (throwaway fixture)
|
|
325
|
+
(cd "$_ap_root/up" && echo newer-f > f.txt && git commit -qam ahead-f) >/dev/null 2>&1
|
|
326
|
+
git -C "$_dn" fetch -q origin >/dev/null 2>&1
|
|
327
|
+
cp "$_ap_root/uap.bak" "$_dn/tracks/_meta/user_adaptation_profile.md"
|
|
328
|
+
python3 - "$_dn/tracks/_meta/user_adaptation_profile.md" <<'PYX'
|
|
329
|
+
import sys, re as _re
|
|
330
|
+
p = sys.argv[1]; s = open(p, encoding='utf-8').read()
|
|
331
|
+
old = " repo-freshness-autopull:"
|
|
332
|
+
assert old in s, "lane10-f fixture: grant key not found"
|
|
333
|
+
# Reuse the lease dates the fixture already computed, so this arm cannot fail for a date reason and
|
|
334
|
+
# be mistaken for a class-join refusal. (First draft omitted them: R5 fired, the file-wide verdict
|
|
335
|
+
# went to 1, and the arm's own vacuity guard caught it — which is what that guard is for.)
|
|
336
|
+
_g = _re.search(r"granted: (\S+)", s).group(1)
|
|
337
|
+
_e = _re.search(r"expires: (\S+)", s).group(1)
|
|
338
|
+
# A VALID grant for a different registered, promotion_eligible class, scope-matched to its
|
|
339
|
+
# registration so R6/R7 pass — the point is a file that is entirely well-formed.
|
|
340
|
+
other = (" dispatch-readonly-sim-local-artifact:\n"
|
|
341
|
+
f" granted: {_g}\n"
|
|
342
|
+
f" expires: {_e}\n"
|
|
343
|
+
" owner: fh-meta:sim-conductor\n"
|
|
344
|
+
" mode: blind-target-tier-sim\n"
|
|
345
|
+
" target: a single local file under the current repo\n"
|
|
346
|
+
" effects: [read, dispatch]\n"
|
|
347
|
+
" sinks: []\n")
|
|
348
|
+
s = s.replace(old, " __TARGET_GRANT__:", 1)
|
|
349
|
+
head, sep, tail = s.partition("standing_consent:\n")
|
|
350
|
+
s = head + sep + other + tail
|
|
351
|
+
# the target class survives ONLY as prose — the shape the old raw grep could not tell from a grant
|
|
352
|
+
s = s.replace("lane fixture — not a real profile",
|
|
353
|
+
"lane fixture — not a real profile\n\n repo-freshness-autopull: 철회함 (revoked)")
|
|
354
|
+
import re
|
|
355
|
+
s = re.sub(r" __TARGET_GRANT__:\n(?: .*\n)*", "", s, count=1)
|
|
356
|
+
open(p, 'w', encoding='utf-8').write(s)
|
|
357
|
+
PYX
|
|
358
|
+
# PRECONDITION, asserted rather than assumed. lane10-e leaves the clone DIVERGED, and a diverged
|
|
359
|
+
# clone refuses the fast-forward for a reason that has nothing to do with consent — so without the
|
|
360
|
+
# reset above the arm passes whether the class join works or not. Measured: a revert probe against
|
|
361
|
+
# the repaired fh_node_check.sh left this lane green, i.e. it was decorative. Same shape as the
|
|
362
|
+
# self-exclusion lane earlier the same day; the probe is what separates the two cases.
|
|
363
|
+
_h5="$(git -C "$_dn" rev-parse HEAD)"
|
|
364
|
+
if [ "$_h5" = "$(git -C "$_dn" rev-parse "origin/$_DEF")" ]; then
|
|
365
|
+
bad "lane10-f VACUOUS: clone is not behind origin, so 'no apply' proves nothing" "$_h5"
|
|
366
|
+
fi
|
|
367
|
+
run "$_dn" "$_ap_root/s_f" >/dev/null 2>&1
|
|
368
|
+
_filewide=0
|
|
369
|
+
bash "$FH_REPO/scripts/consent_registry_check.sh" \
|
|
370
|
+
"$_dn/tracks/_meta/consent_classes.yaml" \
|
|
371
|
+
"$_dn/tracks/_meta/user_adaptation_profile.md" >/dev/null 2>&1 || _filewide=$?
|
|
372
|
+
if [ "$(git -C "$_dn" rev-parse HEAD)" = "$_h5" ] && [ "$_filewide" = "0" ]; then
|
|
373
|
+
ok "lane10-f WRONG-CLASS: another class granted, this one only in prose → no apply (file-wide check still 0)"
|
|
374
|
+
elif [ "$_filewide" != "0" ]; then
|
|
375
|
+
bad "lane10-f VACUOUS: the fixture broke the file-wide verdict (rc=$_filewide), so it re-tests lane10-c's axis, not the class join" "rebuild the fixture so the bare check returns 0"
|
|
376
|
+
else
|
|
377
|
+
bad "lane10-f WRONG-CLASS: applied on a class that was never granted" "$_h5 -> $(git -C "$_dn" rev-parse HEAD)"
|
|
378
|
+
fi
|
|
379
|
+
cp "$_ap_root/uap.bak" "$_dn/tracks/_meta/user_adaptation_profile.md"
|
|
380
|
+
|
|
381
|
+
# CONTROL — after all the refuse arms, prove the apply arm still fires. Without this, a lane
|
|
382
|
+
# suite where the feature has gone inert reports 4 clean refusals and looks perfect.
|
|
383
|
+
git -C "$_dn" reset -q --hard "origin/$_DEF" >/dev/null 2>&1 # noqa: destructive-op (throwaway fixture)
|
|
384
|
+
(cd "$_ap_root/up" && echo newer3 > f.txt && git commit -qam ahead3) >/dev/null 2>&1
|
|
385
|
+
git -C "$_dn" fetch -q origin >/dev/null 2>&1
|
|
386
|
+
_h4="$(git -C "$_dn" rev-parse HEAD)"
|
|
387
|
+
run "$_dn" "$_ap_root/s_f" >/dev/null 2>&1
|
|
388
|
+
[ "$(git -C "$_dn" rev-parse HEAD)" != "$_h4" ] \
|
|
389
|
+
&& ok "lane10-CONTROL: apply arm still fires after the refuse arms (instrument alive)" \
|
|
390
|
+
|| bad "lane10-CONTROL: apply arm went inert — the refuse lanes above prove nothing" "$_h4"
|
|
391
|
+
fi
|
|
392
|
+
rm -rf "$_ap_root"
|
|
393
|
+
|
|
177
394
|
printf '\nnode-check lanes: %d passed, %d failed\n' "$PASS" "$FAIL"
|
|
178
395
|
[ "$FAIL" -eq 0 ] || exit 1
|
|
179
396
|
exit 0
|
|
@@ -357,6 +357,67 @@ CTLPY
|
|
|
357
357
|
[ "$_BARE_CTL" = FIRED ]
|
|
358
358
|
chk $? "CONTROL: that detector fires on a planted declared-shipped subject (got $_BARE_CTL)"
|
|
359
359
|
|
|
360
|
+
echo ""
|
|
361
|
+
echo "── directional_diff_gate verdict arm: exit 0 is not a pass ──"
|
|
362
|
+
# WHY THIS LANE EXISTS (2026-08-15): the block wiring directional_diff_gate.sh --self-test gates on
|
|
363
|
+
# the subject's terminal verdict, not on rc=0 alone, and both halves of that were put there by a
|
|
364
|
+
# cross-family round rather than by the author. Its first draft gated on rc=0 plus a SUBSTRING of
|
|
365
|
+
# the verdict, and two reachable inputs satisfied it while nothing ran:
|
|
366
|
+
# · `✅ calibration passed (0 pairs)` — the subject's own verdict line is
|
|
367
|
+
# `[ "$f" -eq 0 ] && echo "✅ calibration passed ($n pairs)"`, so deleting every lane prints a
|
|
368
|
+
# PASS with n=0. Deletion read as a pass is the quietest face of the not-found-is-not-zero class.
|
|
369
|
+
# · `usage: calibration passed (28 pairs)` — a prose/usage line carrying the words.
|
|
370
|
+
# Without this lane both repairs are reversible to green, which is anchor-is-decorative.
|
|
371
|
+
# LIFTED from selfcheck.sh, not re-spelled — a lane that restates the condition stops seeing edits
|
|
372
|
+
# to it, which is the failure the two lanes above were written to avoid.
|
|
373
|
+
DD_ARM=$(sed -n '/^ _dd_verdict=\$(printf/,/^ fi ;;$/p' "$SELFCHECK" | sed 's/ ;;$//')
|
|
374
|
+
if [ -z "$DD_ARM" ]; then
|
|
375
|
+
echo "FAIL the directional_diff_gate verdict arm is no longer where this lane lifts it from."
|
|
376
|
+
echo " If the block was moved or renamed, update the lane WITH the subject."
|
|
377
|
+
exit 1
|
|
378
|
+
fi
|
|
379
|
+
# An empty lift is not the only bad lift. If the END anchor stops matching — re-indent the arm, or
|
|
380
|
+
# move it out of the `case` — the START anchor still matches and the range runs to EOF, which is
|
|
381
|
+
# non-empty and therefore passes the guard above. What saves it today is luck, not design: the
|
|
382
|
+
# `;;`-stripping applies to every line in the range, so downstream case arms lose their terminators
|
|
383
|
+
# and `eval` dies of a syntax error. A downstream span that happened to be self-contained would
|
|
384
|
+
# instead `eval` several hundred lines of selfcheck.sh, once per fixture, at the repo root. Bound
|
|
385
|
+
# the lift by size so the failure is a lane message rather than an accident. Cross-family, 2026-08-15.
|
|
386
|
+
if [ "$(printf '%s\n' "$DD_ARM" | wc -l)" -gt 15 ]; then
|
|
387
|
+
echo "FAIL the lifted verdict arm is $(printf '%s\n' "$DD_ARM" | wc -l) lines — the sed END anchor"
|
|
388
|
+
echo " stopped matching and the range ran past the block. Fix the anchor, do not widen this bound."
|
|
389
|
+
exit 1
|
|
390
|
+
fi
|
|
391
|
+
dd_arm_says() { # $1 = the subject's stdout; echoes PASS or FAIL
|
|
392
|
+
( _out="$1"; fail=0; _show_failure() { :; }
|
|
393
|
+
eval "$DD_ARM" ) | grep -oE '^(PASS|FAIL)' | head -1
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
[ "$(dd_arm_says '✅ calibration passed (28 pairs)')" = PASS ]
|
|
397
|
+
chk $? "a real verdict with a non-zero pair count passes"
|
|
398
|
+
|
|
399
|
+
[ "$(dd_arm_says '✅ calibration passed (0 pairs)')" = FAIL ]
|
|
400
|
+
chk $? "every lane deleted (0 pairs, exit 0) is caught, not certified"
|
|
401
|
+
|
|
402
|
+
[ "$(dd_arm_says 'usage: calibration passed (28 pairs)')" = FAIL ]
|
|
403
|
+
chk $? "a usage/prose line carrying the words does not satisfy the gate"
|
|
404
|
+
|
|
405
|
+
# CONTROL — the pre-repair form (bare substring, no pair-count floor) must ACCEPT both bad
|
|
406
|
+
# fixtures. Without it these lanes could pass because the fixtures are wrong rather than because
|
|
407
|
+
# the repair works.
|
|
408
|
+
_pre() { printf '%s\n' "$1" | grep -oE 'calibration passed \([0-9]+ pairs\)' | tail -1; }
|
|
409
|
+
[ -n "$(_pre '✅ calibration passed (0 pairs)')" ] && [ -n "$(_pre 'usage: calibration passed (28 pairs)')" ]
|
|
410
|
+
chk $? "CONTROL: the pre-repair substring form accepts both — the fixtures discriminate"
|
|
411
|
+
|
|
412
|
+
# SCOPE of these four lanes, stated because the inherited §SCOPE block below is about a different
|
|
413
|
+
# anchor and does not cover them. The lift is the `0)` arm's BODY only, so these lanes see a change
|
|
414
|
+
# to the verdict predicate and are blind to the routing around it: widening `case ... in 0)` to
|
|
415
|
+
# `0|1)`, dropping `< /dev/null`, dropping the `$_LANE_TO` wrapper, or deleting the rc=124
|
|
416
|
+
# HARNESS-ERROR arm all leave every lane here green. Those are guarded by review, not by this file.
|
|
417
|
+
# The `_pre` control is also a RE-SPELLING of the pre-repair form rather than a lift of it — it has
|
|
418
|
+
# to be, since that form no longer exists in the subject to lift, but it means the control asserts
|
|
419
|
+
# what the old code did from memory. Both limits found by cross-family review, 2026-08-15.
|
|
420
|
+
|
|
360
421
|
# ── SCOPE OF THIS ANCHOR — stated so it is not over-trusted ───────────────────
|
|
361
422
|
# These lanes catch REVERSION (the run-twice form coming back, the helper being gutted, the
|
|
362
423
|
# call-site redirect returning). They do NOT catch deliberate EVASION: a cross-family round
|