@chrono-meta/fh-gate 1.1.0 → 1.2.1

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 (68) hide show
  1. package/.claude/agents/challenger.md +169 -0
  2. package/AGENTS.md +160 -0
  3. package/CATALOG.md +256 -0
  4. package/CHEATSHEET.md +367 -0
  5. package/CLAUDE.md +331 -0
  6. package/CONTRIBUTING.md +198 -0
  7. package/LICENSE +21 -0
  8. package/README.md +61 -8
  9. package/bin/fh-goal.js +9 -0
  10. package/bin/fh-run.js +9 -0
  11. package/docs/codex-compat.md +123 -0
  12. package/docs/pillars.svg +70 -0
  13. package/knowledge/shared/harness-core/fh_integration_contract.md +45 -28
  14. package/package.json +30 -6
  15. package/plugins/fh-commons/README.md +37 -0
  16. package/plugins/fh-commons/agents/quench-challenger.md +373 -0
  17. package/plugins/fh-commons/skills/convergence-loop/SKILL.md +155 -0
  18. package/plugins/fh-commons/skills/deliberation/SKILL.md +288 -0
  19. package/plugins/fh-commons/skills/mcp-circuit-breaker/SKILL.md +196 -0
  20. package/plugins/fh-commons/skills/token-budget-gate/SKILL.md +175 -0
  21. package/plugins/fh-meta/agents/fact-checker.md +121 -0
  22. package/plugins/fh-meta/agents/hub-persona-auditor.md +109 -0
  23. package/plugins/fh-meta/agents/persona-innovator.md +195 -0
  24. package/plugins/fh-meta/skills/agent-composer/SKILL.md +461 -0
  25. package/plugins/fh-meta/skills/agent-composer/SKILL_detail.md +464 -0
  26. package/plugins/fh-meta/skills/apex-review/SKILL.md +185 -0
  27. package/plugins/fh-meta/skills/asset-placement-gate/SKILL.md +135 -0
  28. package/plugins/fh-meta/skills/contention-layer/SKILL.md +127 -0
  29. package/plugins/fh-meta/skills/context-bridge-dispatch/SKILL.md +30 -0
  30. package/plugins/fh-meta/skills/context-bridge-dispatch/SKILL_detail.md +144 -0
  31. package/plugins/fh-meta/skills/context-doctor/SKILL.md +341 -0
  32. package/plugins/fh-meta/skills/cross-ecosystem-synergy-detection/SKILL.md +202 -0
  33. package/plugins/fh-meta/skills/deep-clarify/SKILL.md +144 -0
  34. package/plugins/fh-meta/skills/edit-manifest/SKILL.md +210 -0
  35. package/plugins/fh-meta/skills/field-harvest/SKILL.md +384 -0
  36. package/plugins/fh-meta/skills/frontier-digest/SKILL.md +272 -0
  37. package/plugins/fh-meta/skills/goal-quench/SKILL.md +509 -0
  38. package/plugins/fh-meta/skills/harness-doctor/SKILL.md +277 -0
  39. package/plugins/fh-meta/skills/harness-doctor/SKILL_detail.md +484 -0
  40. package/plugins/fh-meta/skills/harvest-loop/SKILL.md +231 -0
  41. package/plugins/fh-meta/skills/harvest-loop/SKILL_detail.md +201 -0
  42. package/plugins/fh-meta/skills/hub-cc-pr-reviewer/SKILL.md +129 -0
  43. package/plugins/fh-meta/skills/hub-cc-pr-reviewer/SKILL_detail.md +158 -0
  44. package/plugins/fh-meta/skills/install-doctor/SKILL.md +207 -0
  45. package/plugins/fh-meta/skills/install-wizard/SKILL.md +613 -0
  46. package/plugins/fh-meta/skills/marketplace-gate/SKILL.md +193 -0
  47. package/plugins/fh-meta/skills/memory-hygiene/SKILL.md +143 -0
  48. package/plugins/fh-meta/skills/meta-prompt-builder/SKILL.md +167 -0
  49. package/plugins/fh-meta/skills/meta-prompt-builder/SKILL_detail.md +37 -0
  50. package/plugins/fh-meta/skills/pipeline-conductor/SKILL.md +430 -0
  51. package/plugins/fh-meta/skills/plugin-recommender/SKILL.md +221 -0
  52. package/plugins/fh-meta/skills/plugin-recommender/SKILL_detail.md +220 -0
  53. package/plugins/fh-meta/skills/prompt-regression/SKILL.md +178 -0
  54. package/plugins/fh-meta/skills/public-surface-audit/SKILL.md +224 -0
  55. package/plugins/fh-meta/skills/return-path-gate/SKILL.md +257 -0
  56. package/plugins/fh-meta/skills/self-marketing-lint/SKILL.md +129 -0
  57. package/plugins/fh-meta/skills/sim-conductor/SKILL.md +364 -0
  58. package/plugins/fh-meta/skills/sim-conductor/SKILL_detail.md +337 -0
  59. package/plugins/fh-meta/skills/skill-splitter/SKILL.md +126 -0
  60. package/plugins/fh-meta/skills/skill-splitter/SKILL_detail.md +185 -0
  61. package/plugins/fh-meta/skills/source-grounding-audit/SKILL.md +230 -0
  62. package/plugins/fh-meta/skills/source-grounding-audit/SKILL_detail.md +182 -0
  63. package/plugins/fh-meta/skills/steel-quench/SKILL.md +226 -0
  64. package/plugins/fh-meta/skills/steel-quench/SKILL_detail.md +453 -0
  65. package/plugins/fh-meta/skills/verify-bidirectional/SKILL.md +238 -0
  66. package/scripts/fh-gate.sh +175 -40
  67. package/scripts/fh-goal.sh +182 -0
  68. package/scripts/fh-run.sh +269 -0
