@chrono-meta/fh-gate 1.4.95 → 1.4.97

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 (76) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/AGENTS.md +18 -0
  3. package/CHEATSHEET.md +1 -1
  4. package/CLAUDE.md +11 -4
  5. package/README.md +19 -0
  6. package/knowledge/shared/harness-core/fh_detail_protocols.md +12 -0
  7. package/knowledge/shared/harness-core/ship_readiness_gate.md +7 -4
  8. package/knowledge/shared/learnings/subagent_invocations_log.yaml +113 -1
  9. package/package.json +10 -2
  10. package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
  11. package/plugins/fh-commons/agents/quench-challenger.md +49 -23
  12. package/plugins/fh-commons/skills/convergence-loop/SKILL.md +14 -0
  13. package/plugins/fh-commons/skills/deliberation/SKILL.md +14 -0
  14. package/plugins/fh-commons/skills/mcp-circuit-breaker/SKILL.md +10 -1
  15. package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
  16. package/plugins/fh-meta/CHANGELOG.md +36 -0
  17. package/plugins/fh-meta/agents/beginner.md +4 -1
  18. package/plugins/fh-meta/agents/challenger.md +7 -1
  19. package/plugins/fh-meta/agents/expert.md +1 -1
  20. package/plugins/fh-meta/agents/fact-checker.md +7 -1
  21. package/plugins/fh-meta/agents/hub-persona-auditor.md +2 -1
  22. package/plugins/fh-meta/agents/main-player.md +4 -1
  23. package/plugins/fh-meta/agents/persona-innovator.md +10 -2
  24. package/plugins/fh-meta/skills/agent-composer/SKILL.md +2 -2
  25. package/plugins/fh-meta/skills/apex-review/SKILL.md +5 -0
  26. package/plugins/fh-meta/skills/asset-placement-gate/SKILL.md +38 -8
  27. package/plugins/fh-meta/skills/auto-decorrelation/SKILL.md +16 -2
  28. package/plugins/fh-meta/skills/context-doctor/SKILL_detail.md +45 -10
  29. package/plugins/fh-meta/skills/corpus-grounding-expander/SKILL.md +14 -5
  30. package/plugins/fh-meta/skills/cross-ecosystem-synergy-detection/SKILL.md +93 -30
  31. package/plugins/fh-meta/skills/deep-clarify/SKILL.md +28 -9
  32. package/plugins/fh-meta/skills/fh/SKILL.md +4 -0
  33. package/plugins/fh-meta/skills/frontier-digest/SKILL.md +64 -8
  34. package/plugins/fh-meta/skills/frontier-digest/SKILL_detail.md +20 -7
  35. package/plugins/fh-meta/skills/goal-quench/SKILL.md +48 -15
  36. package/plugins/fh-meta/skills/goal-quench/SKILL_detail.md +58 -11
  37. package/plugins/fh-meta/skills/harness-doctor/SKILL_detail.md +109 -33
  38. package/plugins/fh-meta/skills/harvest-loop/SKILL.md +6 -1
  39. package/plugins/fh-meta/skills/hub-cc-pr-reviewer/SKILL.md +126 -17
  40. package/plugins/fh-meta/skills/install-doctor/SKILL.md +50 -14
  41. package/plugins/fh-meta/skills/install-wizard/SKILL.md +26 -7
  42. package/plugins/fh-meta/skills/install-wizard/SKILL_detail.md +68 -21
  43. package/plugins/fh-meta/skills/memory-hygiene/SKILL.md +64 -17
  44. package/plugins/fh-meta/skills/meta-prompt-builder/SKILL.md +38 -4
  45. package/plugins/fh-meta/skills/persona-roster-expander/SKILL.md +15 -7
  46. package/plugins/fh-meta/skills/plugin-recommender/SKILL.md +39 -11
  47. package/plugins/fh-meta/skills/plugin-recommender/SKILL_detail.md +24 -7
  48. package/plugins/fh-meta/skills/prompt-regression/SKILL.md +54 -11
  49. package/plugins/fh-meta/skills/salience-splitter/SKILL.md +120 -7
  50. package/plugins/fh-meta/skills/salience-splitter/SKILL_detail.md +46 -13
  51. package/plugins/fh-meta/skills/sim-conductor/SKILL_detail.md +28 -3
  52. package/plugins/fh-meta/skills/steel-quench/SKILL.md +3 -1
  53. package/plugins/fh-meta/skills/verify-bidirectional/SKILL.md +72 -14
  54. package/scripts/compaction_probe.sh +153 -10
  55. package/scripts/consent_registry_check.sh +63 -0
  56. package/scripts/count_check.sh +47 -1
  57. package/scripts/degrade_direction_scan.sh +276 -6
  58. package/scripts/degrade_probe_capability.sh +105 -0
  59. package/scripts/lane_runner_check.sh +425 -0
  60. package/scripts/package_coverage_check.sh +190 -35
  61. package/scripts/psa_probe_capability.sh +78 -0
  62. package/scripts/psa_scan_lib.sh +164 -0
  63. package/scripts/public_surface_scan_files.sh +38 -0
  64. package/scripts/selfcheck.sh +346 -28
  65. package/scripts/test_capability_entrypoint_shipping.sh +132 -0
  66. package/scripts/test_consent_registry.sh +54 -0
  67. package/scripts/test_count_check_readme_format_lanes.sh +75 -0
  68. package/scripts/test_degrade_scan_shell_probes.sh +415 -0
  69. package/scripts/test_marker_crossfamily_lanes.sh +10 -1
  70. package/scripts/test_marker_floor_lanes.sh +25 -1
  71. package/scripts/test_psa_singlefile_lanes.sh +288 -0
  72. package/scripts/test_reviewer_capability_conformance.sh +10 -1
  73. package/scripts/test_selfcheck_state_lanes.sh +173 -0
  74. package/scripts/validate_yaml.sh +146 -0
  75. package/templates/degrade_direction_scan.sh +276 -6
  76. package/templates/goal-quench-settings-merged.json +30 -0
