@chrono-meta/fh-gate 2.15.0 → 3.0.0
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/rules/fh_4axis_gate.md +38 -0
- package/.claude-plugin/marketplace.json +2 -2
- package/CLAUDE.md +1 -1
- package/README.md +6 -1
- package/knowledge/shared/harness-core/fh_three_layer_canon.md +47 -0
- package/knowledge/shared/harness-core/field_verdict_crossfamily_gate.md +10 -0
- package/knowledge/shared/harness-core/measurement-integrity-checklist.md +15 -1
- package/knowledge/shared/harness-core/ship_readiness_gate.md +20 -3
- package/knowledge/shared/learnings/subagent_invocations_log.yaml +111 -0
- package/package.json +9 -1
- package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-meta/CHANGELOG.md +49 -0
- package/plugins/fh-meta/skills/salience-splitter/SKILL.md +1 -1
- package/scripts/backtick_guard.sh +194 -0
- package/scripts/capability_effect_probe.sh +14 -1
- package/scripts/context_continuity_score.sh +106 -12
- package/scripts/fh-gate.sh +3 -3
- package/scripts/files_manifest_shipping_check.sh +19 -0
- package/scripts/gate_pathspec_check.sh +1 -1
- package/scripts/package_coverage_check.sh +25 -2
- package/scripts/proposal_hook.sh +89 -0
- package/scripts/public_surface_scan_files.sh +11 -2
- package/scripts/revert_probe.sh +250 -0
- package/scripts/selfcheck.sh +67 -3
- package/scripts/sim_isolated_run.sh +97 -7
- package/scripts/test_backtick_guard_lanes.sh +115 -0
- package/scripts/test_degrade_scan_shell_probes.sh +7 -7
- package/scripts/test_files_manifest_shipping_lanes.sh +5 -5
- package/scripts/test_heavy_classifier_lanes.sh +1 -1
- package/scripts/test_lane_runner_lanes.sh +59 -33
- package/scripts/test_mapped_tracks_lanes.sh +1 -1
- package/scripts/test_marker_soul_check_lanes.sh +24 -0
- package/scripts/test_node_check_lanes.sh +34 -34
- package/scripts/test_package_coverage_lanes.sh +53 -27
- package/scripts/test_pipe_verdict_guard_lanes.sh +5 -5
- package/scripts/test_precommit_pointer_index_lanes.sh +33 -0
- package/scripts/test_preprep_drift_anchor.sh +13 -4
- package/scripts/test_preprep_drift_anchor_lanes.sh +23 -0
- package/scripts/test_proposal_hook_lanes.sh +36 -0
- package/scripts/test_revert_probe_lanes.sh +146 -0
- package/scripts/test_session_close_lanes.sh +3 -5
- package/scripts/test_sim_isolated_run_lanes.sh +17 -0
- package/scripts/test_verdict_watermark_lanes.sh +27 -1
- package/scripts/utterance_landing_check.sh +2 -2
- package/templates/.git-hooks/pre-commit +27 -4
- package/templates/settings.PreToolUse.snippet.json +37 -1
- package/plugins/fh-commons/README.md +0 -38
|
@@ -53,7 +53,7 @@ EXPECT=3 # the_bible + pmh-dev + qasp ; _meta and _audit excluded
|
|
|
53
53
|
|
|
54
54
|
# PREMISE — if the worktree can already see the fixtures, every verdict below is meaningless.
|
|
55
55
|
NAIVE_WT=0
|
|
56
|
-
for d in "$TMP/wt"/tracks/*/; do [ -d "$d" ] || continue; case "$(basename "$d")" in _*) continue;; esac; NAIVE_WT=$((NAIVE_WT+1)); done
|
|
56
|
+
for d in "$TMP/wt"/tracks/*/; do [ -d "$d" ] || continue; case "$(basename "$d")" in _*) continue;; esac; NAIVE_WT=$((NAIVE_WT+1)); done # portability-noqa: the [ -d "$d" ] || continue guard is on this SAME line (right after 'do'), not the next 1-2 lines the P8 scanner checks
|
|
57
57
|
[ "$NAIVE_WT" -lt "$EXPECT" ] \
|
|
58
58
|
&& ok "premise: a naive cwd count from the worktree sees $NAIVE_WT of $EXPECT — the blindness is real here" \
|
|
59
59
|
|| ng "premise BROKEN: worktree already sees $NAIVE_WT — fixture does not reproduce the defect"
|
|
@@ -165,6 +165,30 @@ crossfamily: panel(codex,agy) — 겹친 지적 0'
|
|
|
165
165
|
planep P6-real-corpus-negative BLOCK "$GRACE" "$(cat "$REPO_ROOT/tracks/_meta/.axes_23_passed_feat_target-freeze-gate_2026-08-18.marker" 2>/dev/null || echo 'axes-run: x')"
|
|
166
166
|
planep P7-real-corpus-positive PASS "$GRACE" "$(cat "$REPO_ROOT/tracks/_meta/.axes_23_passed_fix_witness-two-commit-discipline_2026-08-17.marker" 2>/dev/null || echo '①영혼: 성공 정의 = 하나 둘 셋 넷 다섯 여섯')"
|
|
167
167
|
|
|
168
|
+
echo
|
|
169
|
+
echo "== ①영혼 2칸(성공정의/절대안함) advisory lanes (2026-09-04, six_axis_review 판정안 4) =="
|
|
170
|
+
# advisory only — 두 케이스 다 rc=0(PASS). 판별은 rc 가 아니라 stdout 의 ⚠️ 문자열이다
|
|
171
|
+
# (known-pair: 한 칸만 있는 마커는 경고가 «찍혀야» 하고, 두 칸 다 있는 마커는 «안 찍혀야» 한다).
|
|
172
|
+
planep_adv() { # $1=id $2=expect(WARN|QUIET) $3=filename-date $4=body
|
|
173
|
+
local id="$1" expect="$2" d="$3" body="$4" out f
|
|
174
|
+
f="$T/.axes_23_passed_fix_x_${d}.marker"
|
|
175
|
+
printf '%s\n' "$body" > "$f"
|
|
176
|
+
out=$( bash -c 'set -uo pipefail; SOUL_PRESENT_GRACE_DATE="$3"; . "$1"; validate_soul_present_leg "$2"' \
|
|
177
|
+
_ "$T/fnp.sh" "$f" "$GRACE" 2>&1 )
|
|
178
|
+
local got=QUIET; printf '%s' "$out" | grep -q '①영혼 2칸 미달' && got=WARN
|
|
179
|
+
if [ "$got" = "$expect" ]; then printf ' ✅ %-40s %s\n' "$id" "$got"
|
|
180
|
+
else printf ' ❌ %-40s expected %s, got %s\n %s\n' "$id" "$expect" "$got" "$(printf '%s' "$out"|head -2)"; FAIL=1; fi
|
|
181
|
+
}
|
|
182
|
+
# known-positive — 「성공 정의」만 있고 「절대 안 함」이 없다 → advisory 가 찍혀야 한다, 그래도 PASS(rc=0)
|
|
183
|
+
planep_adv Q1-one-slot-only-warns WARN "$GRACE" '①영혼: 성공 정의 = 소비자 경로에서 완주해 rc=0 을 낸다
|
|
184
|
+
axes-run: ⓐ=codex'
|
|
185
|
+
# known-negative — 두 칸 다 있다($SOUL 픽스처) → advisory 가 안 찍혀야 한다
|
|
186
|
+
planep_adv Q2-both-slots-quiet QUIET "$GRACE" "$SOUL
|
|
187
|
+
axes-run: ⓐ=codex"
|
|
188
|
+
# 둘 다 rc=0(PASS) 이어야 한다 — advisory 는 커밋을 막지 않는다(형식만 다르고 planep 로도 확인)
|
|
189
|
+
planep Q1b-one-slot-still-passes PASS "$GRACE" '①영혼: 성공 정의 = 소비자 경로에서 완주해 rc=0 을 낸다
|
|
190
|
+
axes-run: ⓐ=codex'
|
|
191
|
+
|
|
168
192
|
echo
|
|
169
193
|
echo "== cross-family findings — regression lanes (codex/gpt-5.5, diff 축, 2026-08-21) =="
|
|
170
194
|
# F1 self-referential FP: codex's own alignment fix flipped G8 to PASS because the
|
|
@@ -42,43 +42,43 @@ echo "── node-check lanes ──"
|
|
|
42
42
|
# LANE 1 (S3-1) — NOT a git repo. The git-hook floor cannot be installed here at all, so it is
|
|
43
43
|
# N/A, not missing. Reporting it would be an unfixable notice repeating every session forever.
|
|
44
44
|
mkdir -p "$TMP/nogit"
|
|
45
|
-
out="$(run "$TMP/nogit" "$TMP/s1" FH_MACHINE_ID=nogitbox)"
|
|
45
|
+
out="$(run "$TMP/nogit" "$TMP/s1" FH_MACHINE_ID=nogitbox)"; rc=$?
|
|
46
46
|
# POSITIVE CONTROL: assert what MUST appear alongside what must not. Absence-only lanes are
|
|
47
47
|
# satisfied by a script that prints nothing at all — an `exit 0` stub passed lanes 1, 4 and 7
|
|
48
48
|
# (cross-family mutation 2026-07-30), so each now pins an expected utterance too.
|
|
49
49
|
case "$out" in
|
|
50
|
-
*"Missing mechanical floor"*) bad "lane1 non-git: must NOT claim a missing floor (N/A, unfixable)" "$out" ;;
|
|
50
|
+
*"Missing mechanical floor"*) bad "lane1 non-git: must NOT claim a missing floor (N/A, unfixable)" "rc=$rc | $out" ;;
|
|
51
51
|
*"first session for this clone"*) ok "lane1 non-git: N/A on the git floor, but still reports the node event" ;;
|
|
52
|
-
*) bad "lane1 non-git: silent — a stub would pass this lane" "$out" ;;
|
|
52
|
+
*) bad "lane1 non-git: silent — a stub would pass this lane" "rc=$rc | $out" ;;
|
|
53
53
|
esac
|
|
54
54
|
|
|
55
55
|
# LANE 2 (S3-2) — hooks exist but belong to another framework (husky/pre-commit). Executable ≠ FH's
|
|
56
56
|
# gate. Silence here is the exact accident this check was built for: FH gates absent, machine quiet.
|
|
57
57
|
mk_git_hub "$TMP/husky" no
|
|
58
|
-
out="$(run "$TMP/husky" "$TMP/s2" FH_MACHINE_ID=huskybox)"
|
|
58
|
+
out="$(run "$TMP/husky" "$TMP/s2" FH_MACHINE_ID=huskybox)"; rc=$?
|
|
59
59
|
case "$out" in
|
|
60
60
|
*"Missing mechanical floor"*) ok "lane2 foreign hooks: FH gate absence reported" ;;
|
|
61
|
-
*) bad "lane2 foreign hooks: non-FH hooks were accepted as FH floors" "$out" ;;
|
|
61
|
+
*) bad "lane2 foreign hooks: non-FH hooks were accepted as FH floors" "rc=$rc | $out" ;;
|
|
62
62
|
esac
|
|
63
63
|
|
|
64
64
|
# LANE 3 (S3-3) — Mode D user on a FRESH clone: companion store present, settings.local.json absent
|
|
65
65
|
# (it is gitignored, so a clone never has it). This is the MEASURED 2026-07-30 incident. Must speak.
|
|
66
66
|
mk_git_hub "$TMP/moded" yes
|
|
67
67
|
mkdir -p "$TMP/companion/.git"
|
|
68
|
-
out="$(run "$TMP/moded" "$TMP/s3" FH_MACHINE_ID=modedbox BE_DIR="$TMP/companion")"
|
|
68
|
+
out="$(run "$TMP/moded" "$TMP/s3" FH_MACHINE_ID=modedbox BE_DIR="$TMP/companion")"; rc=$?
|
|
69
69
|
case "$out" in
|
|
70
70
|
*companion-load*) ok "lane3 fresh Mode D clone: companion-load absence surfaced" ;;
|
|
71
|
-
*) bad "lane3 fresh Mode D clone: SILENT — the measured incident would recur" "$out" ;;
|
|
71
|
+
*) bad "lane3 fresh Mode D clone: SILENT — the measured incident would recur" "rc=$rc | $out" ;;
|
|
72
72
|
esac
|
|
73
73
|
|
|
74
74
|
# LANE 4 (M2-4) — public non-Mode-D user: no companion store anywhere. The companion item must not
|
|
75
75
|
# appear, or the majority path gets a false positive for a feature it does not use.
|
|
76
76
|
mk_git_hub "$TMP/public" yes
|
|
77
|
-
out="$(run "$TMP/public" "$TMP/s4" FH_MACHINE_ID=publicbox)"
|
|
77
|
+
out="$(run "$TMP/public" "$TMP/s4" FH_MACHINE_ID=publicbox)"; rc=$?
|
|
78
78
|
case "$out" in
|
|
79
|
-
*companion-load*) bad "lane4a non-Mode-D: companion item shown to a user with no store" "$out" ;;
|
|
79
|
+
*companion-load*) bad "lane4a non-Mode-D: companion item shown to a user with no store" "rc=$rc | $out" ;;
|
|
80
80
|
*"first session for this clone"*) ok "lane4a non-Mode-D: companion absent, node event still reported" ;;
|
|
81
|
-
*) bad "lane4a non-Mode-D: silent — a stub would pass this lane" "$out" ;;
|
|
81
|
+
*) bad "lane4a non-Mode-D: silent — a stub would pass this lane" "rc=$rc | $out" ;;
|
|
82
82
|
esac
|
|
83
83
|
|
|
84
84
|
# LANE 4b (S4-1) — CLAUDE.local.md is Claude Code's STANDARD local-override file; anyone may keep
|
|
@@ -86,9 +86,9 @@ esac
|
|
|
86
86
|
# gets a companion notice every session forever (state-based emission makes it permanent, not
|
|
87
87
|
# one-shot). Only the binding INSIDE the file counts.
|
|
88
88
|
printf '# my local notes\nuse tabs not spaces\n' > "$TMP/public/CLAUDE.local.md"
|
|
89
|
-
out="$(run "$TMP/public" "$TMP/s4b" FH_MACHINE_ID=publicbox)"
|
|
89
|
+
out="$(run "$TMP/public" "$TMP/s4b" FH_MACHINE_ID=publicbox)"; rc=$?
|
|
90
90
|
case "$out" in
|
|
91
|
-
*companion-load*) bad "lane4b plain CLAUDE.local.md: existence alone classified the user as Mode D" "$out" ;;
|
|
91
|
+
*companion-load*) bad "lane4b plain CLAUDE.local.md: existence alone classified the user as Mode D" "rc=$rc | $out" ;;
|
|
92
92
|
*) ok "lane4b plain CLAUDE.local.md: not treated as a Mode D signal" ;;
|
|
93
93
|
esac
|
|
94
94
|
|
|
@@ -106,10 +106,10 @@ for binding in 'BE_DIR=/x/store' 'companion store: ~/notes' '컴패니언 스토
|
|
|
106
106
|
'vault: ~/vaults/notes' 'gbrain ingest target: ~/gbrain' 'backend: obsidian'; do
|
|
107
107
|
i=$((i+1))
|
|
108
108
|
printf '# local\n%s\n' "$binding" > "$TMP/public/CLAUDE.local.md"
|
|
109
|
-
out="$(run "$TMP/public" "$TMP/s4c$i" FH_MACHINE_ID=publicbox)"
|
|
109
|
+
out="$(run "$TMP/public" "$TMP/s4c$i" FH_MACHINE_ID=publicbox)"; rc=$?
|
|
110
110
|
case "$out" in
|
|
111
111
|
*companion-load*) ok "lane4c.$i Mode D detected via: $binding" ;;
|
|
112
|
-
*) bad "lane4c.$i binding present but Mode D not detected: $binding" "$out" ;;
|
|
112
|
+
*) bad "lane4c.$i binding present but Mode D not detected: $binding" "rc=$rc | $out" ;;
|
|
113
113
|
esac
|
|
114
114
|
done
|
|
115
115
|
rm -f "$TMP/public/CLAUDE.local.md"
|
|
@@ -136,24 +136,24 @@ done
|
|
|
136
136
|
# exist and working hooks read as missing.
|
|
137
137
|
mk_git_hub "$TMP/wt" yes
|
|
138
138
|
git -C "$TMP/wt" worktree add -q "$TMP/wt_linked" -b lane7 2>/dev/null
|
|
139
|
-
out="$(run "$TMP/wt_linked" "$TMP/s7" FH_MACHINE_ID=wtbox)"
|
|
139
|
+
out="$(run "$TMP/wt_linked" "$TMP/s7" FH_MACHINE_ID=wtbox)"; rc=$?
|
|
140
140
|
case "$out" in
|
|
141
|
-
*"Missing mechanical floor"*) bad "lane7 worktree: false missing-floor (hooks resolve to the main gitdir)" "$out" ;;
|
|
141
|
+
*"Missing mechanical floor"*) bad "lane7 worktree: false missing-floor (hooks resolve to the main gitdir)" "rc=$rc | $out" ;;
|
|
142
142
|
*"first session for this clone"*) ok "lane7 worktree: hooks resolved correctly, node event reported" ;;
|
|
143
|
-
*) bad "lane7 worktree: silent — a stub would pass this lane" "$out" ;;
|
|
143
|
+
*) bad "lane7 worktree: silent — a stub would pass this lane" "rc=$rc | $out" ;;
|
|
144
144
|
esac
|
|
145
145
|
|
|
146
146
|
# LANE 8 (M3-3) — python3 unavailable: the companion verdict is UNMEASURED, never silently "fine".
|
|
147
147
|
# Simulated by a PATH with no python3, for a Mode D hub (so the check is applicable).
|
|
148
148
|
mk_git_hub "$TMP/nopy" yes
|
|
149
149
|
mkdir -p "$TMP/emptybin" "$TMP/companion2/.git"
|
|
150
|
-
out="$(PATH="$TMP/emptybin:/usr/bin:/bin" run "$TMP/nopy" "$TMP/s8" FH_MACHINE_ID=nopybox BE_DIR="$TMP/companion2")"
|
|
150
|
+
out="$(PATH="$TMP/emptybin:/usr/bin:/bin" run "$TMP/nopy" "$TMP/s8" FH_MACHINE_ID=nopybox BE_DIR="$TMP/companion2")"; rc=$?
|
|
151
151
|
if command -v python3 >/dev/null 2>&1 && [ -x /usr/bin/python3 ]; then
|
|
152
152
|
ok "lane8 skipped: /usr/bin/python3 exists so absence cannot be simulated via PATH"
|
|
153
153
|
else
|
|
154
154
|
case "$out" in
|
|
155
155
|
*UNMEASURED*|*unmeasured*) ok "lane8 no python3: reported UNMEASURED, not silence" ;;
|
|
156
|
-
*) bad "lane8 no python3: absence read as pass" "$out" ;;
|
|
156
|
+
*) bad "lane8 no python3: absence read as pass" "rc=$rc | $out" ;;
|
|
157
157
|
esac
|
|
158
158
|
fi
|
|
159
159
|
|
|
@@ -249,11 +249,11 @@ else
|
|
|
249
249
|
fi
|
|
250
250
|
git -C "$_dn" fetch -q origin >/dev/null 2>&1
|
|
251
251
|
_h0="$(git -C "$_dn" rev-parse HEAD)"
|
|
252
|
-
run "$_dn" "$_ap_root/s_a"
|
|
252
|
+
_out_a="$(run "$_dn" "$_ap_root/s_a" 2>&1)"; _rc_a=$?
|
|
253
253
|
if [ "$(git -C "$_dn" rev-parse HEAD)" != "$_h0" ]; then
|
|
254
254
|
ok "lane10-a APPLY: consented + on default branch → fast-forwarded"
|
|
255
255
|
else
|
|
256
|
-
bad "lane10-a APPLY: consented + on default branch did NOT fast-forward (feature inert — every refuse lane below is then vacuous)" "
|
|
256
|
+
bad "lane10-a APPLY: consented + on default branch did NOT fast-forward (feature inert — every refuse lane below is then vacuous)" "before=$_h0 rc=$_rc_a out=$_out_a"
|
|
257
257
|
fi
|
|
258
258
|
|
|
259
259
|
# b — the envelope. Never applies off the default branch, and NEVER switches branches.
|
|
@@ -267,13 +267,13 @@ else
|
|
|
267
267
|
# version of this lane asserted only those two and stayed green against a defeated envelope —
|
|
268
268
|
# decorative. The tip that must not move is the CURRENT branch's own.
|
|
269
269
|
_f0="$(git -C "$_dn" rev-parse feat-x)"
|
|
270
|
-
run "$_dn" "$_ap_root/s_b"
|
|
270
|
+
_out_b="$(run "$_dn" "$_ap_root/s_b" 2>&1)"; _rc_b=$?
|
|
271
271
|
if [ "$(git -C "$_dn" rev-parse "$_DEF")" = "$_m0" ] \
|
|
272
272
|
&& [ "$(git -C "$_dn" rev-parse feat-x)" = "$_f0" ] \
|
|
273
273
|
&& [ "$(git -C "$_dn" branch --show-current)" = "feat-x" ]; then
|
|
274
274
|
ok "lane10-b ENVELOPE: off default branch → no apply on EITHER branch, no switch"
|
|
275
275
|
else
|
|
276
|
-
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)"
|
|
276
|
+
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) rc=$_rc_b out=$_out_b"
|
|
277
277
|
fi
|
|
278
278
|
git -C "$_dn" switch -q "$_DEF" >/dev/null 2>&1
|
|
279
279
|
|
|
@@ -285,29 +285,29 @@ p=sys.argv[1]; s=open(p,encoding='utf-8').read()
|
|
|
285
285
|
open(p,'w',encoding='utf-8').write(re.sub(r'\nstanding_consent:\n(?: .*\n| .*\n)*', '\n', s, count=1))
|
|
286
286
|
PYX
|
|
287
287
|
_h1="$(git -C "$_dn" rev-parse HEAD)"
|
|
288
|
-
run "$_dn" "$_ap_root/s_c"
|
|
288
|
+
_out_c="$(run "$_dn" "$_ap_root/s_c" 2>&1)"; _rc_c=$?
|
|
289
289
|
[ "$(git -C "$_dn" rev-parse HEAD)" = "$_h1" ] \
|
|
290
290
|
&& ok "lane10-c NO-GRANT: absent standing_consent → no apply" \
|
|
291
|
-
|| bad "lane10-c NO-GRANT: applied without a grant" "
|
|
291
|
+
|| bad "lane10-c NO-GRANT: applied without a grant" "before=$_h1 rc=$_rc_c out=$_out_c"
|
|
292
292
|
|
|
293
293
|
# d — expired lease. A lease nobody enforces is not a lease.
|
|
294
294
|
cp "$_ap_root/uap.bak" "$_dn/tracks/_meta/user_adaptation_profile.md"
|
|
295
295
|
sed -i.bak2 's/expires: [0-9-]*/expires: 2020-01-01/' "$_dn/tracks/_meta/user_adaptation_profile.md"
|
|
296
296
|
_h2="$(git -C "$_dn" rev-parse HEAD)"
|
|
297
|
-
run "$_dn" "$_ap_root/s_d"
|
|
297
|
+
_out_d="$(run "$_dn" "$_ap_root/s_d" 2>&1)"; _rc_d=$?
|
|
298
298
|
[ "$(git -C "$_dn" rev-parse HEAD)" = "$_h2" ] \
|
|
299
299
|
&& ok "lane10-d EXPIRED: past-dated lease → no apply" \
|
|
300
|
-
|| bad "lane10-d EXPIRED: applied under an expired lease" "
|
|
300
|
+
|| bad "lane10-d EXPIRED: applied under an expired lease" "before=$_h2 rc=$_rc_d out=$_out_d"
|
|
301
301
|
cp "$_ap_root/uap.bak" "$_dn/tracks/_meta/user_adaptation_profile.md"
|
|
302
302
|
|
|
303
303
|
# e — divergence. --ff-only must refuse rather than absorb, and local work must survive.
|
|
304
304
|
(cd "$_dn" && echo local-only > mine.txt && git add -A && git commit -qm diverge) >/dev/null 2>&1
|
|
305
305
|
_h3="$(git -C "$_dn" rev-parse HEAD)"
|
|
306
|
-
run "$_dn" "$_ap_root/s_e"
|
|
306
|
+
_out_e="$(run "$_dn" "$_ap_root/s_e" 2>&1)"; _rc_e=$?
|
|
307
307
|
if [ "$(git -C "$_dn" rev-parse HEAD)" = "$_h3" ] && [ -f "$_dn/mine.txt" ]; then
|
|
308
308
|
ok "lane10-e DIVERGED: ff refused, local commit survives"
|
|
309
309
|
else
|
|
310
|
-
bad "lane10-e DIVERGED: local history was moved or lost" "$_h3 -> $(git -C "$_dn" rev-parse HEAD)"
|
|
310
|
+
bad "lane10-e DIVERGED: local history was moved or lost" "$_h3 -> $(git -C "$_dn" rev-parse HEAD) rc=$_rc_e out=$_out_e"
|
|
311
311
|
fi
|
|
312
312
|
|
|
313
313
|
# f — WRONG CLASS. The refusal that the other three arms structurally cannot produce.
|
|
@@ -367,7 +367,7 @@ PYX
|
|
|
367
367
|
if [ "$_h5" = "$(git -C "$_dn" rev-parse "origin/$_DEF")" ]; then
|
|
368
368
|
bad "lane10-f VACUOUS: clone is not behind origin, so 'no apply' proves nothing" "$_h5"
|
|
369
369
|
fi
|
|
370
|
-
run "$_dn" "$_ap_root/s_f"
|
|
370
|
+
_out_f="$(run "$_dn" "$_ap_root/s_f" 2>&1)"; _rc_f=$?
|
|
371
371
|
_filewide=0
|
|
372
372
|
bash "$FH_REPO/scripts/consent_registry_check.sh" \
|
|
373
373
|
"$_dn/tracks/_meta/consent_classes.yaml" \
|
|
@@ -375,9 +375,9 @@ PYX
|
|
|
375
375
|
if [ "$(git -C "$_dn" rev-parse HEAD)" = "$_h5" ] && [ "$_filewide" = "0" ]; then
|
|
376
376
|
ok "lane10-f WRONG-CLASS: another class granted, this one only in prose → no apply (file-wide check still 0)"
|
|
377
377
|
elif [ "$_filewide" != "0" ]; then
|
|
378
|
-
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"
|
|
378
|
+
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 | run_rc=$_rc_f run_out=$_out_f"
|
|
379
379
|
else
|
|
380
|
-
bad "lane10-f WRONG-CLASS: applied on a class that was never granted" "$_h5 -> $(git -C "$_dn" rev-parse HEAD)"
|
|
380
|
+
bad "lane10-f WRONG-CLASS: applied on a class that was never granted" "$_h5 -> $(git -C "$_dn" rev-parse HEAD) run_rc=$_rc_f run_out=$_out_f"
|
|
381
381
|
fi
|
|
382
382
|
cp "$_ap_root/uap.bak" "$_dn/tracks/_meta/user_adaptation_profile.md"
|
|
383
383
|
|
|
@@ -387,10 +387,10 @@ PYX
|
|
|
387
387
|
(cd "$_ap_root/up" && echo newer3 > f.txt && git commit -qam ahead3) >/dev/null 2>&1
|
|
388
388
|
git -C "$_dn" fetch -q origin >/dev/null 2>&1
|
|
389
389
|
_h4="$(git -C "$_dn" rev-parse HEAD)"
|
|
390
|
-
run "$_dn" "$_ap_root/s_f"
|
|
390
|
+
_out_ctrl="$(run "$_dn" "$_ap_root/s_f" 2>&1)"; _rc_ctrl=$?
|
|
391
391
|
[ "$(git -C "$_dn" rev-parse HEAD)" != "$_h4" ] \
|
|
392
392
|
&& ok "lane10-CONTROL: apply arm still fires after the refuse arms (instrument alive)" \
|
|
393
|
-
|| bad "lane10-CONTROL: apply arm went inert — the refuse lanes above prove nothing" "
|
|
393
|
+
|| bad "lane10-CONTROL: apply arm went inert — the refuse lanes above prove nothing" "before=$_h4 rc=$_rc_ctrl out=$_out_ctrl"
|
|
394
394
|
fi
|
|
395
395
|
rm -rf "$_ap_root"
|
|
396
396
|
|
|
@@ -36,7 +36,9 @@ trap 'rm -rf "$TMP"' EXIT
|
|
|
36
36
|
|
|
37
37
|
pass=0; fail=0; skipped=0
|
|
38
38
|
ok() { printf ' ✅ %s\n' "$1"; pass=$((pass+1)); }
|
|
39
|
-
|
|
39
|
+
# $2 (optional) = the actual stdout/stderr/rc this case caught — printed so a red lane in CI is
|
|
40
|
+
# diagnosable without re-running it by hand. Truncated/flattened like the node-check lanes' bad().
|
|
41
|
+
bad() { printf ' ❌ %s\n' "$1"; [ -n "${2:-}" ] && printf ' got: %s\n' "$(printf '%s' "$2" | tr '\n' '|' | cut -c1-220)"; fail=$((fail+1)); }
|
|
40
42
|
# A skip is COUNTED and reported. An uncounted skip is how "could not run" becomes indistinguishable
|
|
41
43
|
# from "ran and passed" in the summary line — measured on this very suite in review round 2, where a
|
|
42
44
|
# broken git produced "7 passed, 0 failed" and exit 0 while the real-worktree claim went untested.
|
|
@@ -70,23 +72,30 @@ make_tree() { # make_tree <dir> <git_shape> <cover_target:yes|no> [omit_manifest
|
|
|
70
72
|
}
|
|
71
73
|
|
|
72
74
|
run_tree() { bash "$1/scripts/package_coverage_check.sh" 2>&1; }
|
|
73
|
-
rc_tree()
|
|
75
|
+
# 🟥 no more rc_tree(): it used to re-run the subject a SECOND time just to get $? (`out=$(run_tree
|
|
76
|
+
# ...); rc=$(rc_tree ...)` — two executions of the same script, so a nondeterministic subject could
|
|
77
|
+
# report an (out, rc) pair that never co-occurred in either real run). Every call site below now
|
|
78
|
+
# does `out=$(run_tree ...); rc=$?` — one execution, $? read off THAT command substitution.
|
|
74
79
|
|
|
75
80
|
# Assert a POSITIVE outcome, never merely the absence of the SKIP line. Cross-family review caught
|
|
76
81
|
# the first draft here: it checked only that `SKIP package-coverage` was missing, so a checker that
|
|
77
82
|
# exited 1 on every worktree — the opposite defect, equally broken — would have passed the lane that
|
|
78
83
|
# exists to prove the worktree path works. "Did not say the wrong thing" is not "did the right
|
|
79
84
|
# thing"; a lane phrased as a negative can only ever fail one way.
|
|
85
|
+
# Both functions below stash their single execution's output/rc in _LAST_OUT/_LAST_RC (not `local`,
|
|
86
|
+
# deliberately — the caller reads them straight off the one run instead of re-invoking the subject a
|
|
87
|
+
# second time just to build a diagnostic string, which would be the same two-executions-for-one-
|
|
88
|
+
# verdict shape the L3–L7 dual-execution fix below removes).
|
|
80
89
|
ran_clean() { # ran_clean <dir> -> 0 iff the check actually ran AND reported a clean scan
|
|
81
|
-
local d="$1"
|
|
82
|
-
|
|
83
|
-
[ "$
|
|
84
|
-
&& ! printf '%s' "$
|
|
90
|
+
local d="$1"
|
|
91
|
+
_LAST_OUT=$(bash "$d/scripts/package_coverage_check.sh" 2>&1); _LAST_RC=$?
|
|
92
|
+
[ "$_LAST_RC" -eq 0 ] && printf '%s' "$_LAST_OUT" | grep -q 'PASS package-coverage' \
|
|
93
|
+
&& ! printf '%s' "$_LAST_OUT" | grep -q 'SKIP package-coverage'
|
|
85
94
|
}
|
|
86
95
|
caught_defect() { # caught_defect <dir> -> 0 iff the check FOUND the planted omission
|
|
87
|
-
local d="$1"
|
|
88
|
-
|
|
89
|
-
[ "$
|
|
96
|
+
local d="$1"
|
|
97
|
+
_LAST_OUT=$(bash "$d/scripts/package_coverage_check.sh" 2>&1); _LAST_RC=$?
|
|
98
|
+
[ "$_LAST_RC" -eq 1 ] && printf '%s' "$_LAST_OUT" | grep -q 'scripts/helper.sh'
|
|
90
99
|
}
|
|
91
100
|
# A CLEAN LANE ALONE PROVES NOTHING ABOUT THE WORKTREE PATH. Cross-family review round 2 demonstrated
|
|
92
101
|
# this by EXECUTION: it replaced the subject with a mutant that printed `PASS package-coverage`
|
|
@@ -99,11 +108,11 @@ wt_pair() { # wt_pair <label> <dir> <git_shape>
|
|
|
99
108
|
local label="$1" d="$2" shape="$3"
|
|
100
109
|
make_tree "$d" "$shape" yes
|
|
101
110
|
if ! ran_clean "$d"; then
|
|
102
|
-
bad "$label — clean leg: did not run to a PASS (
|
|
111
|
+
bad "$label — clean leg: did not run to a PASS (rc=$_LAST_RC)" "$_LAST_OUT"; return
|
|
103
112
|
fi
|
|
104
113
|
make_tree "$d" "$shape" no # identical tree, files[] no longer covers the referenced path
|
|
105
114
|
if ! caught_defect "$d"; then
|
|
106
|
-
bad "$label — DEFECT leg: planted omission not caught, so the clean PASS proved nothing"; return
|
|
115
|
+
bad "$label — DEFECT leg: planted omission not caught, so the clean PASS proved nothing (rc=$_LAST_RC)" "$_LAST_OUT"; return
|
|
107
116
|
fi
|
|
108
117
|
ok "$label"
|
|
109
118
|
}
|
|
@@ -171,15 +180,20 @@ else
|
|
|
171
180
|
esac
|
|
172
181
|
}
|
|
173
182
|
mkdir -p "$TMP/emptytpl"
|
|
174
|
-
|
|
183
|
+
# The whole fixture-build chain runs as ONE group so its combined stdout+stderr lands in one
|
|
184
|
+
# variable instead of /dev/null — a failed build used to report only "could not be built", with
|
|
185
|
+
# no way to tell which of the four steps broke or why.
|
|
186
|
+
_l1b_ok=1
|
|
187
|
+
_l1b_out="$( { git "${GIT_ISO[@]}" -C "$TMP/realrepo" init -q --template="$TMP/emptytpl" . \
|
|
175
188
|
&& _gitdir_ok \
|
|
176
|
-
&& git "${GIT_ISO[@]}" -C "$TMP/realrepo" commit -q --allow-empty -m init
|
|
177
|
-
&& git "${GIT_ISO[@]}" -C "$TMP/realrepo" worktree add -q --detach "$TMP/realwt"
|
|
178
|
-
&& [ -f "$TMP/realwt/.git" ];
|
|
189
|
+
&& git "${GIT_ISO[@]}" -C "$TMP/realrepo" commit -q --allow-empty -m init \
|
|
190
|
+
&& git "${GIT_ISO[@]}" -C "$TMP/realrepo" worktree add -q --detach "$TMP/realwt" \
|
|
191
|
+
&& [ -f "$TMP/realwt/.git" ]; } 2>&1 )" || _l1b_ok=0
|
|
192
|
+
if [ "$_l1b_ok" -eq 1 ]; then
|
|
179
193
|
wt_pair "L1-b real \`git worktree add\` (.git is a genuine gitdir pointer): scans for real" \
|
|
180
194
|
"$TMP/realwt" none # the REAL .git file is already in place; do not overwrite it
|
|
181
195
|
else
|
|
182
|
-
bad "L1-b git IS installed but the real-worktree fixture could not be built — the claim was testable and was not tested"
|
|
196
|
+
bad "L1-b git IS installed but the real-worktree fixture could not be built — the claim was testable and was not tested" "$_l1b_out"
|
|
183
197
|
fi
|
|
184
198
|
fi
|
|
185
199
|
|
|
@@ -190,11 +204,11 @@ wt_pair "L2 ordinary checkout (.git is a DIR): scans for real — both legs" "$T
|
|
|
190
204
|
# The widening from -d to -e must not cost the legitimate skip. An installed npm package has no
|
|
191
205
|
# .git of either kind; making it fail there would fire on every consumer running `npm test`.
|
|
192
206
|
make_tree "$TMP/l3" none yes
|
|
193
|
-
out=$(run_tree "$TMP/l3"); rc
|
|
207
|
+
out=$(run_tree "$TMP/l3"); rc=$?
|
|
194
208
|
if printf '%s' "$out" | grep -q 'SKIP package-coverage' && [ "$rc" -eq 0 ]; then
|
|
195
209
|
ok "L3 package mode (no .git): still skips, exit 0"
|
|
196
210
|
else
|
|
197
|
-
bad "L3 package mode (no .git): expected SKIP+0, got rc=$rc
|
|
211
|
+
bad "L3 package mode (no .git): expected SKIP+0, got rc=$rc" "$out"
|
|
198
212
|
fi
|
|
199
213
|
|
|
200
214
|
# ── L4 · a checkout with no manifest is UNMEASURED, not clean ────────────────────────
|
|
@@ -203,48 +217,60 @@ fi
|
|
|
203
217
|
# file list cannot be read, which is "cannot measure", not "nothing to measure". An anchor that
|
|
204
218
|
# pins the wrong direction is worse than no anchor — it makes the hole look deliberate.
|
|
205
219
|
make_tree "$TMP/l4" dir yes omit
|
|
206
|
-
out=$(run_tree "$TMP/l4"); rc
|
|
220
|
+
out=$(run_tree "$TMP/l4"); rc=$?
|
|
207
221
|
if [ "$rc" -eq 1 ] && printf '%s' "$out" | grep -q 'UNMEASURED, not clean'; then
|
|
208
222
|
ok "L4 .git present but no package.json: FAILS as unmeasured, not a clean skip"
|
|
209
223
|
else
|
|
210
|
-
bad "L4 no package.json: expected exit 1 (unmeasured), got rc=$rc
|
|
224
|
+
bad "L4 no package.json: expected exit 1 (unmeasured), got rc=$rc" "$out"
|
|
211
225
|
fi
|
|
212
226
|
|
|
213
227
|
# ── L5/L6 · KNOWN PAIR ───────────────────────────────────────────────────────────────
|
|
214
228
|
# Same tree twice; the ONLY difference is whether files[] covers the referenced path.
|
|
215
229
|
# L5 known-POSITIVE: referenced, exists, not shipped -> must FAIL.
|
|
216
230
|
make_tree "$TMP/l5" dir no
|
|
217
|
-
out=$(run_tree "$TMP/l5"); rc
|
|
231
|
+
out=$(run_tree "$TMP/l5"); rc=$?
|
|
218
232
|
if [ "$rc" -eq 1 ] && printf '%s' "$out" | grep -q 'scripts/helper.sh'; then
|
|
219
233
|
ok "L5 known-positive (referenced ∧ exists ∧ ¬shipped): FAIL, names the path"
|
|
220
234
|
else
|
|
221
|
-
bad "L5 known-positive: expected exit 1 naming scripts/helper.sh, got rc=$rc"
|
|
235
|
+
bad "L5 known-positive: expected exit 1 naming scripts/helper.sh, got rc=$rc" "$out"
|
|
222
236
|
fi
|
|
223
237
|
|
|
224
238
|
# L6 known-NEGATIVE: identical, but the path is in files[] -> must PASS.
|
|
225
239
|
make_tree "$TMP/l6" dir yes
|
|
226
|
-
out=$(run_tree "$TMP/l6"); rc
|
|
240
|
+
out=$(run_tree "$TMP/l6"); rc=$?
|
|
227
241
|
if [ "$rc" -eq 0 ] && printf '%s' "$out" | grep -q 'PASS package-coverage'; then
|
|
228
242
|
ok "L6 known-negative (same tree, path shipped): PASS"
|
|
229
243
|
else
|
|
230
|
-
bad "L6 known-negative: expected exit 0 PASS, got rc=$rc"
|
|
244
|
+
bad "L6 known-negative: expected exit 0 PASS, got rc=$rc" "$out"
|
|
231
245
|
fi
|
|
232
246
|
|
|
233
247
|
# ── L7 · the impossible-zero guard is not reachable by an empty files[] ──────────────
|
|
234
248
|
# A manifest with no shipped docs must report the extractor as broken, not print a pass.
|
|
235
249
|
mkdir -p "$TMP/l7/scripts"; cp "$SUBJECT" "$TMP/l7/scripts/"; mkdir -p "$TMP/l7/.git"
|
|
236
250
|
printf '{"files":[]}\n' > "$TMP/l7/package.json"
|
|
237
|
-
out=$(run_tree "$TMP/l7"); rc
|
|
251
|
+
out=$(run_tree "$TMP/l7"); rc=$?
|
|
238
252
|
if [ "$rc" -eq 1 ] && printf '%s' "$out" | grep -q 'the check broke, it did not pass'; then
|
|
239
253
|
ok "L7 zero shipped docs: reported as broken extractor, not as a pass"
|
|
240
254
|
else
|
|
241
|
-
bad "L7 zero shipped docs: expected exit 1 'check broke', got rc=$rc"
|
|
255
|
+
bad "L7 zero shipped docs: expected exit 1 'check broke', got rc=$rc" "$out"
|
|
242
256
|
fi
|
|
243
257
|
|
|
244
258
|
echo
|
|
245
259
|
if [ "$skipped" -gt 0 ]; then
|
|
246
260
|
echo "package-coverage lanes: ${pass} passed, ${fail} failed, ${skipped} UNCALIBRATED (not verified here)"
|
|
247
261
|
else
|
|
248
|
-
|
|
262
|
+
|
|
263
|
+
# ── lane 7: tarball oracle JSON without files[] → text-listing fallback (CI 2026-09-04, v3.0.0) ──
|
|
264
|
+
# Known-pair: stub npm returns JSON lacking files[] but delegates the text `pack --dry-run` to the
|
|
265
|
+
# real npm → PASS (7a). Stub returns the same JSON and an EMPTY text listing → ORACLE_UNAVAILABLE rc=2 (7b).
|
|
266
|
+
cd "$REPO_ROOT" || exit 10; _REAL_NPM=$(command -v npm); _ST=$(mktemp -d) # earlier lanes cd into fixtures — the subject reads .git/package.json from cwd
|
|
267
|
+
printf '#!/bin/bash\nif [ "$*" = "pack --dry-run --json" ]; then echo "[{\\"id\\":\\"x\\"}]"; else exec %s "$@"; fi\n' "$_REAL_NPM" > "$_ST/npm"; chmod +x "$_ST/npm"
|
|
268
|
+
o=$(PATH="$_ST:$PATH" bash "$SUBJECT" --vs-tarball 2>&1); rc=$?
|
|
269
|
+
if [ "$rc" = 0 ] && printf '%s' "$o" | grep -q "^PASS package-coverage"; then echo " ✅ lane 7a: JSON without files[] → text listing fallback PASSes"; pass=$((pass+1)); else echo " ❌ lane 7a: fallback did not PASS (rc=$rc)"; printf "%s\n" "$o" | grep -E "UNAVAILABLE|head:" | cut -c1-220; fail=$((fail+1)); fi
|
|
270
|
+
printf '#!/bin/bash\nif [ "$*" = "pack --dry-run --json" ]; then echo "[{\\"id\\":\\"x\\"}]"; elif [ "$*" = "pack --dry-run" ]; then exit 0; else exec %s "$@"; fi\n' "$_REAL_NPM" > "$_ST/npm"
|
|
271
|
+
o=$(PATH="$_ST:$PATH" bash "$SUBJECT" --vs-tarball 2>&1); rc=$?
|
|
272
|
+
if [ "$rc" = 2 ] && printf '%s' "$o" | grep -q "UNAVAILABLE"; then echo " ✅ lane 7b: JSON without files[] AND empty text listing → UNAVAILABLE rc=2 (fail-closed)"; pass=$((pass+1)); else echo " ❌ lane 7b: expected rc=2 UNAVAILABLE, got rc=$rc"; fail=$((fail+1)); fi
|
|
273
|
+
rm -rf "$_ST"
|
|
274
|
+
echo "package-coverage lanes: ${pass} passed, ${fail} failed"
|
|
249
275
|
fi
|
|
250
276
|
[ "$fail" -eq 0 ] || exit 1
|
|
@@ -45,9 +45,9 @@ expect() {
|
|
|
45
45
|
echo "[pipe-verdict-guard] known pairs"
|
|
46
46
|
echo "-- R1: PIPESTATUS under zsh (deterministic) --"
|
|
47
47
|
# The exact shape emitted 6× in this project, including twice on 2026-07-31.
|
|
48
|
-
expect "R1 the measured shape" HIT 'bash x.sh | tail -5; echo "exit=${PIPESTATUS[0]}"'
|
|
49
|
-
expect "R1 any index" HIT 'a | b; rc=${PIPESTATUS[1]}'
|
|
50
|
-
expect "R1 inside a larger command" HIT 'cd /r && npm t | tail; E=${PIPESTATUS[0]}; echo $E'
|
|
48
|
+
expect "R1 the measured shape" HIT 'bash x.sh | tail -5; echo "exit=${PIPESTATUS[0]}"' # portability-noqa: fixture string fed to pipe_verdict_guard.sh for static analysis, never executed by this shell
|
|
49
|
+
expect "R1 any index" HIT 'a | b; rc=${PIPESTATUS[1]}' # portability-noqa: same as above
|
|
50
|
+
expect "R1 inside a larger command" HIT 'cd /r && npm t | tail; E=${PIPESTATUS[0]}; echo $E' # portability-noqa: same as above
|
|
51
51
|
# zsh's own spelling is correct here and must never be flagged.
|
|
52
52
|
expect "R1 zsh spelling is CLEAN" CLEAN 'a | b; rc=$pipestatus[1]'
|
|
53
53
|
|
|
@@ -83,7 +83,7 @@ echo x | grep -q y
|
|
|
83
83
|
rc=$?'
|
|
84
84
|
# B: zsh accepts `$PIPESTATUS[0]` without braces; the brace-anchored regex missed it.
|
|
85
85
|
expect "B PIPESTATUS without braces" HIT 'a | b; rc=$PIPESTATUS[0]'
|
|
86
|
-
expect "B braced form still caught" HIT 'a | b; rc=${PIPESTATUS[0]}'
|
|
86
|
+
expect "B braced form still caught" HIT 'a | b; rc=${PIPESTATUS[0]}' # portability-noqa: fixture string fed to pipe_verdict_guard.sh for static analysis, never executed by this shell
|
|
87
87
|
|
|
88
88
|
echo "-- D: statement-continuation flatten + wrapped filter (leg-C MED round, 2026-08-01) --"
|
|
89
89
|
# The blanket newline→`;` rewrite broke the CONTINUATION shapes: a newline after `|`/`&&` or a
|
|
@@ -167,7 +167,7 @@ echo "-- C: delivery channel (closes N=8 — detection without delivery is decor
|
|
|
167
167
|
# NAMED RESIDUAL (cross-family LOW, accepted): $(…) capture strips NUL bytes, so a mutant emitting
|
|
168
168
|
# NUL+JSON would pass C1. The producer's hits text is static ASCII+⚠️ with no NUL source, so the
|
|
169
169
|
# lane does not pay for a byte-exact harness; revisit only if the producer ever emits dynamic bytes.
|
|
170
|
-
HIT_CMD='bash x.sh | tail -5; echo "exit=${PIPESTATUS[0]}"'
|
|
170
|
+
HIT_CMD='bash x.sh | tail -5; echo "exit=${PIPESTATUS[0]}"' # portability-noqa: fixture string fed to the guard for static analysis, never executed by this shell
|
|
171
171
|
payload() { python3 -c 'import json,sys; print(json.dumps({"tool_name":"Bash","tool_input":{"command":sys.argv[1]}}))' "$1"; }
|
|
172
172
|
|
|
173
173
|
# All C lanes measure ONE invocation: stdout, stderr, and exit code from the same run (a pair of
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_precommit_pointer_index_lanes.sh — known pair for templates/.git-hooks/pre-commit [Pointers]:
|
|
3
|
+
# the Detail-pointer gate must read the STAGED blob, not the working tree. Found 2026-09-03 (arm C
|
|
4
|
+
# wt2, A3 triage): `[ -f "$REPO_ROOT/$f" ] || continue` let a staged .md with a broken
|
|
5
|
+
# `**Detail**: See §X` pointer land silently when the file was rm'd from disk after staging
|
|
6
|
+
# (git commits the INDEX). Runs in a disposable shallow clone — never touches this checkout.
|
|
7
|
+
# P1 staged broken pointer, file rm'd from disk → commit BLOCKED (❌ pointer line printed)
|
|
8
|
+
# N1 staged VALID pointer, file rm'd from disk → [Pointers] block runs with no ❌ (control: index read works)
|
|
9
|
+
# N2 staged broken pointer, file still on disk → BLOCKED (the pre-fix path also caught this — control)
|
|
10
|
+
# Usage: bash scripts/test_precommit_pointer_index_lanes.sh [--hook <path>] (default = templates/.git-hooks/pre-commit)
|
|
11
|
+
set -u; ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"; HOOK="$ROOT/templates/.git-hooks/pre-commit"
|
|
12
|
+
[ "${1:-}" = "--hook" ] && HOOK="$2"
|
|
13
|
+
T=$(mktemp -d); trap 'rm -rf "$T"' EXIT; pass=0; fail=0
|
|
14
|
+
git clone -q --depth 1 "file://$ROOT" "$T/r" 2>/dev/null || { echo "❌ clone failed"; exit 10; }
|
|
15
|
+
cd "$T/r" && git config user.email t@t && git config user.name t && mkdir -p .git/hooks && cp "$HOOK" .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit
|
|
16
|
+
mkdir -p docs/lanefix; printf '## §Alive\ntext\n' > docs/lanefix/target.md; git add docs/lanefix/target.md; git -c core.hooksPath=/dev/null commit -qm base 2>/dev/null
|
|
17
|
+
run_case(){ local label="$1" want="$2" ptr="$3" rm_after="$4"
|
|
18
|
+
printf '# probe\n\n> **Detail**: See `docs/lanefix/target.md §%s`\n' "$ptr" > docs/lanefix/probe.md
|
|
19
|
+
git add docs/lanefix/probe.md; [ "$rm_after" = 1 ] && rm -f docs/lanefix/probe.md
|
|
20
|
+
out=$(FH_SKIP_GATE_AXES=1 git commit -qm probe 2>&1); rc=$?
|
|
21
|
+
# Discriminate on the [Pointers] block itself, not on commit rc: the fixture clone carries no
|
|
22
|
+
# marker/manifest, so OTHER axes block every commit here — rc is not this lane's signal.
|
|
23
|
+
if printf '%s' "$out" | grep -q "Detail pointer §"; then got=BLOCK_PTR
|
|
24
|
+
elif printf '%s' "$out" | grep -q "unreadable from the index"; then got=BLOCK_INDEX
|
|
25
|
+
elif printf '%s' "$out" | grep -q "\[Pointers\]"; then got=PTR_OK
|
|
26
|
+
else got=PTR_NOT_RUN; fi
|
|
27
|
+
git reset -q --hard HEAD 2>/dev/null; # noqa: destructive-op — disposable clone under mktemp git rm -q --cached docs/lanefix/probe.md 2>/dev/null; rm -f docs/lanefix/probe.md
|
|
28
|
+
if [ "$got" = "$want" ]; then printf ' ✅ %-48s %s\n' "$label" "$got"; pass=$((pass+1)); else printf ' ❌ %-48s %s (expected %s)\n' "$label" "$got" "$want"; fail=$((fail+1)); printf '%s\n' "$out" | grep -E "Pointers|❌" | head -4 | sed 's/^/ /'; fi; }
|
|
29
|
+
echo "[precommit-pointer-index] hook=$HOOK"
|
|
30
|
+
run_case "P1 broken pointer, staged then rm'd from disk" BLOCK_PTR Ghost 1
|
|
31
|
+
run_case "N1 valid pointer, staged then rm'd (index read)" PTR_OK Alive 1
|
|
32
|
+
run_case "N2 broken pointer, still on disk (old path too)" BLOCK_PTR Ghost 0
|
|
33
|
+
echo "[precommit-pointer-index] $pass passed, $fail failed"; [ "$fail" -eq 0 ]
|
|
@@ -16,7 +16,16 @@ set -uo pipefail
|
|
|
16
16
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
17
17
|
SRC="$HERE/plugins/fh-commons/skills/preprep"
|
|
18
18
|
# standalone 배포 위치는 환경변수로 받는다. 기본값을 박으면 다른 머신에서 거짓 SKIP 이 된다.
|
|
19
|
-
|
|
19
|
+
# 🟥 2026-09-03 — 그런데 «아무도 그 변수를 안 걸어서» D2 가 여태 SKIP 이었고, 그 사이 컴패니언
|
|
20
|
+
# 저장소의 fork(724줄)가 정본(789줄)과 갈라져 L9~L11 을 안 부르고 있었다 — 정본 주석이 이미
|
|
21
|
+
# 한 번 적어 둔 사고의 2회째(fh_signal_2026-09-03_preprep-standalone-anchor-skip.md). 슬롯은
|
|
22
|
+
# 있고 소비처가 0 인 형태. 처방: 명시 변수가 없으면 운영자 로컬 바인딩이 이미 export 하는
|
|
23
|
+
# FH_COMPANION_STORE 아래 preprep/ 을 «자동 후보»로 쓴다 — 기본값을 박는 게 아니라 이미
|
|
24
|
+
# 선언된 경로를 읽는 것이라 다른 머신에서 거짓 SKIP 을 만들지 않는다(없으면 여전히 SKIP).
|
|
25
|
+
DIST="${PREPREP_STANDALONE_DIR:-}"; DIST_SRC="PREPREP_STANDALONE_DIR"
|
|
26
|
+
if [ -z "$DIST" ] && [ -n "${FH_COMPANION_STORE:-}" ] && [ -d "${FH_COMPANION_STORE}/preprep" ]; then
|
|
27
|
+
DIST="${FH_COMPANION_STORE}/preprep"; DIST_SRC="FH_COMPANION_STORE/preprep (자동 후보)"
|
|
28
|
+
fi
|
|
20
29
|
PASS=0; FAIL=0; SKIP=0
|
|
21
30
|
ok(){ echo " ✅ $1"; PASS=$((PASS+1)); }
|
|
22
31
|
ng(){ echo " ❌ $1"; FAIL=$((FAIL+1)); }
|
|
@@ -41,16 +50,16 @@ fi
|
|
|
41
50
|
|
|
42
51
|
# D2 — standalone 대조
|
|
43
52
|
if [ -z "$DIST" ]; then
|
|
44
|
-
sk "D2 standalone 대조 — PREPREP_STANDALONE_DIR
|
|
53
|
+
sk "D2 standalone 대조 — PREPREP_STANDALONE_DIR 미설정이고 FH_COMPANION_STORE/preprep 도 없어 배포본을 못 찾았다. UNCHECKED — 배포본이 있는 머신이면 둘 중 하나를 export 해라"
|
|
45
54
|
elif [ ! -d "$DIST" ]; then
|
|
46
|
-
ng "D2
|
|
55
|
+
ng "D2 $DIST_SRC 이 가리키는 곳이 없다: $DIST (설정됐는데 부재 = 드리프트 아니라 배선 결함)"
|
|
47
56
|
else
|
|
48
57
|
drift=""
|
|
49
58
|
for f in preprep.py interslide_deps.py lane_progression.py lane_adjacent_dup.py; do
|
|
50
59
|
if [ ! -f "$DIST/$f" ]; then drift="$drift $f(부재)"
|
|
51
60
|
elif ! cmp -s "$SRC/$f" "$DIST/$f"; then drift="$drift $f(갈림)"; fi
|
|
52
61
|
done
|
|
53
|
-
[ -z "$drift" ] && ok "D2 standalone 코드 5파일이 단일 소스와 바이트 동일" \
|
|
62
|
+
[ -z "$drift" ] && ok "D2 standalone 코드 5파일이 단일 소스와 바이트 동일 ($DIST_SRC)" \
|
|
54
63
|
|| ng "D2 드리프트:$drift ⇒ 사본이 둘이 됐다. 단일 소스에서 다시 뽑아라"
|
|
55
64
|
fi
|
|
56
65
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_preprep_drift_anchor_lanes.sh — known pairs for the D2 leg of scripts/test_preprep_drift_anchor.sh
|
|
3
|
+
# WHY (2026-09-03): D2 compared the standalone copy only when PREPREP_STANDALONE_DIR was set. Nobody
|
|
4
|
+
# set it, so the leg SKIPPED for weeks while the companion-store fork drifted (724 vs 789 lines, L9-L11
|
|
5
|
+
# never called) — the second occurrence of the exact accident the canon's own comment records.
|
|
6
|
+
# Fix under test: with the env var unset, the anchor falls back to $FH_COMPANION_STORE/preprep when it
|
|
7
|
+
# exists. These lanes pin: fallback used · fallback discriminates (identical PASS / drifted FAIL) ·
|
|
8
|
+
# explicit var still wins · nothing set → SKIP (never a silent PASS).
|
|
9
|
+
set -u
|
|
10
|
+
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"; A="$HERE/scripts/test_preprep_drift_anchor.sh"
|
|
11
|
+
SRC="$HERE/plugins/fh-commons/skills/preprep"; T=$(mktemp -d); pass=0; fail=0
|
|
12
|
+
chk(){ if [ "$1" = 0 ]; then echo " ✅ $2"; pass=$((pass+1)); else echo " ❌ $2"; fail=$((fail+1)); fi; }
|
|
13
|
+
mk(){ mkdir -p "$1"; for f in preprep.py interslide_deps.py lane_progression.py lane_adjacent_dup.py lane_promise.py; do cp "$SRC/$f" "$1/$f"; done; }
|
|
14
|
+
echo "[preprep-drift-anchor] D2 known pairs"
|
|
15
|
+
# L1 nothing set → D2 SKIP (skip != pass), rc 0 (D2 is not a FAIL)
|
|
16
|
+
out=$(env -u PREPREP_STANDALONE_DIR FH_COMPANION_STORE="$T/nostore" bash "$A" 2>&1); printf '%s' "$out" | grep -q "D2 .*SKIPPED"; chk $? "L1 var unset + no companion preprep → D2 SKIPPED (not PASS)"
|
|
17
|
+
# L2 companion copy identical → fallback used, D2 PASS
|
|
18
|
+
mk "$T/be/preprep"; out=$(env -u PREPREP_STANDALONE_DIR FH_COMPANION_STORE="$T/be" bash "$A" 2>&1); printf '%s' "$out" | grep -q "✅ D2 .*자동 후보"; chk $? "L2 fallback FH_COMPANION_STORE/preprep identical → D2 PASS via 자동 후보"
|
|
19
|
+
# L3 companion copy drifted → D2 FAIL, rc 1 (the accident class)
|
|
20
|
+
printf '\n# drift\n' >> "$T/be/preprep/preprep.py"; env -u PREPREP_STANDALONE_DIR FH_COMPANION_STORE="$T/be" bash "$A" >"$T/l3.out" 2>&1; rc=$?; [ "$rc" -ne 0 ] && grep -q "D2 드리프트.*preprep.py(갈림)" "$T/l3.out"; chk $? "L3 fallback copy drifted → D2 FAIL rc=$rc (known-positive)"
|
|
21
|
+
# L4 explicit var wins over companion
|
|
22
|
+
mk "$T/explicit"; out=$(PREPREP_STANDALONE_DIR="$T/explicit" FH_COMPANION_STORE="$T/be" bash "$A" 2>&1); printf '%s' "$out" | grep -q "✅ D2 .*(PREPREP_STANDALONE_DIR)"; chk $? "L4 explicit PREPREP_STANDALONE_DIR wins over drifted companion copy"
|
|
23
|
+
rm -rf "$T"; echo "[preprep-drift-anchor] $pass passed, $fail failed"; [ "$fail" -eq 0 ]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_proposal_hook_lanes.sh — known pairs for scripts/proposal_hook.sh (written before shipping; r4 KP + Bash path)
|
|
3
|
+
set -u; HDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"; T=$(mktemp -d); pass=0; fail=0
|
|
4
|
+
export CLAUDE_PROJECT_DIR="$T"
|
|
5
|
+
exp(){ local label="$1" want="$2" payload="$3" got; got=$(printf '%s' "$payload" | bash "$HDIR/proposal_hook.sh" 2>/dev/null | wc -c | tr -d ' '); [ "$got" -gt 0 ] && got=HIT || got=CLEAN
|
|
6
|
+
if [ "$got" = "$want" ]; then printf ' ✅ %-52s %s\n' "$label" "$got"; pass=$((pass+1)); else printf ' ❌ %-52s %s (expected %s)\n' "$label" "$got" "$want"; fail=$((fail+1)); fi; }
|
|
7
|
+
echo "[proposal-hook] known pairs"
|
|
8
|
+
exp "T1 Edit comm+LC_ALL (verdict compare)" HIT '{"tool_name":"Edit","tool_input":{"file_path":"/x/scripts/sim_isolated_run.sh","old_string":" comm -13 <(sort a) <(sort b) \\","new_string":" comm -13 <(LC_ALL=C sort a) <(LC_ALL=C sort b) \\"}}'
|
|
9
|
+
exp "T2 Edit adds fail-open guard" HIT '{"tool_name":"Edit","tool_input":{"file_path":"/x/scripts/capability_effect_probe.sh","old_string":" h=$(ls -A \"$HOME\" | shasum)","new_string":" h=$(ls -A \"$HOME\" | shasum) || { echo LS_FAILED; exit 10; }"}}'
|
|
10
|
+
exp "T3 Edit continue guard" HIT '{"tool_name":"Edit","tool_input":{"file_path":"/x/templates/regression_guard.sh","old_string":" [ -e \"$blk\" ] && [ -s \"$blk\" ] || continue","new_string":" [ -e \"$blk\" ] || { echo missing >&2; continue; }"}}'
|
|
11
|
+
exp "HARD Edit usage string only (r4 0/5)" CLEAN '{"tool_name":"Edit","tool_input":{"file_path":"/x/scripts/daily_report.sh","old_string":" *) echo \"usage: d.sh [run]\" >&2; exit 2 ;;","new_string":" *) echo \"usage: d.sh [run|--self-test] (DR_DATE)\" >&2; exit 2 ;;"}}'
|
|
12
|
+
exp "docs file with tokens" CLEAN '{"tool_name":"Edit","tool_input":{"file_path":"/x/docs/a.md","old_string":"a","new_string":"exit 1 || continue"}}'
|
|
13
|
+
exp "Write new scripts/*.sh with verdict" HIT '{"tool_name":"Write","tool_input":{"file_path":"/x/scripts/new_check.sh","content":"#!/bin/bash\n[ -f x ] || exit 1"}}'
|
|
14
|
+
exp "Bash sed -i on scripts/*.sh with token" HIT '{"tool_name":"Bash","tool_input":{"command":"sed -i \"\" \"s/comm -13/LC_ALL=C comm -13/\" scripts/sim_isolated_run.sh && [ -s out ] || exit 1"}}'
|
|
15
|
+
exp "Bash sed -i token ONLY inside quotes (a1)" HIT '{"tool_name":"Bash","tool_input":{"command":"sed -i \"\" \"s/exit 1/exit 2/\" scripts/target.sh"}}'
|
|
16
|
+
exp "Bash sed -i single-quoted token (a1b)" HIT '{"tool_name":"Bash","tool_input":{"command":"sed -i '"'"''"'"' '"'"'s/|| continue/|| { echo x; continue; }/'"'"' scripts/target.sh"}}'
|
|
17
|
+
exp "Bash sed -i no token anywhere (a3)" CLEAN '{"tool_name":"Bash","tool_input":{"command":"sed -i \"\" s/foo/bar/ scripts/target.sh"}}'
|
|
18
|
+
exp "Bash redirect into scripts/*.sh w/ token" HIT '{"tool_name":"Bash","tool_input":{"command":"printf \"%s\\n\" x >> scripts/x.sh; grep -q y scripts/x.sh || exit 3"}}'
|
|
19
|
+
exp "Bash redirect into docs (no)" CLEAN '{"tool_name":"Bash","tool_input":{"command":"echo \"exit 1\" >> docs/a.md || exit 1"}}'
|
|
20
|
+
exp "Bash ls only (no target)" CLEAN '{"tool_name":"Bash","tool_input":{"command":"ls scripts/ && [ -d scripts ] || exit 1"}}'
|
|
21
|
+
exp "G1 Edit templates/.git-hooks/pre-commit (no .sh)" HIT '{"tool_name":"Edit","tool_input":{"file_path":"/x/templates/.git-hooks/pre-commit","old_string":" [ -f x ] || continue","new_string":" [ -f x ] || { echo missing; PTR_FAIL=1; continue; }"}}'
|
|
22
|
+
exp "G1-ctrl Edit .git-hooks docs-ish no token" CLEAN '{"tool_name":"Edit","tool_input":{"file_path":"/x/templates/.git-hooks/pre-commit","old_string":"# note a","new_string":"# note b"}}'
|
|
23
|
+
exp "noqa exempts" CLEAN '{"tool_name":"Edit","tool_input":{"file_path":"/x/scripts/a.sh","old_string":"a","new_string":"exit 1 # noqa: proposal-hook"}}'
|
|
24
|
+
exp "unparseable payload silent" CLEAN 'not json'
|
|
25
|
+
msg(){ printf '%s' "$2" | bash "$HDIR/proposal_hook.sh" 2>/dev/null; }
|
|
26
|
+
fact(){ local label="$1" want="$2" payload="$3" got; got=$(msg x "$payload"); if printf '%s' "$got" | grep -q -- "$want"; then printf ' ✅ %-52s carries «%s»\n' "$label" "$want"; pass=$((pass+1)); else printf ' ❌ %-52s missing «%s»\n' "$label" "$want"; fail=$((fail+1)); fi; }
|
|
27
|
+
mkdir -p "$T/scripts"; : > "$T/scripts/test_has_lane_lanes.sh"; printf 'scan of scripts/covered.sh\nfindings: 0\n' > "$T/scripts/.degrade_scan_last_2026-09-03.txt"
|
|
28
|
+
E='{"tool_name":"Edit","tool_input":{"file_path":"/x/scripts/%s","old_string":"a","new_string":"[ -f x ] || exit 1"}}'
|
|
29
|
+
fact "F1 lane exists → fact line, no known-pair item" "이미 있다" "$(printf "$E" has_lane.sh)"
|
|
30
|
+
fact "F1b lane exists → scan item still proposed" "degrade_direction_scan.sh 로" "$(printf "$E" has_lane.sh)"
|
|
31
|
+
fact "F2 self-lane (test_*_lanes.sh) → fact line" "이 파일 자체가 레인" "$(printf "$E" test_has_lane_lanes.sh)"
|
|
32
|
+
fact "F3 scan covers file → fact line w/ findings" "findings: 0" "$(printf "$E" covered.sh)"
|
|
33
|
+
fact "F4 neither → both items proposed" "known-pair(고친 케이스 + 반대 케이스) 컨트롤 degrade_direction_scan.sh" "$(printf "$E" bare.sh)"
|
|
34
|
+
got=$(msg x "$(printf "$E" bare.sh)"); if printf '%s' "$got" | grep -q "사실:"; then echo " ❌ F4-ctrl bare file must carry NO fact line"; fail=$((fail+1)); else echo " ✅ F4-ctrl bare file carries no fact line (known-negative)"; pass=$((pass+1)); fi
|
|
35
|
+
[ -f "$T/.claude/.proposal_hook_events.tsv" ] && [ "$(grep -c FIRE "$T/.claude/.proposal_hook_events.tsv")" -ge 5 ]; r=$?; [ $r = 0 ] && { echo " ✅ evidence file carries a FIRE row per hit"; pass=$((pass+1)); } || { echo " ❌ evidence file missing/short"; fail=$((fail+1)); }
|
|
36
|
+
rm -rf "$T"; echo "[proposal-hook] $pass passed, $fail failed"; [ "$fail" -eq 0 ]
|