@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
@@ -157,7 +157,17 @@ done
157
157
  #### 5-2. CATALOG.md Open Item Count
158
158
 
159
159
  ```bash
160
- awk '/^### /{count++} count<=5{print}' CATALOG.md 2>/dev/null | grep -c "^- Open:" || echo "0"
160
+ # An ABSENT CATALOG.md must not render as "0 open items" that is the not-found-as-zero class this
161
+ # whole file exists to close, and the old form produced it twice over: `2>/dev/null` swallowed awk's
162
+ # error, and `grep -c … || echo "0"` printed a SECOND "0" on no-match (the counter already prints
163
+ # its own "0" before exiting non-zero). `|| true` is the safe fallback — it prints nothing.
164
+ if [ ! -f CATALOG.md ]; then
165
+ echo "5-2 UNMEASURED: CATALOG.md not found — absent is not 'no open items'"
166
+ else
167
+ open_count=$(awk '/^### /{count++} count<=5{print}' CATALOG.md | grep -c "^- Open:" || true)
168
+ open_count=$(( ${open_count:-0} + 0 ))
169
+ echo "5-2 Open items (5 most recent sections): ${open_count}"
170
+ fi
161
171
  ```
162
172
 
163
173
  > Do not use `grep -c "^- Open:" CATALOG.md` — returns hundreds as false positives counting entire history.
@@ -165,12 +175,24 @@ awk '/^### /{count++} count<=5{print}' CATALOG.md 2>/dev/null | grep -c "^- Open
165
175
  #### 5-3. Field Project CLAUDE.md Existence
166
176
 
