@chrono-meta/fh-gate 1.4.89 → 1.4.91
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/judgment_circuits.txt +14 -0
- package/.claude/rules/fh_4axis_gate.md +7 -0
- package/.claude-plugin/marketplace.json +2 -2
- package/AGENTS.md +25 -0
- package/CLAUDE.md +202 -12
- package/knowledge/shared/harness-core/dispatch_conditional_prohibition.md +105 -0
- package/knowledge/shared/harness-core/fh_three_layer_canon.md +307 -0
- package/knowledge/shared/harness-core/harness_incubator_doctrine.md +100 -0
- package/knowledge/shared/harness-core/onboarding_acceleration_autopilot.md +3 -1
- package/knowledge/shared/harness-core/ship_readiness_gate.md +181 -13
- package/knowledge/shared/learnings/subagent_invocations_log.yaml +640 -0
- package/knowledge/shared/rules/multi_session_close_protocol.md +118 -0
- package/package.json +23 -2
- 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/auto-decorrelation/SKILL.md +56 -8
- package/plugins/fh-meta/skills/install-wizard/SKILL.md +33 -0
- package/plugins/fh-meta/skills/install-wizard/SKILL_detail.md +104 -15
- package/scripts/branch_claim.sh +266 -0
- package/scripts/chamber_run.sh +64 -2
- package/scripts/chamber_witness.sh +439 -0
- package/scripts/compaction_probe.sh +456 -0
- package/scripts/digest_landing_check.sh +385 -0
- package/scripts/directional_diff_gate.sh +459 -0
- package/scripts/fh_env_delta_scan.sh +15 -0
- package/scripts/fh_session_load.sh +34 -1
- package/scripts/field_canon_preload.sh +129 -0
- package/scripts/hook_source_lib.sh +41 -0
- package/scripts/judgment_circuit_lint.sh +239 -0
- package/scripts/novelty_claim_check.sh +193 -0
- package/scripts/relay_channel.sh +645 -0
- package/scripts/reviewer_capability_corpus.tsv +124 -0
- package/scripts/selfcheck.sh +106 -0
- package/scripts/session_close_check.sh +134 -1
- package/scripts/test_branch_claim_lanes.sh +231 -0
- package/scripts/test_dispatch_log_lanes.sh +35 -1
- package/scripts/test_field_canon_lanes.sh +142 -0
- package/scripts/test_hook_source_gate_lanes.sh +81 -0
- package/scripts/test_marker_crossfamily_lanes.sh +132 -0
- package/scripts/test_marker_floor_lanes.sh +9 -8
- package/scripts/test_relay_channel_lanes.sh +583 -0
- package/scripts/test_reviewer_capability_conformance.sh +173 -0
- package/scripts/test_wizard_snippet_merge_lanes.sh +104 -11
- package/scripts/utterance_landing_check.sh +209 -0
- package/templates/.git-hooks/pre-commit +297 -13
- package/templates/settings.Compaction.snippet.json +56 -0
- package/templates/settings.FieldCanon.snippet.json +51 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# 판단 회로 등록부 — `scripts/judgment_circuit_lint.sh` 의 트리거 (pre-commit advisory)
|
|
2
|
+
#
|
|
3
|
+
# 여기 등록된 파일이 스테이징되면 회로 린트가 돈다. **글롭이 아니라 명시 등록**인 이유:
|
|
4
|
+
# "어떤 문서가 판단 회로인가"는 사람이 내리는 결정이지 확장자가 추론할 일이 아니다.
|
|
5
|
+
# 글롭으로 걸면 모든 .md 에서 발화하고, 그런 게이트는 결국 꺼진다.
|
|
6
|
+
#
|
|
7
|
+
# 판정: CIRCUIT(금지 0 ∧ 허용 ≥3/5) · PARTIAL · PERSONA(금지 ≥1 ∧ 허용 ≤2/5)
|
|
8
|
+
# 전부 ADVISORY — 차단하지 않는다. 히트는 "이게 회로임을 증명하라"는 뜻이다.
|
|
9
|
+
#
|
|
10
|
+
# 형식: 레포 상대경로 한 줄에 하나. `#` 로 시작하면 주석.
|
|
11
|
+
|
|
12
|
+
# FH 자신의 판단 회로 (§Identity · 독트린 · 게이트 방향). 2026-08-08 실측 = CIRCUIT 4/5,
|
|
13
|
+
# 없는 형식 = 자기봉인("~는 이 작업의 대상이 아니다"). 그 한 칸이 다음 라운드 후보다.
|
|
14
|
+
CLAUDE.md
|
|
@@ -68,6 +68,13 @@ FH asset modified → Axis 1 (templates/regression_guard.sh --pr {BRANCH})
|
|
|
68
68
|
→ Axis 2 (/steel-quench) → Axis 3 (/phantom-quench)
|
|
69
69
|
→ marker: tracks/_meta/.axes_23_passed_{branch}_{date}.marker
|
|
70
70
|
(required fields: axis2-engine / axis2-model / floor-status / axis2-evidence;
|
|
71
|
+
+ **`crossfamily:`** — required on LOAD-BEARING changes only, and TYPED since 2026-08-08:
|
|
72
|
+
`panel(<families>)` | `declined` | `DEGRADED_SINGLE_FAMILY` | `DEGRADED_PANEL_UNUSED` |
|
|
73
|
+
`UNKNOWN`, the three degrade values requiring substantive grounds on the same line.
|
|
74
|
+
Presence has been required since c1fa459; what was free prose is now a closed enum,
|
|
75
|
+
because a presence check catches silence but not a confident wrong answer — a sibling
|
|
76
|
+
harness shipped `crossfamily: none — 도달 불가` that was later found false and then cited
|
|
77
|
+
as grounds. Fixtures: `scripts/test_marker_crossfamily_lanes.sh`;
|
|
71
78
|
**recorded-by-convention, validated by nothing**: `axis2-rounds` (per-round yield vector) —
|
|
72
79
|
steel-quench §Convergence Criteria consumes it, and a hook check for it was built and then
|
|
73
80
|
REMOVED the same day for firing on 100% of markers. The convergence claim it supports is
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "fh-meta",
|
|
14
|
-
"version": "1.4.
|
|
14
|
+
"version": "1.4.91",
|
|
15
15
|
"description": "Hub meta-operations toolkit — 35 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.91",
|
|
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/AGENTS.md
CHANGED
|
@@ -104,6 +104,31 @@ control; FH supplies the quality gate after goal completion.
|
|
|
104
104
|
> **Detail**: See `knowledge/shared/harness-core/agents_md_runtime_details.md §Invocation-patterns`
|
|
105
105
|
> — single, parallel, and wave composition examples — read when choosing a dispatch shape.
|
|
106
106
|
|
|
107
|
+
### Before dispatching without being asked
|
|
108
|
+
|
|
109
|
+
FH treats isolated delegation as part of what a harness *is*, so it leans toward dispatching rather
|
|
110
|
+
than absorbing separable work inline. One boundary comes with that, and it is runtime-agnostic:
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
unprompted dispatch is available only when a standing request from the user is RECORDED —
|
|
114
|
+
in a durable local file, carrying all three of:
|
|
115
|
+
the user's own words, quoted · a dated lease · a scope line
|
|
116
|
+
no such record, or the lease has lapsed, or you cannot tell (fresh clone · a runtime whose
|
|
117
|
+
own prompt may restrict this)
|
|
118
|
+
→ the request is absent, and absent is not granted → ask per invocation
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Four notes for anyone porting this. A record is only a record if the *user* authored the quoted
|
|
122
|
+
words — an agent writing the file on its own initiative has produced nothing. Consent is **leased,
|
|
123
|
+
not owned**: past the lease date the record stops counting and you go back to asking. Every run that
|
|
124
|
+
proceeds *without* a prompt should say that it is doing so, so an unnoticed grant cannot accumulate
|
|
125
|
+
silently. And the request buys not-being-asked about the dispatch; it never relaxes a gate at what
|
|
126
|
+
the dispatched agent then touches (publish, delete, history rewrite).
|
|
127
|
+
|
|
128
|
+
The Claude-side rationale — including why the boundary is phrased around a *conditional* rather
|
|
129
|
+
than a prohibition — lives in `CLAUDE.md §Agent Dispatch Operation` and is specific to that runtime.
|
|
130
|
+
The constraint above is not.
|
|
131
|
+
|
|
107
132
|
> **Detail**: See `knowledge/shared/harness-core/agents_md_runtime_details.md §Codex-entry-points`
|
|
108
133
|
> — runnable `fh-run` and `codex exec` forms — read before invoking FH from Codex.
|
|
109
134
|
|
package/CLAUDE.md
CHANGED
|
@@ -43,6 +43,23 @@ core invariants never melt). The nursery also **verifies what it births**: harne
|
|
|
43
43
|
| **③ AI Collaboration Guide** | Accumulates and distributes best practices for token efficiency and dialogue methodology — "how to ask, delegate, and record". | `CHEATSHEET.md` · `knowledge/shared/dialogue/ai_dialogue_playbook.md` · `MEMORY.md` intent-based + associative recall (`knowledge/shared/dialogue/memory_intent_recall.md`) |
|
|
44
44
|
| **Core Axis** | **Harness Engineering (How)** — the methodology and practice axis that realizes the three layers above. The 6-axis framework is the operating unit. **A harness is a means, not an end** — Field harness: "simpler over time" (complexity = warning signal). Meta-harness: *optimize*, not necessarily simplify — complexity earns its scope; red flags are orphaned, redundant, and decorative units, not complexity itself. | `harness_6axis_framework.md` · `hub_compounding_loop.md` · `claude_code_runtime_flow.md` · `plugins/*/agents/` (sub-agents) |
|
|
45
45
|
|
|
46
|
+
> **3층 정본 — 공정 · 엔진 · 정체성**: FH 를 설명하는 뼈대는 세 층이고 셋의 관계가 정본으로 적혀 있다 — **3단 공정**(엔진을 벼리는 순서: 초기 영혼 → 중간 탈상관 가속화 → **마무리 4축 태우기**) → **4대 엔진**(영혼·품질게이트·질문하기·맥락유지) → **5대 정체성**(사람이 실제로 쓰는 기능). **Read `knowledge/shared/harness-core/fh_three_layer_canon.md`** before naming, re-scoping, or citing any of the three — it also defines the **4 verification axes** (ⓐ구현 · ⓑ재는 방식 · ⓒ주장 · ⓓ앵커) that the third stage actually consists of, and states why the three are *not* a clean stack. Grade table stays canonical in `ship_readiness_gate.md`; this pointer never carries grades.
|
|
47
|
+
|
|
48
|
+
> **자기 대조는 상시 의무 — 트리거는 발화가 아니라 «지금 FH/PMH 자산을 건드리고 있다»**
|
|
49
|
+
> (운영자 결정 2026-08-09; 이 저장소든 **다른 사용자의 install 이든** 동일). §FH Improvement
|
|
50
|
+
> 4-Axis Auto-Gate 와 **같은 트리거**이므로 새 트리거도 새 파일도 만들지 않는다 — 기록 자리는
|
|
51
|
+
> **4축 마커의 기존 필드**(`axis2-*` · `axis3-*` · `residual`)다.
|
|
52
|
+
> **마커에 반드시 남는 3줄** ① **①영혼** — 설계 *전에* 쓴 «성공 정의 / 절대 안 함»(없으면 `없음`)
|
|
53
|
+
> · ② **돌린 축과 안 돌린 축을 각각 이름으로**(ⓐ구현 · ⓑ재는 방식 · ⓒ주장 · ⓓ앵커)
|
|
54
|
+
> · ③ **각 축의 컨트롤과 그 생사**. 축을 «돌렸다»의 **최소 증거 = 컨트롤이 살아 있는 실행 출력**
|
|
55
|
+
> 이다 — 안 고른 이유만 적은 것은 준수가 아니다.
|
|
56
|
+
> **비용 경계**: 넷을 매번 다 돌리지 않는다. 실패 모드에 맞춰 **고른다**.
|
|
57
|
+
> 🟥 **자평이다 · 게임 가능하다 · 훅이 없다 — 셋 다 안 닫혔다.** 지금 기계 앵커가 붙은 축은 ⓓ
|
|
58
|
+
> 하나뿐이나 그건 **현 상태이지 원리적 한계가 아니다**(ⓐⓒ 도 기계화할 수 있다 — cross-family
|
|
59
|
+
> 지목). 닫는 방향은 **cross-family 가 그 마커를 읽는 것**이지 자기 채점을 더 성실히 하는 게 아니다.
|
|
60
|
+
> **근거·사례·표본 한계는 `fh_three_layer_canon.md` §1-c — 인용하기 전에 읽어라.** 이 규칙은
|
|
61
|
+
> **n=1 세션 표본**에서 모든 install 로 일반화한 것이고, 그 절이 그 사실을 명시한다.
|
|
62
|
+
|
|
46
63
|
## Core Reference Documents (Consult First)
|
|
47
64
|
|
|
48
65
|
Four foundational assets for hub operations. **Mandatory pre-reference** before new design, protocol proposals, or framework extensions.
|
|
@@ -215,11 +232,31 @@ into muscle memory and disarm it.
|
|
|
215
232
|
> *Origin*: before that change the server had `enforce_admins: false`, so an admin push *satisfied*
|
|
216
233
|
> the rule and merely printed `Bypassed rule violations` — a notice, not a block. A rule that
|
|
217
234
|
> announces its own bypass is not a floor.
|
|
218
|
-
>
|
|
219
|
-
>
|
|
220
|
-
>
|
|
221
|
-
>
|
|
222
|
-
>
|
|
235
|
+
> ✅ **Retraction — the server-side force-push surface is CLOSED, and the way it was misread is the
|
|
236
|
+
> durable part.** An earlier version of this block said `allow_force_pushes` on `main` was "still
|
|
237
|
+
> `true`", that two API writes "did not persist", and that the **server-side** history-rewrite
|
|
238
|
+
> surface therefore "remains open". The field reading was correct; the conclusion was not.
|
|
239
|
+
> **Branch protection is two independent layers — legacy protection and rulesets coexist, and the
|
|
240
|
+
> strictest wins** — so a field on the protection object is never the effective answer by itself.
|
|
241
|
+
> Measured on this repo 2026-08-09: `GET /repos/{owner}/{repo}/rules/branches/main` returns
|
|
242
|
+
> `non_fast_forward` from ruleset `main-no-force-push` — `enforcement: active`,
|
|
243
|
+
> `current_user_can_bypass: never`, `bypass_actors: []`, live since 2026-07-25 — while the legacy
|
|
244
|
+
> object still reports `allow_force_pushes: true`. The two API writes that "did not persist" were
|
|
245
|
+
> writing to the layer that does not govern *this* outcome while the stricter ruleset is active — not
|
|
246
|
+
> a layer that is inert in general: disable or retarget the ruleset and the legacy toggle governs
|
|
247
|
+
> again. **Read BOTH layers before declaring any branch surface open or closed** — `/rules/branches/
|
|
248
|
+
> {branch}` shows only what the *rulesets* impose, and `/branches/{branch}/protection` only what
|
|
249
|
+
> *legacy protection* imposes; neither is the effective view alone. A protection-object field read by
|
|
250
|
+
> itself misjudged this three times ([[reference_github_protection_two_layers]]).
|
|
251
|
+
> **Scope of the retraction, stated narrowly on purpose**: it covers *force-push / non-fast-forward*,
|
|
252
|
+
> which is what `non_fast_forward` blocks. Branch **deletion** is a separate rule and is closed on the
|
|
253
|
+
> other layer (`allow_deletions: false`, same GET). PR-routing is likewise a different field —
|
|
254
|
+
> `required_pull_request_reviews` present with `enforce_admins: true` — not something
|
|
255
|
+
> `required_status_checks` says anything about.
|
|
256
|
+
> ⚠️ A *different* residual on `main` is still real and must not be folded into the one just
|
|
257
|
+
> retracted: `required_status_checks.contexts` is `[]` — pushes are routed through PRs by the fields
|
|
258
|
+
> just named, but **no check is required to be green** for one to merge (named again in the 4-axis
|
|
259
|
+
> section below).
|
|
223
260
|
|
|
224
261
|
## Permission-Denial Guidance (When Auto-Mode Blocks an Action)
|
|
225
262
|
|
|
@@ -313,7 +350,19 @@ deconfliction: this gate reviews **field code being authored**; the Irreversibil
|
|
|
313
350
|
**Degrade direction (fail-closed)**: no different-family auditor reachable → **NOT-CONVERGED** —
|
|
314
351
|
block the autonomous merge / ask the operator / proceed only under an **explicit, logged
|
|
315
352
|
same-family-only acknowledgment**; never a silent same-family pass (§Irreversibility Surface-Class
|
|
316
|
-
Degrade Invariant). **
|
|
353
|
+
Degrade Invariant). **That acknowledgment is now typed, not prose**: the Axes 2–3 marker carries
|
|
354
|
+
the marker's **`crossfamily:`** line — required on load-bearing changes since c1fa459, and a closed
|
|
355
|
+
enum since 2026-08-08 — `panel(<families>)` · `declined` · `DEGRADED_SINGLE_FAMILY` ·
|
|
356
|
+
`DEGRADED_PANEL_UNUSED` · `UNKNOWN`, the three degrade values hard-blocked at commit without
|
|
357
|
+
substantive grounds on the same line naming what was probed (`templates/.git-hooks/pre-commit`, fixtures
|
|
358
|
+
`scripts/test_marker_crossfamily_lanes.sh`). *could not* (`DEGRADED_SINGLE_FAMILY`) · *did not*
|
|
359
|
+
(`DEGRADED_PANEL_UNUSED`) · *did not look* (`UNKNOWN`) are **separate values on purpose** — free
|
|
360
|
+
prose collapsed them, which renders an unrun probe as a zero finding and an unused panel as an
|
|
361
|
+
unavailable one. This is also what reconciles this line with `auto-decorrelation`
|
|
362
|
+
Step 6's *"silent degrade, never hard-fail"*: **not-blocking and not-silent are different
|
|
363
|
+
properties**, and only the second is mandatory on a reversible commit surface. Before 2026-08-08
|
|
364
|
+
this axis was convention-only — measured on this repo, 140 markers carried the tier verdict 129
|
|
365
|
+
times and any decorrelation verdict **4** times. **Residency**: sanitize company code before any external-family dispatch; domain data never leaves.
|
|
317
366
|
**Autonomy**: autonomous once UAP-consented; **in autonomous loops the gate is part of the delegated
|
|
318
367
|
pipeline**, not an afterthought, and a below-floor orchestrator RUNS the review by default
|
|
319
368
|
(run-first, ask-last — `sonnet_floor_doctrine.md`).
|
|
@@ -370,8 +419,13 @@ installed **FH assets** run the **4-axis gate**; **field scaffolds** run **`asse
|
|
|
370
419
|
repo, so this routing is not redundant with it) · **autonomy floor** — discover/rank trusted at opus-tier+;
|
|
371
420
|
below-floor, present the raw recommend and ask · **HITL per item**, and `"끝까지 해줘 / 자율로 완주"` → full-autonomy under the `/goal-quench`
|
|
372
421
|
gate: autonomy removes the per-item *prompt*, **never the gate**. Honesty boundary that must not soften in
|
|
373
|
-
summary: the chamber
|
|
374
|
-
|
|
422
|
+
summary: the chamber **overwhelmingly screens** — hand-counted 2026-08-08 from the run ledger:
|
|
423
|
+
9 full runs, **8 KILL, 1 EMIT**. It has birthed **once** (run #9 `forge-wiki`, shipped publicly), so
|
|
424
|
+
"it has not birthed" — the earlier wording here — is no longer true. But do not upgrade the claim
|
|
425
|
+
either: that run's workspace carries only a verdict file, with no intent/budget/blind-persona
|
|
426
|
+
artifacts, so the **formal flow** is not what produced it. The first end-to-end formal run is #10 and
|
|
427
|
+
it KILLed. Either way simulate-first stays a one-line HITL recommendation, never a push-button
|
|
428
|
+
autonomous emit.
|
|
375
429
|
|
|
376
430
|
> **Detail**: See `knowledge/shared/harness-core/onboarding_acceleration_autopilot.md` — full Phase-0 branch
|
|
377
431
|
> logic + `chamber_run.sh` scope, the per-phase skill composition, the remaining guards (no-reinvention
|
|
@@ -592,13 +646,138 @@ Self-healing is not only FH-self-dev (Mode D 4-axis) and reactive (`verify-bidir
|
|
|
592
646
|
|
|
593
647
|
> **Runtime authority (canonical):** one explicit governor per context + capability-routed sidecars; sidecar findings are evidence candidates, not terminal verdicts, until source-closed by the governor *via a mechanical anchor* — never governor agreement alone. CC=action/governor · Codex=repo-grounded audit sidecar · Gemini/agy=breadth/multimodal sidecar · other runtimes=portable `AGENTS.md` entrypoint only. Full doctrine + Maintenance-Cost Rule: `knowledge/shared/harness-core/multi_model_sidecar_strategy.md §Runtime Authority`.
|
|
594
648
|
|
|
595
|
-
|
|
649
|
+
**Isolated delegation is a component of the identity, not an optional extra** (operator decision,
|
|
650
|
+
2026-08-08). FH/PMH are defined as governor + orchestrator; a harness that cannot dispatch is a
|
|
651
|
+
contradiction in its own terms. So **agent dispatch is default-active**: reach for it whenever a unit
|
|
652
|
+
of work is genuinely separable — independent tasks, a blind evaluation that must not see the author's
|
|
653
|
+
reasoning, a search that would otherwise flood this context — **without waiting to be asked**. The
|
|
654
|
+
earlier wording here ("used when the task warrants it — not as a default mode") is superseded.
|
|
655
|
+
|
|
656
|
+
**Two different things turn it off, and conflating them is how a session talks itself into
|
|
657
|
+
dispatching.** ⓐ the user saying not to, *in this environment* — a veto of **this file's posture**;
|
|
658
|
+
ⓑ **the absence of a request**, wherever the runtime carries the conditional line described below.
|
|
659
|
+
Under a conditional, silence is not permission. ⓐ is FH's own default being withdrawn by its owner;
|
|
660
|
+
ⓑ is a sentence in a layer FH does not author. Do not read them as one operation.
|
|
661
|
+
|
|
662
|
+
⚠️ **Neither direction has a confirmed hook-level floor. Say that plainly rather than implying one.**
|
|
663
|
+
```
|
|
664
|
+
opening salience only for the POSTURE. The prohibition met in the field is CONDITIONAL,
|
|
665
|
+
which changes what "override" even means — see the measured block below.
|
|
666
|
+
blocking ALSO not hook-enforced. `SubagentStart` fires on spawn but is **context-only** —
|
|
667
|
+
it cannot block, exit 2 only surfaces stderr, and it has no decision field
|
|
668
|
+
(official hooks reference, read 2026-08-08). It can INJECT context at the moment
|
|
669
|
+
of dispatch, which is better-placed salience than this file, but still salience.
|
|
670
|
+
UNVERIFIED whether a `permissions` deny entry or a `PreToolUse` matcher can target subagent
|
|
671
|
+
spawning at all — the reference does not name a tool for it, and this repo has no
|
|
672
|
+
precedent. **Do not cite a blocking mechanism until someone runs the known pair**
|
|
673
|
+
(configure the deny, attempt a dispatch, observe). Until then: unverified, not absent.
|
|
674
|
+
```
|
|
675
|
+
**The conditional line, and what a session must do about it.** Some runtimes ship the default
|
|
676
|
+
*"Do not call the AgentTool unless the user requested it"* (with a workflows/deep-research twin).
|
|
677
|
+
Measured 2026-08-09 on this machine: it is **not** a hard-coded constant and **not** global — it is
|
|
678
|
+
the third branch of a three-tier resolution, replaceable and flag-switchable, and gated to one model
|
|
679
|
+
bundle. So it is a **conditional**, and a request *satisfies* it rather than having to outrank it.
|
|
680
|
+
Which request, though, is an **interpretation** — narrow (per-invocation) or standing — and FH takes
|
|
681
|
+
the standing reading only under provenance:
|
|
682
|
+
|
|
683
|
+
```
|
|
684
|
+
RECORDED a durable local binding (CLAUDE.local.md, or the onboarding answer) carrying
|
|
685
|
+
three things, each of which adds information the others do not:
|
|
686
|
+
the operator's own words, quoted — who granted it
|
|
687
|
+
a dated lease — until when (§OAL: consent is leased)
|
|
688
|
+
a scope line — for what. A runtime may carry more than
|
|
689
|
+
one prohibition line; granting one never
|
|
690
|
+
grants the other
|
|
691
|
+
Any of the three missing → not a record → treat as NOT RECORDED.
|
|
692
|
+
→ dispatch without asking, inside that scope, until the lease lapses
|
|
693
|
+
(Nothing else is a field. The request removes the PROMPT and never a gate — that
|
|
694
|
+
holds by the rule below, not by a line the author ticks. A field whose content is
|
|
695
|
+
already fixed by the rule certifies nothing and can only fail closed on a typo.)
|
|
696
|
+
NOT RECORDED no binding · fresh clone · ephemeral session whose local files were reclaimed ·
|
|
697
|
+
conditional-line presence UNKNOWN (a session cannot read its own system prompt —
|
|
698
|
+
unknown is not absent; assume the conditional applies)
|
|
699
|
+
→ request ABSENT. absent ≠ granted → ask per invocation.
|
|
700
|
+
This is the default for every install that is not the author's.
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
**A session may not write its own permission slip.** The authority is the quoted operator utterance,
|
|
704
|
+
never the paragraph's existence; an entry missing any of the three **is not a record** —
|
|
705
|
+
treat it as absent — *any of the three above*, not some longer list; the schema is the whole test.
|
|
706
|
+
**Lease length is the operator's to set, never the session's**, and renewal needs a fresh utterance:
|
|
707
|
+
a session that re-dates an expired lease on its own has forged a record, which is this paragraph's
|
|
708
|
+
whole subject. **Named residual, stated at the same strength as §Operational Adaptation Loop's:
|
|
709
|
+
the record is self-attested.** Every part of it is writable by the beneficiary, so form-checking
|
|
710
|
+
catches silence, not forgery — and this one is **unmitigated on a default install**: the binding is
|
|
711
|
+
gitignored, so there is no write-time history to check the cited dates against. (An operator who
|
|
712
|
+
mirrors it into a private version-controlled store gets that check; that is *their* setup, not a
|
|
713
|
+
property of the rule, and an earlier draft of this sentence claimed it generally.) The lease is the
|
|
714
|
+
only part that decays on its own — **and nothing reads it**. Correcting a wrong reason given earlier
|
|
715
|
+
in this branch: that gap is *not* "below the mechanization threshold, so don't build it."
|
|
716
|
+
`scripts/consent_registry_check.sh` already enforces leases (requires `expires`, rejects past dates,
|
|
717
|
+
caps at 365 days) and is lane-tested. It is **unwired here**, which is a different defect with a
|
|
718
|
+
different fix, and "don't build" was covering for it. Wiring it is a real decision, not a chore:
|
|
719
|
+
the registry's own floor forbids `promotion_eligible: true` for a class whose effects feed
|
|
720
|
+
irreversible sinks, and a dispatched subagent does — so registering this grant would either be
|
|
721
|
+
rejected by that floor or require declaring it something the registry does not govern. That is the
|
|
722
|
+
operator's call, and until it is made the lease is **honoured by reading, not by machinery**.
|
|
723
|
+
|
|
724
|
+
**Scope of the consent carve-out — it exits ONE clause, not the section.** §Operational Adaptation
|
|
725
|
+
Loop has two separable parts: (i) the *derivation* rule (standing consent inferred from 3× accepted),
|
|
726
|
+
and (ii) **action-class floors that hold regardless of how consent arrived** — a class never promotes
|
|
727
|
+
if its sinks are irreversible, if it *feeds* such a sink (taint propagates through reversible steps),
|
|
728
|
+
or if that is unknown. A direct operator instruction is outside **(i) only**. **(ii) still applies in
|
|
729
|
+
full**, and it bites here: a dispatched subagent inherits tools and therefore feeds publish/delete/
|
|
730
|
+
history-rewrite sinks — so the standing request buys *not being asked about the dispatch*, never a
|
|
731
|
+
relaxed gate at the sink. The per-run announce duty and expiry from §OAL likewise survive.
|
|
732
|
+
**Discriminator is mechanical, not introspective**: the test is whether a literal operator utterance
|
|
733
|
+
is quoted in the record (greppable) — **not** whether the session judges its own reasoning to be
|
|
734
|
+
instruction-shaped. A self-test the session administers to itself is the thing §OAL forbids
|
|
735
|
+
("decided mechanically from the registry — never by the session's own judgment").
|
|
736
|
+
|
|
737
|
+
> **Detail**: `knowledge/shared/harness-core/dispatch_conditional_prohibition.md` — the resolution
|
|
738
|
+
> order, the model-bundle gate, the calibrated where-it-is-not table, and the reproduction commands.
|
|
739
|
+
> **Read it before citing any of these numbers or claiming the line is absent from a surface.**
|
|
740
|
+
|
|
741
|
+
An earlier draft of this very block asserted "a `SubagentStart` hook can deny, and a denial there is a
|
|
742
|
+
real floor." That was false, taken on trust from an adjacent session and written here before the
|
|
743
|
+
reference was read. A second draft, on 2026-08-09, then wrote that the constant's call site "needs
|
|
744
|
+
binary inspection, which was blocked" — **also false**: three plain `grep` calls resolved it, and an
|
|
745
|
+
adversarial reviewer demonstrated that by doing it. Declaring something unmeasurable before trying the
|
|
746
|
+
cheap tool is [[feedback_impossible_verdict_may_be_unread_half]]; the honest label is *"not yet
|
|
747
|
+
measured,"* never *"blocked."* A blind target-tier sim then read it back correctly — which shows a sim measures
|
|
748
|
+
whether text is *followable*, never whether it is *true*. Both checks are needed; neither substitutes.
|
|
749
|
+
|
|
750
|
+
So "default-active" is a **posture, not a guarantee**. Measured 2026-08-08: a session running under
|
|
751
|
+
exactly that system-prompt instruction worked alone for a full session and dispatched only at the two
|
|
752
|
+
points where the operator named it — while this file said dispatch was available. A session that
|
|
753
|
+
*cannot* dispatch must **say so** rather than quietly doing everything inline; the silent version is
|
|
754
|
+
what made that case invisible until the operator asked. Writing "default is active" into a remote
|
|
755
|
+
canon without this paragraph produces the next session that reads it and still cannot comply.
|
|
756
|
+
|
|
757
|
+
**Onboarding**: at first setup, ask whether this environment wants dispatch and **write the answer into
|
|
758
|
+
the local binding** (`CLAUDE.local.md`) **in the three-part form above — quoted words, a lease the user
|
|
759
|
+
picks, a scope** — not only into the session. Ask for all three at that moment; a record written with
|
|
760
|
+
two of them is invalid, and the only route past an invalid record is the session inventing the third,
|
|
761
|
+
which is forgery. **A gate that blocks every new install is not a strict gate, it is a bypass
|
|
762
|
+
trainer** — so the ask is **wired, not left to prose**: `install-wizard` **Step 3-D** collects all
|
|
763
|
+
three at setup and writes them down, and carries any mechanical settings change under the same
|
|
764
|
+
approval (operator decision, 2026-08-09: *users of FH/PMH run parallel by default; where a mechanical
|
|
765
|
+
config change is needed, take consent through the install-wizard contract and change it then*).
|
|
766
|
+
A recorded **decline** is also a record — it stops later sessions re-asking. This is why the answer
|
|
767
|
+
belongs at setup — that is the one moment where the
|
|
768
|
+
choice is cheap to make, and a durable record is the whole point: a *yes* left in a transcript expires
|
|
769
|
+
with the transcript, while the conditional line above is re-evaluated by every cold session. A recorded
|
|
770
|
+
standing request is therefore not bookkeeping — it is the thing that satisfies the condition. Wiring the
|
|
771
|
+
*blocking* direction to a mechanism still waits on the **UNVERIFIED (deny-mechanism) line** above — not
|
|
772
|
+
the UNVERIFIED (call-sites) line, which is a different open question; the honest install note says so.
|
|
773
|
+
|
|
774
|
+
Three execution paths:
|
|
596
775
|
|
|
597
776
|
| Path | Situation | Method |
|
|
598
777
|
|---|---|---|
|
|
599
778
|
| **Direct edit** | Simple modification of mapped project files | Read/Edit with absolute path (no cwd switch needed) |
|
|
600
|
-
| **Agent dispatch** | Field project work · single independent task | Inject Context Card then dispatch Agent |
|
|
601
|
-
| **Parallel dispatch** | 2+ genuinely independent tasks
|
|
779
|
+
| **Agent dispatch** | Field project work · single independent task — **same RECORDED requirement as the row below**; the provenance gate is per *spawn*, not per *fan-out width* | Inject Context Card then dispatch Agent |
|
|
780
|
+
| **Parallel dispatch** | 2+ genuinely independent tasks — **requires a RECORDED standing request** (see the provenance gate above); otherwise ask per invocation | Dispatch parallel Agents |
|
|
602
781
|
|
|
603
782
|
**Why not Agent View by default**: Agent View introduces worktree isolation (blocks settings.json writes, Stop hook timing differs), session context gaps (session card stale content bug), and path friction — with no benefit unless the user is actively managing multiple agent sessions. Parallel agents via `Agent` tool work identically in a standard session.
|
|
604
783
|
|
|
@@ -638,7 +817,10 @@ true statement about one thing (provenance) was standing in for an untested clai
|
|
|
638
817
|
recurrence is 1, below this repo's own N≥3 mechanization threshold. If it recurs, the check is a
|
|
639
818
|
two-line hook addition, not a research problem.
|
|
640
819
|
|
|
641
|
-
**Forbidden responses**: *"I can't do that — I'm not in that project's cwd"* — self-check Agent dispatch
|
|
820
|
+
**Forbidden responses**: *"I can't do that — I'm not in that project's cwd"* — self-check Agent dispatch
|
|
821
|
+
first. And **silently working alone while dispatch is unavailable**: if this environment blocks
|
|
822
|
+
subagents, name it once rather than absorbing the whole job inline — an unstated constraint reads as a
|
|
823
|
+
capability the harness simply chose not to use.
|
|
642
824
|
|
|
643
825
|
Mapped paths: check `auto_project_mapping.md` or `find ~/projects -maxdepth 1 -type d` for actuals.
|
|
644
826
|
|
|
@@ -788,6 +970,14 @@ Closing phrase detected ("wrap up", "done", "good work", "end session", etc.)
|
|
|
788
970
|
> (④-c: why-its-own-step origin, ownership split, salience/backstops) · `§Open-PR-Sweep-Origin` (①-b) — read
|
|
789
971
|
> when executing that close step.
|
|
790
972
|
|
|
973
|
+
> **다축 마감 (같은 하네스에 세션이 둘 이상일 때)**: 순서·판별·쓰기 규율은
|
|
974
|
+
> `knowledge/shared/rules/multi_session_close_protocol.md` 가 정본이다. **접기 전에 살아있는
|
|
975
|
+
> peer 에게 「더 있나」를 묻는 단계가 있고, 그 질문은 `gh pr list` 재대조로 대체되지 않는다**
|
|
976
|
+
> (PR 이 없는 델타를 구조적으로 못 잡는다 — 2026-08-09 실측 8건). 세 가지를 각각 못 믿는다:
|
|
977
|
+
> **보냈다≠닿았다 · 살아있다≠일하는중 · 닫혔다≠안열렸다.** 기계 표면화는
|
|
978
|
+
> `session_close_check.sh` ①-c·①-d, 내용 착지는 `scripts/utterance_landing_check.sh`.
|
|
979
|
+
> **읽어라 — 이 요약에는 판별 규칙도 쓰기 규율도 없다.**
|
|
980
|
+
|
|
791
981
|
**Card-last guard**: ①–④-c (incl. ①-b open-PR sweep, ④-c handoff lifecycle) must ALL complete before
|
|
792
982
|
⑤ runs. **Mechanical floor**: `scripts/session_close_check.sh` is **wired into `templates/.git-hooks/pre-push`** (2026-07-20) — it runs on *every* push, so it is no longer prose-invoked. Enforcement is surface-matched: an ordinary push **surfaces** ❌ violations (advisory — a branch push is reversible), and the **close push blocks** on them: run step ⑥ as **`FH_SESSION_CLOSE=1 git push`** → exit 1 (card-last violated / required close artifact missing) stops the push until fixed. *Why not block always*: ⑤ card-last is a close-time invariant, while ④ mandates writing `fh_completed_*` **during** the session — an unconditional block would pit the two rules against each other and train `--no-verify`, disarming the Destructive-Op gate in the same hook. Any new information produced during ①–④ (new commits from a merged self-PR, model changes,
|
|
793
983
|
new findings, a carry item flipped to DONE) feeds INTO ⑤ — card is never written mid-sequence and
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# The conditional dispatch prohibition — what it actually is
|
|
2
|
+
|
|
3
|
+
> Detail file for `CLAUDE.md §Agent Dispatch Operation`. The resident summary carries the
|
|
4
|
+
> **behavioural rule**; this file carries the **measurement** behind it. Read this before citing any
|
|
5
|
+
> number here, before claiming the line is absent from a surface, and before re-running the probe.
|
|
6
|
+
|
|
7
|
+
## The two lines
|
|
8
|
+
|
|
9
|
+
Some runtimes ship these as a default addition to the session prompt:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
Do not call the AgentTool unless the user requested it
|
|
13
|
+
Do not use workflows or deep-research unless the user requested it
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## What was measured
|
|
17
|
+
|
|
18
|
+
**Scope of the measurement — state it before the numbers.** n=1 machine · macOS · install path
|
|
19
|
+
`~/.local/share/claude/versions/` · CLI versions 2.1.223–226, all four identical. Nothing here is a
|
|
20
|
+
claim about other platforms, other install paths, or later versions. Re-run before citing elsewhere.
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
occurrences of each line 3
|
|
24
|
+
CONTROL "AgentTool" 37 ← instrument alive on this target
|
|
25
|
+
NEG-CONTROL "zzz_known_negative" 0 ← instrument not hallucinating hits
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
An unqualified `grep -c` (without `-a`) returns **empty** on this binary — it dies silently. That is
|
|
29
|
+
exactly the instrument death the control pair exists to catch; a bare `0` from it would have read as
|
|
30
|
+
a measurement.
|
|
31
|
+
|
|
32
|
+
### Resolution order — it is a fallback, not a constant
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
function JWb(e){
|
|
36
|
+
let t = wk()?.tengu_heron_brook; // ① client-data string → returned if non-empty
|
|
37
|
+
if (typeof t === "string" && t.trim() !== "") return t.trim();
|
|
38
|
+
let r = nt("tengu_heron_brook",""); // ② remote flag string → returned if non-empty
|
|
39
|
+
if (r.trim() !== "") return r.trim();
|
|
40
|
+
if (Hbo(e)) return H3p; // ③ the two lines — only if the gate below passes
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
function Hbo(e){
|
|
44
|
+
if (e === void 0) return false;
|
|
45
|
+
if (lB(Eo(e), "opus_5_prompt_bundle") !== true) return false; // model-bundle gate
|
|
46
|
+
return !nt(VE_, false); // VE_ = "tengu_fennel_godwit"
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Three consequences:
|
|
51
|
+
|
|
52
|
+
1. **Not hard-coded.** It is the third branch of a three-tier resolution — ① and ② replace it
|
|
53
|
+
wholesale, and a remote kill-switch disables it.
|
|
54
|
+
2. **Not global — model-scoped.** It attaches only under `opus_5_prompt_bundle`. A session on another
|
|
55
|
+
bundle never receives it. **Do not reason about this line without knowing which bundle you are on.**
|
|
56
|
+
3. **Genuinely conditional.** All 3 occurrences of each prefix carry `unless the user requested it`;
|
|
57
|
+
no unconditional variant exists (checked with a live negative control on the phrasing regex).
|
|
58
|
+
|
|
59
|
+
### Where the text is *not* — two rows, kept apart on purpose
|
|
60
|
+
|
|
61
|
+
Merging these is the `not-found ≠ zero` defect this repo keeps naming
|
|
62
|
+
([[feedback_not_found_is_not_zero_family]]).
|
|
63
|
+
|
|
64
|
+
| Row | Targets |
|
|
65
|
+
|---|---|
|
|
66
|
+
| **MEASURED ZERO** — target exists, grep calibrated on that target | user + project `settings.json` · project `settings.local.json` · `~/.claude.json` · `~/.claude/plugins/**` · both launcher app bundles |
|
|
67
|
+
| **LAYER ABSENT** — nothing to search; silence, not a zero | user `settings.local.json` · `.claude/agents/**` (user and project) · output-styles (user and project) |
|
|
68
|
+
|
|
69
|
+
**INFERENCE, not measurement**: *"so it cannot be turned off from config."* Only the
|
|
70
|
+
**operator-facing** config layers were searched **for the text**. Branch ② above shows a *remote*
|
|
71
|
+
flag does control it. The honest statement is: the text is not in operator config, and no
|
|
72
|
+
operator-config key is known to gate it.
|
|
73
|
+
|
|
74
|
+
## Reproduction
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
cd ~/.local/share/claude/versions
|
|
78
|
+
V=2.1.226 # 실제 설치 버전으로 교체
|
|
79
|
+
grep -a -o "Do not call the AgentTool unless the user requested it" "$V" | wc -l # 3
|
|
80
|
+
grep -a -o "AgentTool" "$V" | wc -l # 37 CONTROL
|
|
81
|
+
grep -a -o "zzz_known_negative_zzz" "$V" | wc -l # 0 NEG-CONTROL
|
|
82
|
+
off=$(grep -abo 'if(Hbo(e))return' "$V" | head -1 | cut -d: -f1)
|
|
83
|
+
dd if="$V" bs=1 skip=$((off-900)) count=1500 2>/dev/null | tr -d '\0' # JWb
|
|
84
|
+
off=$(grep -abo 'function Hbo(' "$V" | head -1 | cut -d: -f1)
|
|
85
|
+
dd if="$V" bs=1 skip=$off count=420 2>/dev/null | tr -d '\0' # Hbo
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Two retractions this file exists to record
|
|
89
|
+
|
|
90
|
+
- **"A system prompt outranks both, by construction, so opening is impossible."** Right rule, wrong
|
|
91
|
+
sentence. An *absolute* prohibition cannot be reopened from a lower layer; a *conditional* one is
|
|
92
|
+
opened by its condition becoming true. Check whether the sentence is unconditional **before**
|
|
93
|
+
reaching for the precedence rule.
|
|
94
|
+
- **"Reading the call site needs binary inspection, which was blocked."** False. Three plain `grep`
|
|
95
|
+
calls resolved it; an adversarial reviewer demonstrated that by simply running them. One earlier
|
|
96
|
+
attempt using a different tool had been denied, and that denial was generalised into *"this is
|
|
97
|
+
unmeasurable"* — the same shape as [[feedback_impossible_verdict_may_be_unread_half]]. **A blocked
|
|
98
|
+
tool is not a blocked question.** The honest label is *"not yet measured."*
|
|
99
|
+
|
|
100
|
+
## Named residuals
|
|
101
|
+
|
|
102
|
+
- The measurement is n=1 machine. Platform and install-path variance unmeasured.
|
|
103
|
+
- Whether any **operator-config** key gates the constant is unmeasured; only text-absence was checked.
|
|
104
|
+
- What ① (client-data) actually carries in this environment was not inspected — only that it takes
|
|
105
|
+
precedence.
|