@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,184 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# fh_node_check.sh — per-NODE environment floor check, fired at SessionStart.
|
|
3
|
+
#
|
|
4
|
+
# WHY A NODE-SCOPED CHECK EXISTS:
|
|
5
|
+
# A user's context (companion store, memory, session card) travels between machines; the machine's
|
|
6
|
+
# own wiring does not. A rich context makes an unwired laptop read as "already configured".
|
|
7
|
+
# Measured 2026-07-30: a machine holding the full companion store and memory ran sessions with its
|
|
8
|
+
# SessionStart hooks unregistered; nothing surfaced it — it was found by accident.
|
|
9
|
+
#
|
|
10
|
+
# WHY IT IS NOT INSIDE fh_session_load.sh:
|
|
11
|
+
# That script is registered in the gitignored .claude/settings.local.json, so on a fresh clone it
|
|
12
|
+
# is not registered — the situation this check exists for is the one in which it could not run.
|
|
13
|
+
# This script carries no operator-private path, so it can be registered from
|
|
14
|
+
# templates/settings.SessionStart.snippet.json, which IS tracked and ships with a clone.
|
|
15
|
+
# HONEST SCOPE: registration still requires the wizard to merge that snippet — every
|
|
16
|
+
# .claude/settings*.json path in this repo is gitignored, so no SessionStart entry can be tracked.
|
|
17
|
+
# The chicken-and-egg is REDUCED (script + snippet ship), not ELIMINATED (a user who never runs
|
|
18
|
+
# the wizard still gets nothing). An earlier revision of this header claimed "survives a clone";
|
|
19
|
+
# that was false, and a cross-family review caught it before merge. Do not restore the claim
|
|
20
|
+
# without running `git check-ignore` on the settings paths first.
|
|
21
|
+
#
|
|
22
|
+
# EMISSION IS STATE-BASED, NOT EVENT-BASED — this is the load-bearing design decision:
|
|
23
|
+
# A missing floor is a PERSISTENT CONDITION, so it is reported EVERY session until fixed.
|
|
24
|
+
# A healthy machine is silent AFTER its one event line (first session / machine change / infra
|
|
25
|
+
# delta) — events report once, conditions report until they stop being true.
|
|
26
|
+
# Earlier revisions fired on events only (first session / machine change / idle >= 7 days / HEAD
|
|
27
|
+
# advanced >= 20 commits) and that was wrong in both directions: on this hub's measured velocity
|
|
28
|
+
# (~33 commits/week) the commit axis fired every ~4 days on a healthy machine (noise, and an
|
|
29
|
+
# ignored detector cannot be revived), while a broken machine reported once then went quiet
|
|
30
|
+
# forever — reproducing the very accident above.
|
|
31
|
+
#
|
|
32
|
+
# Detector, never a gate: always exits 0, and it RECOMMENDS — it cannot compel.
|
|
33
|
+
# State: tracks/_meta/.fh_node_state (gitignored; excluded from companion sync — it is machine-local
|
|
34
|
+
# by nature, and mirroring it would make node identity flap between machines).
|
|
35
|
+
# FH_NODE_STATE overrides the state path (used by the wizard's verification so that verifying does
|
|
36
|
+
# not consume a one-shot event report).
|
|
37
|
+
|
|
38
|
+
set -uo pipefail
|
|
39
|
+
|
|
40
|
+
FH="${HUB_DIR:-${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}}"
|
|
41
|
+
STATE="${FH_NODE_STATE:-$FH/tracks/_meta/.fh_node_state}"
|
|
42
|
+
|
|
43
|
+
NODE_ID="${FH_MACHINE_ID:-$(hostname -s 2>/dev/null || echo unknown)}"
|
|
44
|
+
HEAD_NOW="$(git -C "$FH" rev-parse --short HEAD 2>/dev/null || echo none)"
|
|
45
|
+
NOW="$(date +%s)"
|
|
46
|
+
|
|
47
|
+
PREV_ID=""; PREV_EPOCH=0; PREV_HEAD=""
|
|
48
|
+
if [ -f "$STATE" ]; then
|
|
49
|
+
IFS='|' read -r PREV_ID PREV_EPOCH PREV_HEAD < "$STATE" 2>/dev/null || true
|
|
50
|
+
fi
|
|
51
|
+
# Never feed a file-sourced value straight into arithmetic (bash arithmetic evaluates command
|
|
52
|
+
# substitution inside array subscripts).
|
|
53
|
+
case "${PREV_EPOCH:-}" in ''|*[!0-9]*) PREV_EPOCH=0 ;; esac
|
|
54
|
+
|
|
55
|
+
# ── floor probes — always run, before any decision about whether to speak ──────
|
|
56
|
+
MISS=""
|
|
57
|
+
|
|
58
|
+
# ① git-side floor. Probe the EXECUTABLE HOOK, not the config key: `core.hooksPath` unset is a
|
|
59
|
+
# normal working install when hooks live in .git/hooks, and a set-but-empty path is a broken
|
|
60
|
+
# install the key alone reports as fine. Resolve the directory with `git rev-parse --git-path`,
|
|
61
|
+
# which handles set/unset, relative/absolute, AND linked worktrees (where .git is a file, so a
|
|
62
|
+
# hand-built "$FH/.git/hooks" is simply wrong — FH runs worktree-isolated agents, so that path
|
|
63
|
+
# is reachable, not hypothetical).
|
|
64
|
+
if ! git -C "$FH" rev-parse --git-dir >/dev/null 2>&1; then
|
|
65
|
+
# NOT a git repo (plugin-only / marketplace install, or a non-repo directory). A git hook cannot
|
|
66
|
+
# be installed here at all, so this floor is N/A — not missing. Applicability is decided
|
|
67
|
+
# mechanically, per CLAUDE.md §Irreversibility Surface-Class: reporting it would print an
|
|
68
|
+
# UNFIXABLE notice every session (emission is state-based), training the reader to ignore the
|
|
69
|
+
# one check that must not be ignored.
|
|
70
|
+
:
|
|
71
|
+
else
|
|
72
|
+
# --path-format needs git >= 2.31; fall back to the relative form resolved against the repo root
|
|
73
|
+
# (still correct in a linked worktree, where a hand-built "$FH/.git/hooks" does not exist at all).
|
|
74
|
+
HD="$(git -C "$FH" rev-parse --path-format=absolute --git-path hooks 2>/dev/null)"
|
|
75
|
+
if [ -z "$HD" ]; then
|
|
76
|
+
_rel="$(git -C "$FH" rev-parse --git-path hooks 2>/dev/null || echo .git/hooks)"
|
|
77
|
+
case "$_rel" in /*) HD="$_rel" ;; *) HD="$(git -C "$FH" rev-parse --show-toplevel 2>/dev/null || echo "$FH")/$_rel" ;; esac
|
|
78
|
+
fi
|
|
79
|
+
for h in pre-commit pre-push; do
|
|
80
|
+
if [ ! -x "$HD/$h" ]; then
|
|
81
|
+
MISS="${MISS}no executable ${h} hook · "
|
|
82
|
+
elif ! grep -qE 'fh-gate|FH .*Gate|4-Axis|4축' "$HD/$h" 2>/dev/null; then
|
|
83
|
+
# Executable is not the same proposition as OURS. husky and pre-commit-framework are standard
|
|
84
|
+
# equipment in the JS/Python projects FH maps, and they install an executable pre-commit that
|
|
85
|
+
# runs a linter — under an executable-only probe such a machine reports "floors present" and
|
|
86
|
+
# then goes silent, which is precisely the accident this check exists to prevent.
|
|
87
|
+
MISS="${MISS}${h} hook present but not FH's gate (another framework owns it) · "
|
|
88
|
+
fi
|
|
89
|
+
done
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# ② companion-load hook (Mode D only). Reported as INFORMATION, never as a missing floor: this hook
|
|
93
|
+
# is registered for ALL users, and a public non-Mode-D user has no companion store to load, so
|
|
94
|
+
# listing it under ❌ would be a false positive for the majority path.
|
|
95
|
+
# APPLICABILITY: "is this a Mode D user", NOT "does settings.local.json exist". Keying on that
|
|
96
|
+
# file was wrong in the worst possible way — it is gitignored, so a FRESH CLONE never has it, and
|
|
97
|
+
# a fresh clone with a full companion store is EXACTLY the measured 2026-07-30 incident. The gate
|
|
98
|
+
# silenced its own flagship case. Mode D signals that survive a clone: an exported BE_DIR, or the
|
|
99
|
+
# operator's CLAUDE.local.md binding.
|
|
100
|
+
COMPANION_NOTE=""
|
|
101
|
+
_IS_MODE_D=""
|
|
102
|
+
{ [ -n "${BE_DIR:-}" ] && [ -d "$BE_DIR" ]; } && _IS_MODE_D=1
|
|
103
|
+
# CLAUDE.local.md is Claude Code's STANDARD local-override file — anyone may have one for any
|
|
104
|
+
# reason, and having one says nothing about a companion store. Keying on its EXISTENCE re-admitted
|
|
105
|
+
# the majority-path false positive through a second door, and state-based emission made it permanent
|
|
106
|
+
# rather than one-shot (cross-family review 2026-07-30). So key on the file MENTIONING a companion
|
|
107
|
+
# binding instead.
|
|
108
|
+
# The vocabulary spans every backend the wizard documents — the store is a ROLE, not a repo layout
|
|
109
|
+
# (Obsidian vault · gbrain ingest target · *-be repo all qualify), and an FH-flavoured regex would
|
|
110
|
+
# have silently excluded two first-class backends: the same "flagship case goes quiet" shape as the
|
|
111
|
+
# fresh-clone defect, one door over.
|
|
112
|
+
# HONEST SCOPE: this is a MENTION test, not a semantic one. "I do not use a companion store" also
|
|
113
|
+
# matches. The cost of that over-match is one informational line, never a floor claim — deliberately
|
|
114
|
+
# the cheap direction, since the expensive direction is silence.
|
|
115
|
+
[ -f "$FH/CLAUDE.local.md" ] \
|
|
116
|
+
&& grep -qiE 'BE_DIR|companion[ -]store|컴패니언|vault|gbrain|obsidian' "$FH/CLAUDE.local.md" 2>/dev/null \
|
|
117
|
+
&& _IS_MODE_D=1
|
|
118
|
+
if [ -n "$_IS_MODE_D" ] && ! command -v python3 >/dev/null 2>&1; then
|
|
119
|
+
# not found ≠ 0: without a JSON parser the registration verdict is unknown, not clean.
|
|
120
|
+
COMPANION_NOTE="companion-load registration UNMEASURED (no python3 — cannot parse the hook config; do not read this as 'registered')"
|
|
121
|
+
elif [ -n "$_IS_MODE_D" ]; then
|
|
122
|
+
python3 - "$FH" <<'PY' || COMPANION_NOTE="companion-load SessionStart not registered (Mode D — freshness + env-delta will not fire at turn 0)"
|
|
123
|
+
import json, os, sys
|
|
124
|
+
hub = sys.argv[1]
|
|
125
|
+
for p in (os.path.join(hub, ".claude", "settings.local.json"),
|
|
126
|
+
os.path.expanduser("~/.claude/settings.json")):
|
|
127
|
+
try:
|
|
128
|
+
groups = json.load(open(p)).get("hooks", {}).get("SessionStart", [])
|
|
129
|
+
except Exception:
|
|
130
|
+
continue
|
|
131
|
+
if any("fh_session_load" in h.get("command", "") for g in groups for h in g.get("hooks", [])):
|
|
132
|
+
sys.exit(0)
|
|
133
|
+
sys.exit(1)
|
|
134
|
+
PY
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
# ── event: infra delta since the commit this clone last saw ───────────────────
|
|
138
|
+
# Reported ONCE per pull (it is an event). Distinguishes "no change" from "could not measure".
|
|
139
|
+
INFRA=""; INFRA_NOTE=""
|
|
140
|
+
if [ -n "$PREV_HEAD" ] && [ "$PREV_HEAD" != "$HEAD_NOW" ]; then
|
|
141
|
+
if INFRA_RAW="$(git -C "$FH" diff --name-only "${PREV_HEAD}..HEAD" 2>/dev/null)"; then
|
|
142
|
+
INFRA="$(printf '%s\n' "$INFRA_RAW" \
|
|
143
|
+
| grep -E '^(templates/\.git-hooks/|templates/settings\.|scripts/fh_|plugins/[^/]+/skills/install-(wizard|doctor)/)' \
|
|
144
|
+
| head -6)"
|
|
145
|
+
else
|
|
146
|
+
INFRA_NOTE="UNMEASURED — cannot reach the previously seen commit ($PREV_HEAD), so the infra delta was not computed (rebase, shallow clone, or GC). Not the same as 'nothing changed'."
|
|
147
|
+
fi
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
IDENTITY=""
|
|
151
|
+
if [ -z "$PREV_ID" ]; then IDENTITY="first session for this clone"
|
|
152
|
+
elif [ "$PREV_ID" != "$NODE_ID" ]; then IDENTITY="machine changed ($PREV_ID → $NODE_ID)"; fi
|
|
153
|
+
|
|
154
|
+
# ── state write — unconditional, and a failure is reported, never swallowed ────
|
|
155
|
+
# Writing only when the check speaks would make the recorded timestamp mean "last time it spoke",
|
|
156
|
+
# and a write failure would make this banner repeat forever with no explanation.
|
|
157
|
+
STATE_WARN=""
|
|
158
|
+
if ! { mkdir -p "$(dirname "$STATE")" 2>/dev/null \
|
|
159
|
+
&& printf '%s|%s|%s' "$NODE_ID" "$NOW" "$HEAD_NOW" > "$STATE" 2>/dev/null; }; then
|
|
160
|
+
STATE_WARN="could not record node state ($STATE) — this notice may repeat every session"
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
# ── emit: condition (every session) OR event (once) ───────────────────────────
|
|
164
|
+
[ -n "$MISS$COMPANION_NOTE$INFRA$INFRA_NOTE$IDENTITY$STATE_WARN" ] || exit 0
|
|
165
|
+
|
|
166
|
+
if [ -n "$MISS" ]; then
|
|
167
|
+
echo "🖥️ [node] Missing mechanical floor on this machine (node: $NODE_ID): ${MISS% · }"
|
|
168
|
+
echo " → Run /install-doctor, then /install-wizard. A rich context (memory, companion store)"
|
|
169
|
+
echo " is a different proposition from this machine being wired."
|
|
170
|
+
elif [ -n "$IDENTITY" ]; then
|
|
171
|
+
echo "🖥️ [node] $IDENTITY (node: $NODE_ID) — floors present."
|
|
172
|
+
fi
|
|
173
|
+
[ -n "$STATE_WARN" ] && echo " ⚠️ $STATE_WARN"
|
|
174
|
+
[ -n "$COMPANION_NOTE" ] && echo " ℹ️ $COMPANION_NOTE"
|
|
175
|
+
if [ -n "$INFRA_NOTE" ]; then
|
|
176
|
+
echo " ⚠️ $INFRA_NOTE"
|
|
177
|
+
elif [ -n "$INFRA" ]; then
|
|
178
|
+
echo " 🆕 Install-relevant assets changed since this clone last ran — being registered is not"
|
|
179
|
+
echo " the same as being current, and a pull moves files without wiring hooks:"
|
|
180
|
+
printf '%s\n' "$INFRA" | while IFS= read -r f; do [ -n "$f" ] && echo " - $f"; done
|
|
181
|
+
echo " → Re-run /install-wizard (idempotent)."
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
exit 0
|
|
@@ -27,6 +27,51 @@
|
|
|
27
27
|
set -uo pipefail
|
|
28
28
|
|
|
29
29
|
FH="${HUB_DIR:-${CLAUDE_PROJECT_DIR:-$HOME/projects/forge-harness}}"
|
|
30
|
+
BE="${BE_DIR:-}" # companion-store path — supplied by the gitignored hook registration; no public default.
|
|
31
|
+
# Resolved HERE (not at the Mode-D block below) because the frontier-digest check
|
|
32
|
+
# needs it: on a multi-node setup the digest producer may be a DIFFERENT machine.
|
|
33
|
+
|
|
34
|
+
# ── node re-entry floor check ────────────────────────────────────────────────
|
|
35
|
+
# 이 검사는 scripts/fh_node_check.sh 로 분리했다. 이유: 이 파일(fh_session_load.sh)은 gitignored
|
|
36
|
+
# settings.local.json 에 등록되므로 새 클론/새 기계에선 애초에 안 돈다 — 검사가 존재 이유가 되는
|
|
37
|
+
# 상황에서 도달 불가였다(Sonnet 타깃-티어 심 2026-07-30 지적).
|
|
38
|
+
# ⚠️ 분리해도 자동 배선은 아니다: .claude/settings.json 도 gitignored 라(.gitignore:3-4) 등록 자체는
|
|
39
|
+
# 추적될 수 없다. 추적되는 건 templates/settings.SessionStart.snippet.json 이고, 배선은 위자드가 한다.
|
|
40
|
+
# 여기서 다시 호출하지 않는다: 두 곳에서 부르면 같은 이벤트를 두 번 찍는다.
|
|
41
|
+
|
|
42
|
+
# ── §early-refresh: 컴패니언 refresh 를 frontier 판정보다 먼저 한다 ─────────────
|
|
43
|
+
# 왜 순서가 문제인가: frontier 판정은 러너가 아닌 노드에서 $BE/tracks-meta 를 본다. refresh 가
|
|
44
|
+
# 그 뒤에 있으면 **그날의 첫 세션**은 아직 안 끌어온 워킹트리를 읽어 오늘 digest 를 못 보고,
|
|
45
|
+
# 이 수정이 없애려던 바로 그 오경보를 그대로 낸다(하루 한 번, 가장 값진 시점에서 실패).
|
|
46
|
+
# cross-family 리뷰 2026-07-30 [HIGH] 지적. 비-Mode-D(공개) 사용자는 BE 가 비어 통째로 건너뛴다.
|
|
47
|
+
PULL_NOTE="(no companion store configured)"
|
|
48
|
+
if [ -d "$BE/.git" ]; then
|
|
49
|
+
export GIT_TERMINAL_PROMPT=0
|
|
50
|
+
export GIT_SSH_COMMAND="${GIT_SSH_COMMAND:-ssh -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=accept-new}"
|
|
51
|
+
|
|
52
|
+
# Hard wall-clock deadline on the ONLY network step. ConnectTimeout bounds the handshake, but a
|
|
53
|
+
# slow/stalled TRANSFER after connect has no bound — measured 2026-07-12: SessionStart worst-case
|
|
54
|
+
# 17.5s with 2 hook-timeout kills, all attributable to the fetch. perl-alarm is the portable
|
|
55
|
+
# watchdog (macOS ships no coreutils `timeout`); on overrun the fetch dies and the offline branch
|
|
56
|
+
# reports honestly. FH_FETCH_DEADLINE overrides (seconds). If perl is absent the wrapper degrades
|
|
57
|
+
# to running the command with NO deadline — a missing watchdog must never become a permanently
|
|
58
|
+
# skipped fetch misreported as "offline" (challenger catch 2026-07-12).
|
|
59
|
+
if command -v perl >/dev/null 2>&1; then
|
|
60
|
+
_deadline() { perl -e 'alarm shift @ARGV; exec @ARGV' "$@"; }
|
|
61
|
+
else
|
|
62
|
+
_deadline() { shift; "$@"; }
|
|
63
|
+
fi
|
|
64
|
+
PULL_NOTE=""
|
|
65
|
+
if _deadline "${FH_FETCH_DEADLINE:-8}" git -C "$BE" fetch --quiet >/dev/null 2>&1; then
|
|
66
|
+
if git -C "$BE" merge --ff-only --quiet >/dev/null 2>&1; then
|
|
67
|
+
PULL_NOTE="fetched + fast-forwarded"
|
|
68
|
+
else
|
|
69
|
+
PULL_NOTE="fetched but NOT fast-forward (companion diverged — read local + newest remote)"
|
|
70
|
+
fi
|
|
71
|
+
else
|
|
72
|
+
PULL_NOTE="fetch skipped (offline or deadline hit — read local state)"
|
|
73
|
+
fi
|
|
74
|
+
fi
|
|
30
75
|
|
|
31
76
|
# ── frontier-digest: 부재를 0으로 읽지 않는다 ────────────────────────────────────
|
|
32
77
|
# 왜: digest 는 launchd 로 매일 09:00 에 돌지만 **31회 중 6회(19%) 산출물 없이 끝났다**
|
|
@@ -51,9 +96,19 @@ _FD_STAMP="$(date +%H:%M) 기준"
|
|
|
51
96
|
# 존재 판정은 러너 digest_ready 와 동일 술어(glob + -size +1k). 정확명 [ -f ] 는 러너와 관대함이
|
|
52
97
|
# 갈린다 — partial 파일(>0 <1k)이 성공으로 오독되고, suffix 착지가 영구 오경보가 된다
|
|
53
98
|
# (divergent-leniency: 같은 상태를 두 술어가 다르게 읽으면 한쪽 결과가 무음으로 샌다).
|
|
54
|
-
|
|
99
|
+
_fd_hit() { find "$1" -maxdepth 1 -name "frontier_digest_$(date +%Y_%m_%d)*.md" -size +1k 2>/dev/null | grep -q .; }
|
|
100
|
+
# 노드-로컬만 보면 **다른 머신이 만든 산출물이 구조적으로 안 보인다**. 멀티머신에선 러너가 한 대이고
|
|
101
|
+
# 나머지 노드는 컴패니언 스토어로만 그 산출물을 받는다 → 러너 아닌 노드가 매일 "실패다" 오경보를 낸다.
|
|
102
|
+
# (2026-07-30 실측: 프로가 07-24~30 매일 정상 생산 중인데 에어는 7일 연속 FAILED 를 띄웠다.
|
|
103
|
+
# 계기의 스코프가 대상보다 좁았던 케이스 — 대상은 '오늘 digest 가 있나'지 '이 디스크에 있나'가 아니다.)
|
|
104
|
+
# 술어는 로컬과 **동일**(glob + -size +1k) — divergent-leniency 를 만들지 않는다.
|
|
105
|
+
_fd_ready() { _fd_hit "$FH/tracks/_meta" || { [ -n "$BE" ] && _fd_hit "$BE/tracks-meta"; }; }
|
|
55
106
|
if _fd_ready; then
|
|
56
|
-
|
|
107
|
+
# 로컬엔 없고 컴패니언에만 있으면 = 이 노드는 러너가 아니다. 침묵하면 토폴로지가 안 보이므로 한 줄 알린다.
|
|
108
|
+
if ! _fd_hit "$FH/tracks/_meta"; then
|
|
109
|
+
echo "ℹ️ [frontier-digest] 오늘 digest 는 **다른 노드**가 생산했다(컴패니언 스토어 경유) — 이 노드는 러너가 아니다."
|
|
110
|
+
echo " 읽을 것: \$BE_DIR/tracks-meta/frontier_digest_$(date +%Y_%m_%d)*.md"
|
|
111
|
+
fi
|
|
57
112
|
elif [ "$((10#$_FD_NOW))" -lt "$_FD_SCHED" ]; then
|
|
58
113
|
echo "ℹ️ [frontier-digest] 오늘 digest 는 09:00 예정 — 아직 전이다($_FD_STAMP). 부재는 정상."
|
|
59
114
|
elif [ -f "$_FD_LOG" ]; then
|
|
@@ -77,7 +132,7 @@ elif [ -f "$_FD_LOG" ]; then
|
|
|
77
132
|
else
|
|
78
133
|
echo "⚠️ [frontier-digest] 스케줄(09:00) 지났는데 로그도 산출물도 없다($_FD_STAMP) — 잡이 아예 안 돌았을 수 있다(launchd 확인)."
|
|
79
134
|
fi
|
|
80
|
-
|
|
135
|
+
# (BE is resolved at the top of this script — the frontier-digest block above needs it too.)
|
|
81
136
|
|
|
82
137
|
# Non-Mode-D / no companion store → silent no-op (this is the majority path for public users).
|
|
83
138
|
[ -d "$BE/.git" ] || exit 0
|
|
@@ -89,31 +144,7 @@ BE="${BE_DIR:-}" # companion-store path — supplied by the gitignored hook re
|
|
|
89
144
|
# - fail-fast env: no credential/SSH/host-key prompts can hang SessionStart.
|
|
90
145
|
# - fetch + merge --ff-only: a fast-forward is the only safe hook mutation; a diverged companion
|
|
91
146
|
# simply does not advance (no merge commit, no conflict state left behind) and we say so.
|
|
92
|
-
|
|
93
|
-
export GIT_SSH_COMMAND="${GIT_SSH_COMMAND:-ssh -o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=accept-new}"
|
|
94
|
-
|
|
95
|
-
# Hard wall-clock deadline on the ONLY network step. ConnectTimeout bounds the handshake, but a
|
|
96
|
-
# slow/stalled TRANSFER after connect has no bound — measured 2026-07-12: SessionStart worst-case
|
|
97
|
-
# 17.5s with 2 hook-timeout kills, all attributable to the fetch. perl-alarm is the portable
|
|
98
|
-
# watchdog (macOS ships no coreutils `timeout`); on overrun the fetch dies and the offline branch
|
|
99
|
-
# reports honestly. FH_FETCH_DEADLINE overrides (seconds). If perl is absent the wrapper degrades
|
|
100
|
-
# to running the command with NO deadline — a missing watchdog must never become a permanently
|
|
101
|
-
# skipped fetch misreported as "offline" (challenger catch 2026-07-12).
|
|
102
|
-
if command -v perl >/dev/null 2>&1; then
|
|
103
|
-
_deadline() { perl -e 'alarm shift @ARGV; exec @ARGV' "$@"; }
|
|
104
|
-
else
|
|
105
|
-
_deadline() { shift; "$@"; }
|
|
106
|
-
fi
|
|
107
|
-
PULL_NOTE=""
|
|
108
|
-
if _deadline "${FH_FETCH_DEADLINE:-8}" git -C "$BE" fetch --quiet >/dev/null 2>&1; then
|
|
109
|
-
if git -C "$BE" merge --ff-only --quiet >/dev/null 2>&1; then
|
|
110
|
-
PULL_NOTE="fetched + fast-forwarded"
|
|
111
|
-
else
|
|
112
|
-
PULL_NOTE="fetched but NOT fast-forward (companion diverged — read local + newest remote)"
|
|
113
|
-
fi
|
|
114
|
-
else
|
|
115
|
-
PULL_NOTE="fetch skipped (offline or deadline hit — read local state)"
|
|
116
|
-
fi
|
|
147
|
+
# (컴패니언 refresh 는 위 §early-refresh 로 올렸다 — frontier 판정이 최신 트리를 보게 하려고.)
|
|
117
148
|
|
|
118
149
|
# 2) Session card date (the pointer the operator's close chain writes last).
|
|
119
150
|
CARD="$FH/tracks/_meta/reference_next_session_starter.md"
|
|
@@ -43,6 +43,10 @@ fi
|
|
|
43
43
|
# no shipped hook invokes it.
|
|
44
44
|
ACCEPTED_ABSENT=(
|
|
45
45
|
".claude/registry/LOCAL_SKILL_REGISTRY.md"
|
|
46
|
+
# An INSTALL DESTINATION the user creates (`cp templates/local_fh_context.md
|
|
47
|
+
# .claude/rules/local_fh_context.md`), not a file FH ships. Shipping it would overwrite the
|
|
48
|
+
# user's own cross-context wiring — the template it is copied FROM is what ships.
|
|
49
|
+
".claude/rules/local_fh_context.md"
|
|
46
50
|
".claude/regression/probes.md"
|
|
47
51
|
"scripts/sync-to-be.sh"
|
|
48
52
|
"scripts/sync_guard_check.sh"
|
|
@@ -83,6 +87,24 @@ for s in shipped:
|
|
|
83
87
|
# Only a path that REALLY EXISTS here but is left out of the tarball is this defect.
|
|
84
88
|
# A path that exists nowhere is the ordinary phantom-reference class the ref-path
|
|
85
89
|
# check above already owns; a path outside files[] that is also absent is nothing.
|
|
90
|
+
# EXISTENCE, not tracked-ness. A 2026-07-30 revision narrowed this to `git ls-files`
|
|
91
|
+
# to silence what looked like a machine-local false positive; measurement showed that was a
|
|
92
|
+
# WEAKENING — an existing-but-untracked path named by a shipped doc is exactly the defect
|
|
93
|
+
# (the npm user cannot have that file), and selfcheck's ref-path check SKIPs gitignored
|
|
94
|
+
# paths, so nothing else owns it. Reverted.
|
|
95
|
+
#
|
|
96
|
+
# WIDENING IS DEFERRED, AND THE REASON IS NOT A MEASUREMENT. Dropping `exists` entirely
|
|
97
|
+
# (flag every referenced ∧ ¬covered path) is arguably the correct predicate, but it cannot
|
|
98
|
+
# be evaluated while the extractor below is known-broken: its `(sh|py|js|md|json|…)`
|
|
99
|
+
# alternation puts `js` before `json`, so `settings.json` is captured as `settings.js`.
|
|
100
|
+
# A first pass at this comment cited a count of artifacts as evidence that `exists` is
|
|
101
|
+
# load-bearing — that count came FROM the broken extractor, i.e. an instrument was used to
|
|
102
|
+
# justify keeping a predicate before the instrument itself was validated (the circularity
|
|
103
|
+
# CLAUDE.md §Instrument-Calibration exists to forbid; a cross-family review caught it, and
|
|
104
|
+
# an independent extractor produced materially different numbers).
|
|
105
|
+
# HONEST STATE: fix the `js|json` alternation first, re-measure, then decide. Until then
|
|
106
|
+
# this check's true coverage is UNQUANTIFIED — treat a PASS as "no defect of the narrow
|
|
107
|
+
# exists-and-uncovered kind", not as "every shipped reference is sound".
|
|
86
108
|
if os.path.exists(m) and not covered(m):
|
|
87
109
|
if m in accepted:
|
|
88
110
|
exercised.add(m)
|
package/scripts/selfcheck.sh
CHANGED
|
@@ -132,6 +132,51 @@ fi
|
|
|
132
132
|
# prevent. test_card_drift_probe.sh had shipped with ZERO callers since it was written; wiring it
|
|
133
133
|
# here closes that, and the anchors are added to files[] in the same change so package mode runs
|
|
134
134
|
# them too rather than reporting a deleted anchor.
|
|
135
|
+
# sidecar_wait stdin plumbing. Its subject is dispatched by auto-decorrelation / steel-quench /
|
|
136
|
+
# sim-conductor / AGENTS.md as the REQUIRED wait form, so a regression there silently empties every
|
|
137
|
+
# cross-family verification. The anchor's first version shipped in tests/ with ZERO callers — the
|
|
138
|
+
# same defect the comment above records for test_card_drift_probe.sh, repeated one file later.
|
|
139
|
+
if [ ! -f scripts/sidecar_wait.sh ]; then
|
|
140
|
+
echo "SKIP test_sidecar_wait_stdin.sh (subject scripts/sidecar_wait.sh absent)"
|
|
141
|
+
elif [ -f scripts/test_sidecar_wait_stdin.sh ]; then
|
|
142
|
+
if ! bash scripts/test_sidecar_wait_stdin.sh; then
|
|
143
|
+
fail=1
|
|
144
|
+
fi
|
|
145
|
+
else
|
|
146
|
+
echo "FAIL test_sidecar_wait_stdin.sh: sidecar_wait.sh present but its anchor is missing"
|
|
147
|
+
fail=1
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
# fh_node_check.sh gets the same treatment, and for the same reason: three adversarial rounds on it
|
|
151
|
+
# produced defects that were ALL negative legs (floor N/A on a non-git install · another framework's
|
|
152
|
+
# hooks counted as ours · the Mode D applicability gate silencing its own flagship case), and each
|
|
153
|
+
# round's fix reverted a previous one because no anchor pinned it. Subject-present-but-anchor-absent
|
|
154
|
+
# is a FAIL, not a skip — that is how an anchor gets quietly dropped.
|
|
155
|
+
# Same treatment for the sidecar calibrator, same reason: its verdicts are all distinctions between
|
|
156
|
+
# states that look identical from outside ("the sidecar ran" vs "the model I pinned answered",
|
|
157
|
+
# "absent" vs "unmeasured"), and its lanes are hermetic stubs, so running them costs nothing.
|
|
158
|
+
if [ ! -f scripts/sidecar_calibrate.sh ]; then
|
|
159
|
+
echo "SKIP test_sidecar_calibrate_lanes.sh (subject scripts/sidecar_calibrate.sh absent)"
|
|
160
|
+
elif [ -f scripts/test_sidecar_calibrate_lanes.sh ]; then
|
|
161
|
+
if ! bash scripts/test_sidecar_calibrate_lanes.sh; then
|
|
162
|
+
fail=1
|
|
163
|
+
fi
|
|
164
|
+
else
|
|
165
|
+
echo "FAIL test_sidecar_calibrate_lanes.sh: sidecar_calibrate.sh present but its anchor is missing"
|
|
166
|
+
fail=1
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
if [ ! -f scripts/fh_node_check.sh ]; then
|
|
170
|
+
echo "SKIP test_node_check_lanes.sh (subject scripts/fh_node_check.sh absent)"
|
|
171
|
+
elif [ -f scripts/test_node_check_lanes.sh ]; then
|
|
172
|
+
if ! bash scripts/test_node_check_lanes.sh; then
|
|
173
|
+
fail=1
|
|
174
|
+
fi
|
|
175
|
+
else
|
|
176
|
+
echo "FAIL test_node_check_lanes.sh: fh_node_check.sh present but its anchor is missing"
|
|
177
|
+
fail=1
|
|
178
|
+
fi
|
|
179
|
+
|
|
135
180
|
for _anchor in scripts/test_session_close_lanes.sh scripts/test_card_drift_probe.sh; do
|
|
136
181
|
if [ ! -f scripts/session_close_check.sh ]; then
|
|
137
182
|
echo "SKIP ${_anchor##*/} (subject scripts/session_close_check.sh absent)"
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# sidecar_calibrate.sh — measure the cross-family sidecar panel before trusting it.
|
|
3
|
+
#
|
|
4
|
+
# WHY: `auto-decorrelation` and the load-bearing cross-family gate both ask "is a different-family
|
|
5
|
+
# auditor reachable?" — and until now that question was answered from memory. Two measured failures
|
|
6
|
+
# on 2026-07-30, one in each direction:
|
|
7
|
+
# · A marker was written claiming `cross-family unavailable this session`. Probed later in the same
|
|
8
|
+
# session, codex ran fine. Unavailability was DECLARED, never measured.
|
|
9
|
+
# · agy pinned with the slug `gemini-3.1-pro-high` answered as **Gemini 3.6 Flash** — silently, with
|
|
10
|
+
# no error. "The sidecar ran" and "the model I pinned answered" are different propositions, and
|
|
11
|
+
# only the second one licenses a claim about model-family diversity.
|
|
12
|
+
# A panel you have not probed is not a panel; it is an assumption with a hostname.
|
|
13
|
+
#
|
|
14
|
+
# WHAT IT MEASURES, per runtime — four legs, because each catches a different lie:
|
|
15
|
+
# REACHABLE the binary exists and runs at all
|
|
16
|
+
# PIN-OK / UNTRUSTED-PIN
|
|
17
|
+
# a discriminating identity probe: does the answer NAME the model that was
|
|
18
|
+
# pinned? A generic "OK" proves nothing — any model returns it. This is the only
|
|
19
|
+
# anchor for a runtime that falls back silently.
|
|
20
|
+
# control: rejects-bogus / accepts-bogus
|
|
21
|
+
# pin a model that cannot exist. This measures ONE thing only: whether unknown
|
|
22
|
+
# names are validated. It does NOT mean known names are served faithfully, and
|
|
23
|
+
# the gap between those is where the real trap lives — agy REJECTS nonsense yet
|
|
24
|
+
# silently served Flash for `gemini-3.1-pro-high`, a slug from its own
|
|
25
|
+
# catalogue (measured 2026-07-30). So `rejects-bogus` must never be read as
|
|
26
|
+
# "the pin is safe": the identity probe still decides. `accepts-bogus` is the
|
|
27
|
+
# stronger warning — there, the identity probe is the ONLY evidence at all.
|
|
28
|
+
# VERDICT-OK / VERDICT-UNPARSEABLE
|
|
29
|
+
# ask for one bare token. A runtime that answers with agentic prose cannot carry
|
|
30
|
+
# a machine-read verdict. Measured for agy at 1.0.14 (2026-07-04) and NOT
|
|
31
|
+
# inherited here: the version has moved, and this file re-measures rather than
|
|
32
|
+
# quoting. Fitness is a per-run measurement, not a property.
|
|
33
|
+
#
|
|
34
|
+
# Detector, never a gate: ALWAYS exits 0. It reports; the caller decides. A calibration run that
|
|
35
|
+
# could block would make callers stop running it, which is the failure this exists to prevent.
|
|
36
|
+
#
|
|
37
|
+
# Cost: real API calls (3 short probes per runtime). Use --only to scope. `--stub-model` exists for
|
|
38
|
+
# the lane harness so it can drive stub CLIs without touching a real catalogue.
|
|
39
|
+
#
|
|
40
|
+
# Usage: bash scripts/sidecar_calibrate.sh [--only codex|agy] [--stub-model NAME] [--quiet]
|
|
41
|
+
# Output: one block per runtime, then a PANEL line — the line a marker's `crossfamily:` leg quotes.
|
|
42
|
+
|
|
43
|
+
set -uo pipefail
|
|
44
|
+
|
|
45
|
+
ONLY=""; STUB_MODEL=""; QUIET=""
|
|
46
|
+
while [ $# -gt 0 ]; do
|
|
47
|
+
case "$1" in
|
|
48
|
+
--only) ONLY="${2:-}"; shift 2 ;;
|
|
49
|
+
--stub-model) STUB_MODEL="${2:-}"; shift 2 ;;
|
|
50
|
+
--quiet) QUIET=1; shift ;;
|
|
51
|
+
*) shift ;;
|
|
52
|
+
esac
|
|
53
|
+
done
|
|
54
|
+
|
|
55
|
+
TIMEOUT_BIN=""
|
|
56
|
+
command -v timeout >/dev/null 2>&1 && TIMEOUT_BIN="timeout"
|
|
57
|
+
command -v gtimeout >/dev/null 2>&1 && TIMEOUT_BIN="gtimeout"
|
|
58
|
+
# No coreutils timeout on stock macOS. perl's alarm is the portable watchdog; if perl is missing too,
|
|
59
|
+
# run WITHOUT a deadline rather than skipping the probe — a missing watchdog must never become a
|
|
60
|
+
# silently skipped measurement reported as absence (the same rule fh_session_load.sh applies).
|
|
61
|
+
_run() {
|
|
62
|
+
local secs="$1"; shift
|
|
63
|
+
if [ -n "$TIMEOUT_BIN" ]; then "$TIMEOUT_BIN" "$secs" "$@"
|
|
64
|
+
elif command -v perl >/dev/null 2>&1; then perl -e 'alarm shift @ARGV; exec @ARGV' "$secs" "$@"
|
|
65
|
+
else "$@"; fi
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# _answer — reduce a runtime's stdout to THE MODEL'S ANSWER.
|
|
69
|
+
# Measured on the first real run (2026-07-30): matching against whole stdout is unsound, because a
|
|
70
|
+
# real CLI prints a session banner that REPEATS the pinned model back (`codex exec` emits its
|
|
71
|
+
# version, workdir and model config before the reply). Under a whole-stdout match, a runtime that
|
|
72
|
+
# merely echoes its own configuration passes the identity probe — the transport layer supplying the
|
|
73
|
+
# very evidence the probe exists to obtain from the model. So: take the last non-empty line, skipping
|
|
74
|
+
# trailing telemetry (`tokens used`, bare numbers, rule lines).
|
|
75
|
+
# RESIDUAL, named: this is a heuristic on line position. A runtime that prints its answer and then
|
|
76
|
+
# unrecognised trailing chatter would be mis-read. It is checked by the banner-echo lane, not proven
|
|
77
|
+
# in general; a structured output mode (JSON) would replace the heuristic and none is used here yet.
|
|
78
|
+
_answer() {
|
|
79
|
+
awk 'BEGIN{last=""}
|
|
80
|
+
{line=$0
|
|
81
|
+
gsub(/\r/,"",line)
|
|
82
|
+
gsub(/^[[:space:]]+|[[:space:]]+$/,"",line)
|
|
83
|
+
if (line=="") next
|
|
84
|
+
if (line ~ /^[-=_]{3,}$/) next
|
|
85
|
+
if (tolower(line) ~ /^tokens? used/) next
|
|
86
|
+
if (line ~ /^[0-9,.]+$/) next
|
|
87
|
+
last=line}
|
|
88
|
+
END{print last}'
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
IDENTITY_PROMPT="Answer with one line only: your exact model name and version."
|
|
92
|
+
VERDICT_PROMPT="Reply with exactly one word, PASS or FAIL, and nothing else. The word is PASS."
|
|
93
|
+
BOGUS_MODEL="zzz-nonexistent-model-9.9"
|
|
94
|
+
|
|
95
|
+
PANEL=""
|
|
96
|
+
|
|
97
|
+
# build_cmd <runtime> <model> <prompt> — fills CMD as an argv array.
|
|
98
|
+
# NOT a shell function passed to the watchdog: `timeout`/`gtimeout` exec a BINARY and cannot see
|
|
99
|
+
# shell functions, so an earlier revision fed them a function name and every probe came back as the
|
|
100
|
+
# runtime failing to run (caught by lane 3/4b/5b, not by reading).
|
|
101
|
+
build_cmd() {
|
|
102
|
+
case "$1" in
|
|
103
|
+
codex) CMD=(codex exec -m "$2" -c model_reasoning_effort=high --skip-git-repo-check "$3") ;;
|
|
104
|
+
agy) CMD=(agy -p "$3" --model "$2" --print-timeout 170s) ;;
|
|
105
|
+
*) CMD=("$1" "$3") ;;
|
|
106
|
+
esac
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
# probe_runtime <name> <real-model-pin>
|
|
110
|
+
probe_runtime() {
|
|
111
|
+
local rt="$1" model="$2"
|
|
112
|
+
[ -n "$ONLY" ] && [ "$ONLY" != "$rt" ] && return 0
|
|
113
|
+
[ -n "$STUB_MODEL" ] && model="$STUB_MODEL"
|
|
114
|
+
|
|
115
|
+
if ! command -v "$rt" >/dev/null 2>&1; then
|
|
116
|
+
printf '%-6s ABSENT — not installed on this machine (absence measured, not assumed)\n' "$rt"
|
|
117
|
+
return 0
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
local id_out pin_state ctl_out ctl_state v_out v_state
|
|
121
|
+
build_cmd "$rt" "$model" "$IDENTITY_PROMPT"
|
|
122
|
+
id_out="$(_run 200 "${CMD[@]}" 2>&1 | _answer)"
|
|
123
|
+
|
|
124
|
+
# Discriminating check — the answer must be the MODEL's self-report naming the model that was
|
|
125
|
+
# pinned. What counts as "naming it" is the VERSION token, not every token of the pin slug:
|
|
126
|
+
# vendors answer with a product name (`gpt-5.6-sol` → "GPT-5.6 Codex"), and demanding the suffix
|
|
127
|
+
# made this report UNTRUSTED-PIN for a pin that had actually held (measured 2026-07-30). The
|
|
128
|
+
# version is also exactly where the real failures differ — 3.1 asked, 3.6 answered. If a pin
|
|
129
|
+
# carries no version token at all, fall back to requiring the name words, since then there is
|
|
130
|
+
# nothing sharper to test.
|
|
131
|
+
local ver name_words hit=0
|
|
132
|
+
ver="$(printf '%s' "$model" | grep -oE '[0-9]+\.[0-9]+' | head -1)"
|
|
133
|
+
name_words="$(printf '%s' "$model" | tr 'A-Z' 'a-z' | sed -E 's/[^a-z]+/ /g' \
|
|
134
|
+
| tr ' ' '\n' | grep -E '^[a-z]{3,}$' \
|
|
135
|
+
| grep -vE '^(high|low|medium|thinking|the|exec)$' | head -1)"
|
|
136
|
+
if [ -n "$id_out" ]; then
|
|
137
|
+
if [ -n "$ver" ]; then
|
|
138
|
+
printf '%s' "$id_out" | grep -qF "$ver" && hit=1
|
|
139
|
+
elif [ -n "$name_words" ]; then
|
|
140
|
+
printf '%s' "$id_out" | tr 'A-Z' 'a-z' | grep -qF "$name_words" && hit=1
|
|
141
|
+
fi
|
|
142
|
+
fi
|
|
143
|
+
if [ "$hit" -eq 1 ]; then pin_state="PIN-OK"; else pin_state="UNTRUSTED-PIN"; fi
|
|
144
|
+
|
|
145
|
+
build_cmd "$rt" "$BOGUS_MODEL" "$IDENTITY_PROMPT"
|
|
146
|
+
ctl_out="$(_run 120 "${CMD[@]}" 2>&1)"
|
|
147
|
+
if [ $? -ne 0 ] || printf '%s' "$ctl_out" | grep -qiE 'not supported|invalid|unknown model|error'; then
|
|
148
|
+
ctl_state="rejects-bogus"
|
|
149
|
+
else
|
|
150
|
+
ctl_state="accepts-bogus"
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
build_cmd "$rt" "$model" "$VERDICT_PROMPT"
|
|
154
|
+
v_out="$(_run 200 "${CMD[@]}" 2>&1 | _answer)"
|
|
155
|
+
# Parseable = a bare verdict token is recoverable from a short answer. Prose that merely CONTAINS
|
|
156
|
+
# the word does not qualify: a verdict channel must be readable without a human deciding what the
|
|
157
|
+
# runtime meant.
|
|
158
|
+
local v_compact
|
|
159
|
+
v_compact="$(printf '%s' "$v_out" | tr -s '[:space:]' ' ' | sed 's/^ *//; s/ *$//')"
|
|
160
|
+
if [ "${#v_compact}" -le 12 ] && printf '%s' "$v_compact" | grep -qiE '^(pass|fail)[.!]?$'; then
|
|
161
|
+
v_state="VERDICT-OK"
|
|
162
|
+
else
|
|
163
|
+
v_state="VERDICT-UNPARSEABLE"
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
printf '%-6s REACHABLE · %s · control: %s · %s\n' "$rt" "$pin_state" "$ctl_state" "$v_state"
|
|
167
|
+
[ -z "$QUIET" ] && printf ' pinned: %s\n identity said: %s\n' "$model" "$(printf '%s' "$id_out" | cut -c1-100)"
|
|
168
|
+
if [ "$ctl_state" = "accepts-bogus" ]; then
|
|
169
|
+
printf ' ⚠️ this runtime does not validate the pin at all, so the identity probe is the ONLY\n'
|
|
170
|
+
printf ' evidence that the intended model answered — a clean run proves nothing here.\n'
|
|
171
|
+
elif [ "$pin_state" = "UNTRUSTED-PIN" ]; then
|
|
172
|
+
printf ' ⚠️ it rejects UNKNOWN names, which says nothing about serving KNOWN ones faithfully.\n'
|
|
173
|
+
printf ' The identity probe disagreed with the pin — treat this runtime as substituting.\n'
|
|
174
|
+
fi
|
|
175
|
+
# Only a runtime whose pin is trustworthy counts toward the panel. A reachable runtime answering as
|
|
176
|
+
# some other model contributes no family diversity, which is the entire point of the panel.
|
|
177
|
+
[ "$pin_state" = "PIN-OK" ] && PANEL="${PANEL:+$PANEL, }$rt"
|
|
178
|
+
return 0
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
probe_runtime codex "gpt-5.6-sol"
|
|
182
|
+
probe_runtime agy "Gemini 3.1 Pro (High)"
|
|
183
|
+
|
|
184
|
+
if [ -n "$PANEL" ]; then
|
|
185
|
+
echo "PANEL: $PANEL — usable different-family auditor(s), pin verified this run"
|
|
186
|
+
else
|
|
187
|
+
echo "PANEL: none — no runtime passed the identity probe on this machine, this run"
|
|
188
|
+
echo " State this, do not infer it: a marker's crossfamily leg may say 'none' but never stay silent."
|
|
189
|
+
fi
|
|
190
|
+
exit 0
|