167
177
  ```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
178
+ # The index file may not exist at all. `not found` != `0 problems` — an absent
179
+ # index must print MISSING INDEX, never an empty (= "all OK") result.
180
+ field_index=.claude/memory/reference_field_projects.md
181
+ if [ ! -f "$field_index" ]; then
182
+ echo "MISSING INDEX: $field_index (5-3 UNMEASURED — not a pass)"
183
+ else
184
+ grep -oE '`~/[^`]+`' "$field_index" \
185
+ | tr -d '`' | sed "s|~|$HOME|g" | while read p; do
186
+ [ -f "$p/CLAUDE.md" ] && echo "OK: $p" || echo "MISSING CLAUDE.md: $p"
187
+ done
188
+ fi
172
189
  ```
173
190
 
191
+ > Known-pair calibration for this block: a **known-positive** path that does exist
192
+ > (`.claude/rules/fh_4axis_gate.md`) must print its `OK:`/`MISSING CLAUDE.md:` verdict, while an
193
+ > absent index must print `MISSING INDEX`. Do not add `2>/dev/null` back — it was what turned a
194
+ > non-existent directory into a silent zero-byte "everything is fine".
195
+
174
196
  ---
175
197
 
176
198
  ## §L5-Detail — Bash Scripts for L5-A, L5-B, L5-C
@@ -178,23 +200,33 @@ grep -oE '`~/[^`]+`' .claude/memory/reference_field_projects.md 2>/dev/null \
178
200
  ### L5-A Skill Activity (bash)
179
201
 
180
202
  ```bash
203
+ # SHELL PORTABILITY (2026-08-12, measured): the earlier form used `for f in $recent_sessions`.
204
+ # bash word-splits an unquoted parameter expansion on IFS; **zsh does not** (SH_WORD_SPLIT is off by
205
+ # default, and zsh is the macOS login shell these fences get pasted into). Under zsh the loop ran
206
+ # ONCE with the entire newline-joined list as a single filename, every grep missed, and this block
207
+ # reported EVERY skill as INACTIVE_90D — the harness declaring its own skills dead. Note the
208
+ # direction: not a permissive false clean, a false ALARM, which is why no lane caught it.
209
+ # `printf | while IFS= read -r` is the portable iteration; it is also correct for names with spaces.
210
+ # `grep -qi` (not `-li`) — `-l` printed each matching filename into the report as noise.
211
+ # `count=$(... | grep -c x || true)`: `|| true` PRINTS NOTHING, so it cannot produce the "0\n0"
212
+ # two-line disarm that `|| echo 0` produces (see the L5-C note below). Do not "simplify" it back.
181
213
  recent_sessions=$(find tracks/ -name "session_*.md" -mtime -30 2>/dev/null)
182
214
  if [ -z "$recent_sessions" ]; then
183
215
  echo "L5-A SKIP: no session records — re-diagnose after 30 days"
184
216
  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
217
+ sessions_90d=$(find tracks/ -name "session_*.md" -mtime -90 2>/dev/null)
218
+
219
+ ls plugins/fh-meta/skills/ 2>/dev/null | while IFS= read -r skill; do
220
+ [ -n "$skill" ] || continue
221
+ count=$(printf '%s\n' "$recent_sessions" | while IFS= read -r f; do
222
+ [ -f "$f" ] && grep -qi "$skill" "$f" 2>/dev/null && printf 'x\n'
223
+ done | grep -c x || true)
224
+ count=$(( ${count:-0} + 0 ))
192
225
  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
226
+ count_90d=$(printf '%s\n' "$sessions_90d" | while IFS= read -r f; do
227
+ [ -f "$f" ] && grep -qi "$skill" "$f" 2>/dev/null && printf 'x\n'
228
+ done | grep -c x || true)
229
+ count_90d=$(( ${count_90d:-0} + 0 ))
198
230
  if [ "$count_90d" -eq 0 ]; then
199
231
  echo "INACTIVE_90D: $skill (no call record in 90 days)"
200
232
  else
@@ -216,7 +248,10 @@ recent_sessions=$(find tracks/ -name "session_*.md" -mtime -30 2>/dev/null)
216
248
  [ -z "$recent_sessions" ] && echo "L5-B SKIP: no session records" && exit 0
217
249
 
218
250
  # hub-persona-auditor misuse: code PR or internal refactoring context
219
- for f in $recent_sessions; do
251
+ # `printf | while read`, not `for f in $recent_sessions` — zsh does not word-split a parameter
252
+ # expansion, so the `for` form scanned one bogus blob filename and reported "no misuse" every time.
253
+ printf '%s\n' "$recent_sessions" | while IFS= read -r f; do
254
+ [ -f "$f" ] || continue
220
255
  if grep -q "hub-persona-auditor" "$f" 2>/dev/null; then
221
256
  context=$(grep -n "hub-persona-auditor" "$f" | head -3)
222
257
  echo "$context" | while IFS=: read linenum rest; do
@@ -229,7 +264,8 @@ for f in $recent_sessions; do
229
264
  done
230
265
 
231
266
  # sim-conductor misuse: first run without onboarding context
232
- for f in $recent_sessions; do
267
+ printf '%s\n' "$recent_sessions" | while IFS= read -r f; do
268
+ [ -f "$f" ] || continue
233
269
  if grep -q "sim-conductor" "$f" 2>/dev/null; then
234
270
  context=$(grep -n "sim-conductor" "$f" | head -3)
235
271
  echo "$context" | while IFS=: read linenum rest; do
@@ -242,8 +278,13 @@ for f in $recent_sessions; do
242
278
  done
243
279
 
244
280
  # harness-doctor self-loop misuse
245
- for f in $recent_sessions; do
246
- count=$(grep -c "harness-doctor" "$f" 2>/dev/null || echo 0)
281
+ printf '%s\n' "$recent_sessions" | while IFS= read -r f; do
282
+ [ -f "$f" ] || continue
283
+ # `grep -c` prints 0 AND exits 1 on no-match, so `|| echo 0` appends a SECOND line
284
+ # ("0\n0") and every later [ -eq/-gt ] test dies with "integer expression expected".
285
+ # `|| true` keeps the exit code from killing the assignment without corrupting the value.
286
+ count=$(grep -c "harness-doctor" "$f" || true)
287
+ count=$(( ${count:-0} + 0 ))
247
288
  if [ "$count" -gt 2 ]; then
248
289
  echo "L5-B MISUSE (suspected): harness-doctor @ $f — self-loop suspected (${count} mentions in same session)"
249
290
  fi
@@ -281,14 +322,21 @@ if [ "$skill_count" -gt 0 ]; then
281
322
  fi
282
323
 
283
324
  # 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
325
+ # NEVER use `grep -c ... || echo N/A` here: grep -c prints "0" and exits 1 on no-match, so the
326
+ # fallback appends a second line ("0\nN/A"), the != "N/A" test passes, and $(( )) then dies with a
327
+ # syntax error the E3 line vanishes entirely (silent non-measurement, not a skip).
328
+ # Separate the two questions: (a) is history reachable at all, (b) what is the count.
329
+ current_open=$(awk '/^### /{count++} count<=5{print}' CATALOG.md 2>/dev/null | grep -c "^- Open:" || true)
330
+ current_open=$(( ${current_open:-0} + 0 ))
331
+ past_catalog=$(git show "HEAD@{30 days ago}:CATALOG.md" 2>/dev/null || true)
332
+ if [ -z "$past_catalog" ]; then
333
+ echo "E3_SKIP: git history < 30 days or CATALOG.md absent then (UNMEASURED — not zero)"
334
+ else
335
+ past_open=$(printf '%s\n' "$past_catalog" \
336
+ | awk '/^### /{count++} count<=5{print}' | grep -c "^- Open:" || true)
337
+ past_open=$(( ${past_open:-0} + 0 ))
288
338
  consumed=$((past_open - current_open))
289
339
  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
340
  fi
293
341
 
294
342
  # E4: harvest signals
@@ -318,13 +366,28 @@ if [ ! -f "$manifest" ]; then
318
366
  && echo "E7_WARN: ${recent_asset_edits} asset edit(s) in 14d but no edit_manifest.yaml" \
319
367
  || echo "E7: no recent asset edits"
320
368
  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"
369
+ # `grep -c` on zero matches prints "0" and exits 1 → `|| echo 0` made this "0\n0", the
370
+ # -eq test then errored out (non-zero = false), and control fell through to the `||` branch:
371
+ # E7_OK. The evolution-loop blindness detector was structurally unable to raise its own alarm.
372
+ # `|| true` + arithmetic normalisation is what makes the WARN arm reachable.
373
+ manifest_recent=$(grep -c "$(date +%Y-%m)" "$manifest" || true)
374
+ manifest_recent=$(( ${manifest_recent:-0} + 0 ))
375
+ if [ "$recent_asset_edits" -gt 0 ] && [ "$manifest_recent" -eq 0 ]; then
376
+ echo "E7_WARN: ${recent_asset_edits} asset edit(s) in 14d but no manifest entry this month"
377
+ else
378
+ echo "E7_OK: edit_manifest.yaml present with ${manifest_recent} entr(ies) this month"
379
+ fi
325
380
  fi
326
381
  ```
327
382
 
383
+ > **Known-pair calibration (mandatory before trusting E7).** Run both arms:
384
+ > **negative arm** — point `manifest` at a file with no current-month entries while asset edits
385
+ > exist → must print `E7_WARN`; **positive arm** — the real
386
+ > `tracks/_meta/edit_manifest.yaml` → must print `E7_OK` with a non-zero entry count.
387
+ > If the negative arm prints `E7_OK`, the detector is dead and its silence means nothing.
388
+ > Note the `if/else` form: the old `[ ... ] && echo A || echo B` chain also falls to `B`
389
+ > whenever the *first* test is false, which is a second route to the same false pass.
390
+
328
391
  ---
329
392
 
330
393
  ## §Step11 — PR Change Consistency Check Bash Scripts
@@ -332,13 +395,26 @@ fi
332
395
  ### 11-1. Detect Changed Files
333
396
 
334
397
  ```bash
398
+ # Two defects lived here and both produced phantom downstream counts:
399
+ # (1) `return 0` is only valid inside a function/sourced script. Run as a plain block it
400
+ # printed "can only `return' from a function" to stderr and CARRIED ON into 11-2A/B/C.
401
+ # (2) `echo "" | wc -l` is 1, not 0 — so "no changes" was announced as "1 total".
402
+ # `exit 0` actually stops the block, and `grep -c .` counts non-empty lines only.
335
403
  changed=$(git diff main..HEAD --name-only 2>/dev/null)
336
404
  [ -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) ==="
405
+ if [ -z "$changed" ]; then
406
+ echo "PR_CHECK: no changes detected vs main skip (Step 11 UNMEASURED, run nothing below)"
407
+ exit 0
408
+ fi
409
+ changed_count=$(printf '%s\n' "$changed" | grep -c . || true)
410
+ echo "=== Changed files ($(( ${changed_count:-0} + 0 )) total) ==="
339
411
  echo "$changed"
340
412
  ```
341
413
 
414
+ > Steps 11-2A/B/C consume `$changed` and must not run when it is empty. If you inline this
415
+ > into a larger function, swap `exit 0` for `return 0` — but never leave a bare `return` in a
416
+ > block that is executed directly, which is what silently disarmed the guard.
417
+
342
418
  ### 11-2A. SKILL.md Changes → Count Drift + README + CATALOG
343
419
 
344
420
  ```bash
@@ -123,7 +123,12 @@ Output final proposal list → Y: PR creation / N: persist to tracks/_meta/fh_si
123
123
 
124
124
  ### Step 3.75 — Critic (Isolated Agent)
125
125
 
126
- > Source: SAGE (arXiv 2603.15255). Isolation = Critic does not inherit synthesizer reasoning chain resolves Cost of Consensus.
126
+ > Isolation = the Critic does not inherit the synthesizer's reasoning chain. **This is an FH design
127
+ > decision, not a borrowed one.** Its evidence is arXiv 2605.00914 (*The Cost of Consensus*): plurality
128
+ > voting discards correct answers already present in the pool (oracle gap ≤32.3pp) and peer rationales
129
+ > destabilize previously-correct reasoning (≤70.0%) — so the reviewer must not stand inside the
130
+ > generator's chain. *Corrected 2026-08-11: this line previously cited SAGE (arXiv 2603.15255) as the
131
+ > isolation source. SAGE's four agents **co-evolve from a shared backbone** — the opposite structure.*
127
132
 
128
133
  Critic evaluation: Done When logic validation · failure mode exploration (2+ edge cases) · claim vs. implementation alignment · scope appropriateness (Too Narrow / Too Broad).
129
134
 
@@ -73,18 +73,48 @@ Self-precision catch areas after first cc review (following previous PR self-cat
73
73
  - Check explicit statement that audience-specific guides are limited to original developer environment
74
74
  - Check explicit statement of organization-specific areas
75
75
 
76
- Self-catch areas 0 items = skip this entire catch matrix (no token-filling / following `feedback_simplification_evidence`).
76
+ Self-catch areas 0 items = skip this entire catch matrix do not pad with token-filling to make the section look populated.
77
77
 
78
78
  ### Step 4. Review Comment Attachment
79
79
 
80
- Attach the review comment (8-matrix results + self-catch + refinement suggestions + merge recommendation) via `gh pr comment`. Within this skill's execution authority (automatic).
80
+ **Mandatory before any `gh pr comment`: run `/public-surface-audit` over the composed comment text.**
81
+ A PR comment is a **paste on a public surface**, and the repo's mechanical privacy floor does not
82
+ reach it — the pre-commit confidentiality guard scans *staged tracked content* and has **no view of
83
+ PR-body text** (`.claude/rules/fh_4axis_gate.md §Reviewer-visible evidence` says so explicitly).
84
+ This step's own inputs make that acute: Step 2 matrix #2 greps the operator's **local memory files**,
85
+ so an unfiltered paste can carry absolute home paths and private memory prose onto a public PR.
86
+
87
+ ```
88
+ verdict CLEAN → attach (automatic, within this skill's authority)
89
+ verdict REVIEW / LEAK → do NOT attach. Redact the flagged spans, re-scan, then attach
90
+ verdict NOT_CONFIGURED, or the
91
+ skill is unavailable → do NOT attach automatically. This is an irreversible surface
92
+ (a posted comment is public the instant it lands and may be
93
+ mirrored before deletion) → **fail-closed**: hand the composed
94
+ text to the operator, or take an explicit logged override
95
+ ```
96
+
97
+ **Never paste raw Step 2 grep output.** Write a *sanitized capsule* — what was checked, what it
98
+ returned, what was found — never the matched lines themselves. Same rule as the marker: the file is
99
+ a local artifact, the capsule is what crosses the boundary.
100
+
101
+ Then attach the review comment (8-matrix results + self-catch + refinement suggestions + merge
102
+ recommendation) via `gh pr comment`.
81
103
 
82
104
  > **Detail**: See `SKILL_detail.md §Step 4 Comment Template` — `gh pr comment` heredoc template — read when attaching the comment.
83
105
 
84
106
  ### Step 5. Admin Override Merge Recommendation
85
107
 
86
108
  **User decision delegation** (this skill = review/recording automation / no merge authority):
87
- - Beta stage policy (`enforce_admins: false`) adherence admin override possible
109
+ - **Read the branch-protection state at run time never from this line.** This repo moved to
110
+ `enforce_admins: true` + `required_approving_review_count: 0` on 2026-07-20, and an earlier version
111
+ of this bullet still claimed `false`: a gate skill was recommending an override on a **field that
112
+ had already flipped**. Protection is also two independent layers (legacy + rulesets, strictest
113
+ wins), so one object is never the effective answer — check both:
114
+ `gh api repos/{owner}/{repo}/branches/main/protection` **and**
115
+ `gh api repos/{owner}/{repo}/rules/branches/main`
116
+ - Self-approve is impossible when this cc authored the PR → after a completed review, `--admin` is
117
+ the normal route, not a shortcut
88
118
  - Self-approve blocked (GHE policy) → admin override path adherence
89
119
  - When this cc authored the change, admin override path is mandatory
90
120
  - N+1th operation proof = baseline stabilization acceleration path
@@ -96,39 +126,118 @@ Attach the review comment (8-matrix results + self-catch + refinement suggestion
96
126
  | Stage | Approval |
97
127
  |---|---|
98
128
  | Step 1~3 check auto-activation | **Automatic** (editable afterward) |
99
- | Step 4 review comment attachment | **Automatic** (gh pr comment within this skill's execution authority) |
129
+ | Step 4 review comment attachment | **Automatic only after `/public-surface-audit` on the comment text returns CLEAN.** REVIEW/LEAK → redact and re-scan; NOT_CONFIGURED or audit unavailable → **fail-closed**, hand to the operator (a posted comment is public on landing) |
100
130
  | Step 5 admin override merge execution | **User decision** (this skill = recommendation only / no merge authority) |
101
131
 
102
132
  ## Constraints
103
133
 
104
134
  - **This skill = review/recording automation / no merge authority** — user admin override or other reviewer merge decision
105
135
  - **No single-person decision application** — following `fact-checker` rule (narrow 1 / broad N+1 / this cc self-catch joins fact-checker count)
106
- - **Simplification guard consistency** (`feedback_simplification_evidence`) — when creating/modifying this skill, update SKILL.md only. No new auxiliary files
107
- - **Markdown editing discipline mandatory** (`feedback_markdown_edit_discipline`) — Edit first. No Write
108
- - **Frontmatter description plain text only baseline** (`feedback_skill_frontmatter_description_plain_text`) — avoid markdown bold
136
+ - **Simplification guard consistency** — when creating/modifying this skill, update SKILL.md only. No new auxiliary files
137
+ - **Markdown editing discipline mandatory** — Edit first. No Write
138
+ - **Frontmatter description plain text only baseline** — avoid markdown bold
139
+
140
+ > The three rules above were previously each attributed to a `memory feedback_*.md` file. Those files
141
+ > do not exist (verified 2026-08-11 against the operator's memory root, with a known-positive control
142
+ > in the same run). The **rules stand on their own**; only the pointers were dead, and citing a
143
+ > non-resolving file as the authority is the phantom-reference class this skill is supposed to catch.
144
+ > Do not re-attach a memory citation here unless `ls` resolves it **in the same run that cites it**.
109
145
 
110
146
  > **Detail**: See `SKILL_detail.md §Sister Asset Utilization Path`, `§External User Environment Adaptation Path`, `§Disable Path`, `§Persona Synergy Catch` — cross-ecosystem utilization, external-environment fallback, own-PRS disable resolution, and deep-insight simultaneous-activation handling — read when operating in an external user environment, resolving own-PRS conflict, or coordinating with deep-insight.
111
147
 
112
148
  ## Done When
113
149
 
150
+ This is a **gate/routing skill** — its output is a merge verdict — so every judged condition below
151
+ names its adversarial pairing. No judge-only path.
152
+
114
153
  ```
115
154
  All 5 Steps completed
116
- + Baseline consistency check 8-matrix results output (✅/⚠️/❌ each item)
155
+ mandatory-pass: each step produced its output or is marked N/A with a reason
156
+
157
+ + Baseline consistency check 8-matrix results output (OK/WARN/BLOCK each item)
158
+ — measured: count items REPORTED vs items ACTUALLY CHECKED; the two must
159
+ match. Matrix #2 (memory baseline) reports SKIPPED when no memory file
160
+ resolves — it is never folded into the pass count (see §References)
161
+
162
+ + Axis 1 run in --pr mode with a typed verdict read from
163
+ REGRESSION_GUARD_RESULT_FILE
164
+ — mandatory-pass: result is `pass` or `block`. `skip` and exit 3 are NOT
165
+ passes; they mean Axis 1 did not examine this PR and the recommendation
166
+ may not cite it as green
167
+
168
+ + /public-surface-audit run over the composed comment text BEFORE any
169
+ gh pr comment
170
+ — mandatory-pass, fail-closed: CLEAN attaches; REVIEW/LEAK redact-and-rescan;
171
+ NOT_CONFIGURED or audit unavailable hands to the operator. A posted comment
172
+ is irreversible, so tooling-down is a block, never a free skip
173
+
117
174
  + Review comment attached via gh pr comment command
118
- + Admin override merge recommendation output (merge execution is user's decision)
119
- + External verification path: harvest-loop Step 3.75 Critic isolation Agent can independently judge based on above criteria (skill_quality_rubric.md verifiable criteria)
175
+ mandatory-pass: the comment URL is returned by the command
176
+
177
+ + Admin override merge recommendation output (merge execution is user's
178
+ decision)
179
+ — judged; adversarial pairing: the branch-protection state is re-read at run
180
+ time from BOTH layers (`.../branches/main/protection` and
181
+ `.../rules/branches/main`, strictest wins) in the same run that recommends.
182
+ A recommendation citing this file's prose instead of a live read is
183
+ unfounded — that exact defect already shipped once here, on a field that
184
+ had flipped
185
+
186
+ + External verification path: an isolated Critic agent can reach the same
187
+ verdict from the artifacts alone
188
+ — judged; adversarial pairing: the reviewer-visible evidence must be
189
+ reproducible WITHOUT the author's local files. Any verdict resting on a
190
+ gitignored local artifact ships labelled LOCAL-ONLY ATTESTATION -
191
+ UNVERIFIED, which leaves the condition UNMET rather than met
192
+ (`.claude/rules/fh_4axis_gate.md` §Reviewer-visible evidence)
120
193
  ```
121
194
 
122
- **→ Mandatory when PR contains SKILL.md / rules / templates changes: `bash templates/regression_guard.sh`** — run Axis 1 (backward check) before merge recommendation is issued. If regression_guard exits with M-tier block, merge recommendation must change to ❌ regardless of other checks.
195
+ **→ Mandatory when PR contains SKILL.md / rules / templates changes: run Axis 1 (backward check) in
196
+ `--pr` mode, against the PR's head branch** — before the merge recommendation is issued. If
197
+ regression_guard reports an M-tier block, the merge recommendation must change to ❌ regardless of
198
+ other checks.
199
+
200
+ ```bash
201
+ # Precondition: the PR head branch must exist locally. This skill reads the PR via `gh pr diff`
202
+ # without checking anything out, so fetch the head ref first or --pr has nothing to resolve.
203
+ PR_BRANCH="$(gh pr view "$PR" --json headRefName -q .headRefName)"
204
+ git fetch origin "$PR_BRANCH":"refs/remotes/origin/$PR_BRANCH" # skip if already present
205
+ bash templates/regression_guard.sh --pr "origin/$PR_BRANCH"
206
+ ```
207
+
208
+ ⚠️ **Do not run it with no arguments.** Bare `bash templates/regression_guard.sh` diffs the **working
209
+ tree**, and this skill's own workflow leaves the reviewer standing on a clean `main` — so the bare
210
+ form returns `REGRESSION_GUARD_RESULT=skip` with `exit 0` **100% of the time**, and the mandatory
211
+ Axis-1 gate never examines the PR at all. Measured 2026-08-11 on a clean checkout: bare form →
212
+ `rc=0 / result=skip`; `--pr <branch>` on the same commit → `rc=0 / result=pass` having actually read
213
+ the changed SKILL.md. Canonical form is `--pr {BRANCH}` (`.claude/rules/fh_4axis_gate.md`).
214
+
123
215
  **Read the verdict from the typed channel, not the exit code** — `exit 0` means pass **or** skip
124
216
  (not-checked). Run with `REGRESSION_GUARD_RESULT_FILE=/tmp/rg.$$` and read `result=` from that file:
125
- `skip` means Axis 1 **did not examine** this PR (no matching file) record it as "Axis 1 N/A", never
126
- as a green check. A merge recommendation that cites an unexamined Axis 1 as PASS is the 2026-07-22
127
- fail-open class.
217
+ `skip` means Axis 1 **did not examine** this PR (no matching file, or the wrong invocation form)
218
+ record it as "Axis 1 N/A", never as a green check. `exit 3` means the invocation itself failed
219
+ (unresolvable branch) — also not a pass; fetch the ref and re-run. A merge recommendation that cites
220
+ an unexamined Axis 1 as PASS is the 2026-07-22 fail-open class.
128
221
 
129
222
  ## References
130
223
 
131
- - Rule body: `memory feedback_command_tower_gate.md` (hub gate accumulated naming baseline) + `memory feedback_field_to_hub_sync_protocol.md` (Option C Hybrid sync policy)
132
- - Consistency rules: `feedback_simplification_evidence` · `feedback_markdown_edit_discipline` · `feedback_skill_frontmatter_description_plain_text` · `feedback_bidirectional_self_validation` · `feedback_reference_own_hub_assets_first`
224
+ > ⚠️ **The memory filenames below were audited 2026-08-11 and **none of them exist** 8/8 absent in
225
+ > the operator's own memory root, i.e. they were never reachable, not merely absent externally. They
226
+ > are kept, struck, as the record of a phantom-reference class: a gate skill citing rule bodies that
227
+ > resolve nowhere, while matrix #2 silently "skips" and the run still reports an 8-matrix pass.
228
+ > **Matrix #2 is therefore a 7-matrix in practice** — report it as `matrix 2: SKIPPED (no resolvable
229
+ > memory baseline)` rather than folding it into the pass count (`not found ≠ 0`).
230
+ > Re-populate this list only with paths verified by `ls` **in the same run that cites them**.
231
+ >
232
+ > **Arithmetic reconciled 2026-08-11**: the "8/8" above is now true of the list below — all 8 cited
233
+ > filenames are struck (re-verified in one run: 8 cited / 8 absent, with a known-positive and a
234
+ > known-negative control). Previously only 7 were struck while the 8th
235
+ > (`feedback_autonomous_commit_proposal`) was still cited live, and three more were cited as live
236
+ > authority up in §Constraints and §Step 3 — where an executor actually reads, since References is
237
+ > not on the execution path. **Those live citations are removed; the rules they carried are stated
238
+ > directly.** A struck entry in References is not a fix if the same name is still load-bearing above.
239
+
240
+ - ~~Rule body: `memory feedback_command_tower_gate.md` + `memory feedback_field_to_hub_sync_protocol.md`~~ — **absent (verified 2026-08-11)**
241
+ - ~~Consistency rules: `feedback_simplification_evidence` · `feedback_markdown_edit_discipline` · `feedback_skill_frontmatter_description_plain_text` · `feedback_bidirectional_self_validation` · `feedback_reference_own_hub_assets_first`~~ — **absent (verified 2026-08-11)**
133
242
  - Sister skills: `cross-ecosystem-synergy-detection` (sister asset cluster baseline) · `verify-bidirectional` (bidirectional self-validation automation / self-catch auxiliary axis) · `harvest-loop` (weekly audit automation / operation proof accumulation cross-link)
134
- - Autonomous commit proposal §2.19 baseline: `memory feedback_autonomous_commit_proposal.md` ( development source automation + PR proposal under human approval)
243
+ - ~~Autonomous commit proposal §2.19 baseline: `memory feedback_autonomous_commit_proposal.md`~~ — **absent (verified 2026-08-11)**. The rule it stood for is live and lives in `CLAUDE.md §AI Contribution Model`: development-source automation is allowed, PR submission requires explicit human approval. Cite that, not this filename.
@@ -221,20 +221,38 @@ If existing retrospective/audit files exist → `harvest-loop` will create files
221
221
  # Check MCP server transport settings
222
222
  grep -r "\"transport\"" .mcp.json 2>/dev/null | grep -i "http\|sse"
223
223
 
224
- # List MCP servers using HTTP transport
225
- python3 -c "
226
- import json, sys
224
+ # List MCP servers using HTTP transport.
225
+ # `except: pass` made this exit 0 on ANY failure, so the `|| echo` fallback was dead code and a
226
+ # corrupt .mcp.json rendered identically to "no risky servers" — a silent pass on a security check.
227
+ # Four states, four distinct exits: absent(0) · unparseable(2) · risky(1) · all-stdio(0).
228
+ python3 - <<'PY'
229
+ import json, os, sys
230
+ p = '.mcp.json'
231
+ if not os.path.exists(p):
232
+ print(' MCP-CHECK: NOT-APPLICABLE — .mcp.json absent (no MCP servers configured)')
233
+ sys.exit(0)
227
234
  try:
228
- d = json.load(open('.mcp.json'))
229
- servers = d.get('mcpServers', {})
230
- for name, cfg in servers.items():
231
- t = cfg.get('transport', 'stdio')
232
- if t != 'stdio':
233
- print(f' ⚠️ {name}: transport={t}')
234
- except: pass
235
- " 2>/dev/null || echo " .mcp.json absent or unparseable"
235
+ d = json.load(open(p))
236
+ except Exception as e:
237
+ print(f' MCP-CHECK: UNPARSEABLE {p}: {e}')
238
+ print(' Transport risk UNMEASURED — this is NOT a pass. Fix the file and re-run.')
239
+ sys.exit(2)
240
+ servers = (d.get('mcpServers') or {})
241
+ risky = [(n, (c or {}).get('transport', 'stdio')) for n, c in servers.items()
242
+ if (c or {}).get('transport', 'stdio') != 'stdio']
243
+ if risky:
244
+ for n, t in risky:
245
+ print(f' WARNING {n}: transport={t} — verify localhost binding + auth')
246
+ sys.exit(1)
247
+ print(f' MCP-CHECK: PASS — {len(servers)} server(s), all stdio')
248
+ PY
236
249
  ```
237
250
 
251
+ > **Read the exit code, not just the text.** `2` (unparseable) is an *unmeasured* check and must be
252
+ > reported 🟧 in Step 3, never folded into 🟩. Known-pair calibration for this block: a deliberately
253
+ > truncated `.mcp.json` must yield `2`, and a config with one `transport: http` server must yield `1`.
254
+ > If both print nothing, the check is dead.
255
+
238
256
  **Known MCP HTTP transport vulnerability patterns** (based on HTTP port exposure security principles): When MCP servers using HTTP/SSE transport expose ports without authentication, remote access risk within local networks may occur. stdio transport is not affected.
239
257
 
240
258
  | Transport | Risk |
@@ -283,11 +301,29 @@ If you installed only the plugin in a different project cwd:
283
301
  ## Done When
284
302
 
285
303
  ```
286
- All Steps 1~4 completed
287
- + Step 3 diagnosis report output (🟥 immediate action / 🟧 check recommended / 🟩 no conflicts)
288
- + "Plugin install is safe in current environment" confirmed when 0 conflicts
304
+ Steps 0~4 completed (mandatory-pass)
305
+ + Step 0 node-floor results recorded per check, each as one of
306
+ PASS / FAIL / UNMEASURED never blank (measured: 2 floors
307
+ hook exec-bit, SessionStart
308
+ registration)
309
+ + Step 2-6 MCP transport check exited 0/1/2 and the exit code is
310
+ carried into the Step 3 tier (2 = UNMEASURED → 🟧, never 🟩) (mandatory-pass)
311
+ + Step 3 diagnosis report emitted with a tier per area
312
+ (🟥 immediate action / 🟧 check recommended / 🟩 no conflicts) (mandatory-pass)
313
+ + Verdict is derived from the recorded per-area tiers: 🟩 only if
314
+ every area is 🟩 AND none is UNMEASURED (measured: count of
315
+ non-🟩 areas == 0)
289
316
  ```
290
317
 
318
+ **Why `Steps 0~4`, not `1~4`**: Step 0 holds the only *mechanical floor* checks in this skill (hook
319
+ exec-bit, SessionStart registration, Node floor). Leaving it outside the completion condition let a
320
+ run report "done" having never touched the one part that is not judgement.
321
+
322
+ **Why the verdict is counted, not stated**: the previous condition was `"Plugin install is safe in
323
+ current environment" confirmed` — that measures whether a *sentence was printed*, which a run
324
+ satisfies by printing it. An unmeasured area (e.g. an unparseable `.mcp.json`) must not be
325
+ absorbed into 🟩; absence of a finding is not a finding of absence.
326
+
291
327
  ## Simplification Guard
292
328
 
293
329
  - 0 existing assets (new environment) → output "New environment. No conflicts." and exit immediately in Step 1
@@ -301,13 +301,32 @@ On Claude API / MCP failure → refer to [`references/fallback-guide.md`](../../
301
301
  ## Done When
302
302
 
303
303
  ```
304
- ☐ Environment detection complete: shell, CC version, OS, project type identified
305
- Gap diagnosis output: present vs missing items listed
306
- User approval/decline recorded for each suggested item
307
- All approved items installed with no failure state
308
- (failed installs surfaced to user not silently skipped)
309
- Acceleration baseline confirmed: zshrc alias + sentinels active (or user declined)
310
- Summary output: "N items installed, M items skipped — setup complete"
304
+ ☐ Environment detection complete: shell, CC version, OS, project type
305
+ identified each field carries a value or the literal "unknown",
306
+ never blank (measured: 4 fields resolved)
307
+ Settings probe distinguished ABSENT / UNPARSEABLE / OK — an
308
+ unparseable config is never treated as absent (mandatory-pass)
309
+ Gap diagnosis output: present vs missing items listed (measured: count of items
310
+ present + missing == items
311
+ scanned)
312
+ ☐ User approval/decline recorded for each suggested item —
313
+ a missing answer is a decline, never an assumed yes (measured: recorded answers
314
+ == suggested items)
315
+ ☐ All approved items installed with no failure state; every
316
+ failure surfaced to the user, not silently skipped (mandatory-pass)
317
+ ☐ Acceleration baseline: zshrc block either appended with
318
+ SUBSTITUTED values (no literal "{FH_DIR}" in the target file)
319
+ or explicitly declined and the decline recorded (mandatory-pass)
320
+ ☐ Step 3-D dispatch consent recorded in the three-part form
321
+ (quoted words · dated lease · scope) or a recorded decline —
322
+ a two-part record is invalid and counts as absent (mandatory-pass)
323
+ ☐ Summary output: "N items installed, M items skipped" where
324
+ N + M equals the number of items offered (measured: N + M == offered)
325
+ ☐ Nothing was overwritten that the user did not approve (judged — adversarial pairing:
326
+ re-run the wizard on a
327
+ populated .claude/ and diff
328
+ the tree before/after; any
329
+ unapproved delta is a FAIL)
311
330
  ```
312
331
 
313
332
  `--dry-run` mode Done When: gap diagnosis report written, no installation executed.