@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.
Files changed (47) hide show
  1. package/.claude/judgment_circuits.txt +14 -0
  2. package/.claude/rules/fh_4axis_gate.md +7 -0
  3. package/.claude-plugin/marketplace.json +2 -2
  4. package/AGENTS.md +25 -0
  5. package/CLAUDE.md +202 -12
  6. package/knowledge/shared/harness-core/dispatch_conditional_prohibition.md +105 -0
  7. package/knowledge/shared/harness-core/fh_three_layer_canon.md +307 -0
  8. package/knowledge/shared/harness-core/harness_incubator_doctrine.md +100 -0
  9. package/knowledge/shared/harness-core/onboarding_acceleration_autopilot.md +3 -1
  10. package/knowledge/shared/harness-core/ship_readiness_gate.md +181 -13
  11. package/knowledge/shared/learnings/subagent_invocations_log.yaml +640 -0
  12. package/knowledge/shared/rules/multi_session_close_protocol.md +118 -0
  13. package/package.json +23 -2
  14. package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
  15. package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
  16. package/plugins/fh-meta/skills/auto-decorrelation/SKILL.md +56 -8
  17. package/plugins/fh-meta/skills/install-wizard/SKILL.md +33 -0
  18. package/plugins/fh-meta/skills/install-wizard/SKILL_detail.md +104 -15
  19. package/scripts/branch_claim.sh +266 -0
  20. package/scripts/chamber_run.sh +64 -2
  21. package/scripts/chamber_witness.sh +439 -0
  22. package/scripts/compaction_probe.sh +456 -0
  23. package/scripts/digest_landing_check.sh +385 -0
  24. package/scripts/directional_diff_gate.sh +459 -0
  25. package/scripts/fh_env_delta_scan.sh +15 -0
  26. package/scripts/fh_session_load.sh +34 -1
  27. package/scripts/field_canon_preload.sh +129 -0
  28. package/scripts/hook_source_lib.sh +41 -0
  29. package/scripts/judgment_circuit_lint.sh +239 -0
  30. package/scripts/novelty_claim_check.sh +193 -0
  31. package/scripts/relay_channel.sh +645 -0
  32. package/scripts/reviewer_capability_corpus.tsv +124 -0
  33. package/scripts/selfcheck.sh +106 -0
  34. package/scripts/session_close_check.sh +134 -1
  35. package/scripts/test_branch_claim_lanes.sh +231 -0
  36. package/scripts/test_dispatch_log_lanes.sh +35 -1
  37. package/scripts/test_field_canon_lanes.sh +142 -0
  38. package/scripts/test_hook_source_gate_lanes.sh +81 -0
  39. package/scripts/test_marker_crossfamily_lanes.sh +132 -0
  40. package/scripts/test_marker_floor_lanes.sh +9 -8
  41. package/scripts/test_relay_channel_lanes.sh +583 -0
  42. package/scripts/test_reviewer_capability_conformance.sh +173 -0
  43. package/scripts/test_wizard_snippet_merge_lanes.sh +104 -11
  44. package/scripts/utterance_landing_check.sh +209 -0
  45. package/templates/.git-hooks/pre-commit +297 -13
  46. package/templates/settings.Compaction.snippet.json +56 -0
  47. 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.89",
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.89",
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
- > ⚠️ **Unresolved residual**: `allow_force_pushes` on `main` is still `true`. Two documented API
219
- > attempts to set it `false` were accepted without error and did **not** persist (verified by
220
- > independent GET, not by the write response). Force/non-ff pushes are blocked locally by this same
221
- > hook, so the honest-model case is covered but the **server-side** history-rewrite surface on
222
- > `main` remains open. Re-check before relying on it.
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). **Residency**: sanitize company code before any external-family dispatch; domain data never leaves.
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 to date **screens**; it has not *birthed* simulate-first is a one-line HITL
374
- recommendation, never a push-button autonomous emit.
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
- Default operation is a **standard interactive session**. Agent dispatch (single or parallel) is used when the task warrants it — not as a default mode. Three execution paths:
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, explicitly requested | Dispatch parallel Agents |
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 first.
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.