@@ -182,13 +182,22 @@ Sending HALF-OPEN probe to {tool-name}...
182
182
  - If success: circuit → CLOSED, log updated
183
183
  - If fail: circuit remains OPEN, cooldown resets
184
184
 
185
- Reset log entry:
185
+ Reset log entry — **append as a NEW list item (`- tool:`), never as bare indented keys**:
186
186
  ```yaml
187
+ - tool: {tool-name}
187
188
  state: CLOSED
188
189
  reset_at: {ISO-8601}
189
190
  reset_method: probe_success | user_forced
190
191
  ```
191
192
 
193
+ > **Why the leading `- tool:` is load-bearing.** The Step 3 log is a YAML *list*. Appending indented
194
+ > keys with no `-` merges them into the **previous** item instead of adding one, and PyYAML resolves
195
+ > duplicate keys silently by keeping the last value — so `state: OPEN` is overwritten by `CLOSED` and
196
+ > the trip record disappears. Measured 2026-08-11: trip entry + bare-key reset entry parses to
197
+ > **1 item** with `state: CLOSED` (the OPEN row is gone); the same pair written as two list items
198
+ > parses to **2 items** with the trip history intact. The state history is what Step 6 reports on, so
199
+ > a merged entry silently zeroes the failure record.
200
+
192
201
  ---
193
202
 
194
203
  ### Step 6. Report
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fh-meta",
3
- "version": "1.4.95",
3
+ "version": "1.4.97",
4
4
  "engines": {
5
5
  "claudeCode": ">=1.0.0"
6
6
  },
@@ -10,6 +10,42 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
10
10
 
11
11
  ## Plugin Level
12
12
 
