@chrono-meta/fh-gate 2.3.0 → 2.5.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/rules/fh_4axis_gate.md +25 -0
- package/.claude-plugin/marketplace.json +2 -2
- package/CLAUDE.md +107 -7
- package/README.ja.md +14 -2
- package/README.ko.md +14 -1
- package/README.md +16 -2
- package/README.zh.md +12 -1
- package/knowledge/shared/harness-core/fh_three_layer_canon.md +18 -6
- package/knowledge/shared/harness-core/field_verdict_crossfamily_gate.md +262 -7
- package/knowledge/shared/harness-core/harness_incubator_doctrine.md +215 -1
- package/knowledge/shared/harness-core/ship_readiness_gate.md +168 -1
- package/knowledge/shared/harness-core/sonnet_floor_doctrine.md +16 -0
- package/knowledge/shared/learnings/subagent_invocations_log.yaml +120 -0
- package/knowledge/shared/rules/multi_session_close_protocol.md +124 -2
- package/package.json +8 -1
- package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-meta/CHANGELOG.md +105 -0
- package/plugins/fh-meta/skills/auto-decorrelation/SKILL.md +20 -0
- package/plugins/fh-meta/skills/steel-quench/SKILL.md +25 -2
- package/scripts/adapters/mate_agent_boundary.sh +27 -7
- package/scripts/capability_effect_probe.sh +173 -5
- package/scripts/capability_registry_check.sh +69 -3
- package/scripts/digest_landing_check.sh +168 -13
- package/scripts/fh_session_load.sh +71 -0
- package/scripts/relay_channel.sh +151 -6
- package/scripts/selfcheck.sh +47 -0
- package/scripts/target_freeze.sh +239 -0
- package/scripts/test_adapter_lanes.sh +17 -0
- package/scripts/test_evidence_root_psa_lanes.sh +78 -0
- package/scripts/test_marker_standpoint_lanes.sh +83 -0
- package/scripts/test_marker_thirdparty_lanes.sh +73 -0
- package/scripts/test_regression_guard_ci_lanes.sh +170 -0
- package/scripts/test_relay_channel_lanes.sh +146 -17
- package/scripts/test_satellite_publish_gate_lanes.sh +339 -0
- package/scripts/test_target_freeze_lanes.sh +203 -0
- package/templates/.git-hooks/pre-commit +306 -6
- package/templates/PRE-PUBLISH-CHECKLIST.md +22 -0
- package/templates/regression_guard.sh +32 -4
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_satellite_publish_gate_lanes.sh — known-pair anchor for the satellite's publish gate.
|
|
3
|
+
#
|
|
4
|
+
# WHY (2026-08-18, 원정 2차)
|
|
5
|
+
# frontier_digest_daily.sh 는 FH 자신만 대상으로 지어졌고, FH 의 digest 는 gitignored
|
|
6
|
+
# `tracks/` 에 떨어진다 — 그래서 **공개 노출 문제가 없었다**. 위성이 **공개 레포**를 대상으로
|
|
7
|
+
# 돌면 **매 런이 publish** 다(비가역 표면). §Irreversibility Surface-Class Degrade Invariant:
|
|
8
|
+
# 비가역 표면은 fail-CLOSED — 스캐너가 못 재면 «통과»가 아니라 «보류»다.
|
|
9
|
+
#
|
|
10
|
+
# 🟥 이 스위트의 진짜 주장은 «스캔한다»가 아니라 **«세 값을 두 값으로 접지 않는다»** 다.
|
|
11
|
+
# CLEAN(0) · LEAK(1) · NOT-SCANNED(3) 은 처방이 각각 다르다: 각각 게시 / 격리 / 보류.
|
|
12
|
+
# 접으면 «못 쟀다»가 «깨끗하다»로 렌더된다 — [[feedback_not_found_is_not_zero_family]].
|
|
13
|
+
#
|
|
14
|
+
# Lanes
|
|
15
|
+
# P1 CLEAN → rc=0, 파일 그대로 (게시)
|
|
16
|
+
# P2 LEAK → rc=2, 파일이 .QUARANTINE 로 격리 (게시 안 됨)
|
|
17
|
+
# P3 NOT-SCAN → rc=3, 파일이 .UNSCANNED 로 보류 (게시 안 됨) — 스캐너가 죽어도 fail-closed
|
|
18
|
+
# N1 CONTROL — FD_PUBLIC_TARGET 미설정 → 게이트 자체가 안 돈다 (FH 프로덕션 경로 무변경)
|
|
19
|
+
# N2 CONTROL — P2 와 P3 의 rc 가 **다르다**(2 vs 3). 같으면 두 실패가 한 값으로 접힌 것이다
|
|
20
|
+
# P4 착지 증언이 **실제로 로그에 찍힌다** — 직전 digest 가 없으면 «SKIPPED (not a pass)».
|
|
21
|
+
# 배선했다 ≠ 발화한다. 그리고 SKIP 을 통과로 렌더하지 않는 것까지 주장한다
|
|
22
|
+
#
|
|
23
|
+
# R1 착지 증언이 **살아 있다** — 러너가 부르는 형태로 rc=10(계기 사망)이 아니어야 한다
|
|
24
|
+
# R1n CONTROL — 옛 형태(디렉터리를 타깃으로)는 지금도 rc=10 이다. 고친 게 이거라는 증명
|
|
25
|
+
# R2 공개 대상이면 로그가 **OUT_DIR 밖**이다 / R2n CONTROL — 비공개면 종전 자리 그대로
|
|
26
|
+
# R2b LEAK 로그에 **토큰 원문이 없다**(리댁션) — 유출을 격리하며 같은 자리에 복사하지 않는다
|
|
27
|
+
# R3 같은 날 파일이 둘이고 **뒤엣것이 LEAK** 이면 rc=2 — head -1 이면 CLEAN 이 나온다
|
|
28
|
+
#
|
|
29
|
+
# Exit 0 = 전 레인.
|
|
30
|
+
set -u
|
|
31
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
32
|
+
# 🟥 arm 간 전달 파일을 **고정 `/tmp` 이름**으로 쓰면 안 된다 (보안 패스 2026-08-18, MED·재현됨).
|
|
33
|
+
# `/tmp/.lw_clean` · `/tmp/.lg_leak` 처럼 이름이 6개 전부 고정이라 심볼릭 링크 선점으로
|
|
34
|
+
# 임의 파일이 덮인다(`cp` 가 링크를 따라간다). 게다가 정리 코드가 없어 게이트 **로그 사본**이
|
|
35
|
+
# `/tmp` 에 644 로 영구히 남았다 — 기밀성 축의 정반대 방향이다.
|
|
36
|
+
SCRATCH="$(mktemp -d "${TMPDIR:-/tmp}/fh_satlane.XXXXXX")" || exit 1
|
|
37
|
+
trap 'rm -rf "$SCRATCH"' EXIT
|
|
38
|
+
pass=0; fail=0
|
|
39
|
+
t() { if [ "$2" = "$3" ]; then echo " ✅ $1"; pass=$((pass+1)); else echo " ❌ $1 — got [$3] want [$2]"; fail=$((fail+1)); fi }
|
|
40
|
+
|
|
41
|
+
run() { # $1 = digest body · $2 = psa_lib override ("dead") · $3 = arm 이름 → echoes "rc|state"
|
|
42
|
+
local td; td=$(mktemp -d)
|
|
43
|
+
mkdir -p "$td/scripts" "$td/out" "$td/.claude/rules"
|
|
44
|
+
cp "$ROOT/scripts/frontier_digest_daily.sh" "$td/scripts/"
|
|
45
|
+
cp "$ROOT/.claude/rules/.public-surface-patterns.defaults" "$td/.claude/rules/" 2>/dev/null
|
|
46
|
+
# 🟥 오버라이드 파일이 없으면 스캐너는 `override_absent(HIGH operator literals unscanned)` 로
|
|
47
|
+
# **NOT SCANNED** 를 낸다 — MED 히트를 찾고도 «부분 커버리지는 판정이 아니다»라며 거부한다.
|
|
48
|
+
# 엄격하게 옳은 동작이고, 그래서 이 픽스처는 **합성 리터럴**로 자립시킨다: 운영자의 실제
|
|
49
|
+
# 리터럴에 의존하면 이 스위트가 그 파일의 존재 여부에 따라 갈린다.
|
|
50
|
+
printf 'HIGH\tZZ_SYNTHETIC_LEAK_TOKEN_ZZ\n' > "$td/.claude/rules/.public-surface-patterns"
|
|
51
|
+
if [ "$2" = "dead" ]; then printf '#!/usr/bin/env bash\npsa_load(){ :; }\npsa_scan_file(){ return 3; }\n' > "$td/scripts/psa_scan_lib.sh"
|
|
52
|
+
else cp "$ROOT/scripts/psa_scan_lib.sh" "$td/scripts/"; fi
|
|
53
|
+
printf '%s\n' "$1" > "$td/out/frontier_digest_$(date +%Y_%m_%d).md"
|
|
54
|
+
# 러너가 digest_ready 로 이미 있는 파일을 보고 조기 종료하지 않도록, 게이트 경로를 직접 태운다:
|
|
55
|
+
# attempt 루프 진입 전에 skip 하므로 여기서는 러너 본체 대신 게이트 분기만 재현할 수 없다.
|
|
56
|
+
# → 러너를 그대로 돌리되 CLAUDE_BIN 을 no-op 스텁으로 줘서 attempt 를 태운다.
|
|
57
|
+
printf '#!/usr/bin/env bash\nexit 0\n' > "$td/scripts/stub-claude"; chmod +x "$td/scripts/stub-claude"
|
|
58
|
+
local out rc
|
|
59
|
+
# R2 이후 공개 대상의 로그는 OUT_DIR 밖이다 — arm 은 자기 위치를 명시한다(레인이 로그를 읽어야 하므로)
|
|
60
|
+
out=$(cd "$td" && FD_FH_DIR="$td" FD_OUT_DIR="out" FD_PUBLIC_TARGET=1 \
|
|
61
|
+
FD_LOG_DIR="$td/logs" \
|
|
62
|
+
FD_CLAUDE_BIN="$td/scripts/stub-claude" FD_MAX_ATTEMPTS=1 FD_ATTEMPT_TIMEOUT_SECS=20 \
|
|
63
|
+
FD_POLL_SECS=1 bash scripts/frontier_digest_daily.sh 2>&1); rc=$?
|
|
64
|
+
local state=none
|
|
65
|
+
ls "$td/out/"*.QUARANTINE >/dev/null 2>&1 && state=quarantine
|
|
66
|
+
ls "$td/out/"*.UNSCANNED >/dev/null 2>&1 && state=unscanned
|
|
67
|
+
ls "$td/out/"*.md >/dev/null 2>&1 && [ "$state" = none ] && state=published
|
|
68
|
+
# 착지 증언이 로그에 남았는지 (P4 가 읽는다)
|
|
69
|
+
# 🟥 arm 마다 따로 남긴다. 초판은 단일 파일에 덮어써서 **마지막 arm(P3, 스캐너 사망)** 것을
|
|
70
|
+
# 잡았는데, 거기선 게이트가 실패해 증언이 안 도는 게 정상이다 — 레인이 정상 동작을 실패로
|
|
71
|
+
# 렌더할 뻔했다. 판정 대상 arm 을 이름으로 고른다.
|
|
72
|
+
grep -h "landing witness" "$td/logs/"*.log 2>/dev/null | head -1 > "$SCRATCH/lw_$3" || :
|
|
73
|
+
# R2 레인이 읽는다: LEAK arm 의 로그에 토큰 원문이 남았나
|
|
74
|
+
cp "$td/logs/"*.log "$SCRATCH/lg_$3" 2>/dev/null || :
|
|
75
|
+
echo "$rc|$state"
|
|
76
|
+
rm -rf "$td"
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
CLEAN_BODY="# digest
|
|
80
|
+
A clean digest with no operator-private tokens. $(head -c 1200 /dev/zero | tr '\0' 'x')"
|
|
81
|
+
LEAK_BODY="# digest
|
|
82
|
+
this line carries ZZ_SYNTHETIC_LEAK_TOKEN_ZZ which the fixture pattern file flags HIGH. $(head -c 1200 /dev/zero | tr '\0' 'x')"
|
|
83
|
+
|
|
84
|
+
r=$(run "$CLEAN_BODY" live clean); t "P1 CLEAN → 게시" "0|published" "$r"
|
|
85
|
+
r=$(run "$LEAK_BODY" live leak); t "P2 LEAK → 격리(.QUARANTINE)" "2|quarantine" "$r"; P2RC=${r%%|*}
|
|
86
|
+
r=$(run "$CLEAN_BODY" dead dead); t "P3 스캐너 사망 → 보류(.UNSCANNED)" "3|unscanned" "$r"; P3RC=${r%%|*}
|
|
87
|
+
|
|
88
|
+
# N1 — 게이트 미활성(FD_PUBLIC_TARGET 없음)이면 LEAK 본문이어도 게시된다 = FH 경로 무변경
|
|
89
|
+
td=$(mktemp -d); mkdir -p "$td/scripts" "$td/out" "$td/.claude/rules"
|
|
90
|
+
cp "$ROOT/scripts/frontier_digest_daily.sh" "$ROOT/scripts/psa_scan_lib.sh" "$td/scripts/"
|
|
91
|
+
cp "$ROOT/.claude/rules/.public-surface-patterns.defaults" "$td/.claude/rules/" 2>/dev/null
|
|
92
|
+
printf '%s\n' "$LEAK_BODY" > "$td/out/frontier_digest_$(date +%Y_%m_%d).md"
|
|
93
|
+
printf '#!/usr/bin/env bash\nexit 0\n' > "$td/scripts/stub-claude"; chmod +x "$td/scripts/stub-claude"
|
|
94
|
+
out=$(cd "$td" && FD_FH_DIR="$td" FD_OUT_DIR="out" FD_CLAUDE_BIN="$td/scripts/stub-claude" \
|
|
95
|
+
FD_MAX_ATTEMPTS=1 FD_POLL_SECS=1 bash scripts/frontier_digest_daily.sh 2>&1); n1rc=$?
|
|
96
|
+
n1state=published; ls "$td/out/"*.QUARANTINE >/dev/null 2>&1 && n1state=quarantine
|
|
97
|
+
t "N1 control — 게이트 미활성이면 안 돈다(FH 경로 무변경)" "0|published" "$n1rc|$n1state"
|
|
98
|
+
rm -rf "$td"
|
|
99
|
+
|
|
100
|
+
lw=$(cat "$SCRATCH/lw_clean" 2>/dev/null) # 게이트가 CLEAN 인 arm 에서만 증언이 돈다
|
|
101
|
+
case "$lw" in *"landing witness"*) r=logged ;; *) r=absent ;; esac
|
|
102
|
+
t "P4 착지 증언이 로그에 찍힌다(배선했다≠발화한다)" "logged" "$r"
|
|
103
|
+
case "$lw" in *"SKIPPED (not a pass)"*) r=honest ;; *"ALL-LANDED"*) r=honest ;; *"SOME-UNLANDED"*) r=honest ;; *"HARNESS-ERROR"*) r=honest ;; *) r=other ;; esac
|
|
104
|
+
t "P4b SKIP 을 통과로 렌더하지 않는다" "honest" "$r"
|
|
105
|
+
|
|
106
|
+
t "N2 control — LEAK 과 NOT-SCANNED 의 rc 가 다르다" "different" "$([ "${P2RC:-x}" != "${P3RC:-y}" ] && echo different || echo collapsed)"
|
|
107
|
+
|
|
108
|
+
# ── FD_PROFILE 갈래 (2026-08-18) ──────────────────────────────────────────
|
|
109
|
+
# 🟥 이 배선은 **한 번 소실됐다.** forge-wiki 워킹 사본에만 넣었다가 PR 정리 때 rm 했고,
|
|
110
|
+
# 그 사실을 몰라 다음 대상의 약한 산출을 «입장 티어가 낮다»·«스킬을 못 불렀다»로 오진했다.
|
|
111
|
+
# 산출물은 커밋됐는데 그걸 만든 코드가 없던 상태 — 그래서 레인으로 못 박는다.
|
|
112
|
+
_prompt() { FH_DIR="$1" OUT_DIR=out HUMAN_DATE=2026-08-18 TODAY=2026_08_18 PROFILE_PATH="$2" \
|
|
113
|
+
bash -c "$(sed -n '/^_build_prompt()/,/^}/p' "$ROOT/scripts/frontier_digest_daily.sh")
|
|
114
|
+
_build_prompt"; }
|
|
115
|
+
pd=$(mktemp -d); printf 'PROFILE_CANARY_BODY
|
|
116
|
+
' > "$pd/p.md"
|
|
117
|
+
case "$(_prompt "$pd" "p.md")" in *"PROFILE_CANARY_BODY"*) r=in ;; *) r=missing ;; esac
|
|
118
|
+
t "P5 프로필이 프롬프트에 실제로 실린다" "in" "$r"
|
|
119
|
+
case "$(_prompt "$pd" "p.md")" in *"STANDPOINT"*) r=yes ;; *) r=no ;; esac
|
|
120
|
+
t "P5b 입장 지시(대상 소스를 열어라)가 실린다" "yes" "$r"
|
|
121
|
+
case "$(_prompt "$pd" "")" in *"STANDPOINT"*|*"TARGET HARNESS"*) r=leaked ;; *) r=clean ;; esac
|
|
122
|
+
t "N3 control — 프로필 미설정이면 종전 문자열 그대로(FH 경로 무변경)" "clean" "$r"
|
|
123
|
+
case "$(_prompt "$pd" "nonexistent.md")" in *"STANDPOINT"*) r=leaked ;; *) r=clean ;; esac
|
|
124
|
+
t "N4 control — 프로필 경로가 없으면 조용히 종전 경로(존재하지 않는 파일을 실었다고 안 한다)" "clean" "$r"
|
|
125
|
+
rm -rf "$pd"
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
# ── R1~R3 (2026-08-18) · 압축 후 수리분 앵커 ───────────────────────────────
|
|
129
|
+
# 🟥 세 결함 다 **내가 같은 날 만들었고**, 셋 다 레인이 없어서 「배선 완료」로 보고됐다.
|
|
130
|
+
|
|
131
|
+
# R2 · 공개 대상이면 로그가 OUT_DIR **밖**이다
|
|
132
|
+
_logloc() { # $1 = FD_PUBLIC_TARGET 값 → echoes inside|outside
|
|
133
|
+
local td; td=$(mktemp -d); mkdir -p "$td/scripts" "$td/out" "$td/.claude/rules"
|
|
134
|
+
cp "$ROOT/scripts/frontier_digest_daily.sh" "$td/scripts/"
|
|
135
|
+
cp "$ROOT/scripts/psa_scan_lib.sh" "$td/scripts/"
|
|
136
|
+
cp "$ROOT/.claude/rules/.public-surface-patterns.defaults" "$td/.claude/rules/" 2>/dev/null
|
|
137
|
+
printf 'HIGH\tZZ_SYNTHETIC_LEAK_TOKEN_ZZ\n' > "$td/.claude/rules/.public-surface-patterns"
|
|
138
|
+
printf '#!/usr/bin/env bash\nexit 0\n' > "$td/scripts/stub-claude"; chmod +x "$td/scripts/stub-claude"
|
|
139
|
+
printf 'clean body — 아무 토큰도 없다\n%s\n' "$(head -c 1200 /dev/zero | tr '\0' 'x')" \
|
|
140
|
+
> "$td/out/frontier_digest_$(date +%Y_%m_%d).md"
|
|
141
|
+
( cd "$td" && FD_FH_DIR="$td" FD_OUT_DIR="out" FD_PUBLIC_TARGET="$1" \
|
|
142
|
+
FD_CLAUDE_BIN="$td/scripts/stub-claude" FD_MAX_ATTEMPTS=1 FD_POLL_SECS=1 \
|
|
143
|
+
bash scripts/frontier_digest_daily.sh ) >/dev/null 2>&1
|
|
144
|
+
local r=outside
|
|
145
|
+
[ -d "$td/out/logs" ] && r=inside
|
|
146
|
+
echo "$r"; rm -rf "$td"
|
|
147
|
+
}
|
|
148
|
+
t "R2 공개 대상이면 로그가 OUT_DIR 밖에 앉는다" "outside" "$(_logloc 1)"
|
|
149
|
+
t "R2n control — 비공개면 종전 자리 그대로(FH 경로 무변경)" "inside" "$(_logloc 0)"
|
|
150
|
+
|
|
151
|
+
# R2b · LEAK 로그에 토큰 원문이 남지 않는다 (run() 이 "$SCRATCH/lg_leak" 에 복사해 둔다)
|
|
152
|
+
if [ -f "$SCRATCH/lg_leak" ]; then
|
|
153
|
+
if grep -q 'ZZ_SYNTHETIC_LEAK_TOKEN_ZZ' "$SCRATCH/lg_leak"; then r=raw
|
|
154
|
+
elif grep -q 'REDACTED' "$SCRATCH/lg_leak"; then r=redacted
|
|
155
|
+
else r=nofinding; fi
|
|
156
|
+
else r=nolog; fi
|
|
157
|
+
t "R2b LEAK 로그가 토큰 원문을 안 적는다(리댁션)" "redacted" "$r"
|
|
158
|
+
# R2c · 리댁션이 **allowlist 줄 형태**(끝이 `)`)도 가린다 — 초판은 줄 끝 앵커라 놓쳤다
|
|
159
|
+
_red() { bash -c "$(sed -n '/^_redact()/,/^}/p' "$ROOT/scripts/frontier_digest_daily.sh")
|
|
160
|
+
_redact \"\$1\"" _ "$1"; }
|
|
161
|
+
case "$(_red " ⚪ allowlisted — f.md: 'ZZ_TOK_ZZ' (row: f.md :: ZZ_TOK_ZZ)")" in
|
|
162
|
+
*ZZ_TOK_ZZ*) r=raw ;; *REDACTED*) r=redacted ;; *) r=other ;;
|
|
163
|
+
esac
|
|
164
|
+
t "R2c 리댁션이 allowlist 줄 형태도 가린다" "redacted" "$r"
|
|
165
|
+
case "$(_red " ❌ HIGH leak — f.md: 'ZZ_TOK_ZZ'")" in
|
|
166
|
+
*ZZ_TOK_ZZ*) r=raw ;; *REDACTED*) r=redacted ;; *) r=other ;;
|
|
167
|
+
esac
|
|
168
|
+
t "R2c-n control — 종전 형태(줄 끝 인용)도 여전히 가린다" "redacted" "$r"
|
|
169
|
+
|
|
170
|
+
# R2d · **경로 쪽** 토큰 (cross-family/codex F2) — 파일명에 토큰이 들어가면 왼쪽이 남았다
|
|
171
|
+
_leaky_name() {
|
|
172
|
+
local td; td=$(mktemp -d); mkdir -p "$td/scripts" "$td/out" "$td/.claude/rules"
|
|
173
|
+
cp "$ROOT/scripts/frontier_digest_daily.sh" "$ROOT/scripts/psa_scan_lib.sh" "$td/scripts/"
|
|
174
|
+
cp "$ROOT/.claude/rules/.public-surface-patterns.defaults" "$td/.claude/rules/" 2>/dev/null
|
|
175
|
+
printf 'HIGH\tZZ_SYNTHETIC_LEAK_TOKEN_ZZ\n' > "$td/.claude/rules/.public-surface-patterns"
|
|
176
|
+
printf '#!/usr/bin/env bash\nexit 0\n' > "$td/scripts/stub-claude"; chmod +x "$td/scripts/stub-claude"
|
|
177
|
+
local d; d=$(date +%Y_%m_%d)
|
|
178
|
+
# 🟥 파일명 자체에 토큰이 들어간 케이스. 본문에도 넣어야 스캐너가 히트를 낸다.
|
|
179
|
+
printf 'ZZ_SYNTHETIC_LEAK_TOKEN_ZZ\n%s\n' "$(head -c 1200 /dev/zero | tr '\0' 'x')" \
|
|
180
|
+
> "$td/out/frontier_digest_${d}-ZZ_SYNTHETIC_LEAK_TOKEN_ZZ.md"
|
|
181
|
+
( cd "$td" && FD_FH_DIR="$td" FD_OUT_DIR="out" FD_PUBLIC_TARGET=1 FD_LOG_DIR="$td/logs" \
|
|
182
|
+
FD_CLAUDE_BIN="$td/scripts/stub-claude" FD_MAX_ATTEMPTS=1 FD_POLL_SECS=1 \
|
|
183
|
+
bash scripts/frontier_digest_daily.sh ) >/dev/null 2>&1
|
|
184
|
+
if grep -q 'ZZ_SYNTHETIC_LEAK_TOKEN_ZZ' "$td/logs/"*.log 2>/dev/null; then echo raw
|
|
185
|
+
elif grep -q 'FILENAME REDACTED' "$td/logs/"*.log 2>/dev/null; then echo redacted
|
|
186
|
+
else echo other; fi
|
|
187
|
+
rm -rf "$td"
|
|
188
|
+
}
|
|
189
|
+
t "R2d 파일명에 든 토큰도 로그에 안 남는다" "redacted" "$(_leaky_name)"
|
|
190
|
+
|
|
191
|
+
# R3 · 같은 날 파일이 둘이면 **전부** 스캔한다 (head -1 이면 앞엣것만 보고 CLEAN 을 낸다)
|
|
192
|
+
_two_files() {
|
|
193
|
+
local td; td=$(mktemp -d); mkdir -p "$td/scripts" "$td/out" "$td/.claude/rules"
|
|
194
|
+
cp "$ROOT/scripts/frontier_digest_daily.sh" "$td/scripts/"
|
|
195
|
+
cp "$ROOT/scripts/psa_scan_lib.sh" "$td/scripts/"
|
|
196
|
+
cp "$ROOT/.claude/rules/.public-surface-patterns.defaults" "$td/.claude/rules/" 2>/dev/null
|
|
197
|
+
printf 'HIGH\tZZ_SYNTHETIC_LEAK_TOKEN_ZZ\n' > "$td/.claude/rules/.public-surface-patterns"
|
|
198
|
+
printf '#!/usr/bin/env bash\nexit 0\n' > "$td/scripts/stub-claude"; chmod +x "$td/scripts/stub-claude"
|
|
199
|
+
local d; d=$(date +%Y_%m_%d)
|
|
200
|
+
# 🟥 «먼저 걸리는» 쪽을 깨끗하게 둔다 — 초판(head -1)이면 이쪽만 보고 CLEAN 을 냈다.
|
|
201
|
+
printf 'aaa clean\n%s\n' "$(head -c 1200 /dev/zero | tr '\0' 'x')" > "$td/out/frontier_digest_${d}.md"
|
|
202
|
+
printf 'zzz ZZ_SYNTHETIC_LEAK_TOKEN_ZZ\n%s\n' "$(head -c 1200 /dev/zero | tr '\0' 'x')" \
|
|
203
|
+
> "$td/out/frontier_digest_${d}_b.md"
|
|
204
|
+
local rc
|
|
205
|
+
( cd "$td" && FD_FH_DIR="$td" FD_OUT_DIR="out" FD_PUBLIC_TARGET=1 FD_LOG_DIR="$td/logs" \
|
|
206
|
+
FD_CLAUDE_BIN="$td/scripts/stub-claude" FD_MAX_ATTEMPTS=1 FD_POLL_SECS=1 \
|
|
207
|
+
bash scripts/frontier_digest_daily.sh ) >/dev/null 2>&1; rc=$?
|
|
208
|
+
local q=no; ls "$td/out/"*_b.md.QUARANTINE >/dev/null 2>&1 && q=yes
|
|
209
|
+
echo "$rc|$q"; rm -rf "$td"
|
|
210
|
+
}
|
|
211
|
+
r=$(_two_files)
|
|
212
|
+
t "R3 둘째 파일의 LEAK 을 놓치지 않는다(rc)" "2" "${r%%|*}"
|
|
213
|
+
t "R3b 둘째 파일이 실제로 격리된다" "yes" "${r##*|}"
|
|
214
|
+
|
|
215
|
+
# R3c · 선택자 자기모순(digest_ready 참인데 게이트가 볼 파일 0건) → **보류**, 게시 아님
|
|
216
|
+
_selector_race() {
|
|
217
|
+
local td; td=$(mktemp -d); mkdir -p "$td/scripts" "$td/out" "$td/.claude/rules"
|
|
218
|
+
cp "$ROOT/scripts/frontier_digest_daily.sh" "$ROOT/scripts/psa_scan_lib.sh" "$td/scripts/"
|
|
219
|
+
cp "$ROOT/.claude/rules/.public-surface-patterns.defaults" "$td/.claude/rules/" 2>/dev/null
|
|
220
|
+
printf 'HIGH\tZZ_SYNTHETIC_LEAK_TOKEN_ZZ\n' > "$td/.claude/rules/.public-surface-patterns"
|
|
221
|
+
# digest_files 를 «참이라 말하고 목록은 비게» 만든다 — 레이스의 최소 재현
|
|
222
|
+
cat > "$td/drv.sh" <<'EOD'
|
|
223
|
+
FD_FH_DIR="$PWD"; export FD_FH_DIR
|
|
224
|
+
eval "$(sed -n '/^publish_gate()/,/^}/p' scripts/frontier_digest_daily.sh)"
|
|
225
|
+
eval "$(sed -n '/^_redact()/,/^}/p' scripts/frontier_digest_daily.sh)"
|
|
226
|
+
digest_files() { :; } # 0건
|
|
227
|
+
FH_DIR="$PWD"; OUT_DIR=out; LOG_FILE=/dev/null; FD_PUBLIC_TARGET=1
|
|
228
|
+
publish_gate; echo "rc=$?"
|
|
229
|
+
EOD
|
|
230
|
+
local o; o=$(cd "$td" && bash drv.sh 2>&1); rm -rf "$td"; echo "${o##*rc=}"
|
|
231
|
+
}
|
|
232
|
+
t "R3c 선택자 자기모순은 게시가 아니라 보류(3)" "3" "$(_selector_race)"
|
|
233
|
+
|
|
234
|
+
# R1 · 착지 증언이 **러너를 통해** 살아 있다
|
|
235
|
+
# 🟥 초판 레인은 checker 를 **직접** 불러서, R1 을 원복해도 초록이었다 — 호출부를 우회한
|
|
236
|
+
# 장식 앵커다([[feedback_anchor_can_be_decorative]]). 되돌림 프로브가 그걸 잡았다.
|
|
237
|
+
# 지금은 **러너를 돌리고 그 로그**를 읽는다. 러너가 어떻게 부르는지가 판정 대상이다.
|
|
238
|
+
_runner_witness() { # echoes verdict-word from the log
|
|
239
|
+
local td; td=$(mktemp -d)
|
|
240
|
+
( cd "$td" && git init -q . && git config user.email t@t && git config user.name t ) >/dev/null 2>&1
|
|
241
|
+
mkdir -p "$td/scripts" "$td/out" "$td/knowledge"
|
|
242
|
+
cp "$ROOT/scripts/frontier_digest_daily.sh" "$ROOT/scripts/digest_landing_check.sh" \
|
|
243
|
+
"$ROOT/scripts/utterance_landing_check.sh" "$td/scripts/"
|
|
244
|
+
chmod +x "$td/scripts/digest_landing_check.sh"
|
|
245
|
+
printf '#!/usr/bin/env bash\nexit 0\n' > "$td/scripts/stub-claude"; chmod +x "$td/scripts/stub-claude"
|
|
246
|
+
local d; d=$(date +%Y_%m_%d)
|
|
247
|
+
cat > "$td/out/frontier_digest_2001_01_01.md" <<'EOF'
|
|
248
|
+
# prev digest
|
|
249
|
+
## 📌 FH Immediate Application Candidates
|
|
250
|
+
|
|
251
|
+
| # | 티어 | 내용 |
|
|
252
|
+
|---|---|---|
|
|
253
|
+
| **M1** | **M** | `ZZ_LANDED_TOKEN_ZZ` 를 배선한다 |
|
|
254
|
+
EOF
|
|
255
|
+
sleep 1
|
|
256
|
+
printf 'ZZ_LANDED_TOKEN_ZZ 배선함 · ZZ_CTL_ZZ\n' > "$td/out/landed.md"
|
|
257
|
+
printf 'today body\n%s\n' "$(head -c 1200 /dev/zero | tr '\0' 'x')" > "$td/out/frontier_digest_${d}.md"
|
|
258
|
+
( cd "$td" && FD_FH_DIR="$td" FD_OUT_DIR="out" FD_LOG_DIR="$td/logs" \
|
|
259
|
+
FD_LANDING_TRACKED="knowledge" FD_LANDING_IGNORED="out" FD_LANDING_CONTROLS="ZZ_CTL_ZZ" \
|
|
260
|
+
FD_CLAUDE_BIN="$td/scripts/stub-claude" FD_MAX_ATTEMPTS=1 FD_POLL_SECS=1 \
|
|
261
|
+
bash scripts/frontier_digest_daily.sh ) >/dev/null 2>&1
|
|
262
|
+
local lw; lw=$(grep -h "landing witness" "$td/logs/"*.log 2>/dev/null | tail -1)
|
|
263
|
+
case "$lw" in
|
|
264
|
+
*HARNESS-ERROR*) echo dead ;;
|
|
265
|
+
*ALL-LANDED*) echo landed ;;
|
|
266
|
+
*SOME-UNLANDED*) echo measured ;;
|
|
267
|
+
*SKIPPED*) echo skipped ;;
|
|
268
|
+
*) echo none ;;
|
|
269
|
+
esac
|
|
270
|
+
rm -rf "$td"
|
|
271
|
+
}
|
|
272
|
+
t "R1 러너가 부르는 형태에서 계기가 살아 있다(HARNESS-ERROR 아님)" "landed" "$(_runner_witness)"
|
|
273
|
+
|
|
274
|
+
# R5 · «직전 다이제스트» 선택이 **표기 혼재**에서도 옳은가 (첫 실사용 발견)
|
|
275
|
+
# 🟥 실물 코퍼스에는 `2026-06-02`(대시)와 `2026_08_17`(언더바)가 **섞여 있다.** 로케일 정렬은
|
|
276
|
+
# 대시본을 뒤로 보내 **두 달 전 파일**을 직전으로 골랐고, 그 파일엔 후보 절이 없어 매 런
|
|
277
|
+
# HARNESS-ERROR 였다. 픽스처가 한 표기만 쓰면 레인 21개가 다 초록인 채로 이게 산다.
|
|
278
|
+
_prev_pick() {
|
|
279
|
+
local td; td=$(mktemp -d); mkdir -p "$td/out"
|
|
280
|
+
# 🟥 **같은 연도**여야 재현된다. 첫 픽스처는 `2020-06-02` 를 썼는데 그건 로케일 정렬에서도
|
|
281
|
+
# 올바르게 뒤로 가서 **버그 판본도 초록**이었다(되돌림 프로브가 잡은 세 번째 장식 앵커).
|
|
282
|
+
# 실물 코퍼스의 표기를 그대로 쓴다: `2026-06-02` vs `2026_08_17`.
|
|
283
|
+
: > "$td/out/frontier_digest_2026-06-02.md" # 옛 대시 표기, 날짜상 더 과거
|
|
284
|
+
: > "$td/out/frontier_digest_2026_08_17.md" # 언더바 표기, 더 최근
|
|
285
|
+
local d; d=$(date +%Y_%m_%d)
|
|
286
|
+
: > "$td/out/frontier_digest_${d}.md" # 오늘 것 (제외 대상)
|
|
287
|
+
# 러너의 선택 표현을 그대로 떼어 쓴다 — 우회하면 앵커가 장식이 된다
|
|
288
|
+
local expr; expr=$(sed -n '/prev=\$(find/,/tail -1)/p' "$ROOT/scripts/frontier_digest_daily.sh")
|
|
289
|
+
local got; got=$( cd "$td" && FH_DIR="$td" OUT_DIR=out TODAY="$d" bash -c "$expr; basename \"\$prev\"" )
|
|
290
|
+
rm -rf "$td"; echo "$got"
|
|
291
|
+
}
|
|
292
|
+
t "R5 표기가 섞여도 «직전»은 날짜상 최신이다" "frontier_digest_2026_08_17.md" "$(_prev_pick)"
|
|
293
|
+
|
|
294
|
+
# R6 · **엔진 루트 ≠ 대상 루트** (첫 실사용 발견, 2026-08-18)
|
|
295
|
+
# 🟥 위성은 이 러너를 복사하지 않고 FH 의 것을 그대로 돈다(launchd 가 FH 경로를 가리킨다).
|
|
296
|
+
# 초판은 스캐너·패턴·checker 를 전부 `$FH_DIR`(=대상 레포)에서 찾아서, 대상에 그 파일이
|
|
297
|
+
# 없으면 **publish gate 가 영영 fail-closed** 였다 — 실측으로 `psa_scan_lib.sh 부재` 를 봤다.
|
|
298
|
+
# 게다가 그 분기는 파일을 **격리하지 않아** «게시 안 함» 이 거짓이었다(digest 는 이미 트리에 있다).
|
|
299
|
+
_engine_vs_target() { # echoes "rc|state"
|
|
300
|
+
local eng tgt; eng=$(mktemp -d); tgt=$(mktemp -d)
|
|
301
|
+
mkdir -p "$eng/scripts" "$eng/.claude/rules" "$tgt/out" # 🟥 대상에는 scripts/ 를 안 만든다
|
|
302
|
+
cp "$ROOT/scripts/frontier_digest_daily.sh" "$ROOT/scripts/psa_scan_lib.sh" "$eng/scripts/"
|
|
303
|
+
cp "$ROOT/.claude/rules/.public-surface-patterns.defaults" "$eng/.claude/rules/" 2>/dev/null
|
|
304
|
+
printf 'HIGH\tZZ_SYNTHETIC_LEAK_TOKEN_ZZ\n' > "$eng/.claude/rules/.public-surface-patterns"
|
|
305
|
+
printf '#!/usr/bin/env bash\nexit 0\n' > "$eng/scripts/stub-claude"; chmod +x "$eng/scripts/stub-claude"
|
|
306
|
+
local d; d=$(date +%Y_%m_%d)
|
|
307
|
+
printf 'clean\n%s\n' "$(head -c 1200 /dev/zero | tr '\0' 'x')" > "$tgt/out/frontier_digest_${d}.md"
|
|
308
|
+
local rc
|
|
309
|
+
( FD_FH_DIR="$tgt" FD_OUT_DIR="out" FD_PUBLIC_TARGET=1 FD_LOG_DIR="$tgt/logs" \
|
|
310
|
+
FD_CLAUDE_BIN="$eng/scripts/stub-claude" FD_MAX_ATTEMPTS=1 FD_POLL_SECS=1 \
|
|
311
|
+
bash "$eng/scripts/frontier_digest_daily.sh" ) >/dev/null 2>&1; rc=$?
|
|
312
|
+
local state=none
|
|
313
|
+
ls "$tgt/out/"*.UNSCANNED >/dev/null 2>&1 && state=unscanned
|
|
314
|
+
ls "$tgt/out/"*.md >/dev/null 2>&1 && [ "$state" = none ] && state=published
|
|
315
|
+
rm -rf "$eng" "$tgt"; echo "$rc|$state"
|
|
316
|
+
}
|
|
317
|
+
r=$(_engine_vs_target)
|
|
318
|
+
t "R6 도구를 엔진 루트에서 찾는다(대상에 없어도 게이트가 돈다)" "0" "${r%%|*}"
|
|
319
|
+
t "R6b 그래서 CLEAN 한 digest 가 실제로 게시된다" "published" "${r##*|}"
|
|
320
|
+
|
|
321
|
+
# R6c · 스캐너가 **진짜로** 없으면 «게시 안 함» 이 말뿐이 아니라 격리까지 간다
|
|
322
|
+
_lib_absent() {
|
|
323
|
+
local eng tgt; eng=$(mktemp -d); tgt=$(mktemp -d)
|
|
324
|
+
mkdir -p "$eng/scripts" "$tgt/out"
|
|
325
|
+
cp "$ROOT/scripts/frontier_digest_daily.sh" "$eng/scripts/" # 🟥 psa_scan_lib.sh 는 안 넣는다
|
|
326
|
+
printf '#!/usr/bin/env bash\nexit 0\n' > "$eng/scripts/stub-claude"; chmod +x "$eng/scripts/stub-claude"
|
|
327
|
+
local d; d=$(date +%Y_%m_%d)
|
|
328
|
+
printf 'body\n%s\n' "$(head -c 1200 /dev/zero | tr '\0' 'x')" > "$tgt/out/frontier_digest_${d}.md"
|
|
329
|
+
local rc
|
|
330
|
+
( FD_FH_DIR="$tgt" FD_OUT_DIR="out" FD_PUBLIC_TARGET=1 FD_LOG_DIR="$tgt/logs" \
|
|
331
|
+
FD_CLAUDE_BIN="$eng/scripts/stub-claude" FD_MAX_ATTEMPTS=1 FD_POLL_SECS=1 \
|
|
332
|
+
bash "$eng/scripts/frontier_digest_daily.sh" ) >/dev/null 2>&1; rc=$?
|
|
333
|
+
local state=published
|
|
334
|
+
ls "$tgt/out/"*.UNSCANNED >/dev/null 2>&1 && state=quarantined
|
|
335
|
+
rm -rf "$eng" "$tgt"; echo "$state"
|
|
336
|
+
}
|
|
337
|
+
t "R6c 스캐너 부재는 말만이 아니라 실제 격리다" "quarantined" "$(_lib_absent)"
|
|
338
|
+
|
|
339
|
+
echo "----"; echo "satellite publish gate: $pass passed, $fail failed"; [ "$fail" -eq 0 ]
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_target_freeze_lanes.sh — known pairs for scripts/target_freeze.sh
|
|
3
|
+
#
|
|
4
|
+
# 🟥 이 레인이 실제로 재는 것: 「대상이 움직였음을 **가르는가**」. 양성만 재면 «항상 다르다고
|
|
5
|
+
# 하는 계기» 와 구분되지 않고, 그 계기는 매 라운드 판정을 무효화해 아무도 안 쓰게 된다
|
|
6
|
+
# (= 선행 실패 ①의 배경-소음 경로). 그래서 **복원 후 MATCH** 레인이 이 파일의 핵심이다.
|
|
7
|
+
#
|
|
8
|
+
# 🟥 그리고 이 스크립트가 존재하는 이유인 **선행 실패 2종**을 각각 한 레인으로 고정한다:
|
|
9
|
+
# A 줄 수가 같은 in-place 편집 — `base-SHA + diff 줄 수` 지문이 못 잡던 것
|
|
10
|
+
# B untracked 파일 추가 — 같은 지문이 구조적으로 맹목이던 것
|
|
11
|
+
# 이 둘이 빠지면 3번째 시도도 같은 자리에서 무너진다.
|
|
12
|
+
|
|
13
|
+
set -u
|
|
14
|
+
T="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/target_freeze.sh"
|
|
15
|
+
pass=0; fail=0
|
|
16
|
+
WORK=$(mktemp -d)
|
|
17
|
+
trap 'rm -rf "$WORK"' EXIT
|
|
18
|
+
R="$WORK/repo"
|
|
19
|
+
|
|
20
|
+
# run <label> <expect MATCH|WRONG|UNKNOWN> <cmd...>
|
|
21
|
+
run() {
|
|
22
|
+
local label="$1" want="$2"; shift 2
|
|
23
|
+
local out rc got
|
|
24
|
+
out=$("$@" 2>&1); rc=$?
|
|
25
|
+
case "$rc" in
|
|
26
|
+
0) got=MATCH ;;
|
|
27
|
+
1) got=WRONG ;;
|
|
28
|
+
10) got=UNKNOWN ;;
|
|
29
|
+
*) got="rc$rc" ;;
|
|
30
|
+
esac
|
|
31
|
+
if [ "$got" = "$want" ]; then
|
|
32
|
+
printf ' ✅ %-52s %s (expected %s)\n' "$label" "$got" "$want"; pass=$((pass+1))
|
|
33
|
+
else
|
|
34
|
+
printf ' ❌ %-52s %s (expected %s)\n out: %s\n' "$label" "$got" "$want" "$out"; fail=$((fail+1))
|
|
35
|
+
fi
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
echo "[target-freeze] fixture build"
|
|
39
|
+
mkdir -p "$R" && git init -q "$R"
|
|
40
|
+
git -C "$R" config user.email t@t; git -C "$R" config user.name t
|
|
41
|
+
printf 'a\n' >"$R/a.txt"
|
|
42
|
+
git -C "$R" add -A && git -C "$R" commit -qm init
|
|
43
|
+
|
|
44
|
+
echo "[target-freeze] lanes"
|
|
45
|
+
run "pin (최초)" MATCH bash "$T" pin "$R" demo
|
|
46
|
+
|
|
47
|
+
# ── A. 선행 실패 ②-1: 줄 수 불변 in-place 편집 ─────────────────────────────
|
|
48
|
+
printf 'b\n' >"$R/a.txt"
|
|
49
|
+
run "A 줄 수 같은 in-place 편집 → WRONG" WRONG bash "$T" verify "$R" demo
|
|
50
|
+
printf 'a\n' >"$R/a.txt"
|
|
51
|
+
|
|
52
|
+
# ── C. 판별력 컨트롤 — 되돌리면 다시 MATCH («항상 다르다» 계기가 아님) ────────
|
|
53
|
+
run "C 복원 후 → MATCH (판별력 컨트롤)" MATCH bash "$T" verify "$R" demo
|
|
54
|
+
|
|
55
|
+
# ── B. 선행 실패 ②-2: untracked 추가 ────────────────────────────────────────
|
|
56
|
+
printf 'new\n' >"$R/untracked.txt"
|
|
57
|
+
run "B untracked 추가 → WRONG" WRONG bash "$T" verify "$R" demo
|
|
58
|
+
rm -f "$R/untracked.txt"
|
|
59
|
+
run "B 제거 후 → MATCH" MATCH bash "$T" verify "$R" demo
|
|
60
|
+
|
|
61
|
+
# ── gitignore 된 파일은 대상이 아니다 (오탐 경로 — 산출물 디렉토리가 매 런 바뀐다) ──
|
|
62
|
+
printf 'out/\n' >"$R/.gitignore"
|
|
63
|
+
git -C "$R" add .gitignore && git -C "$R" commit -qm ignore
|
|
64
|
+
run "pin (ignore 커밋 후 재핀)" MATCH bash "$T" pin "$R" demo
|
|
65
|
+
mkdir -p "$R/out" && printf 'noise\n' >"$R/out/run.log"
|
|
66
|
+
run "ignored 산출물 생성 → MATCH (오탐 없음)" MATCH bash "$T" verify "$R" demo
|
|
67
|
+
|
|
68
|
+
# ── ⓑ 은폐 비트: 잴 수 없으면 «일치» 를 낼 자격이 없다 ──────────────────────
|
|
69
|
+
git -C "$R" update-index --assume-unchanged a.txt
|
|
70
|
+
printf 'HIDDEN\n' >"$R/a.txt"
|
|
71
|
+
run "assume-unchanged → UNKNOWN (통과 아님)" UNKNOWN bash "$T" verify "$R" demo
|
|
72
|
+
git -C "$R" update-index --no-assume-unchanged a.txt
|
|
73
|
+
printf 'a\n' >"$R/a.txt"
|
|
74
|
+
|
|
75
|
+
# ── ⓒ 미측정을 통과로 접지 않는다 ───────────────────────────────────────────
|
|
76
|
+
run "핀 없는 라벨 → UNKNOWN" UNKNOWN bash "$T" verify "$R" nolabel
|
|
77
|
+
run "git 레포 아님 → UNKNOWN" UNKNOWN bash "$T" verify "$WORK" demo
|
|
78
|
+
|
|
79
|
+
# ── 스테이징만 해도 대상은 움직인 것이다 ────────────────────────────────────
|
|
80
|
+
printf 'staged\n' >"$R/a.txt"; git -C "$R" add a.txt
|
|
81
|
+
run "staged 변경 → WRONG" WRONG bash "$T" verify "$R" demo
|
|
82
|
+
|
|
83
|
+
# ══════════════════════════════════════════════════════════════════════════════
|
|
84
|
+
# cross-family(codex, 2026-08-18)가 **뚫은** 경로들 — 전부 재현 확인 후 여기 고정한다.
|
|
85
|
+
# 🟥 위 11 레인이 초록인 채로 아래가 전부 통과했다: 컨트롤이 있다는 것과 판별력이 있다는
|
|
86
|
+
# 것은 다르다. 이 블록이 없으면 다음 수리가 같은 자리를 조용히 다시 연다.
|
|
87
|
+
# ══════════════════════════════════════════════════════════════════════════════
|
|
88
|
+
echo
|
|
89
|
+
echo "[target-freeze] cross-family 가 뚫은 경로 (fail-open 3 · 오탐 1)"
|
|
90
|
+
|
|
91
|
+
# ── S: 라벨 별칭 — `a/b` 와 `a_b` 가 같은 핀 파일이면 «핀한 적 없는 라벨» 이 MATCH ──
|
|
92
|
+
bash "$T" pin "$R" a_b >/dev/null 2>&1
|
|
93
|
+
run "S 라벨 별칭 a/b (핀 없음) → UNKNOWN" UNKNOWN bash "$T" verify "$R" a/b
|
|
94
|
+
|
|
95
|
+
# ── S: `ls-files -o` 실패가 파이프에 먹혀 «untracked 없음» 으로 접히던 것 ──────────
|
|
96
|
+
FAKEBIN="$WORK/fakebin"; mkdir -p "$FAKEBIN"
|
|
97
|
+
REALGIT=$(command -v git)
|
|
98
|
+
printf '#!/usr/bin/env bash
|
|
99
|
+
if [ "$1" = "-C" ] && [ "$3" = "ls-files" ] && [ "$4" = "-o" ]; then exit 42; fi
|
|
100
|
+
exec %q "$@"
|
|
101
|
+
' "$REALGIT" >"$FAKEBIN/git"
|
|
102
|
+
chmod +x "$FAKEBIN/git"
|
|
103
|
+
bash "$T" pin "$R" lsfail >/dev/null
|
|
104
|
+
printf 'new
|
|
105
|
+
' >"$R/u2.txt"
|
|
106
|
+
run "S ls-files -o 고장 → UNKNOWN (MATCH 아님)" UNKNOWN env PATH="$FAKEBIN:$PATH" bash "$T" verify "$R" lsfail
|
|
107
|
+
rm -f "$R/u2.txt"
|
|
108
|
+
|
|
109
|
+
# ── S: untracked 중첩 git repo — 안의 내용 변경을 못 보므로 **fail-closed** ────────
|
|
110
|
+
mkdir -p "$R/nested" && git init -q "$R/nested"
|
|
111
|
+
git -C "$R/nested" config user.email t@t; git -C "$R/nested" config user.name t
|
|
112
|
+
printf 'one
|
|
113
|
+
' >"$R/nested/x.txt"
|
|
114
|
+
run "S untracked 디렉토리 → UNKNOWN (못 잰다고 말한다)" UNKNOWN bash "$T" pin "$R" nested
|
|
115
|
+
rm -rf "$R/nested"
|
|
116
|
+
|
|
117
|
+
# ── A: 깨진 심볼릭 링크 재지정 — 링크가 가리키는 문자열이 내용이다 ─────────────────
|
|
118
|
+
ln -s missing-one "$R/link"
|
|
119
|
+
run "A 심볼릭 링크 pin" MATCH bash "$T" pin "$R" symlink
|
|
120
|
+
ln -sfn missing-two "$R/link"
|
|
121
|
+
run "A 링크 재지정 → WRONG" WRONG bash "$T" verify "$R" symlink
|
|
122
|
+
ln -sfn missing-one "$R/link"
|
|
123
|
+
run "A 링크 복원 → MATCH (판별력 컨트롤)" MATCH bash "$T" verify "$R" symlink
|
|
124
|
+
rm -f "$R/link"
|
|
125
|
+
|
|
126
|
+
# ── S: 서브모듈 **워킹트리** 내용 변경 (커밋 없이) ────────────────────────────────
|
|
127
|
+
# 🟥 `submodule status` 만으로는 못 잡는다 — 그 줄은 서브모듈의 HEAD 를 말하므로,
|
|
128
|
+
# 커밋 없이 파일만 고치면 SHA 가 그대로다. 수리 1차본이 정확히 여기서 뚫렸다.
|
|
129
|
+
SUB="$WORK/sub"; mkdir -p "$SUB" && git init -q "$SUB"
|
|
130
|
+
git -C "$SUB" config user.email t@t; git -C "$SUB" config user.name t
|
|
131
|
+
printf 'v1\n' >"$SUB/s.txt"; git -C "$SUB" add -A; git -C "$SUB" commit -qm sub
|
|
132
|
+
SR="$WORK/subhost"; mkdir -p "$SR" && git init -q "$SR"
|
|
133
|
+
git -C "$SR" config user.email t@t; git -C "$SR" config user.name t
|
|
134
|
+
printf 'a\n' >"$SR/a.txt"; git -C "$SR" add -A; git -C "$SR" commit -qm init
|
|
135
|
+
if git -C "$SR" -c protocol.file.allow=always submodule add -q "$SUB" sub 2>/dev/null; then
|
|
136
|
+
git -C "$SR" commit -qm addsub
|
|
137
|
+
printf 'v2\n' >"$SR/sub/s.txt"
|
|
138
|
+
run "S 서브모듈 pin" MATCH bash "$T" pin "$SR" sm
|
|
139
|
+
printf 'v3\n' >"$SR/sub/s.txt"
|
|
140
|
+
run "S 서브모듈 워킹트리 변경 → WRONG" WRONG bash "$T" verify "$SR" sm
|
|
141
|
+
printf 'v2\n' >"$SR/sub/s.txt"
|
|
142
|
+
run "S 서브모듈 복원 → MATCH (판별력 컨트롤)" MATCH bash "$T" verify "$SR" sm
|
|
143
|
+
else
|
|
144
|
+
printf ' ⚠️ 서브모듈 레인 skip — 이 git 이 file:// 서브모듈을 거부한다(미측정, 통과 아님)\n'
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
# ── A: 오탐 — 빈 커밋은 **내용을 안 바꾼다**. 트리 해시를 쓰므로 MATCH 여야 한다 ────
|
|
148
|
+
# ★앞 레인이 스테이징을 남겨 뒀다. 그대로 `--allow-empty` 하면 **그 스테이징이 커밋되어**
|
|
149
|
+
# 트리가 실제로 바뀐다 — 초판에서 이 레인이 빨갰던 이유는 스크립트가 아니라 픽스처였다.
|
|
150
|
+
git -C "$R" reset -q --hard HEAD
|
|
151
|
+
bash "$T" pin "$R" emptycommit >/dev/null
|
|
152
|
+
git -C "$R" commit --allow-empty -qm noop
|
|
153
|
+
run "A 빈 커밋(같은 tree) → MATCH (오탐 아님)" MATCH bash "$T" verify "$R" emptycommit
|
|
154
|
+
|
|
155
|
+
# ══════════════════════════════════════════════════════════════════════════════
|
|
156
|
+
# 2번째 계열(agy, 2026-08-18)이 **다른 축**에서 뚫은 것 — codex 지적 목록을 주고 «그 밖»을
|
|
157
|
+
# 요구했다. 계열을 나눈 것이 값을 냈다(같은 계열 둘이면 같은 사각이 남는다).
|
|
158
|
+
# ══════════════════════════════════════════════════════════════════════════════
|
|
159
|
+
echo
|
|
160
|
+
echo "[target-freeze] 2번째 계열이 뚫은 축"
|
|
161
|
+
|
|
162
|
+
# ── S: `GIT_DIR` 이 `git -C` 를 이긴다 — 엉뚱한 레포를 재고 «동결했다» 고 보고하던 것 ──
|
|
163
|
+
OTHER="$WORK/other"; mkdir -p "$OTHER" && git init -q "$OTHER"
|
|
164
|
+
git -C "$OTHER" config user.email t@t; git -C "$OTHER" config user.name t
|
|
165
|
+
printf 'other\n' >"$OTHER/o.txt"; git -C "$OTHER" add -A; git -C "$OTHER" commit -qm other
|
|
166
|
+
git -C "$R" reset -q --hard HEAD
|
|
167
|
+
bash "$T" pin "$R" gitdir >/dev/null
|
|
168
|
+
printf 'moved\n' >"$R/a.txt"
|
|
169
|
+
# 환경변수가 살아 있으면 $R 이 아니라 $OTHER 를 재서 «변화 없음»(MATCH)이 나왔다.
|
|
170
|
+
run "S GIT_DIR 오염 상태에서도 대상은 R → WRONG" WRONG \
|
|
171
|
+
env GIT_DIR="$OTHER/.git" GIT_WORK_TREE="$OTHER" bash "$T" verify "$R" gitdir
|
|
172
|
+
git -C "$R" checkout -q -- a.txt
|
|
173
|
+
run "S 오염 제거 후 복원 → MATCH (판별력 컨트롤)" MATCH bash "$T" verify "$R" gitdir
|
|
174
|
+
|
|
175
|
+
# ── S: `_sha` 가 `| awk` 로 rc 를 잃던 것 — 해시 실패가 «빈 지문» 으로 접히면 MATCH ──────
|
|
176
|
+
# 🟥 이 레인은 **되돌림 프로브가 만들었다**: `_sha` 의 rc 검사를 되돌렸는데 26 레인이
|
|
177
|
+
# 전부 초록이었다 = 그 수리를 덮는 앵커가 하나도 없었다(장식 앵커). 계기가 스스로를
|
|
178
|
+
# 못 재는 자리를 되돌림이 짚어낸 것이다.
|
|
179
|
+
SHABIN="$WORK/shabin"; mkdir -p "$SHABIN"
|
|
180
|
+
REALSHA=$(command -v shasum || true)
|
|
181
|
+
if [ -n "$REALSHA" ]; then
|
|
182
|
+
printf '#!/usr/bin/env bash\ninput=$(cat)\ncase "$input" in boom*) exit 7 ;; esac\nprintf %%s "$input" | %q "$@"\n' "$REALSHA" >"$SHABIN/shasum"
|
|
183
|
+
chmod +x "$SHABIN/shasum"
|
|
184
|
+
git -C "$R" reset -q --hard HEAD
|
|
185
|
+
printf 'boom1\n' >"$R/u4.txt"
|
|
186
|
+
run "S 해시 도구 실패 → UNKNOWN (pin)" UNKNOWN env PATH="$SHABIN:$PATH" bash "$T" pin "$R" shafail
|
|
187
|
+
rm -f "$R/u4.txt"
|
|
188
|
+
run "S 정상 도구로는 pin 됨 (판별력 컨트롤)" MATCH bash "$T" pin "$R" shafail
|
|
189
|
+
else
|
|
190
|
+
printf ' ⚠️ _sha 레인 skip — shasum 부재(미측정, 통과 아님)\n'
|
|
191
|
+
fi
|
|
192
|
+
|
|
193
|
+
# ── B: untracked 파일의 실행 비트 — 이 레포가 이미 데인 축 ────────────────────────
|
|
194
|
+
printf '#!/bin/sh\necho hi\n' >"$R/u3.sh"; chmod 644 "$R/u3.sh"
|
|
195
|
+
run "B 실행비트 pin" MATCH bash "$T" pin "$R" execbit
|
|
196
|
+
chmod +x "$R/u3.sh"
|
|
197
|
+
run "B chmod +x → WRONG (내용은 그대로다)" WRONG bash "$T" verify "$R" execbit
|
|
198
|
+
chmod 644 "$R/u3.sh"
|
|
199
|
+
run "B 비트 복원 → MATCH (판별력 컨트롤)" MATCH bash "$T" verify "$R" execbit
|
|
200
|
+
rm -f "$R/u3.sh"
|
|
201
|
+
|
|
202
|
+
printf '\n[target-freeze] pass=%d fail=%d\n' "$pass" "$fail"
|
|
203
|
+
[ "$fail" -eq 0 ]
|