@chrono-meta/fh-gate 1.4.94 → 1.4.96

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 (75) 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 +1 -1
  5. package/knowledge/shared/harness-core/capability_composition_contract.md +25 -4
  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 +67 -1
  9. package/package.json +7 -1
  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 +40 -14
  15. package/plugins/fh-commons/skills/token-budget-gate/SKILL.md +31 -12
  16. package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
  17. package/plugins/fh-meta/CHANGELOG.md +36 -0
  18. package/plugins/fh-meta/agents/beginner.md +4 -1
  19. package/plugins/fh-meta/agents/challenger.md +7 -1
  20. package/plugins/fh-meta/agents/expert.md +1 -1
  21. package/plugins/fh-meta/agents/fact-checker.md +7 -1
  22. package/plugins/fh-meta/agents/hub-persona-auditor.md +2 -1
  23. package/plugins/fh-meta/agents/main-player.md +4 -1
  24. package/plugins/fh-meta/agents/persona-innovator.md +10 -2
  25. package/plugins/fh-meta/skills/agent-composer/SKILL.md +2 -2
  26. package/plugins/fh-meta/skills/apex-review/SKILL.md +5 -0
  27. package/plugins/fh-meta/skills/asset-placement-gate/SKILL.md +38 -8
  28. package/plugins/fh-meta/skills/auto-decorrelation/SKILL.md +16 -2
  29. package/plugins/fh-meta/skills/context-doctor/SKILL_detail.md +45 -10
  30. package/plugins/fh-meta/skills/corpus-grounding-expander/SKILL.md +14 -5
  31. package/plugins/fh-meta/skills/cross-ecosystem-synergy-detection/SKILL.md +93 -30
  32. package/plugins/fh-meta/skills/deep-clarify/SKILL.md +28 -9
  33. package/plugins/fh-meta/skills/fh/SKILL.md +4 -0
  34. package/plugins/fh-meta/skills/field-harvest/SKILL.md +34 -11
  35. package/plugins/fh-meta/skills/frontier-digest/SKILL.md +64 -8
  36. package/plugins/fh-meta/skills/frontier-digest/SKILL_detail.md +20 -7
  37. package/plugins/fh-meta/skills/goal-quench/SKILL.md +48 -15
  38. package/plugins/fh-meta/skills/goal-quench/SKILL_detail.md +58 -11
  39. package/plugins/fh-meta/skills/harness-doctor/SKILL_detail.md +109 -33
  40. package/plugins/fh-meta/skills/harvest-loop/SKILL.md +6 -1
  41. package/plugins/fh-meta/skills/hub-cc-pr-reviewer/SKILL.md +126 -17
  42. package/plugins/fh-meta/skills/install-doctor/SKILL.md +50 -14
  43. package/plugins/fh-meta/skills/install-wizard/SKILL.md +26 -7
  44. package/plugins/fh-meta/skills/install-wizard/SKILL_detail.md +68 -21
  45. package/plugins/fh-meta/skills/marketplace-gate/SKILL.md +59 -31
  46. package/plugins/fh-meta/skills/memory-hygiene/SKILL.md +64 -17
  47. package/plugins/fh-meta/skills/meta-prompt-builder/SKILL.md +38 -4
  48. package/plugins/fh-meta/skills/persona-roster-expander/SKILL.md +15 -7
  49. package/plugins/fh-meta/skills/pipeline-conductor/SKILL.md +13 -3
  50. package/plugins/fh-meta/skills/plugin-recommender/SKILL.md +39 -11
  51. package/plugins/fh-meta/skills/plugin-recommender/SKILL_detail.md +24 -7
  52. package/plugins/fh-meta/skills/prompt-regression/SKILL.md +54 -11
  53. package/plugins/fh-meta/skills/public-surface-audit/SKILL.md +56 -22
  54. package/plugins/fh-meta/skills/public-surface-audit/SKILL_detail.md +50 -11
  55. package/plugins/fh-meta/skills/return-path-gate/SKILL.md +36 -7
  56. package/plugins/fh-meta/skills/salience-splitter/SKILL.md +120 -7
  57. package/plugins/fh-meta/skills/salience-splitter/SKILL_detail.md +46 -13
  58. package/plugins/fh-meta/skills/sim-conductor/SKILL_detail.md +28 -3
  59. package/plugins/fh-meta/skills/steel-quench/SKILL.md +3 -1
  60. package/plugins/fh-meta/skills/verify-bidirectional/SKILL.md +72 -14
  61. package/scripts/capability_registry_check.sh +280 -0
  62. package/scripts/count_check.sh +47 -1
  63. package/scripts/degrade_direction_scan.sh +276 -6
  64. package/scripts/degrade_probe_capability.sh +105 -0
  65. package/scripts/package_coverage_check.sh +8 -0
  66. package/scripts/psa_probe_capability.sh +78 -0
  67. package/scripts/psa_scan_lib.sh +53 -1
  68. package/scripts/public_surface_scan_files.sh +8 -0
  69. package/scripts/relay_channel.sh +6 -1
  70. package/scripts/selfcheck.sh +15 -0
  71. package/scripts/test_capability_entrypoint_shipping.sh +132 -0
  72. package/scripts/test_count_check_readme_format_lanes.sh +75 -0
  73. package/scripts/test_degrade_scan_shell_probes.sh +415 -0
  74. package/scripts/validate_yaml.sh +146 -0
  75. package/templates/degrade_direction_scan.sh +276 -6
