@chrono-meta/fh-gate 1.4.73 → 1.4.75

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.
Files changed (42) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/CHEATSHEET.md +1 -1
  3. package/CLAUDE.md +14 -1
  4. package/docs/ETHOS.md +106 -0
  5. package/docs/OUTPUT_EVIDENCE.md +118 -0
  6. package/docs/WHY.md +42 -0
  7. package/knowledge/patterns/ensemble_union_detection_task_pattern.md +125 -0
  8. package/knowledge/shared/GLOSSARY.md +77 -0
  9. package/knowledge/shared/harness-core/harness_frontier_diagnosis_2026-06-02.md +1 -1
  10. package/knowledge/shared/harness-core/meta_harness_engineering_definition.md +1 -1
  11. package/knowledge/shared/learnings/subagent_invocations_log.yaml +9 -0
  12. package/knowledge/shared/patterns/multi-persona-review.md +88 -0
  13. package/knowledge/shared/plugin-catalog/recommended_plugins.md +117 -0
  14. package/package.json +28 -1
  15. package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
  16. package/plugins/fh-meta/.claude-plugin/plugin.json +2 -2
  17. package/plugins/fh-meta/CHANGELOG.md +617 -0
  18. package/scripts/below_floor_scan.sh +91 -0
  19. package/scripts/chamber_run.sh +184 -0
  20. package/scripts/degrade_direction_scan.sh +17 -2
  21. package/scripts/fh_env_delta_scan.sh +108 -0
  22. package/scripts/memory_link_check.py +237 -0
  23. package/scripts/memory_nearcheck.py +131 -0
  24. package/scripts/package_coverage_check.sh +140 -0
  25. package/scripts/selfcheck.sh +47 -0
  26. package/scripts/session_close_check.sh +31 -1
  27. package/scripts/sidecar_wait.sh +76 -0
  28. package/scripts/substrate_jump_detector.sh +60 -0
  29. package/scripts/test_card_drift_probe.sh +77 -0
  30. package/scripts/test_degrade_scan_shell_probes.sh +26 -0
  31. package/scripts/test_marker_floor_lanes.sh +45 -0
  32. package/scripts/test_memory_link_check.sh +134 -0
  33. package/scripts/test_session_close_lanes.sh +99 -0
  34. package/scripts/tier_census_grep.sh +54 -0
  35. package/templates/.claude/rules/session.md +153 -0
  36. package/templates/contrib_session.md +34 -0
  37. package/templates/degrade_direction_scan.sh +17 -2
  38. package/templates/goal-quench-hook-setup.md +152 -0
  39. package/templates/starter_profile.md +83 -0
  40. package/templates/temper_check.sh +46 -0
  41. package/plugins/fh-meta/skills/context-bridge-dispatch/SKILL.md +0 -32
  42. package/plugins/fh-meta/skills/self-marketing-lint/SKILL.md +0 -30
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env bash
2
+ # test_marker_floor_lanes.sh — regression fixtures for pre-commit validate_marker_floor lanes.
3
+ #
4
+ # Ships with the 2026-07-10 sonnet-floor lane (Sonnet-Floor Doctrine): each closed hole gets a
5
+ # mechanical regression test (Field-Harness Load-Bearing Change Gate convergence condition).
6
+ # Fixtures assert BOTH directions: the new lane admits exactly its intended shape, and every
7
+ # pre-existing guard still blocks (no degrade-toward-permissive regression).
8
+ #
9
+ # Usage: bash scripts/test_marker_floor_lanes.sh Exit: 0 = all fixtures behave; 1 = regression.
10
+
11
+ set -uo pipefail
12
+ REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
13
+ HOOK="$REPO_ROOT/templates/.git-hooks/pre-commit"
14
+ T=$(mktemp -d); trap 'rm -rf "$T"' EXIT
15
+
16
+ sed -n '/^marker_recreate_hint()/,/^}/p;/^validate_marker_floor()/,/^}/p' "$HOOK" > "$T/fn.sh"
17
+
18
+ run() { bash -c "source '$T/fn.sh'; validate_marker_floor '$1'" >/dev/null 2>&1; }
19
+
20
+ FAIL=0
21
+ check() { # $1=fixture $2=expected(PASS|BLOCK) $3=label
22
+ if run "$1"; then got=PASS; else got=BLOCK; fi
23
+ if [ "$got" = "$2" ]; then echo "✅ $3 → $got"; else echo "❌ $3 → $got (expected $2)"; FAIL=1; fi
24
+ }
25
+
26
+ printf 'axis2-engine: inline\naxis2-model: sonnet\nfloor-status: sonnet-floor\naxis2-anchor: regression test 5/5 pass\naxis2-evidence: PASS no-S, 2B applied\n' > "$T/m1"
27
+ printf 'axis2-engine: inline\naxis2-model: sonnet\nfloor-status: sonnet-floor\naxis2-evidence: PASS no-S\n' > "$T/m2"
28
+ printf 'axis2-engine: inline\naxis2-model: haiku\nfloor-status: sonnet-floor\naxis2-anchor: probe 3/3\naxis2-evidence: PASS no-S\n' > "$T/m3"
29
+ printf 'axis2-engine: inline\naxis2-model: sonnet\nfloor-status: at-floor\naxis2-evidence: PASS no-S\n' > "$T/m4"
30
+ printf 'axis2-engine: inline\naxis2-model: haiku\nfloor-status: below-floor\naxis2-evidence: PASS no-S\n' > "$T/m5"
31
+ printf 'axis2-engine: quench-challenger\naxis2-model: opus\nfloor-status: at-floor\naxis2-evidence: 1S/4A fixed\n' > "$T/m6"
32
+ printf 'axis2-engine: inline\naxis2-model: haiku\nfloor-status: below-floor\nbelow-floor-ack: "approved, proceed" — canary-only change\naxis2-evidence: PASS no-S\n' > "$T/m7"
33
+ printf 'axis2-engine: inline\naxis2-model: opus\nfloor-status: bogus-status\naxis2-evidence: PASS\n' > "$T/m8"
34
+
35
+ check "$T/m1" PASS "sonnet-floor + anchor + sonnet model (new lane, intended shape)"
36
+ check "$T/m2" BLOCK "sonnet-floor WITHOUT anchor (anchor is the compensating requirement)"
37
+ check "$T/m3" BLOCK "haiku claiming sonnet-floor (lane mislabel)"
38
+ check "$T/m4" BLOCK "sonnet claiming at-floor (2026-06-10 mislabel class — guard intact)"
39
+ check "$T/m5" BLOCK "below-floor without ack (guard intact)"
40
+ check "$T/m6" PASS "opus at-floor (legacy lane intact)"
41
+ check "$T/m7" PASS "below-floor with quoted ack (legacy lane intact)"
42
+ check "$T/m8" BLOCK "invalid floor-status (fail-closed on unknown value)"
43
+
44
+ [ "$FAIL" -eq 0 ] && echo "── all marker-floor lane fixtures behave ──"
45
+ exit "$FAIL"
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env bash
2
+ # test_memory_link_check.sh — known-pair anchor for scripts/memory_link_check.py.
3
+ #
4
+ # WHY: the checker's --fix-separators path WRITES to personal knowledge files. Everything it gets
5
+ # wrong, it gets wrong silently and in bulk. Two of its rules were found only by attacking it:
6
+ # * a first draft rewrote links inside FENCED blocks — i.e. it "corrected" the examples that
7
+ # document the convention, which is the probe-damages-the-remedy class;
8
+ # * a first measurement counted cross-store links as broken, overstating the defect by 44%.
9
+ # Both are pinned below, alongside the classes.
10
+ #
11
+ # Lanes
12
+ # C1 the five classes separate on a fixture (ok / separator / cross-store-absent / placeholder / dangling)
13
+ # F1 a link inside a fenced block is COUNTED but never REWRITTEN
14
+ # F2 a link inside inline backticks IS rewritten (this corpus styles real links that way —
15
+ # measured 2026-07-28: 25 such links repaired, 0 fenced changes in the same run)
16
+ # F3 aliased links [[target|alias]] keep their alias
17
+ # F4 re-running the fixer changes nothing (idempotent)
18
+ # G1 an empty store reports an extractor failure, never a clean graph
19
+ #
20
+ # Exit 0 = 6/6.
21
+ set -uo pipefail
22
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
23
+ CHK="$ROOT/scripts/memory_link_check.py"
24
+ [ -f "$CHK" ] || { echo "FAIL: $CHK missing"; exit 1; }
25
+
26
+ pass=0; fail=0
27
+ ok() { printf ' ✅ %s\n' "$1"; pass=$((pass+1)); }
28
+ bad() { printf ' ❌ %s\n' "$1"; fail=$((fail+1)); }
29
+
30
+ TD="$(mktemp -d)"; trap 'rm -rf "$TD"' EXIT
31
+ mkdir -p "$TD/store"
32
+ printf -- '---\nname: real_target\n---\nbody\n' > "$TD/store/real_target.md"
33
+ cat > "$TD/store/src.md" <<'EOF'
34
+ ---
35
+ name: src
36
+ ---
37
+ plain ok: [[real_target]]
38
+ plain separator: [[real-target]]
39
+ alias: [[real-target|shown as this]]
40
+ inline: `[[real-target]]`
41
+ dangling: [[nothing_here]]
42
+ placeholder: [[link]]
43
+
44
+ ```
45
+ fenced example — must NOT be rewritten: [[real-target]]
46
+ ```
47
+ EOF
48
+
49
+ _run() { python3 "$CHK" --memory "$TD/store" "$@" 2>/dev/null; }
50
+
51
+ out=$(_run)
52
+ # FIRST match only. `dangling` appears twice in the report — once as a count row and once as a
53
+ # section header ("dangling (nothing on disk...)") — so an unbounded match returned two lines and
54
+ # the numeric comparison silently failed against a correct tool. Instrument fault, fixed here.
55
+ _n() { printf '%s\n' "$out" | awk -v k="$1" '$1==k && $2 ~ /^[0-9]+$/ {print $2; exit}'; }
56
+ if [ "$(_n ok)" = "1" ] && [ "$(_n placeholder)" = "1" ] && [ "$(_n dangling)" = "1" ] && [ "$(_n separator)" -ge 4 ]; then
57
+ ok "C1 classes separate (ok=$(_n ok) separator=$(_n separator) placeholder=$(_n placeholder) dangling=$(_n dangling))"
58
+ else
59
+ bad "C1 class counts wrong"; printf '%s\n' "$out" | sed 's/^/ /'
60
+ fi
61
+
62
+ _run --fix-separators --quiet >/dev/null
63
+ body=$(cat "$TD/store/src.md")
64
+
65
+ if printf '%s' "$body" | sed -n '/```/,/```/p' | grep -q '\[\[real-target\]\]'; then
66
+ ok "F1 fenced example left untouched (the documentation of the rule survives the fixer)"
67
+ else
68
+ bad "F1 the fixer rewrote a link inside a fenced block — it corrected its own example"
69
+ fi
70
+
71
+ if printf '%s' "$body" | grep -q 'inline: `\[\[real_target\]\]`'; then
72
+ ok "F2 inline-backticked link repaired (this store's citation style is a real link)"
73
+ else
74
+ bad "F2 inline-backticked link was not repaired"
75
+ fi
76
+
77
+ if printf '%s' "$body" | grep -q '\[\[real_target|shown as this\]\]'; then
78
+ ok "F3 alias preserved through the rewrite"
79
+ else
80
+ bad "F3 alias lost or target not rewritten"
81
+ fi
82
+
83
+ before=$(cat "$TD/store/src.md")
84
+ _run --fix-separators --quiet >/dev/null
85
+ if [ "$before" = "$(cat "$TD/store/src.md")" ]; then
86
+ ok "F4 idempotent on re-run"
87
+ else
88
+ bad "F4 a second run changed the file again"
89
+ fi
90
+
91
+ mkdir -p "$TD/empty"
92
+ if python3 "$CHK" --memory "$TD/empty" >/dev/null 2>&1; then
93
+ bad "G1 an empty store exited 0 — a scan that cannot see its subject reported a clean graph"
94
+ else
95
+ ok "G1 empty store fails as an extractor error, not a pass"
96
+ fi
97
+
98
+
99
+ # H1/A1 — cross-family findings, both confirmed by execution before acceptance.
100
+ # H1 (agy): an anchor-form link `[[target#section]]` was COUNTED as repairable but the rewrite
101
+ # enumerated closing forms by hand and never matched it — so it was flagged on every
102
+ # run (idempotence broken) and the summary reported more fixes than it made.
103
+ # A1 (gpt-5.5): two notes sharing a normalized name let the fixer reroute an edge to whichever
104
+ # sorted first, and the wrong link then resolves exactly, so no later run flags it.
105
+ printf -- '---\nname: my_topic\n---\nbody\n' > "$TD/store/my_topic.md"
106
+ printf -- '---\nname: anchored\n---\nanchor: [[my-topic#section-1]]\nplain: [[my-topic]]\n' > "$TD/store/anchored.md"
107
+ _run --fix-separators --quiet >/dev/null
108
+ if grep -q '\[\[my_topic#section-1\]\]' "$TD/store/anchored.md"; then
109
+ ok "H1 anchor-form link rewritten (target only, #section preserved)"
110
+ else
111
+ bad "H1 [[target#anchor]] left unrewritten — flagged forever, and the fix count over-reports"
112
+ fi
113
+ # Idempotence here is a STABLE count, not zero: the fenced example is counted every run and
114
+ # deliberately never rewritten, so zero is unreachable by design. Asserting zero was an instrument
115
+ # error in this anchor's first draft — it scored a correct tool as failing.
116
+ before_n=$(out=$(_run); printf '%s\n' "$out" | awk '$1=="separator" && $2 ~ /^[0-9]+$/ {print $2; exit}')
117
+ _run --fix-separators --quiet >/dev/null
118
+ after_n=$(out=$(_run); printf '%s\n' "$out" | awk '$1=="separator" && $2 ~ /^[0-9]+$/ {print $2; exit}')
119
+ if [ "$before_n" = "$after_n" ]; then
120
+ ok "H1b idempotent with anchor forms present (count stable at $after_n — the fenced example)"
121
+ else
122
+ bad "H1b count moved $before_n → $after_n across a second fix pass"
123
+ fi
124
+ printf -- 'A\n' > "$TD/store/collide-x.md"; printf -- 'B\n' > "$TD/store/collide_x.md"
125
+ out=$(_run)
126
+ if printf '%s\n' "$out" | grep -q 'ambiguous'; then
127
+ ok "A1 colliding normalized names surface as a reported class"
128
+ else
129
+ bad "A1 no ambiguous class — a colliding pair can still be auto-rerouted"
130
+ fi
131
+
132
+ echo "----"
133
+ echo "memory-link-check anchor: $pass passed, $fail failed"
134
+ [ "$fail" -eq 0 ] || exit 1
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env bash
2
+ # test_session_close_lanes.sh — known-pair anchor for session_close_check.sh ② (harvest-loop
3
+ # obligation) and ⑤ (card-last invariant).
4
+ #
5
+ # WHY (2026-07-28): ② used to be an UNSATISFIABLE warning — it fired whenever an FH asset was
6
+ # touched today and the script had no way to observe whether harvest-loop ran, so no session could
7
+ # ever discharge it. A line that fires on every healthy close is noise, and noise trains the runner
8
+ # to skim past the ❌ lines that matter. The repair gave it a mechanical discharge (a harvest-loop
9
+ # decision recorded in TODAY's fh_completed file). This file is that repair's regression anchor:
10
+ # the fix is only real if the ⚠️ still fires when NOTHING is recorded, and stops firing when it is.
11
+ #
12
+ # Lanes (each is a decision the gate must get right, not a smoke test):
13
+ # ②-N FH asset touched today, no harvest-loop line anywhere → ⚠️ MUST fire
14
+ # ②-P1 same, plus "harvest-loop 실행 완료" → ✅ must NOT fire
15
+ # ②-P2 same, plus an explicit SKIP note → ✅ must NOT fire
16
+ # (CLAUDE.md ② accepts "harvest-loop (or an explicit skip note)" — a recorded skip is
17
+ # a discharged obligation, not an evaded one)
18
+ # ②-C no FH asset touched today → neither line appears at all
19
+ # (over-firing is a defect in its own right — the whole reason this repair exists)
20
+ # ⑤-N a close artifact newer than the card → ❌ card-last MUST fire (exit 1)
21
+ # ⑤-P card is the newest artifact → ✅ card-last holds
22
+ #
23
+ # Exit 0 = 7/7 lanes calibrated · exit 1 = the gate's instrument is wrong (do not trust its verdict)
24
+
25
+ set -uo pipefail
26
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
27
+ CHECK="$SCRIPT_DIR/session_close_check.sh"
28
+ TODAY=$(date +%Y-%m-%d)
29
+ FAILED=0
30
+
31
+ if [ ! -f "$CHECK" ]; then
32
+ echo "FAIL session-close lanes: subject $CHECK missing"
33
+ exit 1
34
+ fi
35
+
36
+ # Builds a throwaway repo whose HEAD commit touches (or does not touch) an FH asset path.
37
+ _fixture() { # $1=touch_fh_asset(0/1) $2=completed-file body (empty = no file)
38
+ local touch_fh="$1" body="$2" T
39
+ T=$(mktemp -d)
40
+ ( cd "$T" \
41
+ && git init -q . \
42
+ && git config user.email anchor@local && git config user.name anchor \
43
+ && if [ "$touch_fh" = 1 ]; then echo x > CLAUDE.md; else echo x > unrelated.txt; fi \
44
+ && git add -A && git commit -qm "fixture" ) >/dev/null 2>&1
45
+ mkdir -p "$T/tracks/_meta"
46
+ [ -n "$body" ] && printf '%s\n' "$body" > "$T/tracks/_meta/fh_completed_${TODAY}.md"
47
+ printf '# card\n' > "$T/tracks/_meta/reference_next_session_starter.md"
48
+ printf '%s' "$T"
49
+ }
50
+
51
+ _lane() { # $1=name $2=grep-pattern $3=expect(0/1) $4=fixture dir
52
+ local name="$1" pat="$2" expect="$3" T="$4" out hit
53
+ out=$(bash "$CHECK" "$T" 2>/dev/null)
54
+ hit=0
55
+ printf '%s\n' "$out" | grep -q "$pat" && hit=1
56
+ rm -rf "$T"
57
+ if [ "$hit" = "$expect" ]; then
58
+ echo "✅ $name (hit=$hit, expected=$expect)"
59
+ else
60
+ echo "❌ $name — hit=$hit, expected=$expect"
61
+ printf '%s\n' "$out" | sed 's/^/ /'
62
+ FAILED=1
63
+ fi
64
+ }
65
+
66
+ WARN2='⚠️ ② FH assets changed today'
67
+ OK2='✅ ② FH assets changed today'
68
+
69
+ _lane "②-N no harvest-loop decision recorded → warns" "$WARN2" 1 "$(_fixture 1 '- 항목 하나')"
70
+ _lane "②-P1 harvest-loop run recorded → silent" "$WARN2" 0 "$(_fixture 1 '- harvest-loop 실행 완료')"
71
+ _lane "②-P2 explicit skip note → silent" "$WARN2" 0 "$(_fixture 1 '- harvest-loop: skipped — 3-repo session, deferred')"
72
+ _lane "②-P1 run recorded → prints the ✅ form" "$OK2" 1 "$(_fixture 1 '- harvest-loop 실행 완료')"
73
+ _lane "②-C no FH asset touched → no ② line at all" ' ② ' 0 "$(_fixture 0 '- 항목 하나')"
74
+
75
+ # ⑤ card-last: an artifact newer than the card is the bug class the invariant exists to catch.
76
+ T=$(_fixture 1 '- harvest-loop 실행 완료')
77
+ touch "$T/tracks/_meta/fh_completed_${TODAY}.md" # make it strictly newer than the card
78
+ if bash "$CHECK" "$T" >/dev/null 2>&1; then
79
+ echo "❌ ⑤-N artifact newer than card → expected exit 1, got 0 (card-last not enforced)"
80
+ FAILED=1
81
+ else
82
+ echo "✅ ⑤-N artifact newer than card → exit 1 (card-last enforced)"
83
+ fi
84
+ touch "$T/tracks/_meta/reference_next_session_starter.md" # card becomes newest
85
+ if bash "$CHECK" "$T" >/dev/null 2>&1; then
86
+ echo "✅ ⑤-P card newest → exit 0"
87
+ else
88
+ echo "❌ ⑤-P card newest → expected exit 0, got 1 (over-blocking: trains --no-verify)"
89
+ bash "$CHECK" "$T" 2>&1 | sed 's/^/ /'
90
+ FAILED=1
91
+ fi
92
+ rm -rf "$T"
93
+
94
+ if [ "$FAILED" -ne 0 ]; then
95
+ echo "SESSION-CLOSE LANES: FAIL — the gate's instrument is miscalibrated"
96
+ exit 1
97
+ fi
98
+ echo "SESSION-CLOSE LANES: PASS (7/7)"
99
+ exit 0
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env bash
2
+ # tier_census_grep.sh — word-boundary tier-reference census helper (Sonnet-Floor Doctrine).
3
+ #
4
+ # WHY (origin: fh_signal_2026-07-10_session — Sonnet full-loop probe): the naive census pattern
5
+ # `opus|sonnet|haiku|floor|tier|model:` false-positives heavily ("frontier" matches `tier`,
6
+ # "floors" prose, method-sense "model"). The probe self-corrected, but per the doctrine's own
7
+ # prescription ladder (step 1: mechanize) the discipline belongs in a script, not re-derived
8
+ # per session. Built 2026-07-10 on operator instruction (evidence-threshold overridden by
9
+ # explicit "complete it" — recorded, not silent).
10
+ #
11
+ # WHAT: emits candidate tier-reference hits with word-boundary patterns, one line per hit
12
+ # (file:line:text), for the auditor to CLASSIFY per sonnet_floor_doctrine.md's table
13
+ # (trust-floor / availability-gate / advisory / N-A). The script finds candidates; the
14
+ # classification stays a judged step with the doctrine table as its anchor.
15
+ #
16
+ # Sense-filter hints (printed, not auto-applied — de-noising must never hide a real gate):
17
+ # - "frontier|multi-tier|C-tier|A/B-tier" → usually N/A (different axis: content/data tiers)
18
+ # - "hub model|mental model|data model" → usually N/A (methodology sense of "model")
19
+ # - execution tier S/M/L/XL → N/A (token budget, not model tier — fh_detail_protocols)
20
+ #
21
+ # Usage: bash scripts/tier_census_grep.sh <file> [file...] Exit: 0 always (census, not gate)
22
+
23
+ set -uo pipefail
24
+
25
+ if [ $# -eq 0 ]; then
26
+ echo "usage: bash scripts/tier_census_grep.sh <file> [file...]" >&2
27
+ exit 0
28
+ fi
29
+
30
+ PATTERN='\b(opus|sonnet|haiku|fable)\b|\bfloor(-status|-tier|s)?\b|\btiers?\b|(^|[^a-zA-Z])model:'
31
+
32
+ for f in "$@"; do
33
+ if [ ! -f "$f" ]; then
34
+ echo "── $f: NOT FOUND (phantom input — check the path) ──"
35
+ continue
36
+ fi
37
+ echo "── census candidates: $f ──"
38
+ # -P where available (GNU/pcre); BSD grep on macOS supports -E word boundaries via [[:<:]] —
39
+ # portable route: grep -nEi with \b works on GNU; on BSD use perl fallback.
40
+ if echo x | grep -P 'x' >/dev/null 2>&1; then
41
+ grep -nPi "$PATTERN" "$f" || echo " (0 candidates)"
42
+ else
43
+ # 0-hit에도 "(0 candidates)"를 찍는다 — GNU 분기와 출력 대칭 (pmh-parity 포트가 잡은 갭, 역이식 2026-07-10)
44
+ hits=$(perl -ne 'print "$.:$_" if /\b(opus|sonnet|haiku|fable)\b|\bfloor(-status|-tier|s)?\b|\btiers?\b|(^|[^a-zA-Z])model:/i' "$f")
45
+ if [ -n "$hits" ]; then printf '%s\n' "$hits"; else echo " (0 candidates)"; fi
46
+ fi
47
+ done
48
+
49
+ cat <<'HINTS'
50
+ ── classify each hit per sonnet_floor_doctrine.md (trust-floor / availability-gate / advisory / N-A) ──
51
+ N/A sense hints (verify, don't auto-drop): frontier·C-tier·A/B-tier (content-tier axis) ·
52
+ "hub/mental/data model" (methodology sense) · S/M/L/XL execution tier (token budget, not model).
53
+ HINTS
54
+ exit 0
@@ -0,0 +1,153 @@
1
+ <!--
2
+ session.md — Claude Code Session Rules Template
3
+
4
+ Purpose of this file:
5
+ - Define Claude's session operating rules (how to behave)
6
+ - Behavioral guidelines applied across the entire project
7
+ - Commit to Git and share with the team
8
+ - Edited and managed directly by the user
9
+
10
+ Difference from MEMORY.md:
11
+ - MEMORY.md: Stores data/experience learned during conversation (auto-managed by Claude)
12
+ - session.md: Defines procedures/rules for Claude to follow (edited directly by the user)
13
+
14
+ Usage:
15
+ - Copy this file to your project's .claude/rules/session.md
16
+ - Add, remove, or modify sections to fit your project
17
+ - Change sections marked with [CUSTOMIZE] comments to match your project
18
+ -->
19
+
20
+ ### Automatic Actions at Session Start
21
+
22
+ #### Root Memory (Knowledge Hub) Connection
23
+
24
+ At the start of a conversation ("hello", "let's start", "load root memory"), perform the following:
25
+
26
+ 1. Read `{FH_ROOT}/CATALOG.md`
27
+ - Understand recent work context
28
+ - Check today's tasks (todo/plan)
29
+
30
+ 2. Load project memory index
31
+ - Check `.claude/projects/.../memory/MEMORY.md`
32
+ - Prioritize loading memory most relevant to current work
33
+ - Proceed naturally without notifying the user that memory was loaded
34
+
35
+ #### Exceptions
36
+ - If the user explicitly requests not to use memory
37
+ - For simple one-off questions, load is optional
38
+
39
+ ---
40
+
41
+ ### Session Backup Before Tests
42
+
43
+ <!-- [CUSTOMIZE] Adjust trigger conditions to match your test framework -->
44
+
45
+ #### Automatic Backup Trigger
46
+
47
+ At any point when tests could be run, **automatically** perform a session backup:
48
+
49
+ 1. **When I recommend running tests** — **immediately before** the recommendation message
50
+ 2. **When the user signals intent to start tests** — **before** running the test command
51
+
52
+ #### Why Backup
53
+ - Sessions can be forcibly terminated when tests start
54
+ - Prevents loss of conversation context, analysis results, and change history
55
+
56
+ #### How to Backup
57
+
58
+ ```bash
59
+ cat > .claude/session_backup_$(date +%Y%m%d_%H%M%S).md << 'EOF'
60
+ # Session Backup - [Task Title]
61
+
62
+ ## Problem
63
+ - [Issue currently being resolved]
64
+
65
+ ## Changes Made
66
+ - [filename:line]
67
+ - [before/after]
68
+
69
+ ## Next Steps
70
+ - [Things to verify after tests]
71
+ EOF
72
+ ```
73
+
74
+ #### Important
75
+ - Perform **automatically** even without an explicit user request
76
+ - Never recommend tests without first creating a backup
77
+
78
+ ---
79
+
80
+ ### Automatic Response to Issues
81
+
82
+ <!-- [CUSTOMIZE] Adjust report tool/path to match your project -->
83
+
84
+ #### Automatic Check Trigger
85
+
86
+ When the user mentions a problem, **automatically** locate and analyze the latest test report:
87
+
88
+ 1. **Trigger keywords**
89
+ - "something broke", "got an error", "it failed", "not working"
90
+ - "issue occurred", "test failed", "broken", "failed"
91
+
92
+ 2. **Analyze and report**
93
+ - Names of failing test cases
94
+ - Error messages and stack traces
95
+ - Summarize in a concise format
96
+
97
+ ---
98
+
99
+ ### Code Writing Principles
100
+
101
+ <!-- [CUSTOMIZE] Adjust to match your project's coding conventions. The 5 principles below are universal and valid for any project. -->
102
+
103
+ Be conscious of all 5 principles **before** writing code — directly reduces back-and-forth where Claude rushes to create something and the user has to correct it.
104
+
105
+ #### 1. Reference Existing Code (Consistency First)
106
+
107
+ - **Reference targets**: Code with similar functionality or in the same layer within the project
108
+ - **No introducing new patterns** — follow existing patterns first; only abstract when the same pattern repeats 3+ times and needs consolidation
109
+ - **Follow framework Core/Base class patterns** — if the project has `.claude/rules/`, that hierarchy takes precedence
110
+
111
+ #### 2. Independence and Regression Prevention
112
+
113
+ - Verify that new code **does not break existing tests or functionality**
114
+ - Manage side effects (shared state, global variables, file locks)
115
+ - Use `git grep` before changes to understand the impact surface — check for unexpected callers
116
+
117
+ #### 3. Locator and Identifier Stability (UI code only)
118
+
119
+ <!-- [CUSTOMIZE] Can be removed for non-mobile QA / non-web QA projects -->
120
+
121
+ - Do not depend on dynamically generated attributes (auto-generated id, timestamps in content-desc)
122
+ - Avoid absolute XPath — fragile to structural changes
123
+ - Consider i18n for text-based identifiers (multilingual projects)
124
+ - If the project has `.claude/rules/LOCATOR_*` guides, those take precedence
125
+
126
+ #### 4. Flakiness Risk Management
127
+
128
+ - **No `time.sleep`** — use explicit waits (implicit/explicit wait) + condition-based polling
129
+ - No unbounded waits without a timeout
130
+ - Allow tolerance in screenshot-based assertions
131
+ - Minimize assumptions about device/environment state (keyboard visibility, previous screen state, etc.)
132
+
133
+ #### 5. Mandatory grep Before Design (Prevent Missing Own Assets)
134
+
135
+ **Before** designing a new feature or pattern:
136
+
137
+ 1. grep for similar implementations in the project — reuse if already present
138
+ 2. grep learnings from sibling projects in the hub (e.g., `{FH_ROOT}/`) — prevent reinventing solutions already solved elsewhere
139
+ 3. Re-read the project's CLAUDE.md and rules/*.md — check for overlooked constraints
140
+
141
+ Starting design with zero cited references is a warning signal for **missing own assets**. Always present at least 1 grep result before beginning design.
142
+
143
+ ---
144
+
145
+ ### Rule Hierarchy and Priority
146
+
147
+ <!-- [CUSTOMIZE] Define rule sources and priority for your project -->
148
+
149
+ **Priority when conflicts arise:**
150
+ 1. **Framework rules** — code patterns (non-negotiable)
151
+ 2. **Test design philosophy** — "what to test" (QA Identity, etc.)
152
+ 3. **Learned feedback** — rules based on user experience
153
+ 4. **Operational rules** — session backup, report analysis, and other work processes
@@ -0,0 +1,34 @@
1
+ ---
2
+ name: {session title — include the date}
3
+ description: {one-line main achievement or pattern}
4
+ type: contrib-session
5
+ date: YYYY-MM-DD
6
+ tags: [{related}, {tags}]
7
+ contributor: {your-handle}
8
+ ---
9
+
10
+ # {Session title}
11
+
12
+ <!--
13
+ Consent note: placing this file under tracks/_contrib/ is your consent to publish it.
14
+ De-identify before opening the PR: no employer/internal-project/colleague names, no home paths,
15
+ no internal domains, no credentials. The PR gate re-checks, but you scrub first.
16
+ -->
17
+
18
+ ## Context
19
+
20
+ {What project/situation this came from — de-identified. What problem you were working on.}
21
+
22
+ ## What happened / what was found
23
+
24
+ {The work, the pattern, the failure, the fix. Concrete enough to be reusable — file/command level
25
+ where possible, minus anything private.}
26
+
27
+ ## Why it matters beyond my project
28
+
29
+ {The reusable claim: when would another operator hit this? What does this generalize to?}
30
+
31
+ ## Decisions / open questions
32
+
33
+ - Decision: {key call made and why}
34
+ - Open: {what remains unresolved — only if applicable}
@@ -150,9 +150,24 @@ for f in "${FILES[@]}"; do
150
150
  # S5 — the pipefail-fallback disarm: `... | grep -c ... || echo 0` appends a SECOND line under
151
151
  # `set -o pipefail`, so the later `-gt` integer test becomes a bash error (= false) and the guard
152
152
  # passes silently, with the error going only to stderr. Measured class, 2026-07-26.
153
+ #
154
+ # NARROWED 2026-07-28 after hand-verifying all 9 hits this repo produced: 9/9 were false
155
+ # positives, i.e. the probe was pure noise for its own class, and 100% FP trains dismissal of
156
+ # the one hit that will matter. Two distinct causes, both mechanically reproduced:
157
+ # (a) `a || b || echo 0` was read as a pipeline — the old regex could anchor its `\|` on the
158
+ # SECOND bar of the first `||`. No pipe exists, so no second line can ever be produced.
159
+ # (Every `_mtime() { stat -c %Y … || stat -f %m … || echo 0; }` in the tree was flagged.)
160
+ # (b) a real pipeline whose failing stage emits NOTHING (`… | jq -r … || echo 0`) — the
161
+ # fallback then supplies the only line, which is exactly the intended behavior.
162
+ # The disarm needs BOTH a real pipe AND a final stage that emits regardless of upstream failure
163
+ # — a counter (`grep -c`, `wc`). That is the measured shape: `find … | grep -c . || echo 0`
164
+ # yields "9\n0" and the `-gt` guard goes silent. Verified as a known pair (both directions) in
165
+ # scripts/test_degrade_scan_shell_probes.sh; narrowing without that anchor would just trade a
166
+ # noisy probe for a blind one.
153
167
  while IFS= read -r m; do
154
- emit "$f" "${m%%:*}" "S5:pipefail-fallback(sh)" "\`|| echo 0\` fallback on a pipeline — under \`set -o pipefail\` this yields a multi-line value whose integer comparison errors out and silently passes the guard; split the pipeline and sanitize to an integer"
155
- done < <(grep -nE '\|[^|]+\|\|[[:space:]]*echo[[:space:]]+[\"'"'"']?0' "$f" 2>/dev/null \
168
+ emit "$f" "${m%%:*}" "S5:pipefail-fallback(sh)" "\`|| echo 0\` fallback on a pipeline ending in a counter (grep -c/wc) that stage emits even when an upstream stage fails, so under \`set -o pipefail\` the value gains a SECOND line, the integer comparison errors out, and the guard passes silently; split the pipeline and sanitize to an integer"
169
+ done < <(grep -nE '[^|]\|[[:space:]]*([a-z]+[[:space:]]+)*(grep[^|]*-c|wc)[^|]*\|\|[[:space:]]*echo[[:space:]]+[\"'"'"']?0' "$f" 2>/dev/null \
170
+ | grep -vE '^[0-9]+:[[:space:]]*#' \
156
171
  | grep -vE '#[[:space:]]*noqa[:[:space:]]*degrade')
157
172
  fi
158
173