@chrono-meta/fh-gate 1.4.76 → 1.4.78
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/rules/fh_4axis_gate.md +63 -0
- package/.claude-plugin/marketplace.json +2 -2
- package/AGENTS.md +96 -260
- package/docs/codex-compat.md +4 -1
- package/knowledge/shared/harness-core/agents_md_runtime_details.md +233 -0
- package/knowledge/shared/harness-core/multi_model_sidecar_strategy.md +1 -1
- package/knowledge/shared/learnings/subagent_invocations_log.yaml +14 -0
- package/package.json +8 -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/install-doctor/SKILL.md +88 -0
- package/plugins/fh-meta/skills/install-wizard/SKILL.md +1 -1
- package/plugins/fh-meta/skills/install-wizard/SKILL_detail.md +117 -3
- package/scripts/fh_node_check.sh +184 -0
- package/scripts/fh_session_load.sh +59 -28
- package/scripts/package_coverage_check.sh +22 -0
- package/scripts/selfcheck.sh +45 -0
- package/scripts/sidecar_calibrate.sh +190 -0
- package/scripts/sidecar_wait.sh +55 -5
- package/scripts/test_node_check_lanes.sh +179 -0
- package/scripts/test_sidecar_calibrate_lanes.sh +218 -0
- package/scripts/test_sidecar_wait_stdin.sh +169 -0
- package/templates/settings.SessionStart.snippet.json +54 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# AGENTS.md Runtime Details
|
|
2
|
+
|
|
3
|
+
> **Load: on demand.** `AGENTS.md` is the always-loaded runtime entry point. Read only the section
|
|
4
|
+
> named by its imperative pointer.
|
|
5
|
+
|
|
6
|
+
## §Architecture-and-output-routing
|
|
7
|
+
|
|
8
|
+
forge-harness has two distinct layers:
|
|
9
|
+
|
|
10
|
+
| Layer | Contents | Compatibility |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| **Methodology** | `tracks/`, `knowledge/`, `SKILL.md` documents, session protocols | Model-agnostic |
|
|
13
|
+
| **Automation** | Plugin agents, hooks, slash commands, `CLAUDE.md` rules | Claude Code-native |
|
|
14
|
+
|
|
15
|
+
FH agents ship under `plugins/*/agents/` through the plugin channel. `.claude/agents/` is the
|
|
16
|
+
field-project local/override slot, not FH's shipping location. Skills straddle both layers: their
|
|
17
|
+
methodology is portable, while their automatic invocation is Claude-native.
|
|
18
|
+
|
|
19
|
+
The methodology layer is Codex-compatible beta. Gemini, Codex, and other runtimes can apply it by
|
|
20
|
+
replacing hooks and native dispatch with manual invocation.
|
|
21
|
+
|
|
22
|
+
Directory names do not determine publication residency:
|
|
23
|
+
|
|
24
|
+
| Content | Default destination |
|
|
25
|
+
|---|---|
|
|
26
|
+
| Reusable methodology, docs, skills, public guidance, polished external conclusions | Public mirror: `knowledge/`, `plugins/`, `docs/` |
|
|
27
|
+
| Raw signals, operator observations, private validation, handoffs, paper drafts, PR-background reasoning | Private companion store or local-only |
|
|
28
|
+
|
|
29
|
+
In a workspace pairing a public mirror with a private companion store, preserve repository ownership
|
|
30
|
+
even when both are locally available. Treat observational or operator-specific material as
|
|
31
|
+
private-first and promote only the polished result.
|
|
32
|
+
|
|
33
|
+
## §Sidecar-routing-and-waiting
|
|
34
|
+
|
|
35
|
+
A sidecar is a capability-routing layer, not a second harness or co-governor. Gemini/Antigravity is
|
|
36
|
+
suited to breadth and multimodal work. Codex's primary FH role is repo-grounded audit: file reads,
|
|
37
|
+
source-close grep, diff/patch review, gate execution, and phantom/backtrace. A Codex session with
|
|
38
|
+
Browser or Chrome connectors may also take live web-flow automation.
|
|
39
|
+
|
|
40
|
+
Sidecars are Bash/adapter invocations coordinated inline; they bypass plugin agent dispatch and this
|
|
41
|
+
registry. Route Codex to repo-grounded audit by default, not breadth, discovery, or design-depth work.
|
|
42
|
+
|
|
43
|
+
After dispatch, wait mechanically:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
printf '%s' "$prompt" | bash scripts/sidecar_wait.sh out.txt 900 -- codex exec -m gpt-5.5 -
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Interpret only the typed verdict:
|
|
50
|
+
|
|
51
|
+
| Verdict | Meaning |
|
|
52
|
+
|---|---|
|
|
53
|
+
| `SIDECAR_VERDICT=COMPLETE exit=0 bytes=N` | The process completed; read the output file |
|
|
54
|
+
| `SIDECAR_VERDICT=TIMEOUT waited=Ns bytes=N` | The process is still alive; this is not a result |
|
|
55
|
+
| `SIDECAR_VERDICT=EMPTY exit=0` | The completed process returned no content |
|
|
56
|
+
|
|
57
|
+
A live process and a completed empty process can both show a zero-byte file. Never judge state by
|
|
58
|
+
file inspection. Canonical authority and waiting doctrine:
|
|
59
|
+
`knowledge/shared/harness-core/multi_model_sidecar_strategy.md §Runtime Authority` and
|
|
60
|
+
`plugins/fh-meta/skills/auto-decorrelation/SKILL.md §S-1b`.
|
|
61
|
+
|
|
62
|
+
## §Mandatory-checklist-procedures
|
|
63
|
+
|
|
64
|
+
### FH asset changes
|
|
65
|
+
|
|
66
|
+
Read `.claude/rules/fh_4axis_gate.md` before changing an FH asset. It defines the mandatory
|
|
67
|
+
Backward, Adversarial, Forward, and Record axes, marker fields, lightweight exception, and
|
|
68
|
+
substantive carve-out. The pre-commit hook blocks commits that lack required evidence.
|
|
69
|
+
|
|
70
|
+
When running `templates/regression_guard.sh`, prefer `REGRESSION_GUARD_RESULT_FILE=<path>` and read
|
|
71
|
+
`result=pass|review|block|skip|error`. Without that environment variable, read the typed
|
|
72
|
+
`REGRESSION_GUARD_RESULT=` stdout line. Exit 0 alone cannot distinguish PASS from SKIP.
|
|
73
|
+
|
|
74
|
+
### Company residency
|
|
75
|
+
|
|
76
|
+
Keep raw company source, secrets, hostnames, internal names, stack traces, and unredacted findings
|
|
77
|
+
local. Outbound requests may contain only a sanitized summary. An exception requires explicit
|
|
78
|
+
operator approval plus a gitignored audit note. Canonical procedure:
|
|
79
|
+
`CLAUDE.md §Field-Harness Diagnostic`.
|
|
80
|
+
|
|
81
|
+
### Author exposure
|
|
82
|
+
|
|
83
|
+
Before completing a material deliverable, use `agent-composer §Author-Exposure Table`:
|
|
84
|
+
|
|
85
|
+
| Blind spot | Lens |
|
|
86
|
+
|---|---|
|
|
87
|
+
| Cold entry | `beginner` |
|
|
88
|
+
| Everyday friction | `main-player` |
|
|
89
|
+
| Outside currency | `expert` |
|
|
90
|
+
| Optimistic self-verification | `challenger` plus cross-family evidence |
|
|
91
|
+
| Rebuilding an existing asset | `fact-checker` |
|
|
92
|
+
| Ungrounded numbers or references | `phantom-quench` |
|
|
93
|
+
| Unclear | `challenger` |
|
|
94
|
+
|
|
95
|
+
Run an agent lens through `fh-run` or a direct `codex exec` reading the agent spec. The result remains
|
|
96
|
+
evidence for the governor to source-close.
|
|
97
|
+
|
|
98
|
+
### Intent marshaling
|
|
99
|
+
|
|
100
|
+
For ordinary work requests, read
|
|
101
|
+
`knowledge/shared/harness-core/intent_marshaling_general_work.md` before applying its ladder or when a
|
|
102
|
+
capability gap appears. The required loop is: restate deliverable and doneness; enumerate installed
|
|
103
|
+
and mapped capability with trust tiers; compose and run reversible FH-native work; cite the scan
|
|
104
|
+
before declaring a gap; search the internal registry, then external capability, then synthesize
|
|
105
|
+
in-session; apply the Author-Exposure check to material output.
|
|
106
|
+
|
|
107
|
+
Marshaling never upgrades trust. Non-FH sibling `ask-tier` capability remains propose-only.
|
|
108
|
+
Send, post, deploy, delete, and payment retain their own gates. Installing external capability routes
|
|
109
|
+
to `plugin-recommender` HITL; persisting a synthesized skill routes to the New-Skill gate.
|
|
110
|
+
|
|
111
|
+
### Measurement integrity
|
|
112
|
+
|
|
113
|
+
Read `knowledge/shared/harness-core/measurement-integrity-checklist.md` before relying on a scan,
|
|
114
|
+
checker, or metric. Demonstrate that the instrument separates one known-positive target from one
|
|
115
|
+
known-clean target, and inspect at least one hit before stating a count. Report no-target and
|
|
116
|
+
mid-run failure as `UNMEASURED`. Treat all-pass or all-fail output as an instrument warning.
|
|
117
|
+
|
|
118
|
+
### Irreversible surfaces
|
|
119
|
+
|
|
120
|
+
The Pre-Publish and Destructive-Op gates in `CLAUDE.md` fire on intent rather than file paths.
|
|
121
|
+
Read the relevant gate before any publish, delete, or history rewrite. The pre-push hook provides
|
|
122
|
+
only the git-side mechanical backstop.
|
|
123
|
+
|
|
124
|
+
## §Invocation-patterns
|
|
125
|
+
|
|
126
|
+
### Single agent
|
|
127
|
+
|
|
128
|
+
Ask for the required lens directly, for example: "Analyze this SKILL.md for structural flaws before
|
|
129
|
+
I commit it." Claude may description-dispatch `quench-challenger`; a non-Claude runtime invokes the
|
|
130
|
+
agent through the adapter.
|
|
131
|
+
|
|
132
|
+
### Parallel independent work
|
|
133
|
+
|
|
134
|
+
Dispatch two or more agents concurrently only when their tasks are independent, such as a
|
|
135
|
+
`fact-checker` duplicate scan and a `persona-innovator` naming-gap scan. The orchestrator integrates
|
|
136
|
+
their evidence after both finish.
|
|
137
|
+
|
|
138
|
+
### Wave composition
|
|
139
|
+
|
|
140
|
+
For complex dependent work, use `agent-composer`: Wave 0 reconnaissance, Wave 1 execution, then
|
|
141
|
+
Wave 2 synthesis.
|
|
142
|
+
|
|
143
|
+
## §Codex-entry-points
|
|
144
|
+
|
|
145
|
+
Read a skill workflow directly:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
cat plugins/fh-meta/skills/steel-quench/SKILL.md
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Prefer the runtime adapter:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
FH_BACKEND=codex npx --package @chrono-meta/fh-gate fh-run \
|
|
155
|
+
--skill steel-quench \
|
|
156
|
+
--file path/to/artifact.md
|
|
157
|
+
|
|
158
|
+
FH_BACKEND=codex npx --package @chrono-meta/fh-gate fh-run \
|
|
159
|
+
--agent fh-commons:quench-challenger \
|
|
160
|
+
--file path/to/artifact.md
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Direct headless fallback:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
cat plugins/fh-meta/skills/steel-quench/SKILL.md path/to/artifact.md \
|
|
167
|
+
| codex exec -m gpt-5.5 -
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
`codex exec -m gpt-5.5 -` reads stdin headlessly. Interactive `npx @openai/codex` requires a TTY and
|
|
171
|
+
is not the headless substitute.
|
|
172
|
+
|
|
173
|
+
## §Compatibility-tiers
|
|
174
|
+
|
|
175
|
+
| Tier | Definition | Examples |
|
|
176
|
+
|---|---|---|
|
|
177
|
+
| **M1 — Full** | No Claude-native dependency | `token-budget-gate`, `asset-placement-gate`, `phantom-quench`, `deep-clarify`, `convergence-loop` |
|
|
178
|
+
| **M2 — Partial** | Core works; native agent/slash-command steps need adaptation | `deliberation`, `steel-quench`, `harness-doctor`, `context-doctor`, `sim-conductor`, `harvest-loop` |
|
|
179
|
+
| **M3 — Claude-only** | Requires a Claude hook or session-scoped dispatch | `goal-quench`, `hub-cc-pr-reviewer`, `install-wizard` |
|
|
180
|
+
|
|
181
|
+
**Which phase needs adapting** — the operative half of the M2/M3 rows. Without this a tier label
|
|
182
|
+
tells a non-Claude runtime that a skill is "partial" but not *where* to intervene, which is the only
|
|
183
|
+
thing it can act on. (Restored 2026-07-30 during review of the salience split: the rows survived the
|
|
184
|
+
move, these per-skill cues did not, and they existed in no other file.)
|
|
185
|
+
|
|
186
|
+
| Skill | Runs unchanged | Needs substitution |
|
|
187
|
+
|---|---|---|
|
|
188
|
+
| `steel-quench` | Waves 1–3 | the `quench-challenger` agent step |
|
|
189
|
+
| `harvest-loop` | the git-scan phase | PR auto-proposal |
|
|
190
|
+
| `deliberation` | proposal/synthesis structure | Mediator and Jury agent steps |
|
|
191
|
+
| `goal-quench` (M3) | — | Phase 3 depends on a Claude Stop hook |
|
|
192
|
+
| `hub-cc-pr-reviewer` (M3) | — | needs Claude session context |
|
|
193
|
+
| `install-wizard` (M3) | — | writes `settings.json` |
|
|
194
|
+
|
|
195
|
+
For M2, replace `Agent(subagent_type=...)` and slash-command steps with `fh-run` or direct
|
|
196
|
+
`codex exec` reading the relevant spec.
|
|
197
|
+
|
|
198
|
+
Use Codex native goal/session control when available. FH's portable role is the post-goal quality
|
|
199
|
+
gate (`fh-gate`). `fh-goal` is for non-interactive one-shot runs followed automatically by
|
|
200
|
+
`fh-gate`; it does not replace native goal control.
|
|
201
|
+
|
|
202
|
+
## §Beta-removal
|
|
203
|
+
|
|
204
|
+
| Condition | Status |
|
|
205
|
+
|---|---|
|
|
206
|
+
| Known-limitations document published at `docs/codex-compat.md` | Done |
|
|
207
|
+
| At least 5 externally validated M1 skill runs from non-authors | Pending |
|
|
208
|
+
| At least 1 external Codex user confirms methodology reproduction | Pending |
|
|
209
|
+
| README badge removes `beta` | Blocked on the external conditions |
|
|
210
|
+
|
|
211
|
+
Internal author validation does not satisfy the external conditions. The author validated
|
|
212
|
+
`phantom-quench` against a phantom-seeded fixture and `asset-placement-gate` against a duplicate-skill
|
|
213
|
+
proposal on 2026-06-04. See `docs/codex-compat.md` for limitations and validation details.
|
|
214
|
+
|
|
215
|
+
Report external validation through an issue on `chrono-meta/forge-harness` with the
|
|
216
|
+
`codex-validation` label.
|
|
217
|
+
|
|
218
|
+
## §Adding-agents
|
|
219
|
+
|
|
220
|
+
Before adding an agent:
|
|
221
|
+
|
|
222
|
+
1. Run `asset-placement-gate` and confirm the role does not duplicate an existing asset.
|
|
223
|
+
2. Use a plain description with no self-marketing language.
|
|
224
|
+
3. Define at least one explicit `Done When` condition.
|
|
225
|
+
4. Provide at least three natural-language trigger examples.
|
|
226
|
+
5. Make the agent independently executable or document its dependencies.
|
|
227
|
+
6. Add the canonical spec under the correct `plugins/*/agents/` directory.
|
|
228
|
+
7. Synchronize `AGENTS.md` and `.claude/registry/agent_cards.json`.
|
|
229
|
+
|
|
230
|
+
Before commit, apply `.claude/rules/fh_4axis_gate.md §FH Improvement 4-Axis Auto-Gate` and read
|
|
231
|
+
`knowledge/shared/rules/operations.md §Sub-agent Operations`. After at least two weeks of use,
|
|
232
|
+
strengthen an agent when accepted invocations are at least 60%; redefine or deprecate it when
|
|
233
|
+
rejected invocations are at least 40%.
|
|
@@ -716,7 +716,7 @@ Missing any layer = compression risk. (Path conventions adapt per project — se
|
|
|
716
716
|
## References
|
|
717
717
|
|
|
718
718
|
- `README.md §Architecture — 2-layer design` — sidecar note in Automation layer section
|
|
719
|
-
- `
|
|
719
|
+
- `knowledge/shared/harness-core/agents_md_runtime_details.md §Sidecar-routing-and-waiting` — sidecar note distinguishing adapter invocation from agent dispatch
|
|
720
720
|
- FH paper (Zenodo DOI: 10.5281/zenodo.20397566, arXiv: submit/7657304) — harness-as-durable-layer thesis
|
|
721
721
|
- A sister-harness `sidecar-orchestrator` SKILL.md (2026-06-01) — gh copilot + corporate endpoint + 3-tier fallback + 3-layer persistence
|
|
722
722
|
- arXiv:2605.26302 AgingBench — compression aging defense rationale
|
|
@@ -580,3 +580,17 @@
|
|
|
580
580
|
withheld by a "standing session instruction"; the operator pointed out they never said that — it is
|
|
581
581
|
session-level SYSTEM configuration, and the wording blurred the source. Corrected in the markers and
|
|
582
582
|
here. The operator then explicitly requested the agent, which satisfies the configured exception.
|
|
583
|
+
- date: 2026-07-30
|
|
584
|
+
agent: fh-meta:challenger
|
|
585
|
+
task: "Axis-2 adversarial review of the per-node floor check + install-wizard SessionStart gap (5 rounds + 1 confirmation)"
|
|
586
|
+
mode: isolated Agent, opus, same-family (no cross-family sidecar reachable — disclosed in the marker, not silently passed)
|
|
587
|
+
outcome: accepted
|
|
588
|
+
evidence: "S-grade 6 → 5 → 4 → 2 → 0, CONVERGED. Caught, among others: a false 'tracked settings.json survives a clone' claim; two of this repo's own gate scripts already red; a group-level hook merge that deleted a user's own hook; a broken machine going permanently silent after one report; foreign (husky) hooks counted as FH floors; and a Mode D applicability gate that silenced the exact incident that started the work."
|
|
589
|
+
note: "Its standing prescription — write the known-pair lanes BEFORE the fix — was adopted at round 4 and ended three consecutive rounds of fix-induced regressions (0 in rounds 4, 5, confirmation). One prescription was REJECTED with measured grounds (widening the package-coverage predicate: 25 new hits, 24 artifacts), and the challenger then correctly showed my rejection rationale was itself circular; the file now labels that check UNQUANTIFIED."
|
|
590
|
+
- date: 2026-07-30
|
|
591
|
+
agent: general-purpose (Sonnet-pinned target-tier sim)
|
|
592
|
+
task: "Blind fresh-session simulation: would a new machine end up correctly wired if the user opens with a task, not a greeting?"
|
|
593
|
+
mode: isolated Agent, sonnet, run BEFORE the fixes
|
|
594
|
+
outcome: accepted
|
|
595
|
+
evidence: "Two findings, both closed. (1) The node check was unreachable on the fresh-machine case because it lived in a gitignored registration — drove the split into scripts/fh_node_check.sh + a tracked snippet. (2) The wizard wrote a literal '<your-store>' placeholder and reported success, leaving a dead hook — the real BE_DIR is now baked in at write time."
|
|
596
|
+
note: "Sonnet tier was the right instrument here: both findings are about instructions being followed literally, which is exactly where a weaker tier diverges from an author's intent."
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chrono-meta/fh-gate",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.78",
|
|
4
4
|
"description": "FH runtime adapters — run FH governance, skills, and agents via Claude or Codex with machine-parseable gates.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -106,6 +106,7 @@
|
|
|
106
106
|
"scripts/test_memory_link_check.sh",
|
|
107
107
|
"scripts/memory_nearcheck.py",
|
|
108
108
|
"scripts/sidecar_wait.sh",
|
|
109
|
+
"scripts/test_sidecar_wait_stdin.sh",
|
|
109
110
|
"scripts/test_session_close_lanes.sh",
|
|
110
111
|
"scripts/test_card_drift_probe.sh",
|
|
111
112
|
"scripts/universal_guard_check.sh",
|
|
@@ -124,6 +125,11 @@
|
|
|
124
125
|
"README.md",
|
|
125
126
|
"README.ko.md",
|
|
126
127
|
"README.zh.md",
|
|
127
|
-
"README.ja.md"
|
|
128
|
+
"README.ja.md",
|
|
129
|
+
"scripts/fh_node_check.sh",
|
|
130
|
+
"templates/settings.SessionStart.snippet.json",
|
|
131
|
+
"scripts/test_node_check_lanes.sh",
|
|
132
|
+
"scripts/sidecar_calibrate.sh",
|
|
133
|
+
"scripts/test_sidecar_calibrate_lanes.sh"
|
|
128
134
|
]
|
|
129
135
|
}
|
|
@@ -48,6 +48,94 @@ claude --version 2>/dev/null || echo "Claude Code not installed"
|
|
|
48
48
|
|
|
49
49
|
**⚠️ When Bedrock routing confirmed**: Request AWS quota increase (TPM/RPM increase) or raise LiteLLM `max_input_tokens`, then retry. Fundamental solution is switching to direct Anthropic API access.
|
|
50
50
|
|
|
51
|
+
### Node floor check — run on EVERY machine, not once per user
|
|
52
|
+
|
|
53
|
+
A user's context (companion store, memory, session card) travels between machines; **the machine's
|
|
54
|
+
own setup does not**. A rich context makes a fresh laptop read as "already configured", so the two
|
|
55
|
+
mechanical floors below must be checked per node — this is the check `scripts/fh_node_check.sh`
|
|
56
|
+
points at when it reports a missing floor at turn 0.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# HUB_DIR — the same variable the hook scripts use. Do NOT invent a second name (an earlier draft
|
|
60
|
+
# read FH_DIR, which nothing sets, so an operator with HUB_DIR set would have been silently told
|
|
61
|
+
# about a different repo).
|
|
62
|
+
FH="${HUB_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
|
|
63
|
+
|
|
64
|
+
# ① git-side floor. Probe the EXECUTABLE HOOK, not the config key — `core.hooksPath` unset is a
|
|
65
|
+
# normal, working install when hooks sit in .git/hooks, and a set-but-empty path is a broken
|
|
66
|
+
# install that the key alone reports as fine. Both directions are wrong; the file is the truth.
|
|
67
|
+
# Resolve the directory with `git rev-parse --git-path` rather than assembling "$FH/.git/hooks":
|
|
68
|
+
# in a LINKED WORKTREE `.git` is a file, so the assembled path does not exist and every hook
|
|
69
|
+
# reads as missing (verified: a worktree with working hooks reported ❌❌ under the old form).
|
|
70
|
+
# Same resolution the script uses — one predicate, not two.
|
|
71
|
+
# --path-format needs git >= 2.31; on older git fall back to the RELATIVE --git-path form resolved
|
|
72
|
+
# against the toplevel, never to a hand-built "$FH/.git/hooks" (which is the worktree bug above).
|
|
73
|
+
HD="$(git -C "$FH" rev-parse --path-format=absolute --git-path hooks 2>/dev/null)"
|
|
74
|
+
if [ -z "$HD" ]; then
|
|
75
|
+
_rel="$(git -C "$FH" rev-parse --git-path hooks 2>/dev/null || echo .git/hooks)"
|
|
76
|
+
case "$_rel" in /*) HD="$_rel" ;; *) HD="$(git -C "$FH" rev-parse --show-toplevel 2>/dev/null || echo "$FH")/$_rel" ;; esac
|
|
77
|
+
fi
|
|
78
|
+
for h in pre-commit pre-push; do
|
|
79
|
+
if [ -x "$HD/$h" ]; then echo "✅ $h executable ($HD)"
|
|
80
|
+
else echo "❌ $h MISSING or not executable at $HD — that gate is not running on this node"; fi
|
|
81
|
+
done
|
|
82
|
+
|
|
83
|
+
# ② turn-0 load floor (Mode D only): the companion-load hook. Its registration lives in the
|
|
84
|
+
# GITIGNORED settings.local.json, so it does NOT survive a re-clone — hence a per-node check.
|
|
85
|
+
# Parse the JSON and match the SCRIPT NAME inside hooks.SessionStart — not a bare grep for the
|
|
86
|
+
# key. settings.json also carries a SessionStart entry (fh_node_check), so keying on the key
|
|
87
|
+
# alone passes on a machine where the companion load is absent; and a plain substring grep also
|
|
88
|
+
# hits commented-out lines, other hook events, and permission strings. Same predicate the script
|
|
89
|
+
# uses — two predicates reading one state differently is how a result leaks silently.
|
|
90
|
+
# Applicability gate FIRST, and keyed the same way the script keys it: a Mode D user is one with
|
|
91
|
+
# an exported BE_DIR, or a CLAUDE.local.md that MENTIONS a companion binding — NOT one who merely
|
|
92
|
+
# HAS a CLAUDE.local.md (that is Claude Code's standard local-override file; anyone may keep one),
|
|
93
|
+
# and NOT one who has a settings.local.json (gitignored, so a fresh clone lacks it — and a fresh
|
|
94
|
+
# clone with a full companion store is the exact case this must not silence). The vocabulary
|
|
95
|
+
# covers every backend the wizard documents (vault · gbrain · *-be repo), because an FH-flavoured
|
|
96
|
+
# regex would silence two first-class backends. It is a MENTION test, not semantic: "I do not use
|
|
97
|
+
# a companion store" also matches, and that over-match costs one informational line — the cheap
|
|
98
|
+
# direction, since the expensive direction is silence.
|
|
99
|
+
if { [ -n "${BE_DIR:-}" ] && [ -d "$BE_DIR" ]; } \
|
|
100
|
+
|| { [ -f "$FH/CLAUDE.local.md" ] && grep -qiE 'BE_DIR|companion[ -]store|컴패니언|vault|gbrain|obsidian' "$FH/CLAUDE.local.md"; }; then
|
|
101
|
+
python3 - "$FH" <<'PY'
|
|
102
|
+
import json, os, sys
|
|
103
|
+
hub = sys.argv[1]
|
|
104
|
+
for p in (os.path.join(hub, ".claude", "settings.local.json"),
|
|
105
|
+
os.path.expanduser("~/.claude/settings.json")):
|
|
106
|
+
try: groups = json.load(open(p)).get("hooks", {}).get("SessionStart", [])
|
|
107
|
+
except Exception: continue
|
|
108
|
+
if any("fh_session_load" in h.get("command", "")
|
|
109
|
+
for g in groups for h in g.get("hooks", [])):
|
|
110
|
+
print("✅ companion-load SessionStart registered"); sys.exit(0)
|
|
111
|
+
print("❌ companion-load SessionStart MISSING — freshness + env-delta do not fire at turn 0")
|
|
112
|
+
PY
|
|
113
|
+
else
|
|
114
|
+
echo "N/A companion-load SessionStart (not a Mode D setup — no companion store configured)"
|
|
115
|
+
fi
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Bootstrap note — honest scope**: `scripts/fh_node_check.sh` (the check that *reports* a missing
|
|
119
|
+
floor at turn 0) ships with the clone, but its **registration does not**: every
|
|
120
|
+
`.claude/settings*.json` path in this repo is gitignored (confirm with `git check-ignore -v
|
|
121
|
+
.claude/settings.json` — don't trust a line number, they move), so no SessionStart entry can be
|
|
122
|
+
tracked. The tracked artifact is `templates/settings.SessionStart.snippet.json`; `/install-wizard`
|
|
123
|
+
merges it. A user who never runs the wizard still gets no turn-0 signal — that residual is reduced,
|
|
124
|
+
not closed, and this check item is the backstop for it.
|
|
125
|
+
|
|
126
|
+
**Emission model** (so the output is read correctly): a **missing floor is reported every session**
|
|
127
|
+
until it is fixed — it is a persistent condition, not an event. A healthy machine is silent. So
|
|
128
|
+
seeing this banner twice is not a bug, and seeing it once then never again means it was an *event*
|
|
129
|
+
line (identity change / infra delta), not a floor complaint.
|
|
130
|
+
|
|
131
|
+
**Fix for either ❌**: re-run `/install-wizard` (it registers both — `install-wizard/SKILL_detail.md`
|
|
132
|
+
§Mode-D-Companion-Setup step 3 and the 4-axis gate block), or apply the two commands it uses directly.
|
|
133
|
+
|
|
134
|
+
**Why this is a check item and not a note** (measured 2026-07-30): a second machine held the full
|
|
135
|
+
companion store and memory, yet ran with **both** SessionStart hooks absent. Nothing surfaced it —
|
|
136
|
+
the miss was found by accident. A weak tier is where this bites hardest: a mechanized floor is
|
|
137
|
+
tier-independent, while the prose it replaces is exactly what a weaker model drops first.
|
|
138
|
+
|
|
51
139
|
---
|
|
52
140
|
|
|
53
141
|
## Step 1. Existing Asset Inventory
|
|
@@ -88,7 +88,7 @@ All 3 → Proceed in order: Step 0-B (token injection) → Step 0 (environment c
|
|
|
88
88
|
|
|
89
89
|
**Mode D detected (FH developer/researcher)**: Guide companion-store setup before Step 1. **Ask the backend first** — the store is a role (durable private home for artifacts), not a fixed `*-be` git repo: Obsidian vault / gbrain-ingest / `*-be` git repo (default) all qualify. Do not assume the repo path.
|
|
90
90
|
|
|
91
|
-
**Set the store up as a queryable WIKI, not an empty folder** — the store earns its value when the agent can *read it well*, so the scaffold includes: an `INDEX.md` wiki-home (**the `CATALOG.md` read-first pattern applied to the store** — named INDEX to avoid two CATALOG.md across the public + private repos; section map + **read-time-derived** pointers, never hand-maintained placeholders that rot) · session-start read wiring the AI **idempotently adds** to `CLAUDE.local.md` (grep-guarded against duplication: read INDEX → open result/signal files newer than the session card, not only handoffs) · the Raw/Wiki/Conversation ingest axis (`sync_push_protocols.md`). The git `*-be` form is the **default within the no-existing-store branch** (it stays an ask-first choice — see the backend question; SKILL.md does not override the detail file's neutral ordering), recommended *there* because for a non-visual user *observability is the agent querying the wiki* (INDEX + session-start read) — git-versioned, agent-native (grep/Read), no cloud egress; FH's answer to its weak Observability layer. Obsidian (graph-view = the visual observability surface) and cloud are equal options when the user already runs one. **
|
|
91
|
+
**Set the store up as a queryable WIKI, not an empty folder** — the store earns its value when the agent can *read it well*, so the scaffold includes: an `INDEX.md` wiki-home (**the `CATALOG.md` read-first pattern applied to the store** — named INDEX to avoid two CATALOG.md across the public + private repos; section map + **read-time-derived** pointers, never hand-maintained placeholders that rot) · session-start read wiring the AI **idempotently adds** to `CLAUDE.local.md` (grep-guarded against duplication: read INDEX → open result/signal files newer than the session card, not only handoffs) · the Raw/Wiki/Conversation ingest axis (`sync_push_protocols.md`). The git `*-be` form is the **default within the no-existing-store branch** (it stays an ask-first choice — see the backend question; SKILL.md does not override the detail file's neutral ordering), recommended *there* because for a non-visual user *observability is the agent querying the wiki* (INDEX + session-start read) — git-versioned, agent-native (grep/Read), no cloud egress; FH's answer to its weak Observability layer. Obsidian (graph-view = the visual observability surface) and cloud are equal options when the user already runs one. **Mechanical floor (corrected 2026-07-30)**: the `CLAUDE.local.md` read is prose, so the wizard **also registers the `SessionStart` hooks** (`fh_session_load.sh` companion freshness + `fh_env_delta_scan.sh` env-delta) into the gitignored `settings.local.json` — see `SKILL_detail.md §Mode-D-Companion-Setup` step 3. The earlier text here claimed no such hook existed and marked the gap "accepted"; both were wrong, and a clean second-machine install measured the miss (turn-0 load skipped on a task-first entry, on Opus). Prose is the layer **over** the floor, never the floor.
|
|
92
92
|
|
|
93
93
|
**Beyond Mode D (not forced)**: a non-developer user who accumulates their own context into FH and keeps no separate local store benefits too — but since accumulation is unobservable at *first* install, surface this offer only on a **re-run where FH-written artifacts already exist** (e.g. non-empty `tracks/*/session_*.md`), not preemptively at first setup. A user with a local store, or with no FH artifacts, is not prompted.
|
|
94
94
|
|
|
@@ -93,8 +93,71 @@ IDX
|
|
|
93
93
|
WIRING="At session start: read \$BE_DIR/INDEX.md first; then ls -t paper-signals/ handoff/ digests/ and open anything newer than the session card (card=pointer, store commit=truth) — not only handoffs."
|
|
94
94
|
grep -qF "read \$BE_DIR/INDEX.md first" "$HUB_DIR/CLAUDE.local.md" 2>/dev/null \
|
|
95
95
|
|| printf '\n## Companion-store session-start read\n%s\n' "$WIRING" >> "$HUB_DIR/CLAUDE.local.md"
|
|
96
|
+
|
|
97
|
+
# 3. MECHANICAL FLOOR over that prose — register the SessionStart hooks (REQUIRED, not optional).
|
|
98
|
+
# WHY: step 2 writes an INSTRUCTION into CLAUDE.local.md. An instruction is salience: on task-first
|
|
99
|
+
# entry (the user's first message is a task, so the onboarding menu is correctly suppressed) or on a
|
|
100
|
+
# weaker tier, it silently does not fire and the session runs on stale local state. The two hooks
|
|
101
|
+
# below fire BEFORE turn 0 regardless of what the user types.
|
|
102
|
+
# - fh_session_load.sh → companion freshness (this section's own load)
|
|
103
|
+
# - fh_env_delta_scan.sh → undeployed-sibling-repo discovery (CLAUDE.md claim ②; rated
|
|
104
|
+
# PARTIAL/THEATER by the 2026-07-06 three-family audit while prose-only)
|
|
105
|
+
# Registration lives in the GITIGNORED .claude/settings.local.json — BE_DIR is an operator-private
|
|
106
|
+
# path and must never land in the project-shared settings.json. Idempotent: re-running replaces, never dups.
|
|
107
|
+
# Measured miss 2026-07-30 (n=2, a freshly installed second machine): neither hook was registered,
|
|
108
|
+
# so the Mode D load ran only because the operator happened to open with a greeting. A task-first
|
|
109
|
+
# first message would have skipped it entirely — on Opus, not merely on a weak tier.
|
|
110
|
+
if [ -n "${BE_DIR:-}" ] && [ -d "$HUB_DIR/scripts" ]; then
|
|
111
|
+
# The real $BE_DIR is passed IN and baked into the command — never a "<your-store>" placeholder
|
|
112
|
+
# for the user to swap later. A placeholder written into a config file is a hook that reports
|
|
113
|
+
# "registered" and then silently resolves to a nonexistent path; the Sonnet target-tier sim
|
|
114
|
+
# (2026-07-30) named exactly this failure — the script prints success, the operator stops, the
|
|
115
|
+
# hook is dead. If a value must be substituted, substitute it at write time or do not write.
|
|
116
|
+
python3 - "$HUB_DIR" "$BE_DIR" <<'PY'
|
|
117
|
+
import json, os, sys, collections
|
|
118
|
+
hub, be = sys.argv[1], sys.argv[2]
|
|
119
|
+
p = os.path.join(hub, ".claude", "settings.local.json")
|
|
120
|
+
d = collections.OrderedDict()
|
|
121
|
+
if os.path.exists(p):
|
|
122
|
+
with open(p) as fh:
|
|
123
|
+
d = json.load(fh, object_pairs_hook=collections.OrderedDict)
|
|
124
|
+
cmd = lambda s: {"type": "command",
|
|
125
|
+
"command": f'BE_DIR="{be}" bash "$CLAUDE_PROJECT_DIR/scripts/{s}"',
|
|
126
|
+
"timeout": 20}
|
|
127
|
+
if os.path.exists(p): # back up before rewriting someone's config; a traceback mid-write truncates
|
|
128
|
+
import shutil; shutil.copy2(p, p + ".prewizard")
|
|
129
|
+
hooks = d.setdefault("hooks", collections.OrderedDict())
|
|
130
|
+
# Filter at HOOK level, not GROUP level: a user hook sharing a group with an FH hook would otherwise
|
|
131
|
+
# be deleted with the group (cross-family review 2026-07-30 reproduced that loss).
|
|
132
|
+
FH_HOOKS = ("fh_session_load.sh", "fh_env_delta_scan.sh")
|
|
133
|
+
existing = []
|
|
134
|
+
for g in hooks.get("SessionStart", []):
|
|
135
|
+
survivors = [h for h in g.get("hooks", [])
|
|
136
|
+
if not any(n in h.get("command", "") for n in FH_HOOKS)]
|
|
137
|
+
if survivors:
|
|
138
|
+
g = dict(g); g["hooks"] = survivors; existing.append(g)
|
|
139
|
+
hooks["SessionStart"] = existing + [
|
|
140
|
+
{"matcher": "", "hooks": [cmd("fh_session_load.sh"), cmd("fh_env_delta_scan.sh")]}]
|
|
141
|
+
with open(p, "w") as fh:
|
|
142
|
+
json.dump(d, fh, indent=2, ensure_ascii=False); fh.write("\n")
|
|
143
|
+
print("SessionStart hooks registered ->", p, "(backup: .prewizard)")
|
|
144
|
+
PY
|
|
145
|
+
# VERIFY by running it once — a registration that was never executed is not a floor.
|
|
146
|
+
# Expected: a "companion-store freshness" line on stdout. If it prints nothing, BE_DIR is wrong.
|
|
147
|
+
BE_DIR="$BE_DIR" bash "$HUB_DIR/scripts/fh_session_load.sh" | head -3
|
|
148
|
+
fi
|
|
96
149
|
```
|
|
97
150
|
|
|
151
|
+
> **Known-pair check before calling this done** (per `measurement-integrity-checklist.md
|
|
152
|
+
> §Instrument-Calibration`) — **run both legs, do not copy this verdict**:
|
|
153
|
+
> - **known-positive** — `BE_DIR` set → a `companion-store freshness` line prints.
|
|
154
|
+
> - **known-negative** — `BE_DIR` unset → **no companion block prints** and the script exits 0.
|
|
155
|
+
> It is *not* fully silent: the frontier-digest section runs above the Mode-D guard and speaks to
|
|
156
|
+
> every user about their own local digest. That is intended. An earlier draft of this note claimed
|
|
157
|
+
> the negative leg was silent and made that a ship-blocker; running it showed 171 bytes of correct
|
|
158
|
+
> frontier output — the note was written without executing the check it prescribed.
|
|
159
|
+
> If the negative leg prints anything referencing the **companion store**, do not ship.
|
|
160
|
+
|
|
98
161
|
- **Raw / Wiki / Conversation ingest axis** (`sync_push_protocols.md`): classify each artifact by
|
|
99
162
|
processing stage — Raw (unprocessed capture) → stays raw; Wiki (distilled + `[[linked]]`) → the
|
|
100
163
|
compounding layer; Conversation (dialogue/decision log). The Raw→Wiki distill is where linking earns
|
|
@@ -102,9 +165,16 @@ grep -qF "read \$BE_DIR/INDEX.md first" "$HUB_DIR/CLAUDE.local.md" 2>/dev/null \
|
|
|
102
165
|
- **Backend note**: for an **Obsidian** backend the graph view is the *visual* observability surface
|
|
103
166
|
(free for that backend); for the recommended **git `*-be`** form, observability is the agent querying
|
|
104
167
|
INDEX + sections (no visualization needed). gbrain ingests the same markdown.
|
|
105
|
-
- **Salience
|
|
106
|
-
|
|
107
|
-
|
|
168
|
+
- **Salience → mechanical (corrected 2026-07-30)**: this used to read *"the session-start read is prose
|
|
169
|
+
(no SessionStart hook) — accepted limitation; revisit if a target-tier sim measures a miss."* **Both
|
|
170
|
+
halves were wrong by then.** The hook exists (`scripts/fh_session_load.sh`, shipped 2026-07-05), and
|
|
171
|
+
the revisit-trigger has fired: a freshly installed second machine ran with **neither** SessionStart
|
|
172
|
+
hook registered, on Opus — the load fired only because that session happened to open with a greeting
|
|
173
|
+
instead of a task. So step 3 above **registers the hooks**; the CLAUDE.local.md prose stays as the
|
|
174
|
+
human-readable layer *over* that floor, never as the floor itself.
|
|
175
|
+
**Residual (named, not closed)**: registration lives in the gitignored `settings.local.json`, so a
|
|
176
|
+
user who re-clones the hub without re-running the wizard silently loses it again — the honest
|
|
177
|
+
backstop is `install-doctor`'s check item, not the wizard alone.
|
|
108
178
|
|
|
109
179
|
|
|
110
180
|
---
|
|
@@ -446,6 +516,50 @@ source "$FH_DIR/templates/fh_audit_check.zsh"
|
|
|
446
516
|
EOF
|
|
447
517
|
fi
|
|
448
518
|
|
|
519
|
+
# Node floor check hook — ALL users, not Mode D only. Source of truth = the tracked snippet
|
|
520
|
+
# templates/settings.SessionStart.snippet.json (`project_settings_json` key). Registration itself
|
|
521
|
+
# cannot be tracked (every .claude/settings*.json path is gitignored), so the wizard is what wires it
|
|
522
|
+
# — which is exactly why this must not be skipped: without it, a user on a fresh machine gets no
|
|
523
|
+
# turn-0 signal that their floors are missing.
|
|
524
|
+
# NPM-INSTALL PRECONDITION: this block reads the snippet from disk, so both it and
|
|
525
|
+
# scripts/fh_node_check.sh must be in package.json `files[]`. They are (added 2026-07-30 after
|
|
526
|
+
# scripts/package_coverage_check.sh caught the omission — without it an npm-installed wizard hit
|
|
527
|
+
# FileNotFoundError here and registered nothing while reporting success upstream).
|
|
528
|
+
python3 - "$FH_DIR" <<'PY'
|
|
529
|
+
import json, os, sys, collections
|
|
530
|
+
hub = sys.argv[1]
|
|
531
|
+
snippet = os.path.join(hub, "templates", "settings.SessionStart.snippet.json")
|
|
532
|
+
target = os.path.join(hub, ".claude", "settings.json")
|
|
533
|
+
entry = json.load(open(snippet))["project_settings_json"]["hooks"]["SessionStart"]
|
|
534
|
+
d = collections.OrderedDict()
|
|
535
|
+
if os.path.exists(target):
|
|
536
|
+
d = json.load(open(target), object_pairs_hook=collections.OrderedDict)
|
|
537
|
+
import shutil; shutil.copy2(target, target + ".prewizard") # back up before rewriting
|
|
538
|
+
hooks = d.setdefault("hooks", collections.OrderedDict())
|
|
539
|
+
# Merge at HOOK level, not group level. A group-level filter drops the whole group when a user's own
|
|
540
|
+
# hook shares a group with the FH one — the common shape when someone hand-edits or appends to an
|
|
541
|
+
# older wizard's output. (Cross-family review 2026-07-30 reproduced the loss: a group holding
|
|
542
|
+
# [my_telemetry.sh, fh_session_load.sh] lost my_telemetry.sh entirely.)
|
|
543
|
+
kept = []
|
|
544
|
+
for g in hooks.get("SessionStart", []):
|
|
545
|
+
survivors = [h for h in g.get("hooks", []) if "fh_node_check" not in h.get("command", "")]
|
|
546
|
+
if survivors:
|
|
547
|
+
g = dict(g); g["hooks"] = survivors; kept.append(g)
|
|
548
|
+
hooks["SessionStart"] = kept + entry
|
|
549
|
+
os.makedirs(os.path.dirname(target), exist_ok=True)
|
|
550
|
+
with open(target, "w") as fh:
|
|
551
|
+
json.dump(d, fh, indent=2, ensure_ascii=False); fh.write("\n")
|
|
552
|
+
print("node-check SessionStart hook registered ->", target)
|
|
553
|
+
PY
|
|
554
|
+
chmod +x "$FH_DIR/scripts/fh_node_check.sh" 2>/dev/null
|
|
555
|
+
# VERIFY against a THROWAWAY state file (FH_NODE_STATE). Verifying against the real state would
|
|
556
|
+
# consume the user's one-shot event report, so their actual first session goes quiet and the notice
|
|
557
|
+
# is buried in install output instead (cross-family review 2026-07-30).
|
|
558
|
+
# known-pair: healthy machine → run 1 prints an event line, run 2 is SILENT.
|
|
559
|
+
# missing floor → prints EVERY run (a floor gap is a condition, not an event).
|
|
560
|
+
_T="$(mktemp)"; FH_NODE_STATE="$_T" bash "$FH_DIR/scripts/fh_node_check.sh"
|
|
561
|
+
FH_NODE_STATE="$_T" bash "$FH_DIR/scripts/fh_node_check.sh"; rm -f "$_T"
|
|
562
|
+
|
|
449
563
|
# 4-axis verification gate (Mode D / FH-self-development only — OPT-IN, double-confirm required)
|
|
450
564
|
# SCOPE (state this before asking): this gates commits IN YOUR FH CLONE ($FH_DIR) — git commit there is
|
|
451
565
|
# blocked until the 4-axis markers pass. It is FH-internal infra (hardcodes hub paths/markers) and is
|