13
+ ### [1.4.96] — 2026-08-12
14
+
15
+ **fix: 전수 재출하 캠페인 — 「돌았다고 보고하는데 안 도는」 계기들 · 선언이 무효였던 에이전트**
16
+
17
+ 스킬 40종 + 에이전트 8종 전수 적대검토 후 수리(PR #348 · #349). 지배 결함은 하나의 얼굴이었다 —
18
+ **계기가 대상에 안 닿는데 출력은 무결**.
19
+
20
+ - **`quench-challenger` 의 frontmatter 가 깨져 선언이 무효였다.** 비인용 멀티라인 `description`
21
+ 안의 `user:`/`assistant:` 줄이 새 YAML 키로 파싱돼 그 아래 `tools: Read, Grep, Glob` 과
22
+ `model: opus`(HARD FLOOR) 를 **둘 다 무효화**했다. 읽기전용으로 선언된 적대 에이전트가 전체
23
+ 도구로 돌고 있었다. `description` 을 한 줄 인용으로, 예시는 본문으로.
24
+ - **`scripts/validate_yaml.sh`** — 스캔 대상에 **에이전트 8종 편입**(종전 SKILL.md 만). 양 surface
25
+ 중 하나라도 0건이면 `INSTRUMENT ERROR`(exit 3) — 스캔 0을 통과로 렌더하지 않는다. **신규 출하**.
26
+ - **`scripts/degrade_direction_scan.sh`** — **마크다운 ```bash 펜스 추출**. FH 가 실제로 실행하는
27
+ bash 는 대부분 SKILL.md 펜스 안에 사는데 스캐너가 `.py`/`.sh` 만 봤다. 그림자 파일이 **원본
28
+ 줄번호를 보존**해 findings 가 `SKILL.md (```bash fence):202` 로 나온다. 펜스 없는 md 는 종전대로
29
+ `UNSCANNABLE`(미측정을 커버리지로 바꾸지 않는다).
30
+ - **인용 무결성 2건 정정**(원문 직독) — arXiv 2605.00914 의 32.3pp 는 **다수결 oracle gap** 이지
31
+ 「자기평가 시 성능저하」가 아니고(논문 결론은 *isolated self-correction prevails*), arXiv
32
+ 2603.15255(SAGE)는 **co-evolve** 라 「Critic 격리」 근거가 될 수 없다. `harvest-loop` 의 같은
33
+ 오귀속도 동시 수리.
34
+ - **`salience-splitter`** — 4축 게이트가 이름으로 지목한 의무(«split 마다 목적지가 게이트 안인지
35
+ 재질문») 를 본문에 배선 · 컷 판정 「머릿속으로」를 **레이어별 측정 2분기**로(상주=ablation
36
+ 하네스 / SKILL.md=콜드스타트 sim) · 포인터↔헤더 대조를 실행 가능한 형태로(오탐 100% 였다) ·
37
+ orphan 스캔을 `^## §` → `^## ` 로(실물 헤더 139 중 97만 보던 30% 사각).
38
+ - **죽은 명령·경로 정리** — `claude mcp search`(부재) · `codex list-agents`(부재) ·
39
+ `marketplace add` 인자 · `GoogleWebSearch`(존재하지 않는 도구명) · `frontier-digest` 저장 경로가
40
+ 카덴스 글롭과 어긋나 **7일 카덴스에 영원히 안 잡히던** 것.
41
+ - **거짓 PASS 계기들** — `harness-doctor` E7/E3/Step-11(`grep -c || echo 0` 일가가 음성 arm 을
42
+ 통과로 렌더) · `install-doctor` 의 `except: pass`(깨진 MCP 설정이 「위험 없음」과 구별 불가) ·
43
+ `asset-placement-gate`(죽은 스캔과 진짜 무충돌이 둘 다 count=0) · `auto-decorrelation`(zsh 에서
44
+ 다중 사이드카가 **무음 탈락** → single-family degrade).
45
+ - **`AGENTS.md`** — 도구 표 누락 3종(beginner·main-player·expert) 보강 + frontmatter YAML 유효성
46
+ 규율(비-CC 파서에서는 깨진 줄 아래 키가 전부 드롭된다). 문서↔파일 전수 대조 불일치 0.
47
+
48
+
13
49
  ### [1.4.86] — 2026-08-03
14
50
 
15
51
  **fix: 격리를 자칭하던 어블레이션 절차 + ambig 게이트 앵커 + 밀린 출하 자산 반영**
@@ -89,7 +89,10 @@ Did I reach a successful first outcome? : YES / NO (blocked at [where])
89
89
  ## Integration Hooks
90
90
 
91
91
  - **sim-conductor Area A** — beginner is the canonical first-contact persona (A-1). Pairs with main-player (engaged use) and challenger (adversarial).
92
- - **marketplace-gate / install-wizard** README & onboarding-path friendliness cold-read.
92
+ - **marketplace-gate / install-wizard** *(claimed adjacency, not wired measured 2026-08-11)* —
93
+ README & onboarding-path friendliness cold-read. `grep -rc beginner` → marketplace-gate **0**,
94
+ install-wizard 1 (a prose mention in SKILL_detail, not a dispatch). Control: sim-conductor lists
95
+ this agent in its persona table 4×. Treat as a *fit*, not an integration.
93
96
  - **hub-persona-auditor boundary** — *lens*, not artifact-exclusivity. Both may touch a README. `hub-persona-auditor` = multi-reader 4-axis pre-publication audit of external-facing drafts (briefing/card/guide/README as a publication). `beginner` = a single cold-read standpoint surfacing first-contact friction in any artifact (SKILL/README/prompt/config/code). For a publication-readiness verdict on an external draft, defer to hub-persona-auditor; for "can a first-timer actually get started," use beginner.
94
97
 
95
98
  ## Done When
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: challenger
3
3
  description: Frontier-grade adversarial evaluator for harness assets, papers, designs, and code. Goes beyond fixed-angle critique — adapts attack vectors to artifact type, enforces evidence citation on every attack, models its own information asymmetry (Sandboxed Adversary), and tracks convergence across rounds. Returns structured [issue · location · severity] output consumable by steel-quench, harvest-loop, and sim-conductor. Use when you need adversarial pressure that a self-reviewing author cannot generate.
4
+ tools: Read, Grep, Glob, WebSearch, WebFetch
5
+ version: 0.1
4
6
  ---
5
7
 
6
8
  > **Dual registration**: ships in `plugins/fh-meta/agents/challenger.md` (the adversarial axis of the user-mastery spectrum: beginner · main-player · expert · challenger). External plugin installs get it directly — no hub clone required.
@@ -151,7 +153,11 @@ Cross-session mode eliminates accumulated session context — use when the main
151
153
 
152
154
  ## Integration Hooks
153
155
 
154
- **steel-quench Wave 1** *(planned)*: challenger is designed to replace or supplement the devil agent. When wired, S-grade output feeds into Wave 2 defense round. Currently steel-quench calls `fh-commons:quench-challenger` for Wave 1 — explicit challenger wiring is a future integration step.
156
+ **steel-quench Wave 1** *(partially wired 2026-08-11)*: challenger is **already listed in the
157
+ deployed persona panel** (`steel-quench/SKILL_detail.md` §T0 Claude row: `challenger · quench-challenger
158
+ · expert`), so "planned" was stale. What is still absent is an explicit
159
+ `Agent(subagent_type="challenger")` dispatch call — Wave 1 invokes `fh-commons:quench-challenger`.
160
+ Panel membership ≠ a call site; do not read the first as the second.
155
161
 
156
162
  **harvest-loop Step 3a**: challenger runs against existing skills using session findings. S-grade attacks on existing skills → HIGH synthesizer grade. LOW-confidence attacks on new proposals → MED grade (defer pending verification).
157
163
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: expert
3
- description: Frontier-grade domain-authority evaluator. Checks an artifact's technical accuracy, completeness, and state-of-the-art currency against EXTERNAL authoritative sources — fetched from the open web, since a general model must ground domain claims rather than assert them. Top tier of the user-mastery spectrum (beginner → main-player → expert): the professor / prolific author / frontier-harness operator. Every accuracy judgment carries an external citation; ungrounded assertions are withheld. Returns parallax-compatible output. Use when "is this technically correct and current with the field?" matters.
3
+ description: 'Frontier-grade domain-authority evaluator. Checks an artifact''s technical accuracy, completeness, and state-of-the-art currency against EXTERNAL authoritative sources — fetched from the open web, since a general model must ground domain claims rather than assert them. Top tier of the user-mastery spectrum (beginner → main-player → expert): the professor / prolific author / frontier-harness operator. Every accuracy judgment carries an external citation; ungrounded assertions are withheld. Returns parallax-compatible output. Use when "is this technically correct and current with the field?" matters.'
4
4
  tools: Read, WebSearch, WebFetch
5
5
  version: 0.1
6
6
  ---
@@ -18,7 +18,13 @@ The main agent passes you:
18
18
  2. The relevant scope (which directories, which memory files, which CATALOG sections)
19
19
  3. Any specific suspicions (e.g., "I think §X already covers this" or "memory may be stale")
20
20
 
21
- If scope is not specified, default scope = entire hub (`knowledge/`, `tracks/`, `memory/`, `CATALOG.md`).
21
+ If scope is not specified, default scope = `knowledge/`, `tracks/`, `CATALOG.md`.
22
+ **Check each scope element exists before searching it, and report a missing one — never fold it into
23
+ a CLEAR.** Measured 2026-08-11: the previous default also listed `memory/`, which does not exist at
24
+ the repo root (the memory index lives outside the repo, unreachable from `Read/Grep/Glob` without an
25
+ absolute path), and `tracks/` is gitignored so a fresh clone has ~8 files in it. A scope element that
26
+ resolves to nothing produces the exact failure this agent's own self-check names — *"Missing scope =
27
+ false CLEAR"* — so emit `scope-missing: <path>` in the verdict block and mark the verdict PARTIAL.
22
28
 
23
29
  ## Two definitions of "fact-check"
24
30
 
@@ -51,7 +51,8 @@ Each proposal: 1-2 lines, concrete (which section, what change).
51
51
 
52
52
  ### 5. Verdict
53
53
 
54
- One sentence: `SHIP` / `SHIP_AFTER_🟥` / `REVISE` — and the single most important reason.
54
+ One sentence: `SHIP` / `SHIP_AFTER_MANDATORY` / `REVISE` / `OUT_OF_SCOPE` — and the single most important reason.
55
+ (Verdict tokens are ASCII and greppable on purpose: an emoji inside a token — the old `SHIP_AFTER_🟥` — breaks any consumer that matches on it, and `OUT_OF_SCOPE` is emitted below but was missing from this enum, so a reader could not tell it was a legal verdict rather than an error string.)
55
56
 
56
57
  ## Operating rules
57
58
 
@@ -91,7 +91,10 @@ Cross-tier conflicts: [where serving one tier hurts another — e.g., defaults g
91
91
  ## Integration Hooks
92
92
 
93
93
  - **sim-conductor Area A / D-code** — main-player is the engaged-use persona (A-2); Heavy tier supplies the edge-case lens for code artifacts.
94
- - **install-doctor adjacency** — Heavy H5 (conflicts/duplication/overwrite) complements install-doctor; main-player reports the *user-experienced* symptom, install-doctor the structural cause.
94
+ - **install-doctor adjacency** *(one-way install-doctor does not know about this agent; measured
95
+ 2026-08-11: `grep -rc main-player` in that skill → 0)* — Heavy H5 (conflicts/duplication/overwrite)
96
+ complements install-doctor; main-player reports the *user-experienced* symptom, install-doctor the
97
+ structural cause.
95
98
  - **challenger boundary** — challenger *attacks* edges adversarially; main-player reports edges as a real heavy user *experiences and depends on* them. Different vantage, intentionally.
96
99
 
97
100
  ## Done When
@@ -69,7 +69,13 @@ Additional checklist for the human operator:
69
69
 
70
70
  ### 1-a. Load naming history
71
71
 
72
- **Path A (hub environment with naming history)**: Read `MEMORY.md` (hub memory index), then load the naming-relevant files referenced there.
72
+ **Path A (hub environment with naming history)**: read the naming history from `CATALOG.md` and
73
+ `knowledge/shared/` (both repo-root, both present). *Corrected 2026-08-11: this said `MEMORY.md`,
74
+ which does not exist at the repo root (`ls MEMORY.md` → No such file; control: `CATALOG.md` and
75
+ `README.md` resolve). Path A therefore read nothing and rendered as "no naming gap found" — and
76
+ Path B could not catch it, because its condition is "external environment" and this IS the hub.*
77
+ **If neither source resolves, degrade to Path B and say so in Section 0 — a silent empty read is
78
+ not a finding of zero.**
73
79
 
74
80
  **Path B (external environment)**: Skip memory read. Use only the naming pattern taxonomy below (§ Naming pattern taxonomy) and the current invocation context.
75
81
 
@@ -77,7 +83,9 @@ Additional checklist for the human operator:
77
83
 
78
84
  Scan current asset inventory using Grep/Glob:
79
85
  ```
80
- grep -r "candidate\|gap\|unnamed\|no name" <harness-root>/
86
+ Grep tool: pattern `candidate|gap|unnamed|no name`, path `.` (repo root)
87
+ — stated as a Grep-tool call, not a shell line: this agent declares `tools: Read, Grep, Glob`
88
+ and has no Bash, so a shell command here is an instruction it cannot execute.
81
89
  ```
82
90
 
83
91
  Also look for:
@@ -2,7 +2,7 @@
2
2
  name: agent-composer
3
3
  description: Reads the current work context and plans the optimal agent dispatch. Clarifies direction with 1-2 questions when unclear; infers and proceeds immediately when execution path is unclear. Runs an automatic recording gate after each Wave completes. Triggered by "compose agents", "which agent should I use?", "run in parallel", or "agent-composer".
4
4
  user-invocable: true
5
- allowed-tools: ["Read", "Bash", "Glob", "Grep"]
5
+ allowed-tools: ["Read", "Bash", "Glob", "Grep", "Agent", "Skill"]
6
6
  model-note: session-inherit — Sonnet base is first-class (sonnet_floor_doctrine.md); depth-critical judged steps route to dispatch (opus agent / cross-family sidecar, consent-gated), never a substrate requirement
7
7
  ---
8
8
 
@@ -150,7 +150,7 @@ This orchestrator does not read files or understand structure directly — **eve
150
150
 
151
151
  Default composition table by task type.
152
152
 
153
- > **Note**: This table lists known installed agents. Capability fit scoring in Step 0.2 overrides static mapping when `agent_cards.json` has more current data.
153
+ > **Note**: This table lists known installed agents. Capability fit scoring in Step 0.2 overrides static mapping when `.claude/registry/agent_cards.json` (repo-relative — there is no bare `agent_cards.json` at the repo root) has more current data.
154
154
 
155
155
  > **Call method distinction**: `(S)` = Skill tool call / `(A)` = Background dispatch via Agent tool
156
156
 
@@ -177,6 +177,11 @@ Incorporate sim-conductor results into the HTML deck and save the revised versio
177
177
  All steps 0–4 completed
178
178
  + Verdict (✅/⚠️/❌) output for all target decision-makers
179
179
  + Gate verdict (Passed / Conditionally passed / Rejected) stated
180
+ + Deck artifact exists on disk: `apex_review_deck_YYYYMMDD.html` (Step 2) — confirm by printing its
181
+ path and byte size; this is the skill's only durable output, so "reviewed but no deck" is NOT done
182
+ + If the gate verdict is `Conditionally passed`: the `/sim-conductor Area E` verdict has been
183
+ RECEIVED and folded in (the mandatory-next gate below). Without that verdict the run is not done,
184
+ regardless of the A/B/C/D selection
180
185
  + User next step selection (A/B/C/D) confirmed
181
186
  ```
182
187
 
@@ -2,7 +2,7 @@
2
2
  name: asset-placement-gate
3
3
  description: Routes a proposed skill, plugin, or agent to its correct home — forge-harness (FH) meta-skill, project-local agent, or drop — by applying a 4-criteria meta-skill bar followed by a project-local value test.
4
4
  user-invocable: true
5
- allowed-tools: ["Read", "Grep", "Glob"]
5
+ allowed-tools: ["Read", "Grep", "Glob", "Bash"]
6
6
  model: sonnet
7
7
  ---
8
8
 
@@ -73,19 +73,37 @@ Criterion ④ ("no overlap with existing FH skills") is otherwise an LLM **recal
73
73
  ground truth — a duplicate skill with a novel name passes because the judge has no enumerated list to
74
74
  check against (judge-robustness swarm, 2026-06-13). Ground it mechanically first:
75
75
 
76
+ Run this in **Bash** from the FH repo root (the pipeline below is `grep | grep -v | grep -c`, which the
77
+ Grep tool cannot express).
78
+
76
79
  ```bash
77
- # enumerate existing skill names + descriptions (grounds the judged comparison)
78
- grep -riE "name:|description:" plugins/fh-meta/skills/*/SKILL.md plugins/fh-commons/skills/*/SKILL.md
79
- # hard-collision check: WHOLE proposed name or a WHOLE trigger phrase reused verbatim.
80
+ # LEG A — enumerate existing skill names + descriptions (grounds the judged comparison).
81
+ # This leg is ALSO the liveness check for leg B: a dead scan and a genuine no-collision both
82
+ # produce collision-count 0, so the enumerate count must be read FIRST.
83
+ ROSTER="$(grep -riE 'name:|description:' plugins/fh-meta/skills/*/SKILL.md plugins/fh-commons/skills/*/SKILL.md)"
84
+ ROSTER_N="$(printf '%s' "$ROSTER" | grep -c . )"
85
+ echo "roster_entries=$ROSTER_N"
86
+ if [ "$ROSTER_N" -eq 0 ]; then
87
+ echo "SCAN_DEAD — enumerate leg returned 0 rows (wrong cwd / missing plugins tree / glob did not match)."
88
+ echo "Criterion ④ = UNDETERMINED. Do NOT read this as 'no collision'. Fix the cwd and re-run."
89
+ # fail-closed: stop here, do not run leg B, do not pass ④
90
+ fi
91
+ # LEG B — hard-collision check (run ONLY when ROSTER_N > 0).
92
+ # WHOLE proposed name or a WHOLE trigger phrase reused verbatim.
80
93
  # grep -wF (whole-word, fixed-string) on the full strings — NOT -E on tokens (a shared common
81
94
  # word like "review" is not a collision). Exclude the asset's own file (self-match = false hit).
82
- SELF="plugins/fh-meta/skills/<proposed name>/SKILL.md"
83
- grep -rwF -e "<proposed full name>" -e "<full trigger phrase 1>" -e "<full trigger phrase 2>" \
95
+ SELF="plugins/fh-meta/skills/PROPOSED_NAME/SKILL.md"
96
+ grep -rwF -e "PROPOSED_FULL_NAME" -e "FULL_TRIGGER_PHRASE_1" -e "FULL_TRIGGER_PHRASE_2" \
84
97
  plugins/*/skills/*/SKILL.md | grep -v "$SELF" | grep -c .
85
98
  ```
86
99
 
87
- Surface **collision count + nearest existing skill(s)**. Criterion then passes only if **0 whole-name/
88
- whole-trigger collision AND the judged ≤90%-overlap check agrees**. A verbatim whole-name or
100
+ (Replace the `PROPOSED_*` / `FULL_TRIGGER_*` tokens with the literal strings under evaluation.)
101
+
102
+ Surface **roster_entries + collision count + nearest existing skill(s)** — the roster count is part of
103
+ the report, not a private step: `collision=0` is only meaningful next to a non-zero `roster_entries`
104
+ (not-found ≠ zero). Criterion ④ then passes only if **`roster_entries` > 0 AND 0 whole-name/
105
+ whole-trigger collision AND the judged ≤90%-overlap check agrees**. `roster_entries = 0` is
106
+ `SCAN_DEAD` → ④ **UNDETERMINED** (fail-closed), never a pass. A verbatim whole-name or
89
107
  whole-trigger reuse is a hard ④ fail regardless of the LLM judgment. **Honest scope**: the grep grounds
90
108
  *literal* name/trigger reuse only — a post-cutoff duplicate with a *paraphrased* trigger is invisible to
91
109
  both the judge (cutoff) and the grep (literal); that residual leans on the judged half **fed the
@@ -105,10 +123,22 @@ skipping. (Provenance: `tracks/_audit/session_2026_07_25_claude5-context-rules-s
105
123
 
106
124
  ## Done When
107
125
 
126
+ Each condition declares its check class (mandatory-pass / measured / judged); every judged condition
127
+ names its adversarial pairing — no judge-only path.
128
+
108
129
  ```
109
130
  All steps 0–3 completed
131
+ (mandatory-pass — each step's output block is present; a skipped step is a FAIL, not a default pass)
132
+ + Step 0.5 scan is LIVE: roster_entries > 0
133
+ (measured: the reported roster_entries count. 0 = SCAN_DEAD → criterion ④ UNDETERMINED and this
134
+ skill is NOT done — a dead scan and a genuine no-collision both read 0 collisions, so the
135
+ liveness number is what separates them)
110
136
  + Step 3 routing result output (location: FH meta-skill / local agent / drop)
137
+ (judged — adversarial pairing: `fh-meta:challenger` re-argues the case for the destination that was
138
+ NOT chosen, citing the 4 criteria; a routing verdict that survives the opposite case passes, an
139
+ unopposed one does not)
111
140
  + Next action specified (write SKILL.md / create .claude/agents/ / none)
141
+ (mandatory-pass — a literal next-action string from that enum; blank or "TBD" does not satisfy it)
112
142
  ```
113
143
 
114
144
  ---
@@ -103,11 +103,25 @@ probe localhost:11434 && echo "ollama-local(mac)"
103
103
  EP="$FH_SIDECAR_EXTRA"
104
104
  [ -z "$EP" ] && [ -f tracks/_meta/sidecar_endpoints.env ] && \
105
105
  EP="$(grep '^OLLAMA_EXTRA=' tracks/_meta/sidecar_endpoints.env | cut -d= -f2- | tr -d '"')"
106
- for e in $EP; do
107
- case "$e" in *[!0-9a-zA-Z.:-]*|'') continue;; esac # host:port form only reject glob/junk (no word-split hole)
106
+ # Split on NEWLINES via `while read`, never on an unquoted `for e in $EP`. Word-splitting an unquoted
107
+ # variable is a BASH behavior; zsh does NOT word-split, so under zsh a space-separated multi-endpoint
108
+ # list arrives as ONE string, hits the charclass below (space is not in it) and is dropped — silently
109
+ # reporting "no extra sidecars", i.e. the single-family fail-open this skill exists to prevent.
110
+ # Measured 2026-08-11: `EP="host1:11434 host2:11434"` → bash probes 2, zsh probes 0.
111
+ # Accepts either separator: commas/spaces are normalized to newlines first.
112
+ printf '%s\n' "$EP" | tr ' ,' '\n\n' | while IFS= read -r e; do
113
+ [ -z "$e" ] && continue
114
+ case "$e" in *[!0-9a-zA-Z.:-]*)
115
+ # NEVER a silent `continue` — a dropped endpoint must be visible, or an unprobed panel is
116
+ # indistinguishable from an unavailable one (DEGRADED_PANEL_UNUSED vs DEGRADED_SINGLE_FAMILY).
117
+ echo "sidecar-endpoint DROPPED (not host:port form): [$e]" >&2; continue;;
118
+ esac
108
119
  probe "$e" 10 && echo "ollama-extra($e)" # -m10: a sleeping GPU box may wake slower than 6s
109
120
  done
110
121
  ```
122
+ **Shell note**: this block is `sh`-portable and is written so bash and zsh behave identically. Any
123
+ future edit that reintroduces `for e in $EP` re-opens the zsh drop above. A drop line on stderr is a
124
+ **finding, not noise** — carry it into the Step 6 degrade verdict rather than discarding stderr.
111
125
  Endpoint resolution is a **mechanical env/file read** (not a prose instruction the runner must remember),
112
126
  so this discovery is tier-independent — no target-tier sim owed. The extra-endpoint binding lives only in
113
127
  the gitignored `tracks/_meta/sidecar_endpoints.env` (auto-synced to the companion store); the public skill
@@ -29,13 +29,37 @@ ls -d */ 2>/dev/null | head -20
29
29
  ### Step 2 — Large file detection + warning format
30
30
 
31
31
  ```bash
32
- # Detect files exceeding 500 lines (top 10)
33
- find . -name "*.py" -o -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.java" -o -name "*.kt" \
34
- | grep -v node_modules | grep -v .git \
35
- | xargs wc -l 2>/dev/null | sort -rn | head -11 | tail -10
32
+ # Detect files exceeding 500 lines (top 10).
33
+ #
34
+ # Two mechanical guards, both measured 2026-08-11 on this repo:
35
+ # (a) 500-line THRESHOLD via awk. Without it, `head -11 | tail -10` emits 10 rows unconditionally —
36
+ # this repo's largest source file is 450 lines and ZERO files exceed 500, yet the old pipeline
37
+ # still returned 10 rows, which the warning block below then rendered as "Large file detected".
38
+ # Not-found must render as "none", never as a hit.
39
+ # (b) `while read` instead of `xargs wc -l`. xargs splits a long file list into batches and EACH
40
+ # batch emits its own `total` row; those rows then occupy slots in the reported top 10
41
+ # (measured: 6000 files -> 2 `total` rows, one of them inside the top 10). The loop also
42
+ # survives spaces in filenames, which the xargs form did not.
43
+ FILES="$(find . -type f \( -name "*.py" -o -name "*.ts" -o -name "*.tsx" -o -name "*.js" \
44
+ -o -name "*.java" -o -name "*.kt" \) \
45
+ -not -path "*/node_modules/*" -not -path "*/.git/*")"
46
+ SCANNED="$(printf '%s' "$FILES" | grep -c . )"
47
+ BIG="$(printf '%s\n' "$FILES" | while IFS= read -r f; do
48
+ [ -n "$f" ] || continue
49
+ printf '%s\t%s\n' "$(wc -l < "$f")" "$f"
50
+ done | awk -F'\t' '$1 > 500' | sort -rn | head -10)"
51
+
52
+ if [ "$SCANNED" -eq 0 ]; then
53
+ echo "large-file scan: UNMEASURED — 0 source files matched (wrong cwd, or this project uses other extensions)"
54
+ elif [ -z "$BIG" ]; then
55
+ echo "large-file scan: none — 0 of $SCANNED source files exceed 500 lines"
56
+ else
57
+ printf '%s\n' "$BIG"
58
+ fi
36
59
  ```
37
60
 
38
- When files exceeding 500 lines are found:
61
+ Emit the warning below **only for rows the scan actually returned** (`BIG` non-empty). A `none` or
62
+ `UNMEASURED` line is reported as-is and produces **no** warning — and `UNMEASURED` is not `none`:
39
63
 
40
64
  ```
