@chrono-meta/fh-gate 2.0.0 → 2.1.0
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/capabilities/adapters/gstack-content-safety.cap +110 -0
- package/.claude/capabilities/adapters/mate-agent-boundary.cap +110 -0
- package/.claude/capabilities/adapters/qasp-new-code-anchor.cap +116 -0
- package/.claude/capabilities/degrade-direction-scan.cap +24 -0
- package/.claude/capabilities/public-surface-scan.cap +24 -0
- package/.claude/rules/fh_4axis_gate.md +77 -1
- package/.claude-plugin/marketplace.json +3 -3
- package/AGENTS.md +25 -3
- package/CHEATSHEET.md +17 -1
- package/CLAUDE.md +241 -5
- package/README.ja.md +55 -20
- package/README.ko.md +86 -20
- package/README.md +57 -20
- package/README.zh.md +49 -19
- package/knowledge/shared/harness-core/capability_composition_contract.md +68 -0
- package/knowledge/shared/harness-core/fh_three_layer_canon.md +122 -5
- package/knowledge/shared/harness-core/field_verdict_crossfamily_gate.md +115 -3
- package/knowledge/shared/harness-core/harness_incubator_doctrine.md +83 -1
- package/knowledge/shared/harness-core/harness_terminal_correlation_and_recommendations.md +255 -0
- package/knowledge/shared/harness-core/harness_verification_core_extended.md +1 -1
- package/knowledge/shared/harness-core/ship_readiness_gate.md +234 -11
- package/knowledge/shared/learnings/subagent_invocations_log.yaml +128 -0
- package/knowledge/shared/rules/sister_asset_protocol.md +12 -0
- package/package.json +12 -3
- package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-commons/agents/quench-challenger.md +6 -1
- package/plugins/fh-meta/.claude-plugin/plugin.json +2 -2
- package/plugins/fh-meta/CHANGELOG.md +60 -0
- package/plugins/fh-meta/skills/auto-decorrelation/SKILL.md +14 -2
- package/plugins/fh-meta/skills/steel-quench/SKILL_detail.md +13 -0
- package/plugins/fh-meta/skills/verify-bidirectional/SKILL.md +34 -1
- package/scripts/adapters/gstack_content_safety.sh +186 -0
- package/scripts/adapters/mate_agent_boundary.sh +120 -0
- package/scripts/adapters/peer_resolve.sh +99 -0
- package/scripts/adapters/qasp_new_code_anchor.sh +159 -0
- package/scripts/branch_claim.sh +14 -0
- package/scripts/capability_effect_probe.sh +340 -20
- package/scripts/capability_registry_check.sh +271 -20
- package/scripts/chamber_run.sh +24 -3
- package/scripts/chamber_witness.sh +25 -0
- package/scripts/cluster_capability_scan.sh +629 -0
- package/scripts/fh-gate.sh +16 -1
- package/scripts/fh_session_load.sh +70 -0
- package/scripts/package_coverage_check.sh +10 -0
- package/scripts/portability_lint.sh +257 -0
- package/scripts/prepublish_scope_note.sh +139 -0
- package/scripts/relay_channel.sh +70 -7
- package/scripts/selfcheck.sh +167 -5
- package/scripts/test_adapter_lanes.sh +296 -0
- package/scripts/test_capability_entrypoint_shipping.sh +8 -1
- package/scripts/test_fh_gate_regressions.sh +21 -9
- package/scripts/test_marker_axes_run_lanes.sh +87 -3
- package/scripts/test_marker_crossfamily_lanes.sh +31 -1
- package/scripts/test_relay_channel_lanes.sh +76 -0
- package/templates/.git-hooks/pre-commit +239 -12
- package/templates/subagent-tally-hook.json +2 -2
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_adapter_lanes.sh — `scripts/adapters/*` (크로스하네스 **어댑터**)의 회귀 레인.
|
|
3
|
+
#
|
|
4
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
5
|
+
# 무엇을 고정하나
|
|
6
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
7
|
+
# 등록 바(`capability_registry_check.sh`)는 **선언된 캘리브레이션 arm 두 개만** 돌리고,
|
|
8
|
+
# 자기 출력에 «나머지 enum 값은 관측하지 않았다» 고 적는다. 어댑터에서 그 «나머지» 는
|
|
9
|
+
# 하필 가장 중요한 값이다 — **`PEER_ABSENT`**. 「그 하네스를 이 머신에 안 깔았다」가
|
|
10
|
+
# `CLEAN` 으로 접히면 미측정이 통과로 렌더되고, `HARNESS_ERROR` 로 접히면 멀쩡한 이웃이
|
|
11
|
+
# 고장으로 렌더된다. 이 레인 묶음이 그 값과 peer 해석 규칙을 고정한다.
|
|
12
|
+
#
|
|
13
|
+
# 레인 클래스 (되돌림 프로브가 이 태그로 «어느 레인이 빨개져야 하는가» 를 사전등록한다)
|
|
14
|
+
# resolve-absent peer 가 안 보일 때 **전용 값**이 나오는가
|
|
15
|
+
# resolve-alias 별칭 규칙(`<n>-dev` · `_`→`-`)이 실제로 도는가
|
|
16
|
+
# resolve-guard 모호/부분일치를 **고르지 않는가**
|
|
17
|
+
# control peer 가 보이면 **실제 판정**이 나오는가
|
|
18
|
+
# (「늘 PEER_ABSENT 를 찍는 계기」와 구분하는 자리)
|
|
19
|
+
# enum M4 가 관측하지 않는 나머지 enum 값들
|
|
20
|
+
#
|
|
21
|
+
# 사용법
|
|
22
|
+
# bash scripts/test_adapter_lanes.sh # 레인 전부
|
|
23
|
+
# bash scripts/test_adapter_lanes.sh --revert-probe # 레인 + 되돌림 프로브 2종
|
|
24
|
+
# exit: 0 = 전부 기대대로 · 1 = 회귀 · 10 = 하네스 오류(전제 파손 — 판정 아님)
|
|
25
|
+
|
|
26
|
+
set -uo pipefail
|
|
27
|
+
|
|
28
|
+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
29
|
+
# 되돌림 프로브가 **중화된 사본**을 가리키게 하려고 파라미터화한다. 기본값은 실물이다.
|
|
30
|
+
ADAPTER_DIR="${FH_ADAPTER_DIR:-$REPO_ROOT/scripts/adapters}"
|
|
31
|
+
|
|
32
|
+
T="$(mktemp -d)" || { echo "❌ HARNESS-ERROR — mktemp 실패"; exit 10; }
|
|
33
|
+
trap 'rm -rf "$T"' EXIT
|
|
34
|
+
|
|
35
|
+
FAIL=0; N=0
|
|
36
|
+
RED_LANES="" # 이번 실행에서 빨개진 레인 ID (되돌림 프로브가 읽는다)
|
|
37
|
+
|
|
38
|
+
# ── peer 가 이 머신에 **있어야만** 의미가 있는 레인 ──────────────────────────
|
|
39
|
+
# 🟥 실측(2026-08-16): 로컬 23/23 초록인데 **CI 는 9레인 적색**이었다(G2 G3 G6 G9 M2 M3
|
|
40
|
+
# Q2 Q3 Q6). 이 레인들이 루트 오버라이드 없이 어댑터를 불러 `$HOME/projects` 를 보는데,
|
|
41
|
+
# CI 체크아웃엔 peer 레포가 없어 전부 `PEER_ABSENT(20)` 이 나왔다. 즉 레인의 앵커가
|
|
42
|
+
# **운영자 머신의 레이아웃**에 걸려 있었다 — 같은 날 `L13c` 가 gitignored 데이터에 앵커를
|
|
43
|
+
# 걸어 CI 만 빨갛던 것과 **같은 클래스**다([[feedback_ci_measures_state_not_transition]]).
|
|
44
|
+
#
|
|
45
|
+
# 🟥 그리고 이 스킵은 **조용하면 안 된다.** peer 가 없는 환경에서 컨트롤이 전부 빠지면
|
|
46
|
+
# 남는 것은 PEER_ABSENT 레인뿐이고, 그건 「늘 PEER_ABSENT 를 내는 계기」와 **구분되지 않는다**
|
|
47
|
+
# — 컨트롤이 막으려던 바로 그 상태다. 그래서 스킵 수를 세어 **종결 줄에 반드시 찍는다.**
|
|
48
|
+
SKIPPED=0
|
|
49
|
+
_lane_peer() { # $1=peer_root $2=id $3=class $4=설명 $5=expected $6=actual
|
|
50
|
+
if [ -n "$1" ]; then
|
|
51
|
+
_lane "$2" "$3" "$4" "$5" "$6"
|
|
52
|
+
else
|
|
53
|
+
SKIPPED=$((SKIPPED + 1))
|
|
54
|
+
printf ' ⏭ %-6s [%-14s] %s — peer 부재로 SKIP (**통과 아님**)\n' "$2" "$3" "$4"
|
|
55
|
+
fi
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
_lane() { # $1=id $2=class $3=설명 $4=expected_rc $5=actual_rc
|
|
59
|
+
N=$((N + 1))
|
|
60
|
+
if [ "$4" = "$5" ]; then
|
|
61
|
+
printf ' ✅ %-6s [%-14s] %s (rc=%s)\n' "$1" "$2" "$3" "$5"
|
|
62
|
+
else
|
|
63
|
+
printf ' ❌ %-6s [%-14s] %s — expect rc=%s, got rc=%s\n' "$1" "$2" "$3" "$4" "$5"
|
|
64
|
+
FAIL=1; RED_LANES="$RED_LANES $1"
|
|
65
|
+
fi
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# 계기 전제 — 어댑터 3종이 **실제로 거기 있는지** 먼저 본다. 없으면 이 레인 묶음은
|
|
69
|
+
# «전부 초록» 이 아니라 하네스 오류다(빈 대상에 대고 초록을 내는 것이 여기서 막으려는 형태).
|
|
70
|
+
for a in gstack_content_safety mate_agent_boundary qasp_new_code_anchor peer_resolve; do
|
|
71
|
+
[ -r "$ADAPTER_DIR/$a.sh" ] || { echo "❌ HARNESS-ERROR — $ADAPTER_DIR/$a.sh 부재. 빈 대상에 초록을 내지 않는다"; exit 10; }
|
|
72
|
+
done
|
|
73
|
+
|
|
74
|
+
G="$ADAPTER_DIR/gstack_content_safety.sh"
|
|
75
|
+
M="$ADAPTER_DIR/mate_agent_boundary.sh"
|
|
76
|
+
Q="$ADAPTER_DIR/qasp_new_code_anchor.sh"
|
|
77
|
+
|
|
78
|
+
# 「peer 가 안 보이는 세계」 = 잘 형성된 루트 목록인데 그 안에 이 peer 가 없는 것.
|
|
79
|
+
# (빈 **문자열**이 아니다 — 그건 「어디를 볼지 자체가 없다」는 다른 명제이고 전제 파손이다.)
|
|
80
|
+
EMPTY_WORLD="$T/empty_world"; mkdir -p "$EMPTY_WORLD"
|
|
81
|
+
|
|
82
|
+
_rc() { "$@" >/dev/null 2>&1; printf '%s' "$?"; } # 판정은 파이프를 통과시키지 않는다
|
|
83
|
+
|
|
84
|
+
# 실물 peer 루트 — 있으면 alias/control 레인을 돌리고, 없으면 그 레인은 SKIP 으로 **말한다**.
|
|
85
|
+
PEER_HOME="${FH_PROJECTS_HOME:-$HOME/projects}"
|
|
86
|
+
_peer_root() { # $1=이름 → 실물 루트 or 빈 문자열 (별칭 순서는 어댑터와 같다)
|
|
87
|
+
local n="$1" c
|
|
88
|
+
for c in "$n" "$n-dev" "$(printf '%s' "$n" | tr '_' '-')"; do
|
|
89
|
+
[ -d "$PEER_HOME/$c" ] && { printf '%s' "$PEER_HOME/$c"; return; }
|
|
90
|
+
done
|
|
91
|
+
printf ''
|
|
92
|
+
}
|
|
93
|
+
GS_ROOT="$(_peer_root gstack)"; MT_ROOT="$(_peer_root mate)"; QS_ROOT="$(_peer_root qasp)"
|
|
94
|
+
|
|
95
|
+
echo "== 어댑터 레인 (대상: $ADAPTER_DIR) =="
|
|
96
|
+
echo
|
|
97
|
+
|
|
98
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
99
|
+
# ① gstack-content-safety
|
|
100
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
101
|
+
echo "── gstack-content-safety (enum 0=CLEAN 1=BLOCK_HIGH 2=REVIEW_MEDIUM 3=NO_TARGET 10=HARNESS_ERROR 20=PEER_ABSENT)"
|
|
102
|
+
_lane G1 resolve-absent "peer 불가시 → 전용값 PEER_ABSENT(CLEAN 도 HARNESS_ERROR 도 아니다)" 20 \
|
|
103
|
+
"$(FH_CLUSTER_ROOTS="$EMPTY_WORLD" _rc bash "$G" --known-positive)"
|
|
104
|
+
_lane_peer "$GS_ROOT" G2 control "peer 가시 → 실제 판정 BLOCK_HIGH («늘 PEER_ABSENT» 와 구분)" 1 \
|
|
105
|
+
"$(_rc bash "$G" --known-positive)"
|
|
106
|
+
_lane_peer "$GS_ROOT" G3 control "peer 가시 → 음성 arm 은 CLEAN (계기가 «가른다» 는 증거)" 0 \
|
|
107
|
+
"$(_rc bash "$G" --known-negative)"
|
|
108
|
+
if [ -n "$GS_ROOT" ]; then
|
|
109
|
+
_lane G4 resolve-alias "명시 루트 목록(FH_CLUSTER_ROOTS)으로 해석된다" 0 \
|
|
110
|
+
"$(FH_CLUSTER_ROOTS="$GS_ROOT" _rc bash "$G" --known-negative)"
|
|
111
|
+
else
|
|
112
|
+
echo " ⏭ G4 [resolve-alias ] gstack 실물 루트 없음 — SKIP (통과 아님)"
|
|
113
|
+
fi
|
|
114
|
+
# 부분일치로 남의 레포를 집지 않는다. basename 이 **정확히** 별칭 후보여야 한다.
|
|
115
|
+
mkdir -p "$T/world_prefix/gstackx"
|
|
116
|
+
_lane G5 resolve-guard "basename 부분일치는 peer 가 아니다(gstackx ≠ gstack)" 20 \
|
|
117
|
+
"$(FH_CLUSTER_ROOTS="$T/world_prefix/gstackx" _rc bash "$G" --known-positive)"
|
|
118
|
+
mkdir -p "$T/empty_target"
|
|
119
|
+
_lane_peer "$GS_ROOT" G6 enum "빈 디렉토리 → NO_TARGET(3). CLEAN 이 아니다 — 아무것도 안 쟀다" 3 \
|
|
120
|
+
"$(_rc bash "$G" --target "$T/empty_target")"
|
|
121
|
+
_lane_peer "$GS_ROOT" G7 enum "없는 대상 → HARNESS_ERROR(10). 부재 대상은 빈 대상이 아니다" 10 \
|
|
122
|
+
"$(_rc bash "$G" --target "$T/does_not_exist")"
|
|
123
|
+
_lane_peer "$GS_ROOT" G8 enum "인자 없음 → HARNESS_ERROR(10). 안 돈 실행을 판정으로 렌더하지 않는다" 10 \
|
|
124
|
+
"$(_rc bash "$G")"
|
|
125
|
+
# MEDIUM 만 — 자동 차단이 아니라 사람 판단. 리터럴은 이 소스가 자기 스캐너에 물리지 않게 쪼갠다.
|
|
126
|
+
printf 'payroll note\ncontact ssn %s-%s-%s for the record\n' '123' '45' '6789' > "$T/medium_only.txt"
|
|
127
|
+
_lane_peer "$GS_ROOT" G9 enum "MEDIUM 만 → REVIEW_MEDIUM(2). CLEAN 과도 BLOCK 과도 다른 값이다" 2 \
|
|
128
|
+
"$(_rc bash "$G" --target "$T/medium_only.txt")"
|
|
129
|
+
echo
|
|
130
|
+
|
|
131
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
132
|
+
# ② mate-agent-boundary
|
|
133
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
134
|
+
echo "── mate-agent-boundary (enum 0=PASS 1=FAIL 10=HARNESS_ERROR 20=PEER_ABSENT)"
|
|
135
|
+
_lane M1 resolve-absent "peer 불가시 → 전용값 PEER_ABSENT(PASS 로도 HARNESS_ERROR 로도 안 접힌다)" 20 \
|
|
136
|
+
"$(FH_CLUSTER_ROOTS="$EMPTY_WORLD" _rc bash "$M" --known-positive)"
|
|
137
|
+
_lane_peer "$MT_ROOT" M2 control "peer 가시 → 실제 판정 PASS" 0 "$(_rc bash "$M" --known-positive)"
|
|
138
|
+
_lane_peer "$MT_ROOT" M3 control "peer 가시 → 음성 arm 은 FAIL (판별력의 증거)" 1 "$(_rc bash "$M" --known-negative)"
|
|
139
|
+
if [ -n "$MT_ROOT" ]; then
|
|
140
|
+
# 🟥 별칭 레인의 핵심: 이름은 `mate` 인데 실물 디렉토리는 `mate-dev` 다.
|
|
141
|
+
# 별칭 후보에서 `<n>-dev` 가 빠지면 이 레인이 즉시 PEER_ABSENT 로 빨개진다.
|
|
142
|
+
_lane M4 resolve-alias "이름 mate → 실물 $(basename "$MT_ROOT") 로 별칭 해석된다" 0 \
|
|
143
|
+
"$(FH_CLUSTER_ROOTS="$MT_ROOT" _rc bash "$M" --known-positive)"
|
|
144
|
+
else
|
|
145
|
+
echo " ⏭ M4 [resolve-alias ] mate 실물 루트 없음 — SKIP (통과 아님)"
|
|
146
|
+
fi
|
|
147
|
+
_lane_peer "$MT_ROOT" M5 enum "없는 대상 → HARNESS_ERROR(10). peer 헤더가 «위반 0 이 아니다» 라고 못 박은 값" 10 \
|
|
148
|
+
"$(_rc bash "$M" --target "$T/does_not_exist.md")"
|
|
149
|
+
_lane_peer "$MT_ROOT" M6 enum "알 수 없는 모드 → HARNESS_ERROR(10)" 10 "$(_rc bash "$M" --bogus-mode)"
|
|
150
|
+
echo
|
|
151
|
+
|
|
152
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
153
|
+
# ③ qasp-new-code-anchor
|
|
154
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
155
|
+
echo "── qasp-new-code-anchor (enum 0=PASS 1=VIOLATION 2=ARGS 3=HARNESS_ERROR 4=OUT_OF_SCOPE 20=PEER_ABSENT)"
|
|
156
|
+
_lane Q1 resolve-absent "peer 불가시 → 전용값 PEER_ABSENT(PASS 로도 HARNESS_ERROR 로도 안 접힌다)" 20 \
|
|
157
|
+
"$(FH_CLUSTER_ROOTS="$EMPTY_WORLD" _rc bash "$Q" --known-positive)"
|
|
158
|
+
_lane_peer "$QS_ROOT" Q2 control "peer 가시 → 실제 판정 VIOLATION" 1 "$(_rc bash "$Q" --known-positive)"
|
|
159
|
+
_lane_peer "$QS_ROOT" Q3 control "peer 가시 → 음성 arm 은 PASS (판별력의 증거)" 0 "$(_rc bash "$Q" --known-negative)"
|
|
160
|
+
if [ -n "$QS_ROOT" ]; then
|
|
161
|
+
_lane Q4 resolve-alias "이름 qasp → 실물 $(basename "$QS_ROOT") 로 별칭 해석된다" 0 \
|
|
162
|
+
"$(FH_CLUSTER_ROOTS="$QS_ROOT" _rc bash "$Q" --known-negative)"
|
|
163
|
+
# 🟥 모호 가드 — 한 이름에 레포 둘. **고르지 않는다.** 조용히 하나를 고르면 «어느
|
|
164
|
+
# 하네스를 쟀는지» 아무도 모르게 된다. 판정 불가는 PASS 가 아니므로 HARNESS_ERROR.
|
|
165
|
+
mkdir -p "$T/ambig/qasp" "$T/ambig/qasp-dev"
|
|
166
|
+
_lane Q5 resolve-guard "한 이름에 레포 둘 → 고르지 않고 HARNESS_ERROR(3)" 3 \
|
|
167
|
+
"$(FH_CLUSTER_ROOTS="$T/ambig/qasp:$T/ambig/qasp-dev" _rc bash "$Q" --known-positive)"
|
|
168
|
+
else
|
|
169
|
+
echo " ⏭ Q4/Q5 [resolve-* ] qasp 실물 루트 없음 — SKIP (통과 아님)"
|
|
170
|
+
fi
|
|
171
|
+
_lane_peer "$QS_ROOT" Q6 enum "--range 에 base 없음 → ARGS(2). 판정이 아니다" 2 "$(_rc bash "$Q" --range)"
|
|
172
|
+
|
|
173
|
+
# ── 합성 peer — peer 히스토리를 만지지 않고 나머지 enum 을 덮는다 ────────────
|
|
174
|
+
# 🟥 왜 합성 peer 인가: peer 는 `residency: company` 다. 실물 히스토리를 레인으로 쓰면
|
|
175
|
+
# 레인 출력에 조직 경로가 실린다. 그리고 실물 히스토리는 남의 커밋에 따라 조용히
|
|
176
|
+
# 다른 것을 재게 된다(감사 대상은 얼려야 한다). 합성 peer 는 **peer 의 실제 진입점
|
|
177
|
+
# 스크립트를 복사해** 쓰므로, 재는 것은 여전히 «어댑터가 peer 의 exit 을 옳게
|
|
178
|
+
# 재매핑하는가» 다 — 어댑터의 매핑이지 peer 의 히스토리가 아니다.
|
|
179
|
+
if [ -n "$QS_ROOT" ] && [ -r "$QS_ROOT/scripts/new_code_anchor_scan.sh" ]; then
|
|
180
|
+
SYN="$T/synworld/qasp-dev"; mkdir -p "$SYN/scripts" "$SYN/src" "$SYN/tests"
|
|
181
|
+
cp "$QS_ROOT/scripts/new_code_anchor_scan.sh" "$SYN/scripts/" || { echo "❌ HARNESS-ERROR — peer 진입점 복사 실패"; exit 10; }
|
|
182
|
+
GIT=(git -c user.email=lane@localhost -c user.name=lane -c commit.gpgsign=false)
|
|
183
|
+
( cd "$SYN" && "${GIT[@]}" init -q . && printf 'base\n' > README.md \
|
|
184
|
+
&& "${GIT[@]}" add -A && "${GIT[@]}" commit -qm base ) >/dev/null 2>&1 \
|
|
185
|
+
|| { echo "❌ HARNESS-ERROR — 합성 peer 레포 생성 실패"; exit 10; }
|
|
186
|
+
SYN_BASE="$(cd "$SYN" && git rev-parse HEAD)"
|
|
187
|
+
# 범위 밖 언어의 신규 실행 코드만 넣는다 → 「검사되지 않았다」 ≠ 「깨끗하다」
|
|
188
|
+
( cd "$SYN" && printf 'export const f = (a) => a + 1;\n' > src/newthing.ts \
|
|
189
|
+
&& "${GIT[@]}" add -A && "${GIT[@]}" commit -qm oos ) >/dev/null 2>&1
|
|
190
|
+
_lane Q7 enum "범위 밖 언어의 신규 실행 코드 → OUT_OF_SCOPE(4). PASS 로 접히지 않는다" 4 \
|
|
191
|
+
"$(FH_CLUSTER_ROOTS="$SYN" _rc bash "$Q" --range "$SYN_BASE" HEAD)"
|
|
192
|
+
_lane Q8 enum "해석 불가 ref → HARNESS_ERROR(3). 판정이 아니다" 3 \
|
|
193
|
+
"$(FH_CLUSTER_ROOTS="$SYN" _rc bash "$Q" --range no-such-ref-xyz HEAD)"
|
|
194
|
+
else
|
|
195
|
+
echo " ⏭ Q7/Q8 [enum ] qasp 진입점 도달 불가 — SKIP (통과 아님)"
|
|
196
|
+
fi
|
|
197
|
+
echo
|
|
198
|
+
|
|
199
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
200
|
+
# 되돌림 프로브 — 앵커가 장식이 아님을 «되돌려 보고» 잰다 (적용확인 → 실행 → 복원)
|
|
201
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
202
|
+
# 레인이 초록인 것과 레인에 **판별력이 있는 것**은 다른 명제다. 여기서는 peer 해석부를
|
|
203
|
+
# 두 방향으로 중화하고, **사전등록한 레인 집합만** 빨개지는지 본다.
|
|
204
|
+
# R1 «부재 분기» 중화 → 부재를 부재로 못 낸다 ⇒ resolve-absent + resolve-guard(부분일치) 빨강
|
|
205
|
+
# R2 «별칭 목록» 중화 → `-dev` 를 못 찾는다 ⇒ mate/qasp 의 alias·control·enum 레인만 빨강
|
|
206
|
+
#
|
|
207
|
+
# 🟥 **초판 사전등록이 두 군데 틀렸고, 프로브가 그걸 잡았다**(2026-08-16). 적어 두는 이유는
|
|
208
|
+
# 이게 프로브에 판별력이 있다는 증거이기 때문이다 — 사전등록을 관측에 맞춰 조용히 고치면
|
|
209
|
+
# 그 증거가 사라진다. 두 차이는 **레인 결함이 아니라 사전등록 오류**였고, 기계적으로 설명된다:
|
|
210
|
+
# · R1 에 **G5** 가 추가로 빨강: G5 는 `gstackx` 루트를 준다. 중화판은 부재 분기 대신
|
|
211
|
+
# `$PROJECTS_HOME/gstack` 를 내는데 **그 경로는 실재한다** → 어댑터가 실제로 돌아
|
|
212
|
+
# BLOCK_HIGH(1) 을 낸다. G5 도 부재 분기에 결박된 레인이므로 빨강이 맞다.
|
|
213
|
+
# · R2 에서 **Q5 는 초록**: Q5 는 `qasp` 와 `qasp-dev` 둘을 주는 모호 레인이다. 별칭
|
|
214
|
+
# 목록이 `$1` 하나로 줄면 `qasp` 만 맞아 **모호가 아니게 되고**, 어댑터는 진입점
|
|
215
|
+
# 부재로 HARNESS_ERROR(3) 를 낸다 — 기대값과 같은 값이다. 즉 R2 는 Q5 를 원리적으로
|
|
216
|
+
# 구분하지 못한다. 그건 Q5 의 결함이 아니라 **이 프로브 축의 사각지대**이고,
|
|
217
|
+
# Q5 는 R1 이 아니라 별도 축(모호 판정 자체를 지우는 중화)으로만 흔들 수 있다.
|
|
218
|
+
# ★중화는 **실물 파일이 아니라 사본**에 건다(FH_ADAPTER_DIR). 실물 트리를 건드리지 않고,
|
|
219
|
+
# 사본이 실물과 «중화한 줄 말고는 동일» 함을 적용확인 단계에서 대조한다.
|
|
220
|
+
_revert_probe() {
|
|
221
|
+
local name="$1" sedexpr="$2" applied_marker="$3" expect_red="$4" desc="$5"
|
|
222
|
+
local C="$T/neutralized_$name"
|
|
223
|
+
rm -rf "$C"; mkdir -p "$C"
|
|
224
|
+
cp -R "$REPO_ROOT/scripts/adapters/." "$C/" || { echo "❌ HARNESS-ERROR — 사본 생성 실패"; return 10; }
|
|
225
|
+
# ── 1단 적용확인: 중화가 **실제로 들어갔는가**. 안 들어간 채 «초록» 을 보고
|
|
226
|
+
# «레인이 튼튼하다» 고 읽는 것이 이 프로브의 최대 실패모드다.
|
|
227
|
+
sed -i.bak "$sedexpr" "$C/peer_resolve.sh" && rm -f "$C/peer_resolve.sh.bak"
|
|
228
|
+
if ! grep -q "$applied_marker" "$C/peer_resolve.sh"; then
|
|
229
|
+
echo " ❌ $name — 중화가 적용되지 않았다(마커 «$applied_marker» 부재). 프로브를 신뢰할 수 없다"
|
|
230
|
+
return 1
|
|
231
|
+
fi
|
|
232
|
+
# 중화한 파일 말고는 사본이 실물과 동일해야 한다(다른 변수를 안 건드렸다는 확인).
|
|
233
|
+
local other_diff
|
|
234
|
+
other_diff="$(diff -rq "$REPO_ROOT/scripts/adapters" "$C" 2>/dev/null | grep -v 'peer_resolve.sh' | wc -l | tr -d ' ')"
|
|
235
|
+
if [ "$other_diff" != "0" ]; then
|
|
236
|
+
echo " ❌ $name — 사본이 peer_resolve.sh 외에도 다르다($other_diff건). 프로브 격리 실패"
|
|
237
|
+
return 1
|
|
238
|
+
fi
|
|
239
|
+
# ── 2단 실행: 사본을 대상으로 레인을 통째로 다시 돌린다.
|
|
240
|
+
local out red
|
|
241
|
+
out="$(FH_ADAPTER_DIR="$C" bash "${BASH_SOURCE[0]}" --lanes-only 2>&1)"
|
|
242
|
+
red="$(printf '%s\n' "$out" | sed -n 's/^ ❌ \([A-Z][0-9]*\) .*/\1/p' | sort | tr '\n' ' ' | sed 's/ $//')"
|
|
243
|
+
# ── 3단 복원: 사본을 지운다. 실물은 애초에 안 건드렸다.
|
|
244
|
+
rm -rf "$C"
|
|
245
|
+
printf ' %s — %s\n' "$name" "$desc"
|
|
246
|
+
printf ' 사전등록 빨강: [%s]\n' "$expect_red"
|
|
247
|
+
printf ' 실측 빨강: [%s]\n' "$red"
|
|
248
|
+
if [ "$red" = "$expect_red" ]; then
|
|
249
|
+
printf ' ✅ 정확히 그 레인만 빨개졌다 — 이 레인들은 peer 해석부에 실제로 결박돼 있다\n'
|
|
250
|
+
return 0
|
|
251
|
+
fi
|
|
252
|
+
printf ' ❌ 사전등록과 다르다 — 레인이 장식이거나 사전등록이 틀렸다. 둘 다 결함이다\n'
|
|
253
|
+
return 1
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
case "${1:-}" in
|
|
257
|
+
--lanes-only)
|
|
258
|
+
# 되돌림 프로브가 자기를 재귀 호출할 때 쓰는 모드. 프로브를 다시 돌리지 않는다.
|
|
259
|
+
;;
|
|
260
|
+
--revert-probe)
|
|
261
|
+
echo "== 되돌림 프로브 (적용확인 → 실행 → 복원) =="
|
|
262
|
+
RP=0
|
|
263
|
+
# R1: 「없다」를 못 내게 한다 — 부재 분기를 순진한 추측 경로로 바꾼다.
|
|
264
|
+
_revert_probe R1 \
|
|
265
|
+
's|^ printf .peer-resolve: peer| printf "%s" "${FH_PROJECTS_HOME:-$HOME/projects}/$n"; return 0 # NEUTRALIZED_R1\n printf '"'"'peer-resolve: peer|' \
|
|
266
|
+
'NEUTRALIZED_R1' \
|
|
267
|
+
'G1 G5 M1 Q1' \
|
|
268
|
+
'«부재» 분기 중화 → 부재 분기에 결박된 레인만 빨개져야 한다' || RP=1
|
|
269
|
+
echo
|
|
270
|
+
# R2: 별칭 후보를 이름 자신 하나로 줄인다 — `-dev` 를 못 찾게 된다.
|
|
271
|
+
_revert_probe R2 \
|
|
272
|
+
's|^ printf .%s\\n. "\$1" "\$1-dev".*$| printf "%s\\n" "$1" # NEUTRALIZED_R2|' \
|
|
273
|
+
'NEUTRALIZED_R2' \
|
|
274
|
+
'M2 M3 M4 M5 M6 Q2 Q3 Q4 Q6 Q7 Q8' \
|
|
275
|
+
'별칭 목록 중화 → mate/qasp 쪽 레인만 빨개져야 한다(gstack 은 별칭이 필요 없다)' || RP=1
|
|
276
|
+
echo
|
|
277
|
+
[ "$RP" -eq 0 ] || FAIL=1
|
|
278
|
+
;;
|
|
279
|
+
'') ;;
|
|
280
|
+
*) echo "usage: $(basename "$0") [--revert-probe]" >&2; exit 10 ;;
|
|
281
|
+
esac
|
|
282
|
+
|
|
283
|
+
echo
|
|
284
|
+
if [ "$FAIL" -eq 0 ]; then
|
|
285
|
+
# ★`${N}` 로 감싼다 — 한글이 바로 뒤에 오면 bash 가 `$N개` 를 **변수명 `N개`** 로 읽어
|
|
286
|
+
# `unbound variable` 로 죽는다(실측 2026-08-16: 레인 23개 전부 초록인데 마지막 요약
|
|
287
|
+
# 줄에서 rc=1). 계기가 자기 보고 줄에서 죽으면 초록이 빨강으로 렌더된다.
|
|
288
|
+
echo "✅ 레인 ${N}개 전부 기대대로"
|
|
289
|
+
if [ "$SKIPPED" -gt 0 ]; then
|
|
290
|
+
echo "⏭ 단, peer 부재로 **${SKIPPED}개 레인이 안 돌았다** — 그중 컨트롤이 빠지면"
|
|
291
|
+
echo " 남은 초록은 「늘 PEER_ABSENT 를 내는 계기」와 구분되지 않는다. 초록을 그렇게 읽지 마라."
|
|
292
|
+
fi
|
|
293
|
+
else
|
|
294
|
+
echo "❌ 회귀 — 빨강:$RED_LANES (레인 ${N}개 실행 · peer 부재 SKIP ${SKIPPED}개)"
|
|
295
|
+
fi
|
|
296
|
+
exit "$FAIL"
|
|
@@ -41,7 +41,14 @@ command -v node >/dev/null 2>&1 || {
|
|
|
41
41
|
# Discover entry points by convention. A DISCOVERY of zero is an instrument failure, not a clean
|
|
42
42
|
# result: this check exists precisely because the surface it guards is invisible to reference
|
|
43
43
|
# walking, so "found nothing to check" must never render as "everything ships".
|
|
44
|
-
|
|
44
|
+
# 🟥 2026-08-16: 발견 규약이 **한 벌뿐**이었다(`scripts/*_capability.sh`, maxdepth 1). 같은 날
|
|
45
|
+
# 도입된 **어댑터 진입점**(`scripts/adapters/*.sh` — 남의 하네스 능력을 FH 안에서 부르는 기본
|
|
46
|
+
# 경로)은 이름도 위치도 그 규약 밖이라 **이 검사에 구조적으로 안 보였다.** 즉 어댑터 스크립트가
|
|
47
|
+
# `files[]` 에서 빠져도 이 검사는 초록이었다 — 검사기가 지키려던 바로 그 결함이 검사기 자신의
|
|
48
|
+
# 사각에서 재현될 수 있었다. 두 규약을 **합집합**으로 본다(빼지 말고 더한다).
|
|
49
|
+
ENTRIES=$( { find scripts -maxdepth 1 -type f -name '*_capability.sh' 2>/dev/null
|
|
50
|
+
find scripts/adapters -maxdepth 1 -type f -name '*.sh' 2>/dev/null
|
|
51
|
+
} | LC_ALL=C sort -u )
|
|
45
52
|
n=$(printf '%s\n' "$ENTRIES" | grep -c . || true)
|
|
46
53
|
n=$(( ${n:-0} + 0 ))
|
|
47
54
|
if [ "$n" -eq 0 ]; then
|
|
@@ -21,6 +21,18 @@ pass=0; fail=0
|
|
|
21
21
|
TMPROOT=$(mktemp -d "${TMPDIR:-/tmp}/fh_gate_test_XXXXXX")
|
|
22
22
|
trap 'rm -rf "$TMPROOT"' EXIT
|
|
23
23
|
|
|
24
|
+
# Review SUBJECT — a fixture this suite creates, NOT a file it hopes the repo has.
|
|
25
|
+
# Every gate invocation below used the literal `package.json` as its subject. That is an npm-package
|
|
26
|
+
# assumption smuggled into a test: this suite passed here only because this repo happens to ship one.
|
|
27
|
+
# MEASURED 2026-08-16 in a sibling harness that carries this file verbatim and ships no package.json:
|
|
28
|
+
# every lane that actually invokes the gate on a file returned 10 ("0 of N targets resolved →
|
|
29
|
+
# harness error") — 12 failures from this one hardcoded fixture, on top of 18 more from the same
|
|
30
|
+
# missing file killing the gate's version read outright. The suite was measuring the fixture's
|
|
31
|
+
# existence, not the gate's behaviour. A test that only runs in the repo it was written in is not a
|
|
32
|
+
# regression anchor; it is a local habit.
|
|
33
|
+
SUBJECT="$TMPROOT/subject_under_review.txt"
|
|
34
|
+
printf 'fixture file for gate regression lanes — content is irrelevant, existence is not\n' > "$SUBJECT"
|
|
35
|
+
|
|
24
36
|
# --- fake codex backend: writes $FAKE_PAYLOAD to the -o path, exits 0 ---
|
|
25
37
|
# Doubles as the live demonstration of the PATH-trusting residual documented in fh-gate.sh.
|
|
26
38
|
FAKEBIN="$TMPROOT/bin"; mkdir -p "$FAKEBIN"
|
|
@@ -68,16 +80,16 @@ check() {
|
|
|
68
80
|
fi
|
|
69
81
|
}
|
|
70
82
|
|
|
71
|
-
run_gate() { env "$@" bash "$GATE" "
|
|
83
|
+
run_gate() { env "$@" bash "$GATE" "$SUBJECT" quick test; }
|
|
72
84
|
run_fake() {
|
|
73
85
|
local payload="$1"; shift
|
|
74
86
|
env PATH="$FAKEBIN:$PATH" FH_BACKEND=codex FH_MODEL=fake FAKE_PAYLOAD="$payload" \
|
|
75
|
-
bash "$GATE" "
|
|
87
|
+
bash "$GATE" "$SUBJECT" quick test
|
|
76
88
|
}
|
|
77
89
|
run_fake_claude() {
|
|
78
90
|
local payload="$1"; shift
|
|
79
91
|
env PATH="$FAKEBIN:$PATH" FH_BACKEND=claude FH_MODEL=fake FAKE_PAYLOAD="$payload" \
|
|
80
|
-
bash "$GATE" "
|
|
92
|
+
bash "$GATE" "$SUBJECT" quick test
|
|
81
93
|
}
|
|
82
94
|
|
|
83
95
|
echo "── argument / env validation ──"
|
|
@@ -89,7 +101,7 @@ check "FH_TIMEOUT non-integer rejected" 11 run_gate FH_TIMEOUT="abc" FH_DRY_RUN=
|
|
|
89
101
|
check "FH_TIMEOUT integer accepted (no regression)" 12 run_gate FH_TIMEOUT=120 FH_DRY_RUN=1
|
|
90
102
|
# Newline in FH_CALLER forges an extra column-0 FH_GATE_VERDICT line in the legacy contract.
|
|
91
103
|
check "FH_CALLER newline injection rejected" 11 \
|
|
92
|
-
env FH_CALLER=$'ci\nFH_GATE_VERDICT: PASS' FH_DRY_RUN=1 bash "$GATE" "
|
|
104
|
+
env FH_CALLER=$'ci\nFH_GATE_VERDICT: PASS' FH_DRY_RUN=1 bash "$GATE" "$SUBJECT" quick
|
|
93
105
|
|
|
94
106
|
echo
|
|
95
107
|
echo "── dry-run must not be readable as PASS ──"
|
|
@@ -129,7 +141,7 @@ check "claude path: clean PASS → exit 0" 0 run_fake_claude \
|
|
|
129
141
|
check "claude path: is_error envelope → fails closed" 10 \
|
|
130
142
|
env PATH="$FAKEBIN:$PATH" FH_BACKEND=claude FH_MODEL=fake \
|
|
131
143
|
FAKE_ENVELOPE='{"is_error":true,"subtype":"error","structured_output":{"status":"SUCCESS","verdict":"PASS","findings_count":0,"findings_a":0,"findings_b":0,"findings":[]}}' \
|
|
132
|
-
bash "$GATE" "
|
|
144
|
+
bash "$GATE" "$SUBJECT" quick test
|
|
133
145
|
|
|
134
146
|
echo
|
|
135
147
|
echo "── legitimate verdicts still work (no over-blocking regression) ──"
|
|
@@ -199,7 +211,7 @@ NOENT="$TMPROOT/noentropy"; mkdir -p "$NOENT"
|
|
|
199
211
|
printf '#!/usr/bin/env bash\nexit 1\n' > "$NOENT/openssl"; chmod +x "$NOENT/openssl"
|
|
200
212
|
printf '#!/usr/bin/env bash\nexit 1\n' > "$NOENT/od"; chmod +x "$NOENT/od"
|
|
201
213
|
check "no CSPRNG (openssl+od stubbed to fail) → fails closed" 10 \
|
|
202
|
-
env PATH="$NOENT:$PATH" FH_DRY_RUN=1 bash "$GATE" "
|
|
214
|
+
env PATH="$NOENT:$PATH" FH_DRY_RUN=1 bash "$GATE" "$SUBJECT" quick test
|
|
203
215
|
|
|
204
216
|
echo
|
|
205
217
|
echo "── FH_BACKEND=cross (decorrelated review) ──"
|
|
@@ -225,7 +237,7 @@ check_out() { # <name> <expected-exit> <grep-ere that MUST appear> -- <cmd...>
|
|
|
225
237
|
run_cross() { # <claude-payload> <codex-payload>
|
|
226
238
|
env PATH="$FAKEBIN:$PATH" FH_BACKEND=cross FH_MODEL=fake \
|
|
227
239
|
FAKE_PAYLOAD_CLAUDE="$1" FAKE_PAYLOAD_CODEX="$2" FAKE_PAYLOAD="$1" \
|
|
228
|
-
bash "$GATE" "
|
|
240
|
+
bash "$GATE" "$SUBJECT" quick test
|
|
229
241
|
}
|
|
230
242
|
|
|
231
243
|
check_out "cross: both PASS → PASS, decorrelated" 0 'FH_GATE_DECORRELATED: yes' \
|
|
@@ -241,9 +253,9 @@ ONELEG="$TMPROOT/oneleg"; mkdir -p "$ONELEG"; cp "$FAKEBIN/claude" "$ONELEG/clau
|
|
|
241
253
|
check_out "cross: codex absent → single leg, DECORRELATED: no" 0 'FH_GATE_DECORRELATED: no' \
|
|
242
254
|
env PATH="$ONELEG:/usr/bin:/bin" FH_BACKEND=cross FH_MODEL=fake \
|
|
243
255
|
FAKE_PAYLOAD_CLAUDE="$CROSS_PASS" FAKE_PAYLOAD="$CROSS_PASS" \
|
|
244
|
-
bash "$GATE" "
|
|
256
|
+
bash "$GATE" "$SUBJECT" quick test
|
|
245
257
|
check "cross: no family available → fails closed" 10 \
|
|
246
|
-
env PATH="/usr/bin:/bin" FH_BACKEND=cross bash "$GATE" "
|
|
258
|
+
env PATH="/usr/bin:/bin" FH_BACKEND=cross bash "$GATE" "$SUBJECT" quick test
|
|
247
259
|
|
|
248
260
|
echo
|
|
249
261
|
echo "────────────────────────────────────────────────────────────────────"
|
|
@@ -2,9 +2,17 @@
|
|
|
2
2
|
# test_marker_axes_run_lanes.sh — regression fixtures for pre-commit validate_marker_axes_run.
|
|
3
3
|
#
|
|
4
4
|
# 대상: CLAUDE.md §3층 자기 대조가 요구하는 마커 3줄 중 **기계로 볼 수 있는 두 줄** —
|
|
5
|
-
# axes-run:
|
|
5
|
+
# axes-run: 각 축의 실행 여부. 마커 날짜에 따라 **배열이 둘**이다:
|
|
6
|
+
# >= 2026-08-17 ⓐ계열 · ⓑ입장 · ⓒ격리그라운딩 · ⓓ3자대면 · ⓔ첫실사용 · ⓕ되돌림
|
|
7
|
+
# < 2026-08-17 a=계열 · b=첫실사용 · c=기록그라운딩 · d=되돌림 (옛 ASCII 4축)
|
|
6
8
|
# controls: 각 축 컨트롤의 **생사**
|
|
7
9
|
#
|
|
10
|
+
# 🟥 이 헤더 자신이 2026-08-17 까지 그 충돌을 저지르고 있었다 — «네 축(ⓐ다른계열 ⓑ첫실사용
|
|
11
|
+
# ⓒ기록그라운딩 ⓓ되돌림)» 이라고, **기호 표기로 옛 4축 의미**를 가르쳤다. 훅 본문이
|
|
12
|
+
# 「옛 b=첫실사용은 지금 ⓔ」 라고 경고하는 파일의 짝인데 정작 짝이 반대를 가르쳤고,
|
|
13
|
+
# 같은 세션이 이 파일을 11→25레인으로 늘리면서 **헤더는 안 봤다**. 잡은 것은 pmh-dev
|
|
14
|
+
# 입장리뷰(tier2)이고, 이 파일은 전파 자산이라 그 오류가 하류로 배송되던 중이었다.
|
|
15
|
+
#
|
|
8
16
|
# 🟥 이 스위트가 증명하지 않는 것 (계약을 픽스처로 못박는다):
|
|
9
17
|
# 「a=codex」 라고 적혀 있을 때 codex 가 **실제로 돌았는지**는 검사하지 않는다.
|
|
10
18
|
# 마커의 계약은 form + non-vacuity + auditability 이지 provenance 가 아니다 —
|
|
@@ -25,12 +33,19 @@ T=$(mktemp -d); trap 'rm -rf "$T"' EXIT
|
|
|
25
33
|
# 훅에서 함수 + 유예 상수를 그대로 뽑아 쓴다. 복제하면 드리프트하므로 **소스가 하나**다.
|
|
26
34
|
{
|
|
27
35
|
grep -E '^AXES_RUN_GRACE_DATE=' "$HOOK"
|
|
36
|
+
grep -E '^SIX_AXES_GRACE_DATE=' "$HOOK"
|
|
28
37
|
sed -n '/^validate_marker_axes_run()/,/^}/p' "$HOOK"
|
|
29
38
|
} > "$T/fn.sh"
|
|
30
39
|
|
|
31
40
|
# 계기 자체가 살아있는지 먼저 본다 — 추출이 실패하면 모든 레인이 «통과»로 초록이 된다.
|
|
32
|
-
|
|
33
|
-
|
|
41
|
+
# 🟥 상수를 **각각** 확인한다. 하나로 뭉뚱그리면 새 상수가 추출에서 빠져도 초록이고,
|
|
42
|
+
# 그 경우 함수는 unbound 로 죽으며 모든 레인이 BLOCK 으로 몰려 «6축이 잘 막는다» 로
|
|
43
|
+
# 오독된다 — 계기 고장이 판정으로 렌더되는 형태다.
|
|
44
|
+
for _c in AXES_RUN_GRACE_DATE SIX_AXES_GRACE_DATE; do
|
|
45
|
+
grep -q "^${_c}=" "$T/fn.sh" || { echo "❌ 계기 사망: 훅에서 ${_c} 추출 실패 — 레인 결과를 신뢰하지 마라"; exit 1; }
|
|
46
|
+
done
|
|
47
|
+
if ! grep -q 'validate_marker_axes_run()' "$T/fn.sh"; then
|
|
48
|
+
echo "❌ 계기 사망: 훅에서 함수 추출 실패 — 레인 결과를 신뢰하지 마라"
|
|
34
49
|
exit 1
|
|
35
50
|
fi
|
|
36
51
|
|
|
@@ -90,6 +105,75 @@ check "$G2" PASS "파일명에 날짜가 없으면 판정 근거가 없다 →
|
|
|
90
105
|
B6=$(mk 2026-08-10 'axis2-evidence: PASS')
|
|
91
106
|
check "$B6" BLOCK "★유예일 **당일**은 요구한다 (경계가 < 이지 <= 가 아님을 고정)"
|
|
92
107
|
|
|
108
|
+
# ── 6축 확장 (2026-08-17) ───────────────────────────────────────────────────
|
|
109
|
+
# 표기법이 배열을 선언한다: ASCII(a=…d=)=옛 4축 · 기호(ⓐ=…ⓕ=)=현 6축.
|
|
110
|
+
# 그래서 여기서 고정할 것이 셋이다 — ⓐ 새 날짜는 기호 여섯을 요구하나 ⓑ 옛 날짜는
|
|
111
|
+
# 건드리지 않나(소급 차단 금지) ⓒ **혼용이 막히나**(혼용을 허용하면 판별 가능성이
|
|
112
|
+
# 통째로 사라지고, 옛 b=/d= 는 새 배열에서 다른 축이라 무음 오독이 된다).
|
|
113
|
+
D_SIX=2026-08-18 # 6축 유예일 이후
|
|
114
|
+
SIX_OK='axes-run: ⓐ=codex(4건) ⓑ=→standpoint ⓒ=none ⓓ=none ⓔ=CI배선 ⓕ=되돌림(국소성)
|
|
115
|
+
controls: alive — known-positive 3히트 · known-negative 0
|
|
116
|
+
standpoint: not-applicable'
|
|
117
|
+
|
|
118
|
+
S1=$(mk "$D_SIX" "$SIX_OK")
|
|
119
|
+
check "$S1" PASS "6축: 기호 여섯 + 컨트롤 + standpoint → 통과"
|
|
120
|
+
|
|
121
|
+
S2=$(mk "$D_SIX" 'axes-run: ⓐ=codex ⓑ=→standpoint ⓒ=none ⓓ=none ⓕ=되돌림
|
|
122
|
+
controls: alive — ok
|
|
123
|
+
standpoint: tier1')
|
|
124
|
+
check "$S2" BLOCK "6축: ⓔ 가 빠짐 → 차단"
|
|
125
|
+
|
|
126
|
+
S3=$(mk "$D_SIX" 'axes-run: ⓐ=codex b=CI배선 ⓒ=none ⓓ=none ⓔ=실사용 ⓕ=되돌림
|
|
127
|
+
controls: alive — ok')
|
|
128
|
+
check "$S3" BLOCK "★6축: ASCII 키가 기호 키를 대신함 → 차단 (ⓑ 가 빠졌다는 뜻이므로 missing-key 가 잡는다)"
|
|
129
|
+
|
|
130
|
+
# ★ 오탐 컨트롤 — 전용 혼용 가드를 지었다가 **실측 오탐 때문에 지웠다**. 값이 자유 산문이라
|
|
131
|
+
# `f=0.9` 나 `a=제어 b=처리` 가 값 안에 정당하게 들어온다. 과차단은 `--no-verify` 를 훈련시키고
|
|
132
|
+
# 그건 같은 훅의 Destructive-Op 게이트까지 무장해제한다. 이 두 레인이 그 삭제를 고정한다 —
|
|
133
|
+
# 누가 «안전하게» 혼용 가드를 되살리면 여기서 빨개진다.
|
|
134
|
+
S3b=$(mk "$D_SIX" 'axes-run: ⓐ=codex ⓑ=→standpoint ⓒ=none ⓓ=none ⓔ=실사용 ⓕ=되돌림 f=0.9 상승
|
|
135
|
+
controls: alive — ok
|
|
136
|
+
standpoint: tier1')
|
|
137
|
+
check "$S3b" PASS "★오탐 컨트롤: 값 안의 f=0.9 는 혼용이 아니다 → 통과"
|
|
138
|
+
|
|
139
|
+
S3c=$(mk "$D_SIX" 'axes-run: ⓐ=codex ⓑ=→standpoint ⓒ=none ⓓ=none ⓔ=arm a=제어 b=처리 비교 ⓕ=되돌림
|
|
140
|
+
controls: alive — ok
|
|
141
|
+
standpoint: tier1')
|
|
142
|
+
check "$S3c" PASS "★오탐 컨트롤: 값 안의 a=제어 b=처리 는 혼용이 아니다 → 통과"
|
|
143
|
+
|
|
144
|
+
S4=$(mk "$D_SIX" 'axes-run: a=codex b=CI배선 c=none d=되돌림
|
|
145
|
+
controls: alive — ok')
|
|
146
|
+
check "$S4" BLOCK "★6축 유예 후 옛 ASCII 넷만 → 차단 (같은 글자가 다른 축을 가리키므로 그대로 두면 무음 재해석)"
|
|
147
|
+
|
|
148
|
+
S5=$(mk "$D_NEW" 'axes-run: a=codex b=CI배선 c=none d=되돌림
|
|
149
|
+
controls: alive — ok')
|
|
150
|
+
check "$S5" PASS "★컨트롤: 6축 유예 **이전** 마커는 옛 ASCII 넷으로 그대로 통과 (소급 차단 없음)"
|
|
151
|
+
|
|
152
|
+
S6=$(mk 2026-08-17 'axes-run: a=codex b=CI c=none d=되돌림
|
|
153
|
+
controls: alive — ok')
|
|
154
|
+
check "$S6" BLOCK "★6축 유예일 **당일**은 요구한다 (경계가 < 임을 고정 — 상수를 바꾸면 빨개진다)"
|
|
155
|
+
|
|
156
|
+
S7=$(mk "$D_SIX" 'axes-run: ⓐ=none ⓑ=→standpoint ⓒ=none ⓓ=none ⓔ=none ⓕ=none
|
|
157
|
+
controls: n/a — 이번 델타에 측정이 없다')
|
|
158
|
+
check "$S7" BLOCK "★ⓑ=→standpoint 인데 standpoint: 줄이 없음 → 차단 (죽은 포인터)"
|
|
159
|
+
|
|
160
|
+
# ★ fail-open 컨트롤 — 초판은 화살표(→)를 필수로 봤고, 자기 프로브가 `ⓑ=standpoint`(화살표 없음)이
|
|
161
|
+
# 검사를 통째로 비껴가는 것을 실측했다. 죽은 포인터가 표기 하나로 통과하면 그건 fail-open 이다.
|
|
162
|
+
S7b=$(mk "$D_SIX" 'axes-run: ⓐ=none ⓑ=standpoint ⓒ=none ⓓ=none ⓔ=none ⓕ=none
|
|
163
|
+
controls: n/a — 측정 없음')
|
|
164
|
+
check "$S7b" BLOCK "★화살표 없는 ⓑ=standpoint 도 차단 (표기 하나로 검사를 비껴가면 fail-open)"
|
|
165
|
+
|
|
166
|
+
S7c=$(mk "$D_SIX" 'axes-run: ⓐ=none ⓑ=→standpoint(qasp) ⓒ=none ⓓ=none ⓔ=none ⓕ=none
|
|
167
|
+
controls: n/a — 측정 없음
|
|
168
|
+
standpoint: tier1b(qasp)')
|
|
169
|
+
check "$S7c" PASS "★컨트롤: 포인터에 대상이 붙어도 standpoint: 줄이 있으면 통과 (과차단 방지)"
|
|
170
|
+
|
|
171
|
+
S8=$(mk "$D_SIX" 'axes-run: ⓐ=codex ⓑ=→standpoint ⓒ=none ⓓ=none ⓔ=실사용 ⓕ=되돌림
|
|
172
|
+
axes-run: ⓐ=거짓말 ⓑ=x ⓒ=x ⓓ=x ⓔ=x ⓕ=x
|
|
173
|
+
controls: alive — ok
|
|
174
|
+
standpoint: tier1')
|
|
175
|
+
check "$S8" BLOCK "★axes-run 줄이 둘 → 차단 (첫 줄만 읽히므로 둘째 줄은 «없다»가 아니라 «안 보인다»)"
|
|
176
|
+
|
|
93
177
|
# ── ★ 배선 판별자 — 위 레인들은 **함수만** 본다 ──────────────────────────────
|
|
94
178
|
# 실측 2026-08-09: 훅의 호출부에서 `&& validate_marker_axes_run "$MARKER"` 를 떼고
|
|
95
179
|
# 위 11 레인을 돌렸더니 **전부 초록**이었다. 함수는 멀쩡하고 아무도 안 부르는 상태를
|
|
@@ -55,8 +55,38 @@ mk 'crossfamily: panel(codex,gemini) — R1..R3, 9 findings, 8 fixed 1 refuted,
|
|
|
55
55
|
check k1 PASS "panel() with family list + verdict"
|
|
56
56
|
mk 'crossfamily: panel(codex, gemini) — spaced list, the S-grade over-block\n' k1b
|
|
57
57
|
check k1b PASS "panel(codex, gemini) — SPACE after comma (was truncated to 'panel(codex,')"
|
|
58
|
+
# ⚠️ CONTRACT CHANGE 2026-08-16 — `declined` now requires grounds. It used to pass BARE, which is
|
|
59
|
+
# what the old k3 asserted. That was the enum's soft spot: `declined` means the OPERATOR declined
|
|
60
|
+
# sidecars (UAP), a chosen floor — a factual, attributable claim — and it was the ONLY value with no
|
|
61
|
+
# grounds requirement, so an author who had merely judged decorrelation unnecessary reached for the
|
|
62
|
+
# nearest permissive token and passed clean. Measured in this repo the same day, by a session that
|
|
63
|
+
# had corrected a different marker's crossfamily value hours earlier and had the correct semantics
|
|
64
|
+
# printed to it in the hook's own error text. A closed enum stops prose from collapsing distinct
|
|
65
|
+
# states; it does not stop a WRONG token from being a VALID one — that is what these three lanes are.
|
|
66
|
+
# The old bare-declined expectation is not deleted silently; it is inverted here with its reason.
|
|
58
67
|
mk 'crossfamily: declined\n' k3
|
|
59
|
-
check k3
|
|
68
|
+
check k3 BLOCK "declined BARE — no grounds (was PASS until 2026-08-16; an operator decision has a record)"
|
|
69
|
+
mk 'crossfamily: declined — operator declined sidecars, chosen floor, per knowledge/shared/rules/operational_adaptation.md\n' k3b
|
|
70
|
+
check k3b PASS "declined + a record path that RESOLVES (the documented form)"
|
|
71
|
+
# The real defect, verbatim-shaped: a genuine authorial judgment, plausibly worded, wrong token.
|
|
72
|
+
# This is the known-POSITIVE that is not synthetic — it is the sentence that actually shipped.
|
|
73
|
+
mk 'crossfamily: declined — this is a small, mechanically-verifiable hook-wiring fix, not a verdict-enum change; the standpoint axis is the decorrelation applied here\n' k3c
|
|
74
|
+
check k3c BLOCK "declined + AUTHOR-judgment grounds — a choice with a panel reachable is DEGRADED_PANEL_UNUSED"
|
|
75
|
+
# ── the three a cross-family review (codex/gpt-5.6-terra) found against the FIRST version of this
|
|
76
|
+
# lane, which grepped for vocabulary (operator|uap|consent|…). All three reproduced on the spot,
|
|
77
|
+
# which is why the check is now a RESOLVABLE-RECORD test rather than a word list.
|
|
78
|
+
mk 'crossfamily: declined — declined because I judged it unnecessary myself\n' k3d
|
|
79
|
+
check k3d BLOCK "SELF-VALIDATING: echoing the value satisfied the old word list ('declin')"
|
|
80
|
+
mk 'crossfamily: declined — operator documentation says authors may choose freely\n' k3e
|
|
81
|
+
check k3e BLOCK "VACUOUS: carries 'operator' but describes no declination at all"
|
|
82
|
+
mk 'crossfamily: declined — operator declined sidecars, see tracks/_meta/nonexistent_record.md\n' k3f
|
|
83
|
+
check k3f BLOCK "FABRICATED PATH: cites a record that does not exist on disk"
|
|
84
|
+
# ⚠️ INSTRUMENT NOTE, recorded because it nearly produced a false verdict on this very lane:
|
|
85
|
+
# the first attempt to known-pair these ran the extracted function under **zsh** (this operator's
|
|
86
|
+
# interactive shell) while the hook runs under **bash**. zsh does not word-split an unquoted
|
|
87
|
+
# parameter expansion, so the `for _tok in $reason` scan saw ONE token and the legitimate
|
|
88
|
+
# path-citing case scored a false BLOCK — the code was correct and the harness was wrong.
|
|
89
|
+
# Re-run under bash: 7/7. If you hand-test this function, invoke it with `bash`, not the login shell.
|
|
60
90
|
mk 'crossfamily: DEGRADED_SINGLE_FAMILY — probed codex/agy/4090, 0 capable reachable\n' k4
|
|
61
91
|
check k4 PASS "DEGRADED_SINGLE_FAMILY + substantive reason"
|
|
62
92
|
mk 'crossfamily: UNKNOWN — consent unset, panel not probed this round\n' k5
|