@chrono-meta/fh-gate 1.4.36 → 1.4.38
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.
- package/CLAUDE.md +15 -3
- package/README.md +18 -1
- package/package.json +1 -1
- package/plugins/fh-meta/skills/corpus-grounding-expander/SKILL.md +56 -0
- package/plugins/fh-meta/skills/harness-doctor/SKILL.md +14 -0
- package/plugins/fh-meta/skills/persona-roster-expander/SKILL.md +67 -0
- package/plugins/fh-meta/skills/steel-quench/SKILL.md +3 -2
- package/scripts/selfcheck.sh +7 -29
package/CLAUDE.md
CHANGED
|
@@ -373,6 +373,8 @@ Proposal format: `"I see [X]. Want me to run /[skill] to [one-line description]?
|
|
|
373
373
|
| "research this deeply", "survey the literature", "comprehensive analysis", "deep research", "look this up thoroughly", "조사해줘", "리서치" (general topic research, not trend-scan) | **Deep-Research Capability Ladder** (`knowledge/shared/harness-core/deep_research_capability_ladder.md`) — route to the highest available rung: built-in `/deep-research` if present → else Claude `WebSearch`+`WebFetch` synthesis (tier-sensitive) → `/frontier-digest` only if it's AI/harness trend-scan. No-reinvention: FH routes, does not build a research engine. |
|
|
374
374
|
| "orchestrate agents", "parallel dispatch", "combine skills", "multiple agents" | `/agent-composer` |
|
|
375
375
|
| "run a simulation", "external user perspective", "internal audit", "quality check" | `/sim-conductor` |
|
|
376
|
+
| "broaden the grounded corpus", "add another version of the corpus", "ingest the full source as the grounding axiom", "여러 버전으로 통째로 가져와" (verbatim-relay corpus expansion — fail-closed grounding, no generator) | `/corpus-grounding-expander` |
|
|
377
|
+
| "broaden these personas", "what other voices fit this cast", "map these roles to a decision lens", "페르소나 후보군 더 넓혀" (persona seed → tiered judgment-mapped cast; pairs with `persona-innovator` for naming) | `/persona-roster-expander` |
|
|
376
378
|
| "first install", "FH setup", "wizard", "install-wizard" | `/install-wizard` |
|
|
377
379
|
| "connect a project", "map this project", "link to hub" | `auto_project_mapping.md` (mapping) |
|
|
378
380
|
| "harness-ify this project", "full harness setup", "프로젝트 하네스화", "promote to full harness" | `auto_project_mapping.md §6` (Full-Harness Mode) |
|
|
@@ -518,6 +520,15 @@ harvest-loop Step 0-b uses this file as its source — relying on LLM memory aft
|
|
|
518
520
|
```
|
|
519
521
|
Closing phrase detected ("wrap up", "done", "good work", "end session", etc.)
|
|
520
522
|
→ ① Check git diff + unpushed commits (status snapshot)
|
|
523
|
+
→ ①-b Open-PR sweep — `gh pr list --author @me --state open` (+ `gh search prs --author @me
|
|
524
|
+
--state open` for cross-repo). Classify, **surface-not-auto**: a **self-mergeable** PR
|
|
525
|
+
(operator's own repo, checks green) → *propose merge now* (never auto-merge — HITL); an
|
|
526
|
+
**awaiting-external** PR (other repos / corp review) → *surface for tracking only*. Why here:
|
|
527
|
+
the harness's "마감" ≠ the operator's "마감" — a self-authored PR (PR #111) sat open across
|
|
528
|
+
sessions with un-integrated skills + count drift because no close step surfaced it. Pairs with
|
|
529
|
+
the count-consistency check (which now runs at BOTH the local pre-commit hook AND the plugins/**
|
|
530
|
+
PR-CI merge boundary): the sweep surfaces the PR → merging it → the count-check catches any
|
|
531
|
+
drift at the merge (fh_signal_2026-06-21, gate-locality paired fix).
|
|
521
532
|
→ ② If FH assets changed: harvest-loop
|
|
522
533
|
→ ③ Sync local/gitignored session state to your durable companion store, if you keep one
|
|
523
534
|
→ ④ Memory hygiene — update stale entries + record new session findings
|
|
@@ -540,9 +551,10 @@ Closing phrase detected ("wrap up", "done", "good work", "end session", etc.)
|
|
|
540
551
|
→ ⑤ Card update ← ABSOLUTE LAST: must capture ①–④-b outcomes
|
|
541
552
|
→ ⑥ Commit card + push
|
|
542
553
|
```
|
|
543
|
-
**Card-last guard**: ①–④-b must ALL complete before ⑤ runs. Any new
|
|
544
|
-
during ①–④ (new commits, model changes, new findings)
|
|
545
|
-
written mid-sequence and then left open for more work to accumulate
|
|
554
|
+
**Card-last guard**: ①–④-b (incl. ①-b open-PR sweep) must ALL complete before ⑤ runs. Any new
|
|
555
|
+
information produced during ①–④ (new commits from a merged self-PR, model changes, new findings)
|
|
556
|
+
feeds INTO ⑤ — card is never written mid-sequence and then left open for more work to accumulate
|
|
557
|
+
after it.
|
|
546
558
|
|
|
547
559
|
**Mid-session card writes are drafts**: If a task (e.g., a calibration run) internally updates
|
|
548
560
|
the card, that is a draft. The close chain always re-runs ⑤ to capture post-draft activities.
|
package/README.md
CHANGED
|
@@ -182,7 +182,7 @@ All four movements ship. Temper was named before it was built — deliberately (
|
|
|
182
182
|
two more signatures keep it running: `harvest-loop` (each session's lessons become permanent skills) and
|
|
183
183
|
`agent-composer` (orchestrate the dispatch). The other skills wait until you need them — full list below.
|
|
184
184
|
|
|
185
|
-
##
|
|
185
|
+
## 35 skills · 8 agents
|
|
186
186
|
|
|
187
187
|
<details>
|
|
188
188
|
<summary>Full asset activation check</summary>
|
|
@@ -208,6 +208,8 @@ two more signatures keep it running: `harvest-loop` (each session's lessons beco
|
|
|
208
208
|
| `meta-prompt-builder` | Meta prompt design | "Write a prompt for the agent" |
|
|
209
209
|
| `asset-placement-gate` | Hub vs project asset routing | "Should this be shared?" |
|
|
210
210
|
| `cross-ecosystem-synergy-detection` | Cross-tool synergy finder | "Are my tools working together?" |
|
|
211
|
+
| `corpus-grounding-expander` | Multi-version public-domain corpus → verified-axiom grounding store | "Broaden the grounded corpus" |
|
|
212
|
+
| `persona-roster-expander` | Persona seed → tiered, judgment-mapped cast | "Broaden these personas" |
|
|
211
213
|
| `convergence-loop` *(fh-commons)* | N-round convergence loops | "Single-pass seems suspicious" |
|
|
212
214
|
| `token-budget-gate` *(fh-commons)* | Pre-task token cost estimate | "How expensive is this?" |
|
|
213
215
|
| `mcp-circuit-breaker` *(fh-commons)* | MCP tool failure pattern detection | "MCP keeps failing" |
|
|
@@ -283,6 +285,21 @@ recommended only for harness-editing sessions. Details: `docs/OUTPUT_EVIDENCE.md
|
|
|
283
285
|
|
|
284
286
|
If you use external CLIs (Gemini, Codex, `gh copilot`) as sidecars, their costs are billed to their own quota and not visible in CC's token display.
|
|
285
287
|
|
|
288
|
+
### Hardware tiers (local sidecars are optional accelerators)
|
|
289
|
+
|
|
290
|
+
FH needs **no local LLM** — the baseline is whatever runs Claude Code. Local models are *optional*, for
|
|
291
|
+
the canary / cheap-breadth rungs only:
|
|
292
|
+
|
|
293
|
+
| Tier | Spec | Runs locally | What it buys |
|
|
294
|
+
|---|---|---|---|
|
|
295
|
+
| **Minimum** | anything that runs Claude Code | nothing | full methodology + gates; operating FH is ~model-flat (Opus/Sonnet/Haiku 100/97/94) |
|
|
296
|
+
| **Recommended** | laptop-class, ~16GB RAM | one 8B-class quantized model (e.g. an 8B / small Gemma) | a token-free **floor canary** (pre-screen before a metered sim) · offline triage · a cheap-breadth panel arm |
|
|
297
|
+
| **Optional (heavy)** | ~24GB VRAM GPU | a 27–32B model | a *stronger* decorrelation canary |
|
|
298
|
+
|
|
299
|
+
> Local tiers are **canaries, never the terminal verdict** — measured: the floor model missed a subtle
|
|
300
|
+
> adversarial case the frontier caught (and even a 27–32B local scored 1/4 on it). They lower the *cost
|
|
301
|
+
> of breadth*; the verdict stays frontier.
|
|
302
|
+
|
|
286
303
|
---
|
|
287
304
|
|
|
288
305
|
## Multi-Model Sidecar
|
package/package.json
CHANGED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: corpus-grounding-expander
|
|
3
|
+
description: Fetches and normalizes multi-version public-domain corpora into a single verified-axiom store for verbatim-relay grounding, recording per-version license and provenance.
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools: ["Read", "Bash", "WebFetch", "Write"]
|
|
6
|
+
model: sonnet
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# corpus-grounding-expander
|
|
10
|
+
|
|
11
|
+
Builds or broadens the **grounded axiom** a relay system quotes from. When a system's output must be
|
|
12
|
+
constrained to verbatim source (fail-closed grounding), the corpus IS the axiom — this skill expands
|
|
13
|
+
that corpus from multiple public-domain versions so grounding is robust and non-biased (a quote
|
|
14
|
+
verified in ANY version counts), without ever adding a generator.
|
|
15
|
+
|
|
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`.
|
|
19
|
+
|
|
20
|
+
## Triggers
|
|
21
|
+
- "get more sources" / "broaden the grounded corpus"
|
|
22
|
+
- "add another version of the corpus"
|
|
23
|
+
- "ingest the full <source> as the grounding axiom"
|
|
24
|
+
- `/corpus-grounding-expander {source or version}`
|
|
25
|
+
|
|
26
|
+
### Natural Language Triggers (example user phrases)
|
|
27
|
+
- "이 코퍼스 여러 버전으로 통째로 가져와줘"
|
|
28
|
+
- "the grounding DB is just a sample — pull the whole thing, multiple editions"
|
|
29
|
+
- "add a second public-domain edition so we're not biased to one"
|
|
30
|
+
|
|
31
|
+
## Steps
|
|
32
|
+
1. **Source + license check** — identify the public-domain source(s) and confirm each is genuinely
|
|
33
|
+
free to redistribute. Record the license literally (no assumption).
|
|
34
|
+
2. **Fetch (retry-disciplined)** — pull each version; on transient error, backoff-retry before
|
|
35
|
+
declaring the source unavailable (do not silently drop a version).
|
|
36
|
+
3. **Normalize to a single key schema** — map every version to the SAME addressable key
|
|
37
|
+
(e.g. `ref → text`) so cross-version quotation stays aligned. Write per-version files +
|
|
38
|
+
an `_index` recording version id, license, scope, and record count.
|
|
39
|
+
4. **Wire grounding as a union** — the grounding check passes if the quote matches the canonical text
|
|
40
|
+
at that key in ANY version. Never add a path that generates text — grounding is quote-only.
|
|
41
|
+
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).
|
|
43
|
+
|
|
44
|
+
## Done When
|
|
45
|
+
- **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).*
|
|
48
|
+
|
|
49
|
+
## Guards
|
|
50
|
+
- **Grounding, never a generator** — the relay constraint is structural; this skill wires *grounding*.
|
|
51
|
+
- **License-literal** — record the actual license per source; never assume public-domain.
|
|
52
|
+
- **No silent version drop** — a fetch failure is a named residual in the index, not a quiet omission.
|
|
53
|
+
|
|
54
|
+
## Independently executable
|
|
55
|
+
Yes — needs only a network fetch + file write. No dependency on other FH skills (a consumer gate that
|
|
56
|
+
reads the corpus is the system's own, not this skill's).
|
|
@@ -136,9 +136,23 @@ File: {path}
|
|
|
136
136
|
| settings.json JSON syntax error | M-tier |
|
|
137
137
|
| Hooks in settings.json (PostToolUse/Stop) that don't fire in Agent View | S-tier or M-tier |
|
|
138
138
|
| Public-surface FH recommendation or default changed — no N-shot measurement evidence traceable in session records or PR body | S-tier |
|
|
139
|
+
| Pre-commit-adjacent gate reads the working tree (cat/glob of a *tracked* path) with no `--staged`/`--cached`/`git show :` on that path, when its verdict is about *what's committed* | S-tier |
|
|
139
140
|
|
|
140
141
|
Hook divergence verdict: 0 hooks = Normal · 1+ hooks (session-end/Stop) = S-tier · 1+ hooks (PostToolUse file writes or external API) = M-tier (data loss risk in Agent View).
|
|
141
142
|
|
|
143
|
+
**Index-vs-worktree gate lint** (2026-06-21, gate-locality corollary — `[[feedback_gate_locality_principle]]`):
|
|
144
|
+
a gate whose verdict is about *what is being committed* must read the **staged index**
|
|
145
|
+
(`git ls-files --cached` / `git show :path`), not the working tree (`cat`/glob of a tracked file) —
|
|
146
|
+
else it false-PASSes a commit whose staged content ≠ disk (origin: `count_check.sh` read the worktree
|
|
147
|
+
while the pre-commit hook fired on the index; the sibling `regression_guard` already used `--staged`,
|
|
148
|
+
so the lesson existed but was non-local). Mechanical scan — in the pre-commit hook + the scripts it
|
|
149
|
+
invokes (+ `scripts/*-check.sh`): flag a `cat`/`<`/glob read of a tracked path with no
|
|
150
|
+
`--staged`/`--cached`/`git show :` on that path. **Conditional (NOT universal)**: a gate whose verdict is
|
|
151
|
+
about what *runs/deploys* (pre-push tests, build, an auto-fixer that rewrites the worktree) correctly
|
|
152
|
+
reads the working tree — a line-level `# worktree-intentional:` comment suppresses the flag. FP surface
|
|
153
|
+
(isolated-critic): display/log reads and untracked generated files are not violations → keep advisory
|
|
154
|
+
(S-tier, never M).
|
|
155
|
+
|
|
142
156
|
### Step 5. L4 — Connection Diagnosis *(FH only)*
|
|
143
157
|
|
|
144
158
|
- Tracks with no sync in 30+ days → R-tier
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: persona-roster-expander
|
|
3
|
+
description: Expands a named persona seed into a tiered, judgment-mapped cast — tiering each persona by a domain safety rule, mapping each to a decision-lens in the user's vocabulary, then proposing additional voices with sourced anchors.
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools: ["Read", "Grep", "WebSearch", "WebFetch"]
|
|
6
|
+
model: sonnet
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# persona-roster-expander
|
|
10
|
+
|
|
11
|
+
Takes a seed set of named personas and turns it into a usable judgment cast: each persona tiered by a
|
|
12
|
+
domain safety rule, mapped to a concrete decision-lens, plus a few proposed additions filling
|
|
13
|
+
uncovered lenses with real anchors. An innovator-family skill — generative, but bounded by a
|
|
14
|
+
caller-supplied safety rule so the expansion stays faithful to the domain's constraints.
|
|
15
|
+
|
|
16
|
+
> Origin: harvested from the-bible (2026-06-20) — an operator persona seed (priest/nun/angel/devil/
|
|
17
|
+
> God/Jesus/Holy-Spirit/apostles) tiered relay-vs-lens by a relay-safety rule and mapped to
|
|
18
|
+
> engineering-judgment lenses, +4 sourced proposals. See
|
|
19
|
+
> `tracks/_contrib/field_harvest_2026-06-20_gate-locality-and-grounding-capabilities.md`.
|
|
20
|
+
|
|
21
|
+
## Triggers
|
|
22
|
+
- "broaden these personas" / "what other voices fit this cast"
|
|
23
|
+
- "map these roles to a decision lens"
|
|
24
|
+
- "expand the persona roster and keep it faithful to <rule>"
|
|
25
|
+
- `/persona-roster-expander {seed personas + domain + safety rule}`
|
|
26
|
+
|
|
27
|
+
### Natural Language Triggers (example user phrases)
|
|
28
|
+
- "내가 명명한 페르소나 후보군을 더 넓혀줘, 판단 매핑이랑 같이"
|
|
29
|
+
- "take these character archetypes and tell me what engineering judgment each one provides"
|
|
30
|
+
- "add a few more voices that cover a perspective these don't"
|
|
31
|
+
|
|
32
|
+
## Steps
|
|
33
|
+
1. **Collect the seed + the safety rule** — the named personas, the domain (what decisions they
|
|
34
|
+
reflect on), and the caller-supplied **tiering rule** (the hard constraint that decides what each
|
|
35
|
+
persona is allowed to do). Without an explicit safety rule, ask for one — do not invent authority.
|
|
36
|
+
2. **Tier each persona** by the safety rule (e.g. relay/quote-only vs lens/framed-perspective). State
|
|
37
|
+
a one-line justification per persona; the tier caps what the persona may emit.
|
|
38
|
+
3. **Map each to a decision-lens** in the **user's domain vocabulary** — what concrete judgment-
|
|
39
|
+
perspective does this voice provide, and when to invoke it. Pair any adversarial voice with a
|
|
40
|
+
constructive counter-voice (no adversary as the last word).
|
|
41
|
+
4. **Propose 2–4 additions** filling lenses the seed doesn't cover (delegate net-new *name*
|
|
42
|
+
generation to the `persona-innovator` agent — this skill's distinct value is the tiering +
|
|
43
|
+
lens-mapping, not naming). For the strongest 2–3, find a real anchor (a sourced
|
|
44
|
+
example/reference), not a shallow stub.
|
|
45
|
+
5. **Emit the tiered, lens-mapped cast** (named + proposed) as a structured artifact the system can
|
|
46
|
+
load (e.g. `personas.json`).
|
|
47
|
+
|
|
48
|
+
## Done When
|
|
49
|
+
- **Every persona has a tier + lens label + invoke-condition.** *Check class: mandatory-pass (binary — all three fields present per persona).*
|
|
50
|
+
- **Each net-new proposal names a real anchor** (not a stub). *Check class: judged, pair: an anchor-existence check (the cited source/reference resolves).*
|
|
51
|
+
- **The tiering respects the caller's safety rule** (no persona exceeds its tier's allowed emission). *Check class: judged, pair: an adversarial read — find a persona whose mapping lets it act beyond its tier.*
|
|
52
|
+
|
|
53
|
+
## Guards
|
|
54
|
+
- **Caller-supplied safety rule is mandatory** — the skill tiers by the domain's rule, it does not
|
|
55
|
+
invent one (inventing authority is the failure this guard prevents).
|
|
56
|
+
- **Adversary never last** — any devil's-advocate voice is paired with a constructive counter-voice.
|
|
57
|
+
- **Anchors must be real** — a proposal without a resolvable anchor is a stub, not a candidate.
|
|
58
|
+
|
|
59
|
+
## Relationship to `persona-innovator` (agent)
|
|
60
|
+
`persona-innovator` *generates* names, frames, and frontier-absorption signals from scratch. This skill
|
|
61
|
+
takes an **existing seed** and adds the **tier-by-safety-rule + lens-to-domain mapping** — it is the
|
|
62
|
+
*structuring/governance* layer, not the *generator*. When net-new names are needed (Step 4), it
|
|
63
|
+
delegates to `persona-innovator` rather than reinventing generation.
|
|
64
|
+
|
|
65
|
+
## Independently executable
|
|
66
|
+
Yes — needs only the seed + rule + (for anchors) web search. Delegates name *generation* to
|
|
67
|
+
`persona-innovator` when invoked, but has no hard dependency on it for the core tier+lens mapping.
|
|
@@ -135,11 +135,11 @@ Treat the adapter output as the isolated challenger result for Wave 1. This pres
|
|
|
135
135
|
|
|
136
136
|
---
|
|
137
137
|
|
|
138
|
-
## Wave 1 —
|
|
138
|
+
## Wave 1 — 6 Mandatory Attack Angles
|
|
139
139
|
|
|
140
140
|
**Execution principles**: Attacks must be based on real code/files/configs — abstract criticism prohibited.
|
|
141
141
|
Assign severity: **S** (immediate blocker) / **A** (required before deployment) / **B** (improvement recommended).
|
|
142
|
-
Call **fh-commons:quench-challenger** in isolation first (6-axis structural attack); apply
|
|
142
|
+
Call **fh-commons:quench-challenger** in isolation first (6-axis structural attack); apply 6 angles in parallel.
|
|
143
143
|
|
|
144
144
|
Isolation can be achieved by Claude Code `Agent(...)` or by `fh-run --agent fh-commons:quench-challenger` under Codex. Do not run the challenger inline in the same reasoning pass when the attack result gates the defense.
|
|
145
145
|
|
|
@@ -150,6 +150,7 @@ Isolation can be achieved by Claude Code `Agent(...)` or by `fh-run --agent fh-c
|
|
|
150
150
|
| 3 | **Bus factor** | "Single-person dependency — can it operate if that person is absent?" |
|
|
151
151
|
| 4 | **Platform obsolescence** | "Does this structure survive when the external ecosystem expands or changes?" |
|
|
152
152
|
| 5 | **Self-referential structure** | "Is there a closed circuit that evaluates itself by its own criteria?" |
|
|
153
|
+
| 6 | **Gate-locality** | "Is every safety gate readable by the actor that must enforce it? Name any gate defined only in a file/layer the enforcing runtime never loads (e.g. a rule in a Claude-only `CLAUDE.md` that a Gemini/Codex orchestrator reading `AGENTS.md` never sees; a provenance check described in a doc but absent from the write path)." (see `knowledge/shared/harness-core/gate_locality_principle.md`) |
|
|
153
154
|
|
|
154
155
|
**S-grade Immediate Human Gate**: If Wave 1 contains 1+ S-grade blocker → pause, surface options (a) proceed to Wave 2 / (b) human review first / (c) abort. Do not silently enter Wave 2 with unreviewed S-grade items.
|
|
155
156
|
|
package/scripts/selfcheck.sh
CHANGED
|
@@ -35,35 +35,13 @@ done
|
|
|
35
35
|
# Count consistency: stated skill/agent counts vs actual directories.
|
|
36
36
|
# Drift class recurred 4x on 2026-06-10 alone (local_fh_context 26, plugin.json "3 agents",
|
|
37
37
|
# README "5 agents", marketplace.json "3 agents") — this makes the check mechanical and permanent.
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
done
|
|
46
|
-
echo "$n"
|
|
47
|
-
}
|
|
48
|
-
count_agents() { ls plugins/"$1"/agents/*.md 2>/dev/null | wc -l | tr -d ' '; }
|
|
49
|
-
|
|
50
|
-
meta_sk=$(count_active fh-meta); meta_ag=$(count_agents fh-meta)
|
|
51
|
-
com_sk=$(count_active fh-commons); com_ag=$(count_agents fh-commons)
|
|
52
|
-
total_sk=$((meta_sk + com_sk)); total_ag=$((meta_ag + com_ag))
|
|
53
|
-
|
|
54
|
-
count_check() { # count_check <label> <file> <expected-string>
|
|
55
|
-
if grep -q "$3" "$2"; then
|
|
56
|
-
echo "PASS count: $1"
|
|
57
|
-
else
|
|
58
|
-
echo "FAIL count: $1 — expected \"$3\" in $2 (actual: fh-meta ${meta_sk}sk/${meta_ag}ag, fh-commons ${com_sk}sk/${com_ag}ag)"
|
|
59
|
-
fail=1
|
|
60
|
-
fi
|
|
61
|
-
}
|
|
62
|
-
count_check "fh-meta plugin.json" plugins/fh-meta/.claude-plugin/plugin.json "${meta_sk} skills + ${meta_ag} agents"
|
|
63
|
-
count_check "fh-commons plugin.json" plugins/fh-commons/.claude-plugin/plugin.json "${com_sk} skills"
|
|
64
|
-
count_check "marketplace.json fh-meta" .claude-plugin/marketplace.json "${meta_sk} skills + ${meta_ag} agents"
|
|
65
|
-
count_check "README header" README.md "${total_sk} skills · ${total_ag} agents"
|
|
66
|
-
count_check "local_fh_context fh-meta" templates/local_fh_context.md "(fh-meta, ${meta_sk})"
|
|
38
|
+
# Logic extracted to scripts/count_check.sh so the SAME check also runs at commit time in
|
|
39
|
+
# the pre-commit hook (shift-left, gated on a skills-dir add/remove — fh_signal_2026-06-21
|
|
40
|
+
# gate-locality gap: the check previously lived only here at the publish boundary, so a
|
|
41
|
+
# skill-adding PR could merge with stale counts undetected until the next publish).
|
|
42
|
+
if ! bash scripts/count_check.sh; then
|
|
43
|
+
fail=1
|
|
44
|
+
fi
|
|
67
45
|
|
|
68
46
|
# Referenced-path existence: backtick-quoted repo-relative file refs in the always-loaded
|
|
69
47
|
# governance surface (CLAUDE.md + .claude/rules/*.md) must exist. Phantom-reference class
|