41
65
  ⚠️ Large file detected: {filename} ({N} lines)
@@ -67,11 +91,22 @@ If found, suggest adding to the `## Check Items` or `## Token Efficiency` sectio
67
91
  # CLAUDE.md line count
68
92
  wc -l CLAUDE.md .claude/CLAUDE.md 2>/dev/null | sort -rn | head -3
69
93
 
70
- # MEMORY.md line count (200-line limit)
71
- wc -l memory/MEMORY.md 2>/dev/null
72
-
73
- # memory/*.md files exceeding 30K
74
- find memory -name "*.md" -size +30k 2>/dev/null | xargs wc -l | sort -rn | head -10
94
+ # Session memory lives OUTSIDE the repo — under the Claude Code project dir, keyed by a slug of the
95
+ # absolute cwd. There is NO `memory/` at the repo root: the old `wc -l memory/MEMORY.md 2>/dev/null`
96
+ # and `find memory ... 2>/dev/null` both printed nothing on this repo, and that silence read as
97
+ # "no bloat" (not-found rendered as zero). Derive the path; never hardcode a home path.
98
+ MEMDIR="$HOME/.claude/projects/$(pwd | sed 's|/|-|g')/memory"
99
+ if [ -d "$MEMDIR" ]; then
100
+ # MEMORY.md line count (200-line limit)
101
+ if [ -f "$MEMDIR/MEMORY.md" ]; then wc -l "$MEMDIR/MEMORY.md"
102
+ else echo "MEMORY.md: UNMEASURED — index absent under $MEMDIR"; fi
103
+ # topic files exceeding 30K (while-read: survives spaces, no xargs `total` rows)
104
+ find "$MEMDIR" -name "*.md" -size +30k | while IFS= read -r m; do
105
+ printf '%s\t%s\n' "$(wc -l < "$m")" "$m"
106
+ done | sort -rn | head -10
107
+ else
108
+ echo "memory audit: UNMEASURED — no memory dir at $MEMDIR. This is NOT 'zero bloat'."
109
+ fi
75
110
 
