@chrono-meta/fh-gate 1.4.96 → 1.4.98
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 +39 -6
- package/README.ja.md +10 -0
- package/README.ko.md +10 -0
- package/README.md +30 -0
- package/README.zh.md +10 -0
- 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 +125 -0
- package/package.json +9 -2
- 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 +65 -0
- package/plugins/fh-meta/skills/auto-decorrelation/SKILL.md +30 -0
- package/scripts/compaction_probe.sh +153 -10
- package/scripts/consent_registry_check.sh +63 -0
- package/scripts/degrade_direction_scan.sh +10 -1
- package/scripts/digest_landing_check.sh +20 -4
- package/scripts/fh_node_check.sh +60 -1
- package/scripts/fh_session_load.sh +22 -2
- package/scripts/frontier_digest_autopilot.sh +229 -0
- package/scripts/lane_runner_check.sh +637 -0
- package/scripts/package_coverage_check.sh +199 -35
- package/scripts/postinstall_notice.js +34 -0
- package/scripts/psa_scan_lib.sh +164 -0
- package/scripts/public_surface_scan_files.sh +30 -0
- package/scripts/selfcheck.sh +442 -33
- package/scripts/test_consent_registry.sh +54 -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 +221 -0
- package/scripts/test_marker_crossfamily_lanes.sh +10 -1
- package/scripts/test_marker_floor_lanes.sh +25 -1
- package/scripts/test_psa_singlefile_lanes.sh +288 -0
- package/scripts/test_reviewer_capability_conformance.sh +10 -1
- package/scripts/test_selfcheck_state_lanes.sh +173 -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/degrade_direction_scan.sh +10 -1
- package/templates/goal-quench-settings-merged.json +30 -0
|
@@ -728,6 +728,60 @@ YAML
|
|
|
728
728
|
lane "P-SPELL a grant WIDER than its class is still refused (the paired control)" 1 "R7"
|
|
729
729
|
|
|
730
730
|
|
|
731
|
+
# ── PROV : every verdict states what it measured WITH, and it states the TRUTH ──────────────────
|
|
732
|
+
# 2026-08-12. This gate rides selfcheck → prepublishOnly. A release shipped green from a session whose
|
|
733
|
+
# `python3` resolved to an unrelated project's venv that had PyYAML, while the machine's own python3
|
|
734
|
+
# did not. Nothing was bypassed — the PASS was simply not portable, and said nothing about what
|
|
735
|
+
# produced it.
|
|
736
|
+
#
|
|
737
|
+
# 🟥 The first version of this lane compared two arms, one of them "PyYAML hidden" via
|
|
738
|
+
# `env -i PATH=/usr/bin:/bin PYTHONNOUSERSITE=1`. It passed locally and FAILED IN CI — because
|
|
739
|
+
# `PYTHONNOUSERSITE` suppresses only the USER site directory. On this author's machine PyYAML was a
|
|
740
|
+
# `--user` install so it hid; on the CI runner it is a system `dist-packages` install so it did not,
|
|
741
|
+
# both arms returned the same value, and the lane called its own subject decorative.
|
|
742
|
+
# **The control worked only by accident of how the author happened to install a package** — which is
|
|
743
|
+
# the exact defect class the subject under test exists to catch, reproduced inside its own lane.
|
|
744
|
+
#
|
|
745
|
+
# So the lane no longer manufactures an environment. It compares the reported value against an
|
|
746
|
+
# INDEPENDENT ORACLE computed in the same run: whatever `find_spec` says here, the instrument line
|
|
747
|
+
# must say the same thing. That catches removal, hard-coding, truncation, and drift — everywhere,
|
|
748
|
+
# with no assumption about how PyYAML got installed.
|
|
749
|
+
_prov_line=$(bash "$CHK" 2>&1 | grep -o 'instrument (consent-registry): .*' | head -1)
|
|
750
|
+
_prov_oracle=$(python3 - <<'ORACLE' 2>/dev/null
|
|
751
|
+
import importlib.util, sys
|
|
752
|
+
s = importlib.util.find_spec("yaml")
|
|
753
|
+
print((s.origin or "namespace-package") if s is not None else "ABSENT")
|
|
754
|
+
ORACLE
|
|
755
|
+
)
|
|
756
|
+
if [ -z "$_prov_line" ]; then
|
|
757
|
+
echo " ❌ PROV-1 no instrument line on the ordinary path"; fail=$((fail+1))
|
|
758
|
+
elif [ -z "$_prov_oracle" ]; then
|
|
759
|
+
echo " ❌ PROV-1 oracle produced nothing — NOT RUN (unmeasured, not a pass)"; fail=$((fail+1))
|
|
760
|
+
elif ! printf '%s' "$_prov_line" | grep -q ': /'; then
|
|
761
|
+
echo " ❌ PROV-1 line does not name an absolute interpreter path: [$_prov_line]"; fail=$((fail+1))
|
|
762
|
+
elif ! printf '%s' "$_prov_line" | grep -qF "PyYAML $_prov_oracle"; then
|
|
763
|
+
echo " ❌ PROV-1 line disagrees with the oracle — reported [$_prov_line] vs actual [$_prov_oracle]"; fail=$((fail+1))
|
|
764
|
+
else
|
|
765
|
+
echo " ✅ PROV-1 instrument line matches an independent resolution of PyYAML [$_prov_oracle]"; pass=$((pass+1))
|
|
766
|
+
fi
|
|
767
|
+
|
|
768
|
+
# PROV-2 — the ABSENT branch, exercised DETERMINISTICALLY rather than by hiding a package.
|
|
769
|
+
# A `yaml.py` MODULE FILE placed first on PYTHONPATH wins by path order, so find_spec resolves to it
|
|
770
|
+
# — a different, predictable answer that does not depend on how the real PyYAML was installed.
|
|
771
|
+
# ⚠️ A `yaml/` DIRECTORY does NOT work and the first draft used one: a namespace package has LOWER
|
|
772
|
+
# precedence than a regular package, so Python keeps scanning the whole path and still finds the real
|
|
773
|
+
# one. Measured — the arm did not move, and the lane correctly called itself decorative.
|
|
774
|
+
_prov_tmp=$(mktemp -d); printf '# shadow module for the PROV-2 arm\n' > "$_prov_tmp/yaml.py"
|
|
775
|
+
_prov_shadow=$(PYTHONPATH="$_prov_tmp" bash "$CHK" 2>&1 | grep -o 'instrument (consent-registry): .*' | head -1)
|
|
776
|
+
rm -rf "$_prov_tmp"
|
|
777
|
+
if [ -z "$_prov_shadow" ]; then
|
|
778
|
+
echo " ❌ PROV-2 no instrument line under a shadowed yaml"; fail=$((fail+1))
|
|
779
|
+
elif [ "$_prov_shadow" = "$_prov_line" ]; then
|
|
780
|
+
echo " ❌ PROV-2 line did not move when the resolution moved — decorative [$_prov_line]"; fail=$((fail+1))
|
|
781
|
+
else
|
|
782
|
+
echo " ✅ PROV-2 line tracks the resolution when it changes (shadowed → different value)"; pass=$((pass+1))
|
|
783
|
+
fi
|
|
784
|
+
|
|
731
785
|
echo "----"
|
|
732
786
|
echo "consent-registry anchor: $pass passed, $fail failed"
|
|
733
787
|
[ "$fail" -eq 0 ] || exit 1
|
|
@@ -630,6 +630,81 @@ else
|
|
|
630
630
|
bad "PY3 mixed-language markdown lost one language — shadows collide by name or the loop stops at the first hit"
|
|
631
631
|
fi
|
|
632
632
|
|
|
633
|
+
# ── SCOPE: the scanner must HONOR its arguments ────────────────────────────────────────────────
|
|
634
|
+
# This is the contract templates/.git-hooks/pre-commit depends on since 2026-08-13. That hook used
|
|
635
|
+
# to invoke this scanner with NO ARGUMENTS — walking the whole repository — and then discard almost
|
|
636
|
+
# all of it through a `grep -Ff` over the staged load-bearing files it already had in hand.
|
|
637
|
+
# Measured: whole-repo 42s / 260 lines vs one staged file 0s / 2 lines; a sibling harness measured
|
|
638
|
+
# the same call at 2m07s inside a 7m54s–9m40s commit, one of which could not complete at all.
|
|
639
|
+
# The lane exists because the FIX IS SILENTLY REVERSIBLE: if this scanner ever stops honoring its
|
|
640
|
+
# arguments, the hook keeps working and simply becomes slow again — no verdict changes, nothing
|
|
641
|
+
# goes red, and the only symptom is a commit people start bypassing with --no-verify. The same hook
|
|
642
|
+
# carries the Destructive-Op gate, so a trained bypass disarms an irreversible-surface gate too.
|
|
643
|
+
# BEHAVIOURAL, not shape: it asserts the OUTPUT is confined to the named file, which is the property
|
|
644
|
+
# the hook relies on. A grep for "does the hook pass an argument" would pass on a scanner that
|
|
645
|
+
# accepts arguments and ignores them.
|
|
646
|
+
_scope_a="$TMP/scope_a.sh"; _scope_b="$TMP/scope_b.sh"
|
|
647
|
+
printf '#!/usr/bin/env bash\ncheck() { probe || return 0; }\n' > "$_scope_a"
|
|
648
|
+
printf '#!/usr/bin/env bash\nverify() { thing || exit 0; }\n' > "$_scope_b"
|
|
649
|
+
out=$(bash "$SCAN" "$_scope_a" 2>&1)
|
|
650
|
+
if printf '%s' "$out" | grep -q 'scope_a' && ! printf '%s' "$out" | grep -q 'scope_b'; then
|
|
651
|
+
ok "SCOPE1 a named target is scanned and a sibling file is NOT (the scanner honors argv)"
|
|
652
|
+
else
|
|
653
|
+
bad "SCOPE1 scanner did not confine itself to the named target — the pre-commit scoping fix is void"
|
|
654
|
+
fi
|
|
655
|
+
# The negative arm: with no argument at all it must NOT confine itself to nothing. Without this,
|
|
656
|
+
# a scanner that returned empty for every input would satisfy SCOPE1 and the lane would be theatre.
|
|
657
|
+
out=$(bash "$SCAN" "$TMP" 2>&1)
|
|
658
|
+
if printf '%s' "$out" | grep -q 'scope_a' && printf '%s' "$out" | grep -q 'scope_b'; then
|
|
659
|
+
ok "SCOPE2 control: given the containing directory it finds BOTH (it is not simply blind)"
|
|
660
|
+
else
|
|
661
|
+
bad "SCOPE2 control failed — the scanner found neither file, so SCOPE1 proved nothing"
|
|
662
|
+
fi
|
|
663
|
+
|
|
664
|
+
# ── C2: bare X-in-Y substring boolean, with the ALL-CAPS exclusion (2026-08-14) ────────────────
|
|
665
|
+
# Cross-family review found this exclusion shipped with zero fixtures anywhere in this repo —
|
|
666
|
+
# deleting the exclusion left every existing lane green, which is exactly the "advisory noise
|
|
667
|
+
# reduction with no anchor" class this repo's own 4-axis doctrine requires a mechanical test for.
|
|
668
|
+
# Two arms: the probe's own headline example (paid⊂prepaid style substring check) must still
|
|
669
|
+
# fire, and an ALL-CAPS collection-membership check — the shape the exclusion targets — must not.
|
|
670
|
+
_c2_pos="$TMP/c2_pos.sh"; _c2_neg="$TMP/c2_neg.sh"
|
|
671
|
+
printf 'if tok in text:\n pass\n' > "$_c2_pos"
|
|
672
|
+
printf 'if name in PLACEHOLDERS:\n pass\n' > "$_c2_neg"
|
|
673
|
+
out=$(bash "$SCAN" "$_c2_pos" 2>&1)
|
|
674
|
+
if printf '%s' "$out" | grep -q 'C2:substring-boolean'; then
|
|
675
|
+
ok "C2-KP substring-boolean on a bare string var still fires"
|
|
676
|
+
else
|
|
677
|
+
bad "C2-KP the probe's own headline shape (tok in text) no longer fires"
|
|
678
|
+
fi
|
|
679
|
+
out=$(bash "$SCAN" "$_c2_neg" 2>&1)
|
|
680
|
+
if ! printf '%s' "$out" | grep -q 'C2:substring-boolean'; then
|
|
681
|
+
ok "C2-KN ALL-CAPS collection membership (name in PLACEHOLDERS) stays silent"
|
|
682
|
+
else
|
|
683
|
+
bad "C2-KN the ALL-CAPS exclusion is not wired — a legitimate collection check still fires"
|
|
684
|
+
fi
|
|
685
|
+
# Revert probe, run inline rather than trusted by inspection: replace the exclusion clause's line
|
|
686
|
+
# in a COPY of the scanner with a bare closing paren (deleting the line outright would strand the
|
|
687
|
+
# preceding line's trailing backslash continuation and break the pipe chain) and confirm C2-KN
|
|
688
|
+
# flips to a hit — proves the exclusion is load-bearing for this specific fixture, not just
|
|
689
|
+
# present somewhere in the file.
|
|
690
|
+
_scan_reverted="$TMP/scan_reverted.sh"
|
|
691
|
+
_excl_line=$(grep -n "grep -vE 'in \[A-Z_\]" "$SCAN" | head -1 | cut -d: -f1)
|
|
692
|
+
if [ -z "$_excl_line" ]; then
|
|
693
|
+
bad "C2-REVERT could not locate the exclusion line by its known text — fixture cannot run"
|
|
694
|
+
else
|
|
695
|
+
sed "${_excl_line}s/.*/ )/" "$SCAN" > "$_scan_reverted"
|
|
696
|
+
chmod +x "$_scan_reverted"
|
|
697
|
+
# Captured, not piped directly — under `set -o pipefail` (this file's own top line) the scanner's
|
|
698
|
+
# own advisory exit code (non-zero on any finding) would override grep's match result in a direct
|
|
699
|
+
# pipe, reporting FAIL even when the fixture worked exactly as intended.
|
|
700
|
+
_revert_out=$(bash "$_scan_reverted" "$_c2_neg" 2>&1)
|
|
701
|
+
if printf '%s' "$_revert_out" | grep -q 'C2:substring-boolean'; then
|
|
702
|
+
ok "C2-REVERT removing the exclusion line makes C2-KN fire again (the exclusion is load-bearing)"
|
|
703
|
+
else
|
|
704
|
+
bad "C2-REVERT removing the exclusion line did not flip C2-KN — the fixture proves nothing"
|
|
705
|
+
fi
|
|
706
|
+
fi
|
|
707
|
+
|
|
633
708
|
echo "----"
|
|
634
709
|
echo "degrade-scan shell probes: $pass passed, $fail failed"
|
|
635
710
|
[ "$fail" -eq 0 ] || exit 1
|
|
@@ -12,10 +12,32 @@ trap 'rm -rf "$TMP"' EXIT
|
|
|
12
12
|
PASS=0; FAIL=0
|
|
13
13
|
ok(){ PASS=$((PASS+1)); printf ' ✅ %s\n' "$1"; }
|
|
14
14
|
no(){ FAIL=$((FAIL+1)); printf ' ❌ %s — %s\n' "$1" "$2"; }
|
|
15
|
+
|
|
16
|
+
# ── DEBT fix (2026-08-14) — several lanes below depended on the OPERATOR'S REAL
|
|
17
|
+
# `${HOME}/projects/qasp-dev` mapping to resolve a repo and emit anything at all. On a
|
|
18
|
+
# machine/CI runner without that exact local layout, `repo` resolution in field_canon_preload.sh
|
|
19
|
+
# silently fails (`continue`), `emitted` stays 0, and EVERY assertion that checks "is output
|
|
20
|
+
# non-empty" trivially reads as the WRONG kind of empty — reproduced locally: PASS 12 / FAIL 7 with
|
|
21
|
+
# HOME pointed at an empty dir, exactly the CI split. The fix reuses the fixture shape lane ⑭
|
|
22
|
+
# already demonstrates below (a throwaway hub + a throwaway project root with a real `.git` and
|
|
23
|
+
# README.md) instead of depending on the operator's real filesystem — `found→extend`, not a new
|
|
24
|
+
# mechanism. This also turns ⑧'s prior "PASS" honest: before this fix it passed because repo
|
|
25
|
+
# resolution ALWAYS failed (output always empty regardless of dedup), not because dedup worked
|
|
26
|
+
# ([[feedback_anchor_can_be_decorative]]) — with a real resolvable repo, ⑧ now exercises the actual
|
|
27
|
+
# same-session dedup path.
|
|
28
|
+
FHUB="$TMP/fhub"; mkdir -p "$FHUB/tracks/qasp"
|
|
29
|
+
FPROJ="$TMP/fproj"; mkdir -p "$FPROJ/qasp-dev/.git" "$FPROJ/qasp-dev/docs/governance"
|
|
30
|
+
printf '# qasp fixture\n' > "$FPROJ/qasp-dev/README.md"
|
|
31
|
+
printf 'x\n' > "$FPROJ/qasp-dev/docs/governance/g1.md"
|
|
32
|
+
|
|
15
33
|
# ★`${3:+VAR=v}` 는 파라미터 확장이라 bash 가 **환경 할당으로 안 읽는다** — env 로 넘긴다.
|
|
16
34
|
# (이 파일 초판이 그렇게 써서 「레포 부재」 레인이 거짓 적색을 냈다. 계기 결함이었다.)
|
|
17
|
-
|
|
18
|
-
|
|
35
|
+
# $3, when given, overrides FIELD_CANON_PROJECT_ROOT (the "레포 부재" control at line ~35 uses this
|
|
36
|
+
# to deliberately point at /nonexistent — CLAUDE_PROJECT_DIR still resolves via the fixture so that
|
|
37
|
+
# control lane is testing "repo absent", not accidentally "mapped name absent" too).
|
|
38
|
+
run(){ local root="$FPROJ"; [ -n "${3:-}" ] && root="$3"
|
|
39
|
+
printf '%s' "$1" | env CLAUDE_PROJECT_DIR="$FHUB" FIELD_CANON_PROJECT_ROOT="$root" \
|
|
40
|
+
FIELD_CANON_SENTINEL_DIR="$2" bash "$H" 2>&1; }
|
|
19
41
|
|
|
20
42
|
echo "== field-canon preload 레인 =="
|
|
21
43
|
|
|
@@ -47,12 +69,14 @@ printf '' | FIELD_CANON_SENTINEL_DIR="$TMP/6" bash "$H" >/dev/null 2>&1
|
|
|
47
69
|
# (`[[feedback_anchor_can_be_decorative]]` — 레인이 결함 지점을 비켜 가면 초록은 정보가 아니다.)
|
|
48
70
|
# ⚠️ macOS 는 bash **3.2** 다. `set -u` 아래서 빈 배열 `"${a[@]}"` 는 unbound variable 로 죽는다
|
|
49
71
|
# — 초판 레인이 정확히 그렇게 죽어 ⑦⑨⑩ 이 **거짓 적색**을 냈다(대상이 아니라 계기의 사망).
|
|
50
|
-
# 배열 없이 분기한다.
|
|
72
|
+
# 배열 없이 분기한다. (FHUB/FPROJ fixture — see the DEBT-fix comment near the top of this file.)
|
|
51
73
|
runD(){ # $1=payload $2=TMPDIR [$3=CLAUDE_SESSION_ID 를 환경에 심을지]
|
|
52
74
|
if [ -n "${3:-}" ]; then
|
|
53
|
-
printf '%s' "$1" | env TMPDIR="$2"
|
|
75
|
+
printf '%s' "$1" | env TMPDIR="$2" CLAUDE_PROJECT_DIR="$FHUB" FIELD_CANON_PROJECT_ROOT="$FPROJ" \
|
|
76
|
+
CLAUDE_SESSION_ID="$3" bash "$H" 2>&1
|
|
54
77
|
else
|
|
55
|
-
printf '%s' "$1" | env TMPDIR="$2"
|
|
78
|
+
printf '%s' "$1" | env TMPDIR="$2" CLAUDE_PROJECT_DIR="$FHUB" FIELD_CANON_PROJECT_ROOT="$FPROJ" \
|
|
79
|
+
bash "$H" 2>&1
|
|
56
80
|
fi; }
|
|
57
81
|
T7="$TMP/def"; mkdir -p "$T7"
|
|
58
82
|
|
|
@@ -0,0 +1,221 @@
|
|
|
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
|
+
echo "----"
|
|
220
|
+
echo "lane-runner lanes: $pass passed, $fail failed"
|
|
221
|
+
[ "$fail" -eq 0 ] || exit 1
|
|
@@ -17,7 +17,16 @@
|
|
|
17
17
|
# Usage: bash scripts/test_marker_crossfamily_lanes.sh Exit: 0 = all behave; 1 = regression.
|
|
18
18
|
|
|
19
19
|
set -uo pipefail
|
|
20
|
-
|
|
20
|
+
# Script-relative, NOT `git rev-parse --show-toplevel`. Measured 2026-08-13 in a vendored tree
|
|
21
|
+
# (npm install, then `git init` at a level above — a monorepo committing node_modules is the
|
|
22
|
+
# same shape): rev-parse answers with the OUTER repo's root, so this suite looked for the
|
|
23
|
+
# package's own files inside somebody else's checkout, found nothing, and reported
|
|
24
|
+
# HARNESS-ERROR. The consumer sees a red `npm test` caused entirely by where their .git is.
|
|
25
|
+
# The subject of these lanes ships INSIDE this package, so the package root is the only root
|
|
26
|
+
# that can be right. Same form as test_capability_entrypoint_shipping.sh:29.
|
|
27
|
+
# The exposure is new: before these suites were wired into selfcheck.sh they ran nowhere, so
|
|
28
|
+
# the wrong root never cost anything. Wiring a dead lane surfaces every assumption it made.
|
|
29
|
+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
21
30
|
HOOK="$REPO_ROOT/templates/.git-hooks/pre-commit"
|
|
22
31
|
T=$(mktemp -d); trap 'rm -rf "$T"' EXIT
|
|
23
32
|
|
|
@@ -9,12 +9,36 @@
|
|
|
9
9
|
# Usage: bash scripts/test_marker_floor_lanes.sh Exit: 0 = all fixtures behave; 1 = regression.
|
|
10
10
|
|
|
11
11
|
set -uo pipefail
|
|
12
|
-
|
|
12
|
+
# Script-relative, NOT `git rev-parse --show-toplevel`. Measured 2026-08-13 in a vendored tree
|
|
13
|
+
# (npm install, then `git init` at a level above — a monorepo committing node_modules is the
|
|
14
|
+
# same shape): rev-parse answers with the OUTER repo's root, so this suite looked for the
|
|
15
|
+
# package's own files inside somebody else's checkout, found nothing, and reported
|
|
16
|
+
# HARNESS-ERROR. The consumer sees a red `npm test` caused entirely by where their .git is.
|
|
17
|
+
# The subject of these lanes ships INSIDE this package, so the package root is the only root
|
|
18
|
+
# that can be right. Same form as test_capability_entrypoint_shipping.sh:29.
|
|
19
|
+
# The exposure is new: before these suites were wired into selfcheck.sh they ran nowhere, so
|
|
20
|
+
# the wrong root never cost anything. Wiring a dead lane surfaces every assumption it made.
|
|
21
|
+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
13
22
|
HOOK="$REPO_ROOT/templates/.git-hooks/pre-commit"
|
|
14
23
|
T=$(mktemp -d); trap 'rm -rf "$T"' EXIT
|
|
15
24
|
|
|
16
25
|
sed -n '/^marker_recreate_hint()/,/^}/p;/^validate_marker_floor()/,/^}/p' "$HOOK" > "$T/fn.sh"
|
|
17
26
|
|
|
27
|
+
# EXTRACTION CALIBRATION — the sibling suite (test_marker_crossfamily_lanes.sh) has had this and
|
|
28
|
+
# this file did not. Without it, ANY failure to extract renders as a REGRESSION rather than as a
|
|
29
|
+
# broken instrument: an empty fn.sh means `run()` sources nothing, validate_marker_floor is
|
|
30
|
+
# undefined, `bash -c` exits non-zero, every fixture reads as BLOCK, and the three lanes that
|
|
31
|
+
# expect PASS "fail" — a red suite pointing at a gate that is perfectly fine. Measured 2026-08-13
|
|
32
|
+
# in a vendored git tree, where the old `git rev-parse` root sent $HOOK into another repo entirely.
|
|
33
|
+
# The root is fixed above; this guard is what makes the NEXT extraction failure legible instead of
|
|
34
|
+
# a false accusation, whatever causes it (a renamed function, a reformatted hook, a truncated file).
|
|
35
|
+
# exit 10, matching the harness-error contract selfcheck.sh's pair-loop routes to HARNESS ERROR.
|
|
36
|
+
if ! grep -q '^validate_marker_floor()' "$T/fn.sh"; then
|
|
37
|
+
echo "🟥 HARNESS-ERROR: validate_marker_floor() did not extract from $HOOK"
|
|
38
|
+
echo " (this suite measured NOTHING — it is not a regression verdict)"
|
|
39
|
+
exit 10
|
|
40
|
+
fi
|
|
41
|
+
|
|
18
42
|
run() { bash -c "source '$T/fn.sh'; validate_marker_floor '$1'" >/dev/null 2>&1; }
|
|
19
43
|
|
|
20
44
|
FAIL=0
|