@chrono-meta/fh-gate 1.4.59 → 1.4.60
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-plugin/marketplace.json +2 -2
- package/CATALOG.md +27 -0
- package/CLAUDE.md +61 -88
- package/README.ja.md +8 -7
- package/README.ko.md +7 -6
- package/README.md +7 -6
- package/README.zh.md +5 -5
- package/bin/fh-codex-doctor.js +34 -3
- package/bin/fh-gate.js +17 -5
- package/bin/fh-goal.js +13 -5
- package/bin/fh-run.js +13 -5
- package/knowledge/shared/harness-core/claude_md_gate_details.md +88 -1
- package/package.json +2 -1
- package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-meta/skills/harness-doctor/SKILL.md +109 -10
- package/scripts/count_check.sh +8 -1
- package/scripts/fh-gate.sh +150 -13
- package/scripts/fh-goal.sh +46 -5
- package/scripts/fh-run.sh +11 -0
- package/scripts/selfcheck.sh +40 -10
- package/scripts/test_fh_gate_regressions.sh +208 -0
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "fh-meta",
|
|
14
|
-
"version": "1.4.
|
|
14
|
+
"version": "1.4.60",
|
|
15
15
|
"description": "Hub meta-operations toolkit — 33 skills + 7 agents. New in 1.4.53: `fh-codex-doctor` (npm bin) — Codex adapter drift scanner; reads the documented M1/M2/M3 skill tier map + skill/agent source and reports codex-native/adapter-required/claude-native/unclassified per unit, wired into `npm test`/`prepublishOnly` (fail-closed on unclassified Claude-native primitives). New in 1.4.49: steel-quench gains Step 0.6 Verdict-Invariance Probe (groundedness axis — a load-bearing judged gate's verdict must track behavior, not rubric phrasing; measured flip-count over cross-family paraphrases; arXiv:2605.06161 Policy Invariance anchor); multi_model_sidecar_strategy §Vendor-native harness (a model is strongest in its own vendor CLI — Claude/CC, GPT/codex, Gemini/Antigravity; a universal router degrades all of them, so it stays an autocomplete/QA sidecar, never orchestration); predelete_check.sh fail-closed rewrite; memory-hygiene A-TMA anchor. New in 1.4.48: phantom-quench + steel-quench gain external frontier anchors (arXiv:2607.02052 package-hallucination; arXiv:2607.02057 prompt-coverage-adequacy); README model-flat claim reframed from a per-release point-curve to structural invariants (operation flattens across tiers; depth tier-order fixed within a generation). New in 1.4.47: onboarding step ① surfaces the Mode D companion-store session-start load in the auto-read salience anchor (previously only in the local binding + rules, so a greeting could skip the load). New in 1.4.46: context-doctor command-output axis (route to rtk/proxy for verbose CLI stdout, complementing .claudeignore; risk-gated to token-scarce envs). New in 1.4.41: context-doctor 2026 trigger vocab (context engineering/rot/collapse) + phantom-citation hardening; hub measurement-integrity-checklist (cross-model measurement pre-flight: display-name pin/reps≥3/discriminating probe). New in 1.4.40: install-wizard queryable-wiki scaffold (INDEX + session-start read + R/W/C ingest). New in 1.4.39: auto-decorrelation (cross-family verifier sidecar recruitment) + video-ingest (capability-routed video ingestion). New in 1.4.x: verify-axis check-class taxonomy (mandatory-pass/measured/judged), no-reinvention Tier-0 inventory, 7-class failure taxonomy, Destructive-Op Gate, Wave-T (Temper), tier-floor governance, Mode D Model Notice, FC consent lane, default-Sonnet guidance. New in 1.3.0: public-surface-audit, field-harvest Mode B auto-trigger, 4-axis gate scope ext. Validated cross-CLI: Claude Code, Codex, Gemini.",
|
|
16
16
|
"source": "./plugins/fh-meta"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"name": "fh-commons",
|
|
20
|
-
"version": "1.4.
|
|
20
|
+
"version": "1.4.60",
|
|
21
21
|
"description": "Project-agnostic utility skills — 4 skills (convergence-loop · deliberation · mcp-circuit-breaker · token-budget-gate) + 1 agent (quench-challenger). Domain-independent utilities transplantable into any project.",
|
|
22
22
|
"source": "./plugins/fh-commons"
|
|
23
23
|
}
|
package/CATALOG.md
CHANGED
|
@@ -8,6 +8,33 @@ AI reads this file first when searching past work. Open individual files for det
|
|
|
8
8
|
|
|
9
9
|
<!-- Add entries in reverse date order (newest at top) -->
|
|
10
10
|
|
|
11
|
+
> **Backfill note (2026-07-15):** the 07-12 / 07-13 / 07-14 entries below were reconstructed from git
|
|
12
|
+
> history + the session card's completion log during the 2026-07-15 harness-doctor run, which found that
|
|
13
|
+
> CATALOG had stopped at 07-10 while 30 FH-asset commits landed — i.e. 5 days of work were invisible to
|
|
14
|
+
> the CATALOG-first search protocol (CLAUDE.md §Searching Past Work). They are commit-grounded, but they
|
|
15
|
+
> are *reconstructions*, not first-hand session records: the "Decision/Open" lines carry only what the
|
|
16
|
+
> commits and card state, so a judgment made in-session but never written down is not recoverable here.
|
|
17
|
+
> The gap itself is the lesson — Comprehension Debt accrues silently when the close chain's CATALOG step
|
|
18
|
+
> is skipped.
|
|
19
|
+
|
|
20
|
+
### 2026-07-14 | forge-harness | #incubator, #chamber-run, #emit-criterion, #kill-as-success, #envelope-boundary, #ship-gate, #dominance-benchmark, #degrade-lint, #reconstructed
|
|
21
|
+
**File:** knowledge/shared/harness-core/harness_incubator_doctrine.md · knowledge/shared/harness-core/ship_readiness_gate.md · CLAUDE.md §Envelope-Boundary Discipline · scripts/chamber_run.sh · scripts/chamber_candidate_collect.sh · scripts/degrade_direction_scan.sh · tracks/_chamber/
|
|
22
|
+
The incubator's heaviest day (16 commits, PRs #134–#144). **Chamber runs #5–#8 all KILL** — the ledger's honest record stays 8/8 KILL, 0 EMIT: the chamber *screens*, it has not yet *birthed*. Run #5 (degrade-lint EMIT attempt) produced the **measured EMIT criterion** (net-new ∧ artifact-shaped ∧ precision-adequate — scored 0/5 on real code, 5/5 FP); run #6 (harness-orchestrator) added a **4th EMIT axis** (hub-state-independence) + the graduation-order principle; run #7 (cluster-wizard) forced a chamber-scope clarification and exposed that the company-residency guard was prose-only with no machine field → **#144 promoted `residency`+`generality` to registry-scan schema fields**. Discovery pipeline wired end-to-end: `chamber_candidate_collect.sh` (6 sources → dedup → rank → reinvention screen → G4-KILL seen-filter) + `chamber_run.sh` (7-step resumable runner, budget cap, ≥3-blind-persona gate, Emission Gate, ledger auto-append) — honestly scoped: step-4 persona dispatch stays human/Claude-driven (bash cannot spawn isolated Agents), EMIT terminus is HITL. **Ship-readiness gate** formalized (all-green → formal tag) with **v0.1.0 = honest baseline** (0.x semver carries incomplete status; all-green → v1.0.0; npm 1.4.x is a separate plugin-cache number). **Governance dominance benchmark** rounds 1–2: the decorrelated *stack* (lens ∪ cross-family) scored 8/8 on a hard fail-open set vs 5/8 bare and 6/8 single-lens — the finding being that dominance comes from the architecture, not any single reviewer; two blind-spot classes materialized into `degrade_direction_scan.sh` Probes E (falsy-sentinel→PASS) and F (split-positional-verdict). **CLAUDE.md §Envelope-Boundary Discipline** (#138) shipped always-loaded as the reinvention-reflex counterweight — hold the unfamiliar unfamiliar at the boundary instead of normalizing it onto a known asset (blind Sonnet sim 2/2). Identity ①② corrected 🔴→🟡 (#137) after a collapse-to-hardest-mechanism error.
|
|
23
|
+
- Decision: KILL is a first-class success (it blocks reinvention); v0.1.0 ships honestly rather than claiming all-green; EMIT-worthiness is screened cheaply against the 4 axes before dispatch.
|
|
24
|
+
- Open: first EMIT still 0/8 — the incubator has never birthed; persona convergence on one author remains the top blocker; reps ≥ 3 owed on the single-draw dominance numbers.
|
|
25
|
+
|
|
26
|
+
### 2026-07-13 | forge-harness | #chamber-namespace, #honest-correction, #overclaim, #cross-family, #simulate-first, #reconstructed
|
|
27
|
+
**File:** tracks/_chamber/ · CLAUDE.md (simulate-first branch · §Session-Close ④-b) · knowledge/shared/harness-core/harness_incubator_doctrine.md
|
|
28
|
+
Chamber-run namespace + measured routing baseline established (`tracks/_chamber/`, PR #132-ish range), then **two honest-correction passes on the same day**: the simulate-first branch had been written as though a one-command autonomous simulate→EMIT existed, and ④-b's drift-check prose overclaimed its own scope. Both were downgraded to what actually ships (runner exists; EMIT does not; the ④-b grep flags file co-occurrence, it does not determine topical parity — the parity call is judged). The second pass was a **codex cross-family follow-up** that caught residual overclaim the same-family fix had left behind — the sibling-overclaim catch pattern.
|
|
29
|
+
- Decision: prose describing a capability must match the shipped muscle boundary; "runner exists" ≠ "autonomous emit".
|
|
30
|
+
- Open: (rolled into 07-14's chamber work).
|
|
31
|
+
|
|
32
|
+
### 2026-07-12 | forge-harness | #identity, #intent-machinization, #incubator-doctrine, #rules-diet, #salience, #register-consistency, #readme, #reconstructed
|
|
33
|
+
**File:** knowledge/shared/harness-core/harness_incubator_doctrine.md · knowledge/shared/rules/ (6 relocated) · CLAUDE.md §Voice/Tone · README.md + ko/zh/ja
|
|
34
|
+
Identity day. The operator-forged doctrine — **a harness machinizes intent** (`intent → forge → agreement (HITL) → machinery`), its payoff being that trial-and-error relocates off the human into the harness — was mechanized into `harness_incubator_doctrine.md` + the CLAUDE.md §Identity paragraph: FH is the **meta-harness and nursery** that incubates field harnesses in its own sandbox and emits them, operating over other harnesses as **compose ∪ disrupt** (core invariants never melt). **Rules diet** (the day's biggest measured win): 6 always-loaded protocol rules relocated `.claude/rules/` → `knowledge/shared/rules/` with pointers staying in CLAUDE.md — always-loaded footprint 106.5k → 81k chars (operator-measured −18.2k resident on a fresh top-level session; the measurement is only valid from a project-cwd top-level `/context`, not an Agent-View window). **Register-consistency** promoted into public CLAUDE.md §Voice/Tone after an Opus session drifted into 존댓말 — the rule had lived only in memory, and memory-only placement is what let it miss. Onboarding translation-guard fixed (the welcome line must be a plain native translation, not an invented coinage). READMEs reframed (solar-system framing + 4-pillar section) across 4 languages. Releases v1.4.53–v1.4.56.
|
|
35
|
+
- Decision: rules relocation over compression — governance-semantic split, pointers stay; salience rules that miss when memory-only get promoted to always-loaded.
|
|
36
|
+
- Open: the rules diet landed at 81k, barely over the 80k M-tier footprint threshold, and regrew to 85k within 3 days (see 2026-07-15).
|
|
37
|
+
|
|
11
38
|
### 2026-07-10 | forge-harness | #sonnet-floor, #doctrine, #loop-engineering, #tier-census, #cross-family, #pre-commit-gate, #dispatch-first
|
|
12
39
|
**File:** knowledge/shared/harness-core/sonnet_floor_doctrine.md · knowledge/shared/harness-core/loop_engineering.md
|
|
13
40
|
Encoded the operator-declared **Sonnet-Floor Doctrine** (base ops 100% Sonnet-runnable; tier-gated capability = defect; escalation = dispatch, never substrate; depth ladder = effort→dispatch→anchored-Sonnet) as a canonical axiom node, plus **loop_engineering.md** (5-question design-time discipline + FH loop inventory MECH/PROSE census + evidence-threshold hardening backlog). Cross-family evolution pass: codex gpt-5.5 xhigh repo census (T1 tier refs / T2 loop legs / T3 contradictions) + agy Gemini 3.1 Pro breadth (pattern-level only, zero citations imported — phantom-risk URLs). All 6 identified availability-gates fixed: pre-commit Axis-2 gains a **sonnet-floor lane** (anchor-required, R-tier auto-queue, 8/8 regression fixtures in scripts/test_marker_floor_lanes.sh), self_evolution weekly dead-end recast dispatch-first, Mode D notice re-directed (keep Sonnet + dispatch primary), canary opus-judge → Sonnet-governor+anchor, verify-bidirectional "never stay at sonnet" fixed, 9 SKILL.md `model: opus` hard pins retired (session-inherit). Trust-floors tightened to run-first/ask-last (full Sonnet autonomy; gates stay). Sonnet blind sims: 2 dispatched, 1 salience miss caught (loop-stub enumeration) → hardened → re-sim PASS.
|
package/CLAUDE.md
CHANGED
|
@@ -277,20 +277,20 @@ Record sim results in the Axes 2–3 marker + sub-agent invocation log.
|
|
|
277
277
|
> headless `claude -p --model` fallback when in-session model-pin is unavailable, the saturation-disguise
|
|
278
278
|
> retry (compact-then-retry once), and the credit-pool caveat — read when a model-pinned dispatch fails.
|
|
279
279
|
|
|
280
|
-
**Measurement-integrity pre-flight
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
280
|
+
**Measurement-integrity pre-flight**: when the sim/dispatch is a *cross-model measurement* (pinned to a
|
|
281
|
+
tier, comparing model behaviors, or feeding a published claim), **the instrument must be verified before
|
|
282
|
+
the measurement is trusted**.
|
|
283
|
+
|
|
284
|
+
> **Detail**: See `knowledge/shared/harness-core/measurement-integrity-checklist.md` — pin the display
|
|
285
|
+
> name not a slug (silent fallback to a weaker model is a measured failure) · reps ≥ 3 on any
|
|
286
|
+
> borderline/contested verdict (single draw = noise) · use a discriminating identity probe (a generic
|
|
287
|
+
> "OK" proves nothing about which model answered) — read **before** running any cross-model measurement.
|
|
287
288
|
|
|
288
289
|
**Floor-tier canary (optional pre-screen — token-free, *below* the Sonnet sim)**: a local model ≤ Sonnet
|
|
289
|
-
can blind-pre-screen a salience-dependent edit
|
|
290
|
-
a PASS adds cheap floor confidence and you still run the Sonnet sim; a FAIL never blocks alone. The
|
|
291
|
-
verdict stays with the **Sonnet-or-higher governor bound to a mechanical anchor**
|
|
292
|
-
|
|
293
|
-
no weak-local-judge regression of the judge-robustness principle (mechanical anchor over judge-only verdict).
|
|
290
|
+
can blind-pre-screen a salience-dependent edit before the Sonnet dispatch is spent. **Canary, NOT gate**:
|
|
291
|
+
a PASS adds cheap floor confidence and you still run the Sonnet sim; a FAIL never blocks alone. The
|
|
292
|
+
terminal verdict stays with the **Sonnet-or-higher governor bound to a mechanical anchor** — **no
|
|
293
|
+
judge-only path**, no weak-local-judge regression of the judge-robustness principle.
|
|
294
294
|
|
|
295
295
|
> **Detail**: See `knowledge/shared/harness-core/claude_md_gate_details.md §Floor-Tier-Canary` — the local
|
|
296
296
|
> model/panel options, the blind-probe procedure, dogfood evidence, and the FAIL-triage (real salience gap
|
|
@@ -308,13 +308,14 @@ no weak-local-judge regression of the judge-robustness principle (mechanical anc
|
|
|
308
308
|
**Cross-family complement (Axis 2, autonomous when consented)**: `steel-quench` dispatches in-session at the
|
|
309
309
|
session tier — **same family** as the governor, so it shares the governor's blind spots. For a **load-bearing**
|
|
310
310
|
change (gates · irreversible-surface code · doctrine), `auto-decorrelation` is the standing cross-family
|
|
311
|
-
verifier:
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
311
|
+
verifier: it recruits ≥1 **different-family** auditor when the sidecar panel is discoverable, and degrades
|
|
312
|
+
honestly to single-session when none is. **Autonomous once the operator has consented** (one-time, in the
|
|
313
|
+
UAP — `[[user_adaptation_profile]]`); the governor keeps the terminal verdict and **source-grounds** every
|
|
314
|
+
sidecar finding before acting on it (`[[feedback_judge_robustness_mechanical_anchor]]`).
|
|
315
|
+
|
|
316
|
+
> **Detail**: See `knowledge/shared/harness-core/claude_md_gate_details.md §Cross-Family-Complement` — the
|
|
317
|
+
> UAP sidecar mapping (which family for which task class) and the 2026-06-27 dogfood evidence — read when
|
|
318
|
+
> recruiting or configuring a cross-family auditor.
|
|
318
319
|
|
|
319
320
|
### Mode D Model Notice (fires once, at the same trigger as this gate)
|
|
320
321
|
|
|
@@ -334,22 +335,17 @@ advisory) is governed separately by `capability_escalation_consent.md`.
|
|
|
334
335
|
The 4-axis gate above fires on **FH asset** changes. But the correlated blind spot it guards —
|
|
335
336
|
*"when a verdict surface cannot mechanically ground its judgment, it defaults toward PASS instead
|
|
336
337
|
of safe-fail"* — is **model-family-level, not FH-specific**. It lives in any load-bearing code the
|
|
337
|
-
AI writes, including **mapped field projects** (qasp · the-bible · pmh)
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
**prose-specified verdict logic grants discretion; discretion's degrade direction is unconstrained
|
|
342
|
-
(→ optimistic PASS); same-family reviewers share the author's optimistic reading and miss it.**
|
|
338
|
+
AI writes, including **mapped field projects** (qasp · the-bible · pmh), so those changes get the
|
|
339
|
+
**same cross-family adversarial gate** as FH's own assets. Root principle: **prose-specified verdict
|
|
340
|
+
logic grants discretion; discretion's degrade direction is unconstrained (→ optimistic PASS);
|
|
341
|
+
same-family reviewers share the author's optimistic reading and miss it.**
|
|
343
342
|
|
|
344
343
|
**Trigger (per changed file — grep-assisted, salience-dependent, no field hook)**: an AI-authored
|
|
345
344
|
change to a **load-bearing field surface** — a function returning a **verdict/gate enum or exit code** (PASS/FAIL/BLOCK/allow/deny),
|
|
346
345
|
an **irreversible-op** path (publish/delete/history-rewrite), or a **safety invariant** (the-bible
|
|
347
346
|
L1 floor, qasp verdict-binding, a pre-push/pre-commit hook). File+symbol based (grep the diff for a
|
|
348
|
-
verdict-enum return / gate exit / safety-marked function) —
|
|
349
|
-
|
|
350
|
-
projects, so an agent under merge pressure can still under-trigger (unmarked safety logic,
|
|
351
|
-
boolean-return gate helpers, config-driven allow/deny, shell/CI irreversible paths escape the grep).
|
|
352
|
-
The under-trigger residual is named honestly in the detail doc — it is not claimed to be airtight.
|
|
347
|
+
verdict-enum return / gate exit / safety-marked function) — a **strong-advisory grep trigger, not a
|
|
348
|
+
hook**, so under-trigger is a real residual, not an airtight claim.
|
|
353
349
|
|
|
354
350
|
**Gate (before merge, not after)**:
|
|
355
351
|
1. **Degrade-direction lint** (mechanical pre-screen — `scripts/degrade_direction_scan.sh`): flags
|
|
@@ -372,14 +368,11 @@ The under-trigger residual is named honestly in the detail doc — it is not cla
|
|
|
372
368
|
below gate **the act** of publish/delete/rewrite — disjoint by role and by location, no double-gate.)*
|
|
373
369
|
|
|
374
370
|
**Degrade direction — cross-family unavailable is NOT a silent same-family pass** (the gate's own
|
|
375
|
-
standard
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
proceeds only under an **explicit, logged same-family-only acknowledgment** — never a silent
|
|
381
|
-
same-family pass. This **overrides** the delegated skill's default degrade for this surface,
|
|
382
|
-
consistent with §Irreversibility Surface-Class Degrade Invariant (applicable-but-tooling-down ≠ free skip).
|
|
371
|
+
standard): if no different-family auditor is reachable, the gate marks the change **NOT-CONVERGED**
|
|
372
|
+
and either blocks the autonomous merge / asks the operator, or proceeds only under an **explicit,
|
|
373
|
+
logged same-family-only acknowledgment**. This **overrides** the delegated skill's default
|
|
374
|
+
silent-degrade for this surface, consistent with §Irreversibility Surface-Class Degrade Invariant
|
|
375
|
+
(applicable-but-tooling-down ≠ free skip).
|
|
383
376
|
|
|
384
377
|
**Residency**: sanitize company code (redact vendor/domain literals) before any external-family
|
|
385
378
|
dispatch; domain data never leaves. **Autonomy**: autonomous once the operator has consented (UAP),
|
|
@@ -390,8 +383,11 @@ converge loop *before* it is Done. Autonomy floor (§Floor governance): the skip
|
|
|
390
383
|
trusted only at opus-tier+; below-floor RUNS the review by default (run-first, ask-last — asks only
|
|
391
384
|
when no runnable path exists), never silently skips (sonnet_floor_doctrine.md §Autonomy at Sonnet).
|
|
392
385
|
|
|
393
|
-
> **Detail
|
|
394
|
-
>
|
|
386
|
+
> **Detail**: See `knowledge/shared/harness-core/field_verdict_crossfamily_gate.md` — the discretion
|
|
387
|
+
> principle, the four-faces failure signature, why same-family review misses it, the gate mechanics,
|
|
388
|
+
> the n=7 qasp field evidence incl. the **9 default-toward-PASS holes across 3 harnesses** (measured
|
|
389
|
+
> 2026-07-03), the named under-trigger residuals, and autonomous-loop baking — read when applying or
|
|
390
|
+
> auditing this gate.
|
|
395
391
|
|
|
396
392
|
## Field-Harness Diagnostic — "진단해줘 / 개선해줘" on a mapped project (compose → rank → HITL)
|
|
397
393
|
|
|
@@ -559,32 +555,23 @@ not marketplace-gate alone:
|
|
|
559
555
|
`LICENSE`/`README` contains a **private harness name or internal codename** · **module paths encode
|
|
560
556
|
internal acronyms**.
|
|
561
557
|
|
|
562
|
-
**Hook coverage — three distinct actions
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
(staged added lines vs the gitignored `.public-surface-patterns
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
fail-closed if patterns/file-set unresolved, if the parse looks partial, **or if the gitignored operator
|
|
574
|
-
override is absent** — defaults-only would otherwise green-PASS a HIGH company literal on a fresh clone / CI).
|
|
575
|
-
**Named residuals (it is a denylist on the npm CLI, not a universal secret-scanner)**: (i) `npm publish
|
|
576
|
-
--ignore-scripts` / a CI `.npmrc ignore-scripts=true` / `pnpm`/`yarn publish` **skip the lifecycle hook** —
|
|
577
|
-
route publishes through `npm run release` or an explicit CI scan step; (ii) it scans only the **loaded
|
|
578
|
-
patterns**, so an **un-patterned secret shape** (an API key the patterns don't describe) still ships; (iii) on
|
|
579
|
-
a runner without the gitignored override it is defaults-only unless populated; (iv) it scans **working-tree
|
|
580
|
-
content, not the final tarball bytes** — benign here (content-neutral lifecycle: prepare=chmod, no prepack)
|
|
581
|
-
but re-open if a content-generating publish lifecycle is added (cross-family audit 2026-06-27). So of the Pre-Publish surface,
|
|
582
|
-
**(b) commit-time and (c) npm-publish are mechanized** (with the residuals above); only **(a) separate-repo
|
|
583
|
-
go-public stays genuinely un-hookable** (prose + checklist).
|
|
558
|
+
**Hook coverage — three distinct actions, two of them mechanized**:
|
|
559
|
+
|
|
560
|
+
| Action | Enforcement |
|
|
561
|
+
|---|---|
|
|
562
|
+
| **(a) repo-go-public** (`gh repo create --public` · visibility flip · first push to a new public remote) | **Un-hookable** — separate repo, no hook here sees it. Stays **AI-behavioral** (the proactive trigger below) + the portable `templates/PRE-PUBLISH-CHECKLIST.md`. |
|
|
563
|
+
| **(b) committing operator-private tokens into public-tracked content of THIS repo** (= an effective publish of that content) | **Mechanized** — pre-commit confidentiality scan, staged added lines vs the gitignored `.public-surface-patterns`. HIGH/MED block; `PUBLIC_SURFACE_OK=1` overrides + logs. |
|
|
564
|
+
| **(c) `npm publish`** | **Mechanized** — `scripts/public_surface_scan_files.sh` via `prepublishOnly`, scanning the full content of the exact published file set. HIGH/MED block; same override + log; fail-closed on unresolved patterns/file-set. |
|
|
565
|
+
|
|
566
|
+
So only **(a) stays genuinely un-hookable** — that is where this gate's prose is the only floor, which is
|
|
567
|
+
why the proactive trigger matters. (b) and (c) are denylists on their own paths, **not** universal
|
|
568
|
+
secret-scanners: they carry named residuals.
|
|
584
569
|
|
|
585
570
|
> **Detail**: See `knowledge/shared/harness-core/claude_md_gate_details.md §Pre-Publish-Hook-Coverage` — the
|
|
586
|
-
> two-layer pattern (literals only in the gitignored source), honest scope
|
|
587
|
-
> (
|
|
571
|
+
> two-layer pattern (literals only in the gitignored source), honest scope, the full named-residual list for
|
|
572
|
+
> (b) and (c) (`--ignore-scripts` / non-npm clients · un-patterned secret shapes · override-not-populated ·
|
|
573
|
+
> worktree-vs-tarball bytes), and the PR #109 (`fh_signal_2026-06-17` Wave 4) / phantom-gate origin — read
|
|
574
|
+
> when configuring or auditing the scan, or before relying on it as a floor.
|
|
588
575
|
|
|
589
576
|
---
|
|
590
577
|
|
|
@@ -607,36 +594,22 @@ force-push, scrub of tracked history, bulk deletion of session records / tracks
|
|
|
607
594
|
strongest available tier (floor semantics, §Tier-floor); a below-floor pass is provisional.
|
|
608
595
|
3. **Destroy** only what passed — REVIEW blocks a scripted delete chain (script exits 1).
|
|
609
596
|
|
|
610
|
-
**Mechanical floor (pre-push hook — git-side surfaces)**:
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
tag/notes deletes always block) and **blocks** unless `DESTRUCTIVE_OP_OK=1` (an explicit, logged operator
|
|
617
|
-
acknowledgment — used *after* enumerate + recover) is set. The verdict is load-bearing (a merged-branch
|
|
618
|
-
cleanup passes; a silent-loss CHECK does not), so this is the enumerate as a mechanical floor, not prose.
|
|
619
|
-
**What it does and does NOT close (honest)**: it closes the **honest-weak-model** gap — an agent that
|
|
620
|
-
simply *forgot* the prose gate is now mechanically stopped. It does **not** close the **injected/adversarial**
|
|
621
|
-
gap: an agent under instruction can set the override or `--no-verify`, and a client-side hook is readable
|
|
622
|
-
and bypassable by design. The hard floor for the adversarial case is **server-side branch protection**
|
|
623
|
-
(GitHub *Restrict deletions* / *Restrict force pushes*) — this hook is the honest-model floor, branch
|
|
624
|
-
protection is the hard floor. **Scope**: covers only git pushes *from a hook-installed repo* (`npm publish`
|
|
625
|
-
is mechanized separately via `prepublishOnly` — see §Pre-Publish Hook coverage (c)); the remaining non-git
|
|
626
|
-
surface — a separate-repo `gh repo create --public` / visibility flip — is genuinely un-hookable and stays
|
|
627
|
-
prose + `PRE-PUBLISH-CHECKLIST.md`. **Portability**: bash-3.2 safe (macOS
|
|
628
|
-
default `/bin/bash`); the original draft used a bash-4 associative array that crashed fail-OPEN on 3.2 —
|
|
629
|
-
caught in test, a portability defect class worth noting.
|
|
597
|
+
**Mechanical floor (pre-push hook — git-side surfaces)**: at *push* time, **remote branch/ref deletion**
|
|
598
|
+
and **force / non-fast-forward push** are enforced **mechanically** by `templates/.git-hooks/pre-push` —
|
|
599
|
+
it runs the per-ref verdict above and **blocks** unless `DESTRUCTIVE_OP_OK=1` (an explicit, logged operator
|
|
600
|
+
acknowledgment, used *after* enumerate + recover). It closes the **honest-weak-model** gap (a forgotten
|
|
601
|
+
prose gate is now stopped); it does **not** close the injected/adversarial one — the hard floor there is
|
|
602
|
+
**server-side branch protection**. Non-git surfaces are out of its scope.
|
|
630
603
|
|
|
631
604
|
**Degrade direction**: per the Surface-Class Degrade Invariant above, if `predelete_check.sh` is missing
|
|
632
605
|
or errors, this irreversible surface **fails closed** — the pre-push hook blocks (enumerate by hand or
|
|
633
606
|
take the explicit `DESTRUCTIVE_OP_OK=1` override); a tooling-down enumerate step never silently degrades
|
|
634
607
|
into "just delete it."
|
|
635
608
|
|
|
636
|
-
>
|
|
637
|
-
>
|
|
638
|
-
>
|
|
639
|
-
>
|
|
609
|
+
> **Detail**: See `knowledge/shared/harness-core/claude_md_gate_details.md §Destructive-Op-Hook-Coverage`
|
|
610
|
+
> — the per-ref verdict mechanics, what the hook does/does not close (honest scope + adversarial residual),
|
|
611
|
+
> the bash-3.2 portability defect class, and the 2026-06-10 origin incident — read when auditing or
|
|
612
|
+
> configuring the pre-push gate.
|
|
640
613
|
|
|
641
614
|
---
|
|
642
615
|
|
package/README.ja.md
CHANGED
|
@@ -129,13 +129,14 @@ Project B ──→ CLAUDE.md でハブを接続
|
|
|
129
129
|
|
|
130
130
|
スケールが第二の要点です。**スキル · エージェント · プラグイン**は1つの道具です。**ハーネス**は一段上 —
|
|
131
131
|
1つの*星 (star)* です: あるプロジェクトの道具 · ルール · ゲート · 記憶が、1つの働く体へと束ねられたもの。
|
|
132
|
-
**forge-harness
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
132
|
+
**forge-harness はその星たちが暮らす銀河です**: 複数のハーネスを共通の床の上に束ねてドリフトを防ぎ、
|
|
133
|
+
散り散りになる代わりに共に進化させます。
|
|
134
|
+
|
|
135
|
+
この銀河はただの容れ物ではありません。FH はフィールドハーネスを**自らのサンドボックス内で
|
|
136
|
+
シミュレーションとして走らせることができ** — 1回あたりは高くつきますが、試行錯誤が一箇所に集まり
|
|
137
|
+
複利で積み上がるため総コストは安くなります — シミュレーションが検証されれば、その
|
|
138
|
+
プロジェクトを独立した特化ハーネスとして**送り出します (EMIT)**。これが目指す目標です。実際には、
|
|
139
|
+
4つの方法で働きます:
|
|
139
140
|
|
|
140
141
|
**① 組み立て (Assemble)** — FH はハーネスの*クラスター*を最適なトークンコストで運用し、プロジェクトに合う
|
|
141
142
|
ハーネスを手に握らせます。スキルを1つずつ配線するのではなく、**ハーネス**を — そのプラグイン · スキル ·
|
package/README.ko.md
CHANGED
|
@@ -129,12 +129,13 @@ Project B ──→ CLAUDE.md에서 허브 연결
|
|
|
129
129
|
|
|
130
130
|
스케일이 두 번째 핵심입니다. **스킬 · 에이전트 · 플러그인**은 하나의 도구입니다. **하네스**는 한 급 위 —
|
|
131
131
|
하나의 *별(star)*입니다: 한 프로젝트의 도구 · 규칙 · 게이트 · 기억이 하나의 작동하는 몸으로 묶인 것.
|
|
132
|
-
**forge-harness는 그 별들이 사는
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
132
|
+
**forge-harness는 그 별들이 사는 은하계입니다**: 여러 하네스를 공통 바닥 위에 묶어 드리프트를 막고,
|
|
133
|
+
흩어지는 대신 함께 진화하게 합니다.
|
|
134
|
+
|
|
135
|
+
이 은하계는 담는 그릇에 그치지 않습니다. FH는 현장 하네스를 자기 샌드박스 안에서 **시뮬레이션으로
|
|
136
|
+
돌려보고** — 한 번 돌리는 값은 비싸도 시행착오가 한곳에 모여 복리로 쌓이므로 총비용은 쌉니다 —
|
|
137
|
+
검증이 끝나면 그 프로젝트를 독립된 특화 하네스로 **내보냅니다(EMIT)**. 이것은 지향하는 목표입니다.
|
|
138
|
+
구체적으로는 네 가지 방식으로 작동합니다:
|
|
138
139
|
|
|
139
140
|
**① 조립(Assemble)** — FH는 하네스의 *클러스터*를 최적 토큰 비용으로 운용하며, 프로젝트에 맞는
|
|
140
141
|
하네스를 손에 쥐어줍니다. 스킬을 하나하나 배선하는 게 아니라, **하네스**를 — 그 플러그인 · 스킬 ·
|
package/README.md
CHANGED
|
@@ -131,12 +131,13 @@ attention is spent only where a change is irreversible.
|
|
|
131
131
|
|
|
132
132
|
Scale is the second point. A **skill, agent, or plugin** is a tool. A **harness** is a level up — a *star*:
|
|
133
133
|
one project's tools, rules, gates, and memory bound into a single working body. **forge-harness is the
|
|
134
|
-
galaxy those stars live in
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
134
|
+
galaxy those stars live in**: it binds many harnesses onto a shared floor to prevent drift, and lets
|
|
135
|
+
them evolve together instead of scattering.
|
|
136
|
+
|
|
137
|
+
This galaxy is more than a container. FH can run a field harness **in simulation inside its own
|
|
138
|
+
sandbox** — expensive per run, cheaper in total, because the trial-and-error pools in one place and
|
|
139
|
+
compounds — and when the simulation holds, it **emits** the project as an independent, specialized
|
|
140
|
+
harness. That is the goal it is built toward. In practice it works in four ways:
|
|
140
141
|
|
|
141
142
|
**① Assemble** — FH runs a *cluster* of harnesses at optimized token cost and hands you the right one for
|
|
142
143
|
the project. You don't wire up skills one by one; you get a **harness** — its plugins, skills, and agents
|
package/README.zh.md
CHANGED
|
@@ -127,11 +127,11 @@ Project B ──→ 在 CLAUDE.md 中连接中枢
|
|
|
127
127
|
|
|
128
128
|
规模是第二个重点。**技能、agent 或插件** 是一个工具。**框架** 高出一级 —— 是一颗 *星*:
|
|
129
129
|
一个项目的工具、规则、门禁与记忆,绑成一个运作的整体。**forge-harness 就是这些星所栖居的
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
130
|
+
星系**:它把众多框架绑定在共享的下限之上以防止漂移,并让它们一起演化而不是四散。
|
|
131
|
+
|
|
132
|
+
这个星系不只是容器。FH 可以在自己的沙箱里**以仿真方式跑一个现场框架** —— 单次昂贵,总体
|
|
133
|
+
更便宜,因为试错汇聚在一处并复利累积 —— 当仿真验证通过,它就把该项目**输出 (emit)** 为一个
|
|
134
|
+
独立的、特化的框架。这就是它所朝向的目标。实际上,它以四种方式运作:
|
|
135
135
|
|
|
136
136
|
**① 组装 (Assemble)** —— FH 以优化后的 token 成本运行一整 *簇* 框架,并把最合适的那个交到你手上。
|
|
137
137
|
你不是一个个去接线技能;你得到的是一个 **框架** —— 连同它的插件、技能与 agent —— 已按需组装好。
|
package/bin/fh-codex-doctor.js
CHANGED
|
@@ -117,15 +117,25 @@ function extractBacktickNames(line) {
|
|
|
117
117
|
return names;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
// The tier table IS this doctor's instrument. If it cannot be read or parsed, every skill
|
|
121
|
+
// comes back documentedTier=null, no M1 rule can ever fire, findings is empty, and the report
|
|
122
|
+
// says OK with --strict exiting 0 — a broken instrument reporting "no violations". An
|
|
123
|
+
// unreadable source (permissions, wrong root) and a parse yielding zero rows (the table's
|
|
124
|
+
// markdown drifted, e.g. bold dropped from `| **M1 |`) both land there, so both are reported
|
|
125
|
+
// as an instrument failure rather than a clean bill of health.
|
|
120
126
|
function documentedTiers(root) {
|
|
121
127
|
const sources = [
|
|
122
128
|
path.join(root, 'AGENTS.md'),
|
|
123
129
|
];
|
|
124
130
|
const tiers = new Map();
|
|
125
131
|
const evidence = [];
|
|
132
|
+
const sourceErrors = [];
|
|
126
133
|
for (const source of sources) {
|
|
127
134
|
const text = maybeReadText(source);
|
|
128
|
-
if (!text)
|
|
135
|
+
if (!text) {
|
|
136
|
+
sourceErrors.push(`${rel(root, source)}: missing or unreadable`);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
129
139
|
const lines = text.split('\n');
|
|
130
140
|
lines.forEach((line, index) => {
|
|
131
141
|
const tierMatch = line.match(/\|\s*\*\*(M[123])\b/);
|
|
@@ -137,7 +147,12 @@ function documentedTiers(root) {
|
|
|
137
147
|
}
|
|
138
148
|
});
|
|
139
149
|
}
|
|
140
|
-
|
|
150
|
+
if (tiers.size === 0 && sourceErrors.length === 0) {
|
|
151
|
+
sourceErrors.push(
|
|
152
|
+
`${rel(root, sources[0])}: readable but yielded 0 tier rows — the tier table format drifted`
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
return { tiers, evidence, sourceErrors };
|
|
141
156
|
}
|
|
142
157
|
|
|
143
158
|
function compatDocTierMentions(root, skillNames) {
|
|
@@ -333,7 +348,13 @@ function buildReport(root) {
|
|
|
333
348
|
}
|
|
334
349
|
|
|
335
350
|
return {
|
|
336
|
-
|
|
351
|
+
// INSTRUMENT_ERROR outranks both: without a tier table this run measured nothing, and
|
|
352
|
+
// "measured nothing" must not be reported as OK (nor as DRIFT, which would claim a
|
|
353
|
+
// finding it never made).
|
|
354
|
+
status: docs.sourceErrors.length > 0
|
|
355
|
+
? 'INSTRUMENT_ERROR'
|
|
356
|
+
: findings.some((f) => f.severity === 'HIGH') ? 'DRIFT' : 'OK',
|
|
357
|
+
instrumentErrors: docs.sourceErrors,
|
|
337
358
|
root,
|
|
338
359
|
counts,
|
|
339
360
|
agentCards: loadAgentCards(root),
|
|
@@ -411,6 +432,16 @@ function main() {
|
|
|
411
432
|
} else {
|
|
412
433
|
printText(report);
|
|
413
434
|
}
|
|
435
|
+
// An instrument failure is not a passing run. It exits non-zero unconditionally — not only
|
|
436
|
+
// under --strict — because the failure mode it guards is precisely a caller reading exit 0
|
|
437
|
+
// as "no drift" when nothing was measured. 10 = harness error, distinct from 1 = drift found.
|
|
438
|
+
if (report.status === 'INSTRUMENT_ERROR') {
|
|
439
|
+
for (const err of report.instrumentErrors) {
|
|
440
|
+
process.stderr.write(`ERROR: tier source unusable — ${err}\n`);
|
|
441
|
+
}
|
|
442
|
+
process.stderr.write(' Nothing was classified, so no drift could be detected. Failing closed.\n');
|
|
443
|
+
process.exit(10);
|
|
444
|
+
}
|
|
414
445
|
if (args.strict && report.counts.findings.HIGH > 0) {
|
|
415
446
|
process.exit(1);
|
|
416
447
|
}
|
package/bin/fh-gate.js
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
|
+
// Thin wrapper — the exit code IS the contract (0 PASS / 1 PENDING / 2 BLOCKED /
|
|
4
|
+
// 3 ESCALATE / 10 harness-error / 11 arg-error / 12 dry-run). execFileSync throws on any
|
|
5
|
+
// non-zero exit, so an uncaught throw collapsed every one of them into node's own exit 1 —
|
|
6
|
+
// i.e. BLOCKED arrived at the caller as PENDING ("proceed with awareness"). Propagate the
|
|
7
|
+
// real status, and fail closed (10) when there is no status to propagate.
|
|
3
8
|
const { execFileSync } = require('child_process');
|
|
4
9
|
const path = require('path');
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
try {
|
|
11
|
+
execFileSync(
|
|
12
|
+
path.join(__dirname, '..', 'scripts', 'fh-gate.sh'),
|
|
13
|
+
process.argv.slice(2),
|
|
14
|
+
{ stdio: 'inherit' }
|
|
15
|
+
);
|
|
16
|
+
} catch (err) {
|
|
17
|
+
if (typeof err.status === 'number') process.exit(err.status);
|
|
18
|
+
// Killed by a signal, or the script could not be spawned at all: no verdict exists.
|
|
19
|
+
console.error(`fh-gate: could not run the gate (${err.signal || err.code || err.message}) — failing closed`);
|
|
20
|
+
process.exit(10);
|
|
21
|
+
}
|
package/bin/fh-goal.js
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
|
+
// Thin wrapper — propagate the script's real exit status. execFileSync throws on non-zero,
|
|
4
|
+
// so an uncaught throw collapsed every distinct status into node's own exit 1.
|
|
3
5
|
const { execFileSync } = require('child_process');
|
|
4
6
|
const path = require('path');
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
try {
|
|
8
|
+
execFileSync(
|
|
9
|
+
path.join(__dirname, '..', 'scripts', 'fh-goal.sh'),
|
|
10
|
+
process.argv.slice(2),
|
|
11
|
+
{ stdio: 'inherit' }
|
|
12
|
+
);
|
|
13
|
+
} catch (err) {
|
|
14
|
+
if (typeof err.status === 'number') process.exit(err.status);
|
|
15
|
+
console.error(`fh-goal: could not run (${err.signal || err.code || err.message}) — failing closed`);
|
|
16
|
+
process.exit(10);
|
|
17
|
+
}
|
package/bin/fh-run.js
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
|
+
// Thin wrapper — propagate the script's real exit status. execFileSync throws on non-zero,
|
|
4
|
+
// so an uncaught throw collapsed every distinct status into node's own exit 1.
|
|
3
5
|
const { execFileSync } = require('child_process');
|
|
4
6
|
const path = require('path');
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
try {
|
|
8
|
+
execFileSync(
|
|
9
|
+
path.join(__dirname, '..', 'scripts', 'fh-run.sh'),
|
|
10
|
+
process.argv.slice(2),
|
|
11
|
+
{ stdio: 'inherit' }
|
|
12
|
+
);
|
|
13
|
+
} catch (err) {
|
|
14
|
+
if (typeof err.status === 'number') process.exit(err.status);
|
|
15
|
+
console.error(`fh-run: could not run (${err.signal || err.code || err.message}) — failing closed`);
|
|
16
|
+
process.exit(10);
|
|
17
|
+
}
|