@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.
@@ -11,13 +11,13 @@
11
11
  "plugins": [
12
12
  {
13
13
  "name": "fh-meta",
14
- "version": "1.4.59",
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.59",
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** (when the sim/dispatch is a *cross-model measurement* pinned to
281
- a specific tier, comparing model behaviors, or feeding a paper/published claim): consult
282
- `knowledge/shared/harness-core/measurement-integrity-checklist.md` first — pin the **display name** not
283
- a slug (silent fallback to a weaker model is a measured failure), take **reps ≥ 3** on any
284
- borderline/contested verdict (single draw = noise), and use a **discriminating** identity probe (a
285
- generic "OK" proves nothing about which model answered). The instrument must be verified before the
286
- measurement is trusted.
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 *before* the Sonnet dispatch is spent. **Canary, NOT gate**:
290
- a PASS adds cheap floor confidence and you still run the Sonnet sim; a FAIL never blocks alone. The terminal
291
- verdict stays with the **Sonnet-or-higher governor bound to a mechanical anchor** (an opus judge is the
292
- dispatch-recommended strengthener, not a requirement `sonnet_floor_doctrine.md`) — **no judge-only path**,
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: when the configured sidecar panel is discoverable it recruits ≥1 **different-family** auditor (per
312
- the UAP mapping e.g. `codex` `gpt-5.5`/xhigh for repo-grounded code/security, `agy`/Gemini for breadth) and
313
- degrades honestly to single-session when none is. **Autonomous once the operator has consented** (one-time,
314
- in the UAP `[[user_adaptation_profile]]`); the governor keeps the terminal verdict and **source-grounds**
315
- every sidecar finding before acting on it (`[[feedback_judge_robustness_mechanical_anchor]]`). Dogfood
316
- 2026-06-27: a cross-family pass caught a HIGH execution-side-effect blind spot the same-family reviewers +
317
- the target-tier sim all shared the decorrelation value made concrete.
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). FH is the meta-harness:
338
- accelerating a field harness *to FH grade* means the field's load-bearing changes get the **same
339
- cross-family adversarial gate** as FH's own assets. Not doing so is the exact gap that shipped **9
340
- default-toward-PASS holes across 3 harnesses undetected** (measured 2026-07-03). Root principle:
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) — but a **strong-advisory grep trigger,
349
- not a hook**: the FH pre-commit gate is FH-internal and deliberately never installed into field
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, dogfood-caught 2026-07-03): if no different-family auditor is reachable, the gate does
376
- **not** fall back to same-family review and proceed that inherits `auto-decorrelation`'s general
377
- *silent-degrade / never-hard-fail*, which is **fail-OPEN** for a load-bearing pre-merge surface
378
- (the gate's entire value is decorrelation; same-family review shares the author's blind spot). It
379
- marks the change **NOT-CONVERGED** and either blocks the autonomous merge / asks the operator, or
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** (discretion principle · 4-face signature · gate mechanics · n=7 qasp evidence):
394
- > `knowledge/shared/harness-core/field_verdict_crossfamily_gate.md`.
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**: **(a) repo-go-public** (`gh repo create --public` / visibility
563
- flip / first push to a new public remote) is irreversible and usually in a **separate repo** — no hook of
564
- *this* repo can catch it, so it stays **AI-behavioral** (proactive trigger below) **+ a portable checklist**
565
- (`templates/PRE-PUBLISH-CHECKLIST.md`). **(b) committing operator-private tokens into public-tracked content
566
- of THIS repo IS an effective publish** — caught mechanically by the pre-commit **confidentiality scan**
567
- (staged added lines vs the gitignored `.public-surface-patterns`; HIGH/MED block, `PUBLIC_SURFACE_OK=1`
568
- overrides + logs). **(c) `npm publish`** is **mechanically gated against the loaded patterns, on the `npm` CLI path with scripts
569
- enabled** by `scripts/public_surface_scan_files.sh` (wired into `prepublishOnly`; also `npm run release` runs
570
- it *outside* the lifecycle). It scans the **full content of the exact npm-published file set** (`npm pack
571
- --dry-run`) *not* just a commit diff so a token committed before the scan existed, or carried in a
572
- `files[]` entry, is caught at the registry boundary (HIGH/MED block, `PUBLIC_SURFACE_OK=1` override + log;
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 + residuals, and the PR #109
587
- > (`fh_signal_2026-06-17` Wave 4) / phantom-gate origin read when configuring or auditing the scan.
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)**: for the surfaces that happen at *push* time
611
- — **remote branch/ref deletion** and **force / non-fast-forward push** (history rewrite)
612
- `templates/.git-hooks/pre-push` enforces this gate **mechanically**, not just as prose. It detects the
613
- destructive refspec on stdin (delete = local SHA all-zeros; force = remote SHA not an ancestor of local),
614
- runs a **per-ref verdict** (branch delete: SAFE = fully merged allowed · CHECK = commits off base, 0
615
- unique paths blocked for a judged look · REVIEW = unique paths → blocked for recovery; force/non-ff and
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
- > Origin: 2026-06-10 branch cleanup — pre-deletion enumeration recovered a parallel session's card
637
- > (weekly-audit completion + #88 merge state) that existed **only on an unmerged branch** with zero
638
- > unique paths: exactly the CHECK class, invisible to "is it merged?" intuition. Deletion without the
639
- > gate destroys live state without anyone noticing.
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 はその星たちが暮らす銀河です** — 複数のハーネスを1つの重力圏に収め、軌道に
133
- とどめ(共通の床、ドリフトなし)、散り散りになる代わりに共に進化させます。そしてこの系はただの
134
- 容れ物ではなく*ゆりかご (nursery)* です: FH はフィールドハーネスを**自らのサンドボックス内で
135
- シミュレーションとして走らせることができ** — 1回あたりは高くつきますが、総コストは安くなります。
136
- 試行錯誤が一箇所に集まり複利で積み上がるからですシミュレーションが検証されれば、その
137
- プロジェクトを独立した特化ハーネスとして**送り出します**。これが目指す目標です。実際には、
138
- その重力は4つのものから生まれます:
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
- 담는 그릇이 아니라 *요람(nursery)*입니다: FH는 현장 하네스를 **자기 샌드박스 안에서 시뮬레이션으로
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** it holds many harnesses in one gravity well, keeps them in orbit (a
135
- shared floor, no drift), and lets them evolve together instead of scattering. And the system is a
136
- *nursery*, not just a container: FH can run a field harness **in simulation inside its own sandbox** —
137
- expensive per run, cheaper in total, because the trial-and-error pools in one place and compounds and
138
- when the simulation holds, it **emits** the project as an independent, specialized harness. That is the
139
- goal it is built toward. In practice the gravity comes from four things:
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
- 一起演化而不是四散。而且这个系不只是容器,更是*育星摇篮 (nursery)*:FH 可以在自己的沙箱里
132
- **以仿真方式跑一个现场框架** —— 单次昂贵,总体更便宜,因为试错汇聚在一处并复利累积 —— 当仿真
133
- 验证通过,它就把该项目**孵化输出**为一个独立的、特化的框架。这就是它所朝向的目标。实际上,
134
- 这份引力来自四件事:
130
+ 星系**:它把众多框架绑定在共享的下限之上以防止漂移,并让它们一起演化而不是四散。
131
+
132
+ 这个星系不只是容器。FH 可以在自己的沙箱里**以仿真方式跑一个现场框架** —— 单次昂贵,总体
133
+ 更便宜,因为试错汇聚在一处并复利累积 —— 当仿真验证通过,它就把该项目**输出 (emit)** 为一个
134
+ 独立的、特化的框架。这就是它所朝向的目标。实际上,它以四种方式运作:
135
135
 
136
136
  **① 组装 (Assemble)** —— FH 以优化后的 token 成本运行一整 *簇* 框架,并把最合适的那个交到你手上。
137
137
  你不是一个个去接线技能;你得到的是一个 **框架** —— 连同它的插件、技能与 agent —— 已按需组装好。
@@ -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) continue;
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
- return { tiers, evidence };
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
- status: findings.some((f) => f.severity === 'HIGH') ? 'DRIFT' : 'OK',
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
- execFileSync(
6
- path.join(__dirname, '..', 'scripts', 'fh-gate.sh'),
7
- process.argv.slice(2),
8
- { stdio: 'inherit' }
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
- execFileSync(
6
- path.join(__dirname, '..', 'scripts', 'fh-goal.sh'),
7
- process.argv.slice(2),
8
- { stdio: 'inherit' }
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
- execFileSync(
6
- path.join(__dirname, '..', 'scripts', 'fh-run.sh'),
7
- process.argv.slice(2),
8
- { stdio: 'inherit' }
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
+ }