76
111
  # SKILL.md files > 300 lines with no SKILL_detail.md (salience-splitter candidates)
77
112
  find plugins -name "SKILL.md" 2>/dev/null | while read f; do
@@ -14,8 +14,13 @@ that corpus from multiple public-domain versions so grounding is robust and non-
14
14
  verified in ANY version counts), without ever adding a generator.
15
15
 
16
16
  > Origin: harvested from the-bible (2026-06-20) — 6 public-domain Bible versions, 197k verses, as the
17
- > fail-closed grounding axiom. Generalizes to any verbatim-relay corpus (legal statute, RFC text,
18
- > standards). See `tracks/_contrib/field_harvest_2026-06-20_gate-locality-and-grounding-capabilities.md`.
17
+ > fail-closed grounding axiom. **The grounds, inline, because the harvest record does not ship**: one
18
+ > edition's wording makes a fail-closed check reject a faithful quote that followed a *different*
19
+ > edition, so the union across versions is what stops fail-closed grounding from failing closed
20
+ > *against the user* — and it keeps one translation's editorial choices from silently becoming the
21
+ > axiom. Generalizes to any verbatim-relay corpus (legal statute, RFC text, standards).
22
+ > Full harvest record — **hub-local, not distributed in the npm package**:
23
+ > `tracks/_contrib/field_harvest_2026-06-20_gate-locality-and-grounding-capabilities.md`.
19
24
 