@@ -0,0 +1,484 @@
1
+ ---
2
+ name: harness-doctor-detail
3
+ description: Detail file for harness-doctor — bash scripts for Steps 1~6 (L1~L4 diagnostics), L5 pattern analysis bash, Step 11 PR consistency check bash. Load when running diagnostic commands.
4
+ load: on-demand
5
+ ---
6
+
7
+ # harness-doctor — Detail Reference
8
+
9
+ > Load when running diagnostic commands. SKILL.md contains layer descriptions, verdict tables, Done When, and trigger phrases.
10
+
11
+ ---
12
+
13
+ ## §Step-1-3 — Bash Scripts for Steps 1~5 (L1~L3 Diagnostics)
14
+
15
+ ### Step 1. Confirm Diagnostic Target
16
+
17
+ ```bash
18
+ # Check current cwd harness file structure
19
+ ls -la .claude/ 2>/dev/null || echo "NO .claude DIR"
20
+ ls -la CLAUDE.md .claudeignore 2>/dev/null
21
+ ls .claude/rules/ 2>/dev/null
22
+ ls .claude/agents/ 2>/dev/null
23
+
24
+ # tracks/ present = FH or hub environment
25
+ ls -d tracks/ knowledge/ plugins/ 2>/dev/null
26
+ ```
27
+
28
+ ### Step 2. L1 — Structural Completeness
29
+
30
+ ```bash
31
+ for f in CLAUDE.md .claudeignore; do
32
+ [ -f "$f" ] && echo "OK: $f" || echo "MISSING: $f"
33
+ done
34
+ [ -d ".claude" ] && echo "OK: .claude/" || echo "MISSING: .claude/"
35
+ ```
36
+
37
+ ### Step 3. L2 — Complexity Diagnosis
38
+
39
+ #### 3-1. CLAUDE.md Line Count
40
+
41
+ ```bash
42
+ wc -l CLAUDE.md 2>/dev/null
43
+ ```
44
+
45
+ #### 3-2. Rules File Unreferenced Detection
46
+
47
+ ```bash
48
+ ls .claude/rules/*.md 2>/dev/null
49
+
50
+ find .claude/rules -maxdepth 1 -name '*.md' 2>/dev/null | while read -r f; do
51
+ fname=$(basename "$f")
52
+ grep -l "$fname" CLAUDE.md 2>/dev/null \
53
+ && echo "REFERENCED: $fname" \
54
+ || echo "UNREFERENCED: $fname"
55
+ done
56
+ ```
57
+
58
+ #### 3-3. Duplicate Section Detection
59
+
60
+ ```bash
61
+ grep -c "^##" CLAUDE.md 2>/dev/null
62
+ # 15+ sections = S-tier warning
63
+ ```
64
+
65
+ #### 3-4. Periodic Skill Activity Check
66
+
67
+ ```bash
68
+ # Check date of last weekly_audit file
69
+ latest_audit=$(ls -1t tracks/_audit/weekly_audit_*.md 2>/dev/null | head -1)
70
+ if [ -z "$latest_audit" ]; then
71
+ echo "MISSING: no weekly_audit files"
72
+ else
73
+ days_ago=$(( ( $(date +%s) - $(stat -f %m "$latest_audit" 2>/dev/null || stat -c %Y "$latest_audit") ) / 86400 ))
74
+ echo "LAST_AUDIT: $latest_audit (${days_ago} days ago)"
75
+ [ "$days_ago" -gt 30 ] && echo "OVERDUE_M: 30+ days without running" \
76
+ || { [ "$days_ago" -gt 14 ] && echo "OVERDUE_S: 14+ days without running" || echo "OK: cycle normal"; }
77
+ fi
78
+
79
+ # Check last sim-conductor result file
80
+ latest_sim=$(ls -1t tracks/_meta/sim_*.md 2>/dev/null | head -1)
81
+ [ -n "$latest_sim" ] && echo "LAST_SIM: $latest_sim" || echo "INFO: no sim record (optional)"
82
+ ```
83
+
84
+ ### Step 4. L3 — Drift Diagnosis
85
+
86
+ #### 4-1. Broken File References
87
+
88
+ ```bash
89
+ grep -oE '`[./][^`]+`' CLAUDE.md 2>/dev/null | tr -d '`' \
90
+ | grep -vE '\*|[[:space:]]|^/[^/]+$' \
91
+ | sort -u | while read p; do
92
+ [ -e "$p" ] && echo "OK: $p" || echo "BROKEN: $p"
93
+ done
94
+ ```
95
+
96
+ #### 4-2. Stale Rules Detection
97
+
98
+ ```bash
99
+ find .claude/rules/ -name "*.md" -mtime +90 2>/dev/null \
100
+ && echo "STALE: 90+ day unmodified rules files exist" \
101
+ || echo "OK: no stale"
102
+ ```
103
+
104
+ #### 4-3. settings.json Validity
105
+
106
+ ```bash
107
+ [ -f ".claude/settings.json" ] \
108
+ && python3 -c "import json,sys; json.load(open('.claude/settings.json'))" 2>/dev/null \
109
+ && echo "OK: settings.json valid" \
110
+ || echo "BROKEN: settings.json syntax error"
111
+ ```
112
+
113
+ #### 4-4. Hook Divergence Diagnosis
114
+
115
+ ```bash
116
+ if [ -f ".claude/settings.json" ]; then
117
+ hook_count=$(python3 -c "
118
+ import json, sys
119
+ try:
120
+ d = json.load(open('.claude/settings.json'))
121
+ hooks = d.get('hooks', [])
122
+ print(len(hooks))
123
+ except:
124
+ print(0)
125
+ " 2>/dev/null || echo 0)
126
+ if [ "$hook_count" -gt 0 ]; then
127
+ echo "HOOKS_FOUND: ${hook_count} hooks detected in settings.json"
128
+ python3 -c "
129
+ import json
130
+ d = json.load(open('.claude/settings.json'))
131
+ for h in d.get('hooks', []):
132
+ event = h.get('event', 'unknown')
133
+ cmds = [c.get('command','') for m in h.get('matchers',[{}]) for c in m.get('hooks',[])]
134
+ print(f' HOOK: event={event} | commands={cmds[:2]}')
135
+ " 2>/dev/null
136
+ echo "AGENT_VIEW_WARN: above hooks do NOT fire in Agent View (claude agents)"
137
+ echo " Prescription: replace hook-dependent behavior with explicit skills"
138
+ else
139
+ echo "OK: no hooks configured"
140
+ fi
141
+ else
142
+ echo "INFO: no settings.json — hook divergence diagnosis skip"
143
+ fi
144
+ ```
145
+
146
+ ### Step 5. L4 — Connection Diagnosis (FH only)
147
+
148
+ #### 5-1. Field Project Tracks Freshness
149
+
150
+ ```bash
151
+ for d in tracks/*/; do
152
+ last=$(find "$d" -name "*.md" -newer "tracks/.gitkeep" 2>/dev/null | wc -l)
153
+ echo "$d: $last files modified recently"
154
+ done
155
+ ```
156
+
157
+ #### 5-2. CATALOG.md Open Item Count
158
+
159
+ ```bash
160
+ awk '/^### /{count++} count<=5{print}' CATALOG.md 2>/dev/null | grep -c "^- Open:" || echo "0"
161
+ ```
162
+
163
+ > Do not use `grep -c "^- Open:" CATALOG.md` — returns hundreds as false positives counting entire history.
164
+
165
+ #### 5-3. Field Project CLAUDE.md Existence
166
+
167
+ ```bash
168
+ grep -oE '`~/[^`]+`' .claude/memory/reference_field_projects.md 2>/dev/null \
169
+ | tr -d '`' | sed "s|~|$HOME|g" | while read p; do
170
+ [ -f "$p/CLAUDE.md" ] && echo "OK: $p" || echo "MISSING CLAUDE.md: $p"
171
+ done
172
+ ```
173
+
174
+ ---
175
+
176
+ ## §L5-Detail — Bash Scripts for L5-A, L5-B, L5-C
177
+
178
+ ### L5-A Skill Activity (bash)
179
+
180
+ ```bash
181
+ recent_sessions=$(find tracks/ -name "session_*.md" -mtime -30 2>/dev/null)
182
+ if [ -z "$recent_sessions" ]; then
183
+ echo "L5-A SKIP: no session records — re-diagnose after 30 days"
184
+ else
185
+ installed_skills=$(ls plugins/fh-meta/skills/ 2>/dev/null)
186
+
187
+ for skill in $installed_skills; do
188
+ count=0
189
+ for f in $recent_sessions; do
190
+ grep -li "$skill\|/$skill" "$f" 2>/dev/null && ((count++)) || true
191
+ done
192
+ if [ "$count" -eq 0 ]; then
193
+ sessions_90d=$(find tracks/ -name "session_*.md" -mtime -90 2>/dev/null)
194
+ count_90d=0
195
+ for f in $sessions_90d; do
196
+ grep -li "$skill\|/$skill" "$f" 2>/dev/null && ((count_90d++)) || true
197
+ done
198
+ if [ "$count_90d" -eq 0 ]; then
199
+ echo "INACTIVE_90D: $skill (no call record in 90 days)"
200
+ else
201
+ echo "INACTIVE_30D: $skill (no call record in last 30 days)"
202
+ fi
203
+ elif [ "$count" -lt 2 ]; then
204
+ echo "LOW_ACTIVE: $skill (${count} times)"
205
+ else
206
+ echo "ACTIVE: $skill (${count} times)"
207
+ fi
208
+ done
209
+ fi
210
+ ```
211
+
212
+ ### L5-B Call Context Appropriateness (bash)
213
+
214
+ ```bash
215
+ recent_sessions=$(find tracks/ -name "session_*.md" -mtime -30 2>/dev/null)
216
+ [ -z "$recent_sessions" ] && echo "L5-B SKIP: no session records" && exit 0
217
+
218
+ # hub-persona-auditor misuse: code PR or internal refactoring context
219
+ for f in $recent_sessions; do
220
+ if grep -q "hub-persona-auditor" "$f" 2>/dev/null; then
221
+ context=$(grep -n "hub-persona-auditor" "$f" | head -3)
222
+ echo "$context" | while IFS=: read linenum rest; do
223
+ snippet=$(sed -n "$((linenum-3)),$((linenum+3))p" "$f" 2>/dev/null)
224
+ if echo "$snippet" | grep -qiE "code review|PR review|refactoring"; then
225
+ echo "L5-B MISUSE (suspected): hub-persona-auditor @ $f:$linenum — code PR context (false positive possible)"
226
+ fi
227
+ done
228
+ fi
229
+ done
230
+
231
+ # sim-conductor misuse: first run without onboarding context
232
+ for f in $recent_sessions; do
233
+ if grep -q "sim-conductor" "$f" 2>/dev/null; then
234
+ context=$(grep -n "sim-conductor" "$f" | head -3)
235
+ echo "$context" | while IFS=: read linenum rest; do
236
+ snippet=$(sed -n "$((linenum-3)),$((linenum+3))p" "$f" 2>/dev/null)
237
+ if echo "$snippet" | grep -qiE "first time|how do I|not sure how"; then
238
+ echo "L5-B MISUSE (suspected): sim-conductor @ $f:$linenum — first run / no onboarding context (false positive possible)"
239
+ fi
240
+ done
241
+ fi
242
+ done
243
+
244
+ # harness-doctor self-loop misuse
245
+ for f in $recent_sessions; do
246
+ count=$(grep -c "harness-doctor" "$f" 2>/dev/null || echo 0)
247
+ if [ "$count" -gt 2 ]; then
248
+ echo "L5-B MISUSE (suspected): harness-doctor @ $f — self-loop suspected (${count} mentions in same session)"
249
+ fi
250
+ done
251
+ ```
252
+
253
+ ### L5-C Effect Metrics (bash)
254
+
255
+ ```bash
256
+ # E1: CLAUDE.md line count change
257
+ current_lines=$(wc -l < CLAUDE.md 2>/dev/null || echo 0)
258
+ past_lines=$(git show "HEAD@{30 days ago}:CLAUDE.md" 2>/dev/null | wc -l || echo "N/A")
259
+ if [ "$past_lines" != "N/A" ] && [ "$past_lines" -gt 0 ]; then
260
+ delta=$((current_lines - past_lines))
261
+ if [ "$delta" -lt 0 ]; then
262
+ echo "E1_OK: CLAUDE.md ${delta} lines reduced"
263
+ elif [ "$delta" -gt 50 ]; then
264
+ echo "E1_WARN: CLAUDE.md +${delta} lines increased"
265
+ else
266
+ echo "E1_STABLE: CLAUDE.md ±${delta} lines"
267
+ fi
268
+ else
269
+ echo "E1_SKIP: git history < 30 days or no CLAUDE.md"
270
+ fi
271
+
272
+ # E2: Skill complexity ratio
273
+ total_lines=$(cat plugins/fh-meta/skills/*/SKILL.md plugins/fh-commons/skills/*/SKILL.md 2>/dev/null | wc -l | tr -d ' ')
274
+ skill_count=$(ls -d plugins/fh-meta/skills/*/ plugins/fh-commons/skills/*/ 2>/dev/null | wc -l | tr -d ' ')
275
+ if [ "$skill_count" -gt 0 ]; then
276
+ ratio=$((total_lines / skill_count))
277
+ if [ "$ratio" -le 100 ]; then echo "E2_OK: avg ${ratio} lines/skill (healthy)"
278
+ elif [ "$ratio" -le 200 ]; then echo "E2_WARN: avg ${ratio} lines/skill (caution)"
279
+ else echo "E2_FAIL: avg ${ratio} lines/skill (overloaded — separation or compression needed)"
280
+ fi
281
+ fi
282
+
283
+ # E3: CATALOG.md Open item consumption rate
284
+ current_open=$(awk '/^### /{count++} count<=5{print}' CATALOG.md 2>/dev/null | grep -c "^- Open:" || echo 0)
285
+ past_open=$(git show "HEAD@{30 days ago}:CATALOG.md" 2>/dev/null \
286
+ | awk '/^### /{count++} count<=5{print}' | grep -c "^- Open:" || echo "N/A")
287
+ if [ "$past_open" != "N/A" ]; then
288
+ consumed=$((past_open - current_open))
289
+ echo "E3: Open items consumed ${consumed} (30 days ago: ${past_open} → now: ${current_open})"
290
+ else
291
+ echo "E3_SKIP: git history < 30 days"
292
+ fi
293
+
294
+ # E4: harvest signals
295
+ signal_dir=tracks/_meta
296
+ signal_count=$(find "$signal_dir" -name "fh_signal_*.md" -newer "$(date -v-30d +%Y-%m-%d 2>/dev/null || date -d '30 days ago' +%Y-%m-%d)" 2>/dev/null | wc -l | tr -d ' ')
297
+ echo "E4: harvest signals in last 30 days: ${signal_count} (target: ≥3)"
298
+
299
+ # E5: SKILL.md last change
300
+ last_skill_change=$(git log -1 --format="%ad (%ar)" -- "plugins/fh-meta/skills/*/SKILL.md" 2>/dev/null || echo "N/A")
301
+ echo "E5: SKILL.md last change — $last_skill_change"
302
+
303
+ # E6: Cold Audit last execution
304
+ last_audit=$(find "$signal_dir" -name "*cold_audit*" -o -name "*audit*" 2>/dev/null | xargs ls -t 2>/dev/null | head -1)
305
+ if [ -n "$last_audit" ]; then
306
+ last_date=$(stat -f "%Sm" -t "%Y-%m-%d" "$last_audit" 2>/dev/null || stat -c "%y" "$last_audit" 2>/dev/null | cut -d' ' -f1)
307
+ echo "E6: last cold audit — ${last_date}"
308
+ else
309
+ echo "E6_WARN: no cold audit history (30-day SLA recommended)"
310
+ fi
311
+
312
+ # E7: Evolution-loop blindness
313
+ manifest=tracks/_meta/edit_manifest.yaml
314
+ recent_asset_edits=$(git log --since="14 days ago" --name-only --pretty=format: 2>/dev/null \
315
+ | grep -E "SKILL\.md|\.claude/rules/|^CLAUDE\.md" | sort -u | wc -l | tr -d ' ')
316
+ if [ ! -f "$manifest" ]; then
317
+ [ "$recent_asset_edits" -gt 0 ] \
318
+ && echo "E7_WARN: ${recent_asset_edits} asset edit(s) in 14d but no edit_manifest.yaml" \
319
+ || echo "E7: no recent asset edits"
320
+ else
321
+ manifest_recent=$(grep -c "$(date +%Y-%m)" "$manifest" 2>/dev/null || echo 0)
322
+ [ "$recent_asset_edits" -gt 0 ] && [ "$manifest_recent" -eq 0 ] \
323
+ && echo "E7_WARN: asset edits exist but no manifest entry this month" \
324
+ || echo "E7_OK: edit_manifest.yaml present with recent entries"
325
+ fi
326
+ ```
327
+
328
+ ---
329
+
330
+ ## §Step11 — PR Change Consistency Check Bash Scripts
331
+
332
+ ### 11-1. Detect Changed Files
333
+
334
+ ```bash
335
+ changed=$(git diff main..HEAD --name-only 2>/dev/null)
336
+ [ -z "$changed" ] && changed=$(git diff --cached --name-only 2>/dev/null)
337
+ [ -z "$changed" ] && echo "PR_CHECK: no changes detected vs main — skip" && return 0
338
+ echo "=== Changed files ($(echo "$changed" | wc -l | tr -d ' ') total) ==="
339
+ echo "$changed"
340
+ ```
341
+
342
+ ### 11-2A. SKILL.md Changes → Count Drift + README + CATALOG
343
+
344
+ ```bash
345
+ skills_changed=$(echo "$changed" | grep "skills/.*/SKILL\.md" || true)
346
+ if [ -n "$skills_changed" ]; then
347
+ cat > /tmp/_fh_count.py <<'PYEOF'
348
+ import json, glob, os, re
349
+ def claimed_count(desc):
350
+ m = re.search(r'(\d+)\s*skills', desc)
351
+ return int(m.group(1)) if m else None
352
+ for src in sorted(glob.glob('plugins/*')):
353
+ if not os.path.isdir(os.path.join(src, 'skills')):
354
+ continue
355
+ pname = os.path.basename(src)
356
+ actual = len(glob.glob(os.path.join(src, 'skills', '*/')))
357
+ pj = os.path.join(src, '.claude-plugin', 'plugin.json')
358
+ if os.path.exists(pj):
359
+ c = claimed_count(json.load(open(pj)).get('description', ''))
360
+ if c is not None and c != actual:
361
+ print(f"DRIFT: {pname} plugin.json says '{c} skills' but {actual} dirs")
362
+ elif c is not None:
363
+ print(f"OK: {pname} plugin.json count {actual} matches")
364
+ mp = '.claude-plugin/marketplace.json'
365
+ if os.path.exists(mp):
366
+ for p in json.load(open(mp)).get('plugins', []):
367
+ src = p.get('source', '').lstrip('./')
368
+ actual = len(glob.glob(os.path.join(src, 'skills', '*/')))
369
+ c = claimed_count(p.get('description', ''))
370
+ if c is not None and c != actual:
371
+ print(f"DRIFT: marketplace.json[{p['name']}] says '{c} skills' but {actual} dirs")
372
+ PYEOF
373
+ python3 /tmp/_fh_count.py
374
+
375
+ echo "$skills_changed" | while read sp; do
376
+ sname=$(basename "$(dirname "$sp")")
377
+ grep -q "$sname" README.md 2>/dev/null \
378
+ && echo "OK: $sname in README" \
379
+ || echo "MISSING: '$sname' not in README.md"
380
+ grep -q "$sname" CATALOG.md 2>/dev/null \
381
+ && echo "OK: $sname in CATALOG" \
382
+ || echo "MISSING: '$sname' not in CATALOG.md"
383
+ done
384
+ fi
385
+ ```
386
+
387
+ ### 11-2B. Agent File Changes → README + agent_cards.json
388
+
389
+ ```bash
390
+ agents_changed=$(echo "$changed" | grep -E "agents/.*\.md" || true)
391
+ if [ -n "$agents_changed" ]; then
392
+ actual_agents=$(find .claude/agents plugins/*/agents -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
393
+ readme_agents=$(grep -oE '[0-9]+ (fh-meta \+ [0-9]+ fh-commons )?agents?' README.md 2>/dev/null | head -1 || echo "not found")
394
+ echo "Agent files: $actual_agents | README mentions: $readme_agents"
395
+ if [ -f .claude/registry/agent_cards.json ]; then
396
+ cards_count=$(grep -oE '"agent_count":[[:space:]]*[0-9]+' .claude/registry/agent_cards.json | grep -oE '[0-9]+')
397
+ canonical_agents=$(find .claude/agents plugins/fh-meta/agents plugins/fh-commons/agents -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
398
+ [ "$cards_count" = "$canonical_agents" ] \
399
+ && echo "OK: agent_cards.json count ($cards_count) matches tracked agent files" \
400
+ || echo "DRIFT: agent_cards.json says $cards_count but tracked agent files total $canonical_agents — regenerate registry"
401
+ fi
402
+ fi
403
+ ```
404
+
405
+ ### 11-2C. knowledge/shared/ Changes → CATALOG
406
+
407
+ ```bash
408
+ knowledge_changed=$(echo "$changed" | grep "^knowledge/" || true)
409
+ if [ -n "$knowledge_changed" ]; then
410
+ echo "$knowledge_changed" | while read kf; do
411
+ fname=$(basename "$kf" .md)
412
+ grep -q "$fname" CATALOG.md 2>/dev/null \
413
+ && echo "OK: $fname in CATALOG" \
414
+ || echo "MISSING: '$fname' not in CATALOG.md"
415
+ done
416
+ fi
417
+ ```
418
+
419
+ ### 11-2D. README.md Changed → Stale References
420
+
421
+ ```bash
422
+ if echo "$changed" | grep -q "^README\.md$"; then
423
+ grep -n "chrono-code" README.md 2>/dev/null && echo "STALE: 'chrono-code' found — should be 'chrono-meta'" || true
424
+ actual=$(ls -d plugins/fh-meta/skills/*/ plugins/fh-commons/skills/*/ 2>/dev/null | wc -l | tr -d ' ')
425
+ readme_count=$(grep -oE '[0-9]+ (fh-meta[^)]+)?skills' README.md 2>/dev/null | head -1 || echo "not found")
426
+ echo "Actual skills: $actual | README mentions: $readme_count"
427
+ fi
428
+ ```
429
+
430
+ ### 11-2E. AGENTS.md / CLAUDE.md Changed → Cross-Reference
431
+
432
+ ```bash
433
+ if echo "$changed" | grep -qE "^AGENTS\.md$|^CLAUDE\.md$"; then
434
+ if [ -f AGENTS.md ]; then
435
+ agents_in_file=$(grep -c "^##\? " AGENTS.md 2>/dev/null || echo "?")
436
+ actual_agents=$(find .claude/agents plugins/*/agents -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
437
+ echo "AGENTS.md sections: $agents_in_file | Actual agent files: $actual_agents"
438
+ fi
439
+ fi
440
+ ```
441
+
442
+ ### 11-3. Step 8 — harvest-loop Integration
443
+
444
+ ```bash
445
+ ls -1t tracks/_audit/weekly_audit_*.md 2>/dev/null | head -1
446
+ ```
447
+
448
+ If latest weekly_audit exists → propose adding:
449
+
450
+ ```markdown
451
+ ### Harness Structure Check (harness-doctor)
452
+ - [ ] CLAUDE.md line count within threshold
453
+ - [ ] No broken file references
454
+ - [ ] Unreferenced rules files cleaned up
455
+ - [ ] Check tracks with no sync in 30+ days
456
+ - [ ] Review INACTIVE_30D skills (L5-A)
457
+ - [ ] Check suspected misuse patterns (L5-B)
458
+ - [ ] Review effect metrics E1·E3·E5 (L5-C)
459
+ - [ ] Hook divergence — create alternative for Agent View
460
+ - [ ] Harness-defect taxonomy: Context Drift · Schema Misalignment · State Degradation
461
+ ```
462
+
463
+ ### Step 9 — Eval-First Quantitative Gate (v0.x → v1 only)
464
+
465
+ ```bash
466
+ latest_sims=$(find tracks/_meta/ -name "sim_*.md" 2>/dev/null | sort -r | head -5)
467
+ [ -z "$latest_sims" ] && echo "EVAL_SKIP: run sim-conductor 5 times first" && exit 0
468
+ echo "Target sim files:"; echo "$latest_sims"
469
+ # Manual review — aggregate correct-skill-selections / total per sim file
470
+ ```
471
+
472
+ Thresholds: Tool Selection Accuracy > 0.90 · Multi-Step Coherence > 0.85 · Clarification Rate < 0.30.
473
+
474
+ ### Step 10.6 — Cross-Skill Capability (syntax verification reuse)
475
+
476
+ F7 (bash -n parse) is a general-purpose syntax capability. Other skills reuse it:
477
+
478
+ | Skill | Purpose | Axis |
479
+ |---|---|---|
480
+ | harness-doctor Step 10 (this) | Regression detection — new syntax errors from change | *Backward* |
481
+ | steel-quench | Attack vector — "SKILL.md claims bash runs but has syntax errors" | *Adversarial* |
482
+ | source-grounding-audit | Phantom claim — code in docs that doesn't parse = fabricated example | *Forward* |
483
+
484
+ For shared utility: `templates/regression_guard.sh` — extract `count_bad_blocks` function or invoke with ref pair.