@chrono-meta/fh-gate 2.4.0 → 2.5.1
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-plugin/marketplace.json +2 -2
- package/AGENTS.md +11 -0
- package/CLAUDE.md +56 -1
- package/knowledge/shared/harness-core/fh_three_layer_canon.md +18 -6
- package/knowledge/shared/harness-core/field_verdict_crossfamily_gate.md +31 -0
- package/knowledge/shared/harness-core/sonnet_floor_doctrine.md +16 -0
- package/knowledge/shared/learnings/subagent_invocations_log.yaml +126 -0
- package/knowledge/shared/rules/multi_session_close_protocol.md +124 -2
- package/package.json +3 -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 +60 -0
- package/scripts/capability_effect_probe.sh +173 -5
- package/scripts/digest_landing_check.sh +168 -13
- package/scripts/fh_session_load.sh +71 -0
- package/scripts/selfcheck.sh +29 -0
- package/scripts/test_evidence_root_psa_lanes.sh +78 -0
- package/scripts/test_satellite_publish_gate_lanes.sh +339 -0
- package/templates/.git-hooks/pre-commit +64 -6
|
@@ -290,6 +290,77 @@ fi
|
|
|
290
290
|
( cd "$FH" && bash scripts/branch_claim.sh reap >/dev/null 2>&1 ) || true
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
+
# ── 매핑된 프로젝트의 capability 표면화 (2026-08-18) ───────────────────────────
|
|
294
|
+
# WHY HERE (branch-claim 과 같은 자리, early exit **위**): 이건 Mode D 전용이 아니다.
|
|
295
|
+
# companion store 가 없는 설치에서도 프로젝트는 매핑되고, 그때도 형제 하네스의 스킬은
|
|
296
|
+
# 안 보인다. 아래 early exit 밑에 두면 그 설치에서 통째로 죽는다 — 바로 위 branch-claim
|
|
297
|
+
# 블록이 같은 이유로 여기 있다.
|
|
298
|
+
# ⚠️ **정밀화(cross-family 적발)**: `tracks/` 는 npm 산출물에 **안 들어간다**(per-install 데이터).
|
|
299
|
+
# 그러니 «갓 설치한» 소비자에게는 이 블록이 아무것도 안 찍는다 — 값은 그 사람이 프로젝트를
|
|
300
|
+
# **매핑한 뒤부터** 생긴다. 「공개 사용자에게도 산다」는 그런 뜻이지 «설치 즉시 값이 있다» 가
|
|
301
|
+
# 아니다. (실측: 팩 산출물에 tracks 없음 → 침묵 · tracks/someproject 생성 후 → 발화)
|
|
302
|
+
#
|
|
303
|
+
# WHY AT ALL: `CLAUDE.md §Cross-Project Skill Bus` 는 registry 를 **온보딩 Step 1-c** 에서
|
|
304
|
+
# 읽는데, §Active Onboarding 의 Guards 가 «explicit task-entry → skip onboarding menu» 다.
|
|
305
|
+
# 첫 발화가 task 면 메뉴와 함께 **데이터 로드까지 같이 꺼진다.** 메뉴는 UI 고 registry 는
|
|
306
|
+
# 데이터인데 한 단계에 묶여 있어서 UI 를 끄면 데이터도 꺼진다.
|
|
307
|
+
# → Mode D companion-store 로드는 이 병을 이미 한 번 고쳤다(«이건 메뉴가 아니라 데이터
|
|
308
|
+
# 로드다» carve-out + 이 스크립트). **registry 엔 그 처방이 안 붙었다** = 반쪽-픽스
|
|
309
|
+
# 전파경계.
|
|
310
|
+
# 실측(2026-08-18): `tracks/gstack` 이 매핑돼 있고 SKILL.md 가 55개인데, 운영자가 이름을
|
|
311
|
+
# 댈 때까지 세션이 그 존재를 몰랐다. CATALOG·CLAUDE.md·sim-conductor 에 전부 등록돼 있었다.
|
|
312
|
+
#
|
|
313
|
+
# 비용 경계: 이름과 개수만. 목록 전량 로드는 안 한다(토큰). 실물이 없거나 스킬이 0인 트랙은
|
|
314
|
+
# 아예 안 찍는다 — 매 세션 13줄이 뜨면 그건 배경 소음이고 아무도 안 읽는다.
|
|
315
|
+
# 🟥 0 을 «없다» 로 렌더하지 않는다: 프로젝트 루트를 못 찾으면 그렇게 적고, 개수를 0 으로
|
|
316
|
+
# 적지 않는다(부재 ≠ 0).
|
|
317
|
+
_PROJ_ROOT="${FH_PROJECTS_ROOT:-$(dirname "$FH")}"
|
|
318
|
+
if [ -d "$FH/tracks" ]; then
|
|
319
|
+
_reg=""; _mapped=0; _resolved=0
|
|
320
|
+
for _t in "$FH"/tracks/*/; do
|
|
321
|
+
_n=$(basename "$_t")
|
|
322
|
+
case "$_n" in _*) continue ;; esac
|
|
323
|
+
[ "$_n" = "*" ] && continue
|
|
324
|
+
_mapped=$((_mapped + 1))
|
|
325
|
+
_d="$_PROJ_ROOT/$_n"
|
|
326
|
+
[ -d "$_d" ] || continue
|
|
327
|
+
_resolved=$((_resolved + 1))
|
|
328
|
+
# 무거운 디렉터리를 쳐낸다 — 비용 주장이 «내 머신의 내 프로젝트 집합» 위에 서 있으면
|
|
329
|
+
# 안 된다(cross-family 지적). node_modules/.venv/.git 이 depth 3 안에 있으면 선형으로 는다.
|
|
330
|
+
_sk=$(find "$_d" -maxdepth 3 \( -name node_modules -o -name .git -o -name .venv \
|
|
331
|
+
-o -name venv -o -name target -o -name dist \) -prune -o \
|
|
332
|
+
-name SKILL.md -print 2>/dev/null | wc -l | tr -d ' ')
|
|
333
|
+
[ "${_sk:-0}" -gt 0 ] || continue
|
|
334
|
+
_reg="${_reg} ▸ ${_n} — SKILL.md ${_sk}개 (${_d})
|
|
335
|
+
"
|
|
336
|
+
done
|
|
337
|
+
# 🟥 **부재 ≠ 0.** 판별자는 «루트 디렉터리가 있나»가 아니다 — `dirname "$FH"` 는 거의 항상
|
|
338
|
+
# 존재해서 그 분기는 발화 불가한 장식이었다(초판 결함, 같은 커밋에서 자력 적발).
|
|
339
|
+
# 실질 판별자는 **매핑된 트랙이 있는데 하나도 실물로 해소되지 않았다** 이고, 그때만
|
|
340
|
+
# 「못 쟀다」를 말한다. 소비자의 레포가 다른 경로에 있으면 정확히 이 상태가 된다.
|
|
341
|
+
# 🟥 **all-or-nothing 이면 안 된다** (cross-family 적발, 초판 결함): «전부 못 찾았을 때만»
|
|
342
|
+
# 말하면 13개 중 1개만 해소되고 12개가 없을 때 그 12개가 **완전히 침묵**한다 — 이 블록이
|
|
343
|
+
# 없애려는 «부재를 0으로 렌더» 를 블록 자신이 재현한다. 미해소가 하나라도 있으면 말한다.
|
|
344
|
+
_unres=$((_mapped - _resolved))
|
|
345
|
+
if [ "$_unres" -gt 0 ]; then
|
|
346
|
+
_reg="${_reg} ⚠️ 매핑된 트랙 ${_mapped}개 중 ${_unres}개는 «${_PROJ_ROOT}» 밑에서 실물을 못 찾았다 — 그 ${_unres}개는 «0» 이 아니라 **미측정**이다 (경로가 다르면 FH_PROJECTS_ROOT).
|
|
347
|
+
"
|
|
348
|
+
fi
|
|
349
|
+
if [ -n "$_reg" ]; then
|
|
350
|
+
if [ "$_resolved" -gt 0 ]; then
|
|
351
|
+
echo "🧩 [cross-project skill bus] 매핑된 프로젝트에 SKILL.md 가 있다 — 파일 개수만 센 것이지 «지금 세션에서 호출 가능하다» 는 확인이 아니다."
|
|
352
|
+
printf "%s" "$_reg"
|
|
353
|
+
# 목록이 실제로 있을 때만 낸다. 강등(못 찾음) 케이스에 이 안내가 붙으면 «읽을 것이 있다» 는
|
|
354
|
+
# 인상을 주는데 목록은 비어 있다 — 없는 것을 있다고 말하는 쪽이다.
|
|
355
|
+
echo " ⚠️ 이 줄은 «있다» 만 말한다. 쓰기 전에 그쪽 SKILL.md 를 읽어라 — 필드 하네스 용어를"
|
|
356
|
+
echo " 일반 개념으로 정규화하는 것이 이 레포의 상습 실패다."
|
|
357
|
+
else
|
|
358
|
+
echo "🧩 [cross-project skill bus] 매핑은 있는데 실물을 못 찾았다 — 아래는 **미측정**이다."
|
|
359
|
+
printf "%s" "$_reg"
|
|
360
|
+
fi
|
|
361
|
+
fi
|
|
362
|
+
fi
|
|
363
|
+
|
|
293
364
|
# Non-Mode-D / no companion store → silent no-op (this is the majority path for public users).
|
|
294
365
|
[ -d "$BE/.git" ] || exit 0
|
|
295
366
|
|
package/scripts/selfcheck.sh
CHANGED
|
@@ -703,6 +703,35 @@ else
|
|
|
703
703
|
fail=1
|
|
704
704
|
fi
|
|
705
705
|
|
|
706
|
+
# test_satellite_publish_gate_lanes — 위성 공개표면 게이트(2026-08-18, 원정 2차).
|
|
707
|
+
# 🟥 이 배선이 없어서 CI 가 `lane-runner: lane suite(s) with no runner and no declaration` 로
|
|
708
|
+
# 빨갰다. 스위트를 신설하고 아무 데서도 안 돌린 것 — 검사기의 표현대로
|
|
709
|
+
# *"A suite nothing executes is prose."* 그 검사기가 내 신설 레인을 잡았다.
|
|
710
|
+
if [ ! -f scripts/test_satellite_publish_gate_lanes.sh ]; then
|
|
711
|
+
echo "FAIL test_satellite_publish_gate_lanes.sh: missing — publish gate has no anchor"
|
|
712
|
+
fail=1
|
|
713
|
+
elif _out=$(bash scripts/test_satellite_publish_gate_lanes.sh < /dev/null 2>&1); then
|
|
714
|
+
echo "PASS test_satellite_publish_gate_lanes.sh ($(printf '%s\n' "$_out" | grep -oE '[0-9]+ passed, [0-9]+ failed' | tail -1))"
|
|
715
|
+
else
|
|
716
|
+
echo "FAIL test_satellite_publish_gate_lanes.sh: publish gate lanes failed"
|
|
717
|
+
_show_failure "$_out"
|
|
718
|
+
fail=1
|
|
719
|
+
fi
|
|
720
|
+
|
|
721
|
+
# test_evidence_root_psa_lanes — 워크트리에서 기밀성 오버라이드를 찾는가 (R4, 2026-08-18).
|
|
722
|
+
# 🟥 `EVIDENCE_ROOT` 에는 레인이 **하나도 없었다** — 그래서 `tracks/` 만 옮기고 gitignored
|
|
723
|
+
# 패턴 파일은 안 옮긴 반쪽-픽스가 그대로 출하됐다. 되돌림 프로브로 E1 만 적색 확인.
|
|
724
|
+
if [ ! -f scripts/test_evidence_root_psa_lanes.sh ]; then
|
|
725
|
+
echo "FAIL test_evidence_root_psa_lanes.sh: missing — EVIDENCE_ROOT 기밀성 경로에 앵커 없음"
|
|
726
|
+
fail=1
|
|
727
|
+
elif _out=$(bash scripts/test_evidence_root_psa_lanes.sh < /dev/null 2>&1); then
|
|
728
|
+
echo "PASS test_evidence_root_psa_lanes.sh ($(printf '%s\n' "$_out" | grep -oE '[0-9]+ passed, [0-9]+ failed' | tail -1))"
|
|
729
|
+
else
|
|
730
|
+
echo "FAIL test_evidence_root_psa_lanes.sh: evidence-root psa lanes failed"
|
|
731
|
+
_show_failure "$_out"
|
|
732
|
+
fail=1
|
|
733
|
+
fi
|
|
734
|
+
|
|
706
735
|
# cluster_capability_scan — wired 2026-08-16 (정체성 ①-(a) cluster-wizard).
|
|
707
736
|
# 종단 판정줄이 `… 캘리브레이션 통과/실패: N PASS / M FAIL` 이라 위 `_subj` 루프의
|
|
708
737
|
# 캘리브레이션 게이트를 **진짜 판정줄로** 만족한다 — 테스트 케이스 제목이 아니다.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_evidence_root_psa_lanes.sh — pre-commit 의 기밀성 스캐너가 **워크트리에서도 오버라이드를
|
|
3
|
+
# 찾는가** (R4, 2026-08-18).
|
|
4
|
+
#
|
|
5
|
+
# WHY
|
|
6
|
+
# `tracks/` 는 `--git-common-dir` 기준 `EVIDENCE_ROOT` 로 옮겼는데, **똑같이 gitignored 인**
|
|
7
|
+
# `.claude/rules/.public-surface-patterns` 는 `$REPO_ROOT` 에 남아 있었다. 그래서 워크트리
|
|
8
|
+
# 커밋이 매번 **defaults-only** 로 돌았다 — 회사명·실명 토큰 클래스가 통째로 UNSCANNED 고,
|
|
9
|
+
# 그 상태는 **비차단 경고**라 조용히 지나간다. 반쪽-픽스의 전형
|
|
10
|
+
# ([[feedback_half_fix_propagation_boundary]]) 이고, EVIDENCE_ROOT 에는 **레인이 하나도
|
|
11
|
+
# 없었다** — 그래서 반쪽인 채로 출하됐다.
|
|
12
|
+
#
|
|
13
|
+
# Lanes
|
|
14
|
+
# E1 워크트리에서 커밋해도 오버라이드를 찾는다 (경고 배너 없음)
|
|
15
|
+
# E1n CONTROL — 오버라이드를 지우면 같은 실행에서 배너가 **뜬다**(계기가 살아 있다는 증거)
|
|
16
|
+
# E2 CONTROL — 메인 트리는 종전 그대로 동작한다 (회귀 없음)
|
|
17
|
+
set -u
|
|
18
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
19
|
+
pass=0; fail=0
|
|
20
|
+
t() { if [ "$2" = "$3" ]; then echo " ✅ $1"; pass=$((pass+1)); else echo " ❌ $1 — got [$3] want [$2]"; fail=$((fail+1)); fi }
|
|
21
|
+
|
|
22
|
+
BANNER='CONFIDENTIALITY COVERAGE REDUCED'
|
|
23
|
+
|
|
24
|
+
setup() { # $1 = base dir
|
|
25
|
+
local b="$1"
|
|
26
|
+
mkdir -p "$b/main"
|
|
27
|
+
( cd "$b/main" && git init -q . && git config user.email t@t && git config user.name t
|
|
28
|
+
mkdir -p .claude/rules scripts templates/.git-hooks tracks/_meta
|
|
29
|
+
cp "$ROOT/scripts/psa_scan_lib.sh" scripts/
|
|
30
|
+
cp "$ROOT/.claude/rules/.public-surface-patterns.defaults" .claude/rules/ 2>/dev/null
|
|
31
|
+
cp "$ROOT/templates/.git-hooks/pre-commit" templates/.git-hooks/
|
|
32
|
+
printf 'tracks/\n.claude/rules/.public-surface-patterns\n' > .gitignore
|
|
33
|
+
printf 'x\n' > seed.md
|
|
34
|
+
git add -A && git commit -qm base ) >/dev/null 2>&1
|
|
35
|
+
# gitignored 오버라이드 — **메인 트리에만** 둔다 (실제 배치가 그렇다)
|
|
36
|
+
printf 'HIGH\tZZ_R4_OVERRIDE_TOKEN_ZZ\n' > "$b/main/.claude/rules/.public-surface-patterns"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# 🟥 고정 `/tmp` 경로 금지 (보안 패스 2026-08-18, MED·재현됨). `>/tmp/.r4out` 은 이름이
|
|
40
|
+
# 완전 고정이라, 공유 `/tmp` 를 쓰는 CI 러너·다중사용자 호스트에서 공격자가 먼저 심볼릭
|
|
41
|
+
# 링크를 걸어두면 `>` 가 링크를 따라가 **호출자 권한으로 임의 파일을 truncate + overwrite**
|
|
42
|
+
# 한다(sticky bit 는 «남의 파일 삭제»만 막지 «먼저 만든 링크»는 못 막는다). 이 스위트는
|
|
43
|
+
# 출하되고 `selfcheck.sh` 에 배선돼 있어 소비자가 셀프체크만 돌려도 발동한다.
|
|
44
|
+
R4OUT="$(mktemp "${TMPDIR:-/tmp}/fh_r4out.XXXXXX")" || exit 1
|
|
45
|
+
trap 'rm -f "$R4OUT"' EXIT
|
|
46
|
+
|
|
47
|
+
run_hook() { # $1 = 실행 디렉터리 → echoes banner|clean
|
|
48
|
+
local out
|
|
49
|
+
( cd "$1" && printf 'hello\n' > probe.md && git add probe.md \
|
|
50
|
+
&& bash templates/.git-hooks/pre-commit ) >"$R4OUT" 2>&1
|
|
51
|
+
if grep -q "$BANNER" "$R4OUT"; then echo banner; else echo clean; fi
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
B=$(mktemp -d); setup "$B"
|
|
55
|
+
( cd "$B/main" && git worktree add -q "$B/wt" -b wtbranch ) >/dev/null 2>&1
|
|
56
|
+
# 워크트리에는 templates/ 가 체크아웃돼 있다(tracked). 오버라이드는 없다(gitignored).
|
|
57
|
+
t "E1 워크트리에서도 오버라이드를 찾는다" "clean" "$(run_hook "$B/wt")"
|
|
58
|
+
t "E2 control — 메인 트리 회귀 없음" "clean" "$(run_hook "$B/main")"
|
|
59
|
+
mv "$B/main/.claude/rules/.public-surface-patterns" "$B/main/.claude/rules/.psa.bak"
|
|
60
|
+
t "E1n control — 오버라이드가 진짜 없으면 배너가 뜬다" "banner" "$(run_hook "$B/wt")"
|
|
61
|
+
|
|
62
|
+
# ── E3 · «있지만 무효» (cross-family/codex 지적, 2026-08-18) ─────────────────
|
|
63
|
+
# 🟥 지적: E1/E1n 은 **파일 없음**만 재고, EVIDENCE_ROOT 쪽 파일이 «있지만 비었거나 손상»인
|
|
64
|
+
# 경로를 안 잰다. 실측해 보니 기존 기계가 이미 **소리내며** 처리한다 —
|
|
65
|
+
# 빈 파일 → PSA_OVERRIDE_PRESENT=0 → 커버리지 축소 배너
|
|
66
|
+
# 손상 행 → PSA_BAD_ROWS>0 → «gate INACTIVE/INCOMPLETE»
|
|
67
|
+
# 🟥 그리고 **폴백하지 않는 것이 옳은 방향**이다: 워크트리에 있는 다른 파일로 조용히
|
|
68
|
+
# 갈아타면 «어느 패턴셋으로 쟀는지»가 로그에서 사라진다. 시끄럽게 알리는 쪽을 고정한다.
|
|
69
|
+
printf 'HIGH\tZZ_WT_LOCAL_ZZ\n' > "$B/wt/.claude/rules/.public-surface-patterns" # 워크트리 로컬본
|
|
70
|
+
: > "$B/main/.claude/rules/.public-surface-patterns" # EVIDENCE_ROOT = 빈 파일
|
|
71
|
+
t "E3 EVIDENCE_ROOT 가 비었으면 조용히 폴백하지 않고 알린다" "banner" "$(run_hook "$B/wt")"
|
|
72
|
+
printf 'no-tab-garbage-row\n' > "$B/main/.claude/rules/.public-surface-patterns"
|
|
73
|
+
out3=$( ( cd "$B/wt" && printf 'z\n' > p2.md && git add p2.md && bash templates/.git-hooks/pre-commit ) 2>&1 )
|
|
74
|
+
case "$out3" in *"INACTIVE/INCOMPLETE"*) r=announced ;; *) r=silent ;; esac
|
|
75
|
+
t "E3b 손상 행은 «게이트 무효»로 알린다(조용한 통과 아님)" "announced" "$r"
|
|
76
|
+
rm -rf "$B"
|
|
77
|
+
|
|
78
|
+
echo "----"; echo "evidence-root psa: $pass passed, $fail failed"; [ "$fail" -eq 0 ]
|
|
@@ -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 ]
|