20
25
  ## Triggers
21
26
  - "get more sources" / "broaden the grounded corpus"
@@ -39,12 +44,16 @@ verified in ANY version counts), without ever adding a generator.
39
44
  4. **Wire grounding as a union** — the grounding check passes if the quote matches the canonical text
40
45
  at that key in ANY version. Never add a path that generates text — grounding is quote-only.
41
46
  5. **Relay-integrity check** — confirm the consumer (the gate) QUOTES the corpus and cannot emit
42
- un-grounded text; run a fabrication probe (a known non-source quote must fail-closed).
47
+ un-grounded text; run a fabrication probe (a known non-source quote must fail-closed) and write
48
+ the probe's **actual output** into the index under `probe_status:`. Writing the probe *spec*
49
+ without running it is `probe_status: not_run`, not a pass.
43
50
 
44
51
  ## Done When
45
52
  - **Each added source carries a verifiable public-domain/license record** in the index. *Check class: mandatory-pass (binary — license field present and non-empty per version).*
46
- - **Every version is normalized to the same key schema** (cross-version keys align). *Check class: mandatory-pass (a shared sample key resolves in each version or is explicitly canon-scoped).*
47
- - **The index declares the quote-only union contract** (grounding matches verbatim text in ANY version; no generation path) and ships a fabrication-probe spec for the consumer to run. *Check class: mandatory-pass (binary quote-only contract + probe-spec present in the index).*
53
+ - **The index enumerates every version that was ATTEMPTED, each carrying a status** — `fetched` | `failed(<reason>)` | `skipped(<reason>)` | `unknown`. A version that was tried and dropped appears with its reason; **absence from the list is not a zero**, it is an unrecorded drop. *Check class: mandatory-pass (binary the attempted-version list is present and every entry carries one of the four statuses).*
54
+ - **Every version is normalized to the same key schema** (cross-version keys align). *Check class: mandatory-pass (a shared sample key resolves in each version). A key genuinely absent from a version is allowed ONLY when the index records it under `canon_scope:` — the reason plus the explicit list of affected keys and versions. An unrecorded exception is FAIL: "it's canon-scoped" asserted in prose and nowhere in the index does not satisfy this condition.*
55
+ - **The index declares the quote-only union contract** (grounding matches verbatim text in ANY version; no generation path). *Check class: mandatory-pass (binary — quote-only contract present in the index).*
56
+ - **The fabrication probe was RUN and its output shows fail-closed** — a known non-source quote was submitted to the consumer gate and refused, with the refusal output recorded in the index. *Check class: mandatory-pass (binary — `probe_status: executed` plus the attached refusal output). A probe **spec** with no run is `probe_status: not_run` and is a sub-condition, never this one.* **Degrade — labeled, never a silent pass**: no consumer gate exists yet → record `probe_status: no_consumer (<what is missing>)`; the corpus may still ship, but ships **labeled relay-unverified**, this condition stays UNMET, and nothing downstream may cite the corpus as relay-verified until a run is attached.
48
57
 
49
58
  ## Guards
50
59
  - **Grounding, never a generator** — the relay constraint is structural; this skill wires *grounding*.