@@ -184,6 +184,21 @@ else
184
184
  fail=1
185
185
  fi
186
186
 
187
+ # count_check's README format override is a mandatory-pass gate whose pattern is caller-supplied.
188
+ # Same subject-present/anchor-gone shape as the block above: if the guard ships without its known
189
+ # pair, a template that defeats the gate (a rendered newline turns the pattern into an OR search)
190
+ # passes silently — measured 2026-08-12 on a stale README that the gate reported as PASS.
191
+ if [ ! -f scripts/count_check.sh ]; then
192
+ echo "SKIP count_check README-format lanes (subject scripts/count_check.sh absent)"
193
+ elif [ -f scripts/test_count_check_readme_format_lanes.sh ]; then
194
+ if ! bash scripts/test_count_check_readme_format_lanes.sh; then
195
+ fail=1
196
+ fi
197
+ else
198
+ echo "FAIL count_check README-format lanes: count_check.sh present but its anchor is missing"
199
+ fail=1
200
+ fi
201
+
187
202
  # package-coverage — a shipped doc must not point at a file the tarball omits. Distinct from the
188
203
  # ref-path check below: that one asks "does this path exist at all", this one asks "does the
189
204
  # CONSUMER get it". Measured 2026-07-28: 35 paths existed, were named by a shipped doc, and were
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env bash
2
+ # test_capability_entrypoint_shipping.sh — every typed CAPABILITY ENTRY POINT must be in the
3
+ # npm published file set.
4
+ #
5
+ # WHY THIS EXISTS (measured 2026-08-12, cross-family gpt-5.5 + governor widening)
6
+ # `scripts/degrade_probe_capability.sh` AND `scripts/psa_probe_capability.sh` were both absent from
7
+ # `package.json` `files[]`, while their VALIDATOR (`capability_registry_check.sh`) shipped. An npm
8
+ # consumer therefore received the thing that checks capabilities and none of the capabilities — the
9
+ # release's headline feature (scanning ```bash/```python fences) was unreachable on the typed path
10
+ # it advertises.
11
+ #
12
+ # WHY `package_coverage_check.sh` DID NOT CATCH IT — and why this is a separate check rather than a
13
+ # rule added there: that checker walks *references* (a shipped doc names a path → the path must
14
+ # ship). These two files are referenced by NOTHING but their own header. A reference-follower is
15
+ # structurally blind to an ORPHAN; you cannot fix that by adding another pattern to it. The
16
+ # discriminator here is not "is it referenced" but "is it an entry point", which is knowable from
17
+ # the filename convention alone.
18
+ #
19
+ # CLASS: third occurrence of "a shipped surface points outside files[]" in this release cycle
20
+ # 1. SKILL.md pointed at a runner that was not shipped (caught by CI only)
21
+ # 2. AGENTS.md pointed at validate_yaml.sh, not shipped (caught by CI only)
22
+ # 3. the capability entry points themselves, not shipped (caught by cross-family only)
23
+ # The standing rule is N>=3 → mechanize at the front instead of relying on the next reviewer.
24
+ #
25
+ # Usage: bash scripts/test_capability_entrypoint_shipping.sh
26
+ # Exit: 0 = every entry point ships; 1 = at least one is missing (or the check could not run).
27
+ set -uo pipefail
28
+
29
+ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
30
+ cd "$REPO_ROOT" || { echo "FAIL: cannot cd to repo root"; exit 1; }
31
+
32
+ pass=0; fail=0
33
+ ok() { printf ' \342\234\205 %s\n' "$1"; pass=$((pass+1)); }
34
+ bad() { printf ' \342\235\214 %s\n' "$1"; fail=$((fail+1)); }
35
+
36
+ echo "capability entry-point shipping check"
37
+
38
+ command -v node >/dev/null 2>&1 || {
39
+ echo " INSTRUMENT ERROR: node unavailable — files[] cannot be read. NOT a pass."; exit 1; }
40
+
41
+ # Discover entry points by convention. A DISCOVERY of zero is an instrument failure, not a clean
42
+ # result: this check exists precisely because the surface it guards is invisible to reference
43
+ # walking, so "found nothing to check" must never render as "everything ships".
44
+ ENTRIES=$(find scripts -maxdepth 1 -type f -name '*_capability.sh' 2>/dev/null | sort)
45
+ n=$(printf '%s\n' "$ENTRIES" | grep -c . || true)
46
+ n=$(( ${n:-0} + 0 ))
47
+ if [ "$n" -eq 0 ]; then
48
+ echo " INSTRUMENT ERROR: zero *_capability.sh files discovered — the scan did not reach its target."
49
+ echo " (A real zero is possible only if this repo has no typed capabilities; verify by hand before believing it.)"
50
+ exit 1
51
+ fi
52
+
53
+ FILES_JSON=$(node -e 'process.stdout.write(JSON.stringify(require("./package.json").files||[]))' 2>/dev/null) || {
54
+ echo " INSTRUMENT ERROR: could not read package.json files[]"; exit 1; }
55
+
56
+ printf '%s\n' "$ENTRIES" | while IFS= read -r e; do
57
+ [ -n "$e" ] || continue
58
+ if node -e 'const f=JSON.parse(process.argv[1]);process.exit(f.includes(process.argv[2])?0:1)' "$FILES_JSON" "$e"; then
59
+ echo " ok $e"
60
+ else
61
+ echo " MISSING $e"
62
+ fi
63
+ done > "${TMPDIR:-/tmp}/cap_entry_$$.txt"
64
+
65
+ missing=$(grep -c '^ MISSING ' "${TMPDIR:-/tmp}/cap_entry_$$.txt" || true)
66
+ missing=$(( ${missing:-0} + 0 ))
67
+ shipped=$(grep -c '^ ok ' "${TMPDIR:-/tmp}/cap_entry_$$.txt" || true)
68
+ shipped=$(( ${shipped:-0} + 0 ))
69
+ cat "${TMPDIR:-/tmp}/cap_entry_$$.txt"
70
+ rm -f "${TMPDIR:-/tmp}/cap_entry_$$.txt"
71
+
72
+ if [ "$missing" -eq 0 ]; then
73
+ ok "all $shipped capability entry point(s) are in package.json files[]"
74
+ else
75
+ bad "$missing capability entry point(s) absent from files[] — npm consumers get the validator without the capability"
76
+ fi
77
+
78
+ # REVERSE DIRECTION (added 2026-08-12 after a cross-family round broke the first version).
79
+ # The check above walks disk → files[]. That direction alone is blind to the failure that actually
80
+ # loses a capability: MOVE OR DELETE the entry-point file. Discovery then simply does not see it,
81
+ # every remaining entry is still listed, and the lane goes green while `npm pack` ships one fewer
82
+ # capability. So also walk files[] → disk: anything declared as a capability entry point must exist.
83
+ # (A capability removed from BOTH sides is an intentional deletion and correctly flags nothing.)
84
+ DECLARED=$(node -e '
85
+ const f=JSON.parse(process.argv[1]);
86
+ process.stdout.write(f.filter(p=>/^scripts\/.*_capability\.sh$/.test(p)).join("\n"));
87
+ ' "$FILES_JSON")
88
+ dangling=0
89
+ if [ -n "$DECLARED" ]; then
90
+ while IFS= read -r d; do
91
+ [ -n "$d" ] || continue
92
+ # `-f` alone is not enough, and both gaps were demonstrated (round 5): a SYMLINK satisfies `-f`
93
+ # but `npm pack` does not follow it — the tarball simply omits the file — and a ZERO-BYTE file
94
+ # satisfies every existence test while shipping an empty capability. Existence is the weakest
95
+ # of the three properties; assert all of them.
96
+ if [ ! -e "$d" ]; then
97
+ echo " DANGLING $d (declared in files[], absent on disk)"; dangling=$((dangling+1))
98
+ elif [ -L "$d" ]; then
99
+ echo " SYMLINK $d (npm pack does not follow symlinks — the tarball would omit it)"; dangling=$((dangling+1))
100
+ elif [ ! -f "$d" ]; then
101
+ # A DIRECTORY passes -e, is not a symlink, and `-s` reports non-zero size for it — so the
102
+ # three tests above all agreed a directory was a fine entry point (round 6). Regular-file-ness
103
+ # is the property actually being claimed; assert it rather than three proxies for it.
104
+ echo " NOT-A-FILE $d (declared as an entry point but is not a regular file)"; dangling=$((dangling+1))
105
+ elif [ ! -s "$d" ]; then
106
+ echo " EMPTY $d (zero bytes — ships an entry point that cannot run)"; dangling=$((dangling+1))
107
+ fi
108
+ done <<EOF
109
+ $DECLARED
110
+ EOF
111
+ fi
112
+ declared_n=$(printf '%s\n' "$DECLARED" | grep -c . || true); declared_n=$(( ${declared_n:-0} + 0 ))
113
+ if [ "$declared_n" -eq 0 ]; then
114
+ bad "files[] declares ZERO capability entry points — either the convention changed or the list was gutted; a disk-only check cannot notice that"
115
+ elif [ "$dangling" -eq 0 ]; then
116
+ ok "all $declared_n declared capability entry point(s) exist on disk (move/delete would be caught)"
117
+ else
118
+ bad "$dangling declared capability entry point(s) missing from disk — npm would ship a broken files[] and the disk-side check alone stays green"
119
+ fi
120
+
121
+ # CONTROL — the check must be able to say NO. A checker that only ever prints ok is indistinguishable
122
+ # from a checker that is not looking; assert the negative arm on a name that cannot be in files[].
123
+ if node -e 'const f=JSON.parse(process.argv[1]);process.exit(f.includes(process.argv[2])?0:1)' \
124
+ "$FILES_JSON" "scripts/definitely_not_shipped_$$.sh"; then
125
+ bad "CONTROL DEAD — a nonexistent path reported as shipped; the membership test is not testing"
126
+ else
127
+ ok "control alive — a nonexistent path is correctly reported as not shipped"
128
+ fi
129
+
130
+ echo "----"
131
+ echo "capability entry-point shipping: $pass passed, $fail failed (entries=$n, missing=$missing)"
132
+ [ "$fail" -eq 0 ] || exit 1
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env bash
2
+ # test_count_check_readme_format_lanes.sh — known-pair anchor for the README format override guard
3
+ # in `scripts/count_check.sh`.
4
+ #
5
+ # WHY THIS EXISTS
6
+ # `count_check.sh` is a **mandatory-pass** gate wired into selfcheck → prepublishOnly, pre-commit
7
+ # and CI. Its README check renders a caller-supplied printf template and uses the RESULT as a
8
+ # `grep -qE` pattern. Two ways that goes wrong, both measured 2026-08-12:
9
+ # · a template rendering a NEWLINE turns the pattern into an OR search — a stale README (99/99)
10
+ # PASSED because the trailing fragment "8" matched "Node 18" elsewhere in the file;
11
+ # · `printf` failure and value-swallowing directives (`%.0s`) produced partial patterns, unchecked.
12
+ # The guard that closes this shipped with **zero** anchors in the same commit that added four
13
+ # anchors for a sibling fix — the exact "you can delete the feature and stay green" class that
14
+ # commit was closing. This file is that missing half.
15
+ #
16
+ # SCOPE, stated honestly: these lanes exercise the guard's DECISION LOGIC in isolation (render →
17
+ # inspect → accept/reject), not the whole count_check run. The full script needs a git worktree
18
+ # with a populated plugin tree; a fixture that lacks one fails for an unrelated reason ("0 active
19
+ # fh-meta skills") and every arm goes red for the SAME wrong cause — which is how a first attempt
20
+ # at this test nearly certified a guard that had not been exercised at all.
21
+ set -uo pipefail
22
+
23
+ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
24
+ SUBJECT="$REPO_ROOT/scripts/count_check.sh"
25
+ pass=0; fail=0
26
+ ok() { printf ' \342\234\205 %s\n' "$1"; pass=$((pass+1)); }
27
+ bad() { printf ' \342\235\214 %s\n' "$1"; fail=$((fail+1)); }
28
+
29
+ # The guard block is extracted from the subject rather than restated here: a copy drifts from the
30
+ # thing it claims to verify, which is the divergent-normalizer defect this repo has hit before.
31
+ GUARD=$(awk '/^ README_STR=\$\(printf/,/^ \[ -n "\$README_STR" \] && count_check/' "$SUBJECT")
32
+ if [ -z "$GUARD" ]; then
33
+ bad "guard block not found in count_check.sh — the anchor cannot verify what it cannot locate"
34
+ echo "----"; echo "count_check README-format lanes: $pass passed, $fail failed"; exit 1
35
+ fi
36
+
37
+ probe() { # $1 = template ; echoes rc + a reason token
38
+ local fmt="$1"
39
+ bash -c '
40
+ set -uo pipefail
41
+ total_sk=40; total_ag=8; fail=0
42
+ README_FMT="$1"
43
+ count_check() { echo "ACCEPTED:$3"; }
44
+ '"$GUARD"'
45
+ exit $fail
46
+ ' _ "$fmt" 2>&1
47
+ return $?
48
+ }
49
+
50
+ run() { # $1 = label ; $2 = template ; $3 = expect (accept|reject)
51
+ local out rc; out=$(probe "$2"); rc=$?
52
+ case "$3" in
53
+ accept) if [ "$rc" -eq 0 ] && printf '%s' "$out" | grep -q 'ACCEPTED:'; then
54
+ ok "$1 — accepted (no over-block)"
55
+ else bad "$1 — legitimate template was REJECTED (rc=$rc): $out"; fi ;;
56
+ reject) if [ "$rc" -ne 0 ]; then ok "$1 — rejected (rc=$rc)"
57
+ else bad "$1 — a broken template PASSED the gate: $out"; fi ;;
58
+ esac
59
+ }
60
+
61
+ echo "== count_check README format override — known pair =="
62
+ # Accept arms. Two of them on purpose: the default and a localized one. The whole point of the
63
+ # override is downstream harnesses writing their own wording, so an English-only accept arm would
64
+ # not notice a guard that rejects every non-ASCII template.
65
+ run "A1 default English" '%s skills · %s agents' accept
66
+ run "A2 localized (Korean)" '스킬 %s개 · 에이전트 %s개' accept
67
+ run "A3 markdown decoration" '**%s** skills / **%s** agents' accept
68
+ # Reject arms — each must fail for its OWN reason, not a shared incidental one.
69
+ run "R1 renders a newline" '%s
70
+ %s' reject
71
+ run "R2 swallows a value" '%.0s%s agents' reject
72
+
73
+ echo "----"
74
+ echo "count_check README-format lanes: $pass passed, $fail failed"
75
+ [ "$fail" -eq 0 ] || exit 1