@chrono-meta/fh-gate 2.2.0 โ 2.4.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/qasp-new-code-anchor.cap +5 -2
- package/.claude/capabilities/adapters/qasp-web-rules.cap +130 -0
- package/.claude/rules/fh_4axis_gate.md +55 -0
- package/.claude-plugin/marketplace.json +2 -2
- package/CLAUDE.md +51 -6
- 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/field_verdict_crossfamily_gate.md +231 -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/learnings/subagent_invocations_log.yaml +24 -0
- package/package.json +9 -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 +110 -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/fixtures/qasp_web_rules_known_negative.json +7 -0
- package/scripts/adapters/fixtures/qasp_web_rules_known_positive.json +7 -0
- package/scripts/adapters/mate_agent_boundary.sh +27 -7
- package/scripts/adapters/qasp_web_rules.sh +171 -0
- package/scripts/capability_effect_probe.sh +104 -0
- package/scripts/capability_registry_check.sh +69 -3
- package/scripts/relay_channel.sh +151 -6
- package/scripts/selfcheck.sh +18 -0
- package/scripts/target_freeze.sh +239 -0
- package/scripts/test_adapter_lanes.sh +54 -1
- package/scripts/test_marker_axes_run_lanes.sh +19 -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_target_freeze_lanes.sh +203 -0
- package/templates/.git-hooks/pre-commit +262 -0
- package/templates/PRE-PUBLISH-CHECKLIST.md +22 -0
- package/templates/regression_guard.sh +32 -4
|
@@ -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 ]
|
|
@@ -717,6 +717,217 @@ validate_crossfamily_leg() { # $1 = marker path
|
|
|
717
717
|
return 0
|
|
718
718
|
}
|
|
719
719
|
|
|
720
|
+
# โโ standpoint: typed value lane (2026-08-17) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
721
|
+
# Grammar: standpoint: <VALUE>[ โ <reason>]
|
|
722
|
+
#
|
|
723
|
+
# WHY NOW. This field shipped 2026-08-14 with its value DELIBERATELY unvalidated โ the doctrine
|
|
724
|
+
# said "mechanize on the first recorded false value, not before". That value is now on record:
|
|
725
|
+
# a `release_2.3.0` marker wrote `not-applicable` on a delta whose OWN grounds line concedes
|
|
726
|
+
# "์๋น์ install ์ ๊ฒ์ดํธ ์์ฉ์ ๋ฐ๋๋ค (BREAKING 2๊ฑด)", and two 2026-08-14 deltas that changed
|
|
727
|
+
# shipped gate scripts / a shipped SKILL.md carried no line at all. The threshold this field set
|
|
728
|
+
# for itself is met.
|
|
729
|
+
#
|
|
730
|
+
# SCOPE โ channel, not judgment (CLAUDE.md ยงMechanization Boundary). This asserts properties of the
|
|
731
|
+
# RECORD: present ยท single ยท a member of the closed enum ยท non-vacuous ยท not wearing the OTHER
|
|
732
|
+
# axis's tokens. It never asserts the value is CORRECT โ that stays with the reader, by design.
|
|
733
|
+
#
|
|
734
|
+
# ๐ฅ NAMED RESIDUAL โ the EXECUTION claim warns, it does not block. tier2/tier2b/tier3 assert that
|
|
735
|
+
# something was RUN in the target, which ยง7 calls "the load-bearing half". Blocking on that needs a
|
|
736
|
+
# vocabulary grep, and on first contact with the real corpus that grep over-blocked a legitimate
|
|
737
|
+
# marker whose grounds read "๊ทธ ๋ ํฌ์์ ์ค์ ๋ก ํธ์ถํด ์ยท์ arm ์ ํ์ธํ๋ค" โ it did not know ใํธ์ถใ.
|
|
738
|
+
# Over-blocking trains `--no-verify`, which would disarm the Destructive-Op gate in this same hook.
|
|
739
|
+
# So the cost is stated instead of hidden: a fabricated tier2 with a fluent reason PASSES this lane.
|
|
740
|
+
# Closing that is ยง4-b's job (cross-family reads the marker), not this lane's.
|
|
741
|
+
#
|
|
742
|
+
# ๐ฅ DOGFOOD RESULT, STATED SO NOBODY READS THIS AS MORE THAN IT IS โ the very marker whose false
|
|
743
|
+
# value MET this field's mechanization threshold (`release_2.3.0`, which wrote `not-applicable` on a
|
|
744
|
+
# release delta) **PASSES this lane**, because its grounds sentence is long and specific. That is not
|
|
745
|
+
# a bug in the lane; it is the Mechanization Boundary holding: the channel is checked, the judgment
|
|
746
|
+
# is not. Anyone reporting that this wiring "closed the self-report axis" is wrong. What closed is
|
|
747
|
+
# the shape of the record; what remains open is whether the recorded answer is true.
|
|
748
|
+
#
|
|
749
|
+
# Calibration before wiring (2026-08-17): known-pair 12/12 both directions; run against all 24
|
|
750
|
+
# corpus markers carrying the field โ 7 blocked, every one a real defect (6 bare `not-applicable`,
|
|
751
|
+
# independently matching a hand count, + 1 marker carrying TWO contradictory standpoint lines),
|
|
752
|
+
# and 0 over-blocks. Anchor: scripts/test_marker_standpoint_lanes.sh
|
|
753
|
+
validate_standpoint_leg() { # $1 = marker path
|
|
754
|
+
local m="$1" line val reason n quoted=""
|
|
755
|
+
n=$(grep -cE '^[[:space:]]*standpoint:' "$m" 2>/dev/null); n=${n:-0}
|
|
756
|
+
if [ "$n" -eq 0 ]; then
|
|
757
|
+
echo " โ FAIL โ load-bearing file staged with no 'standpoint:' line in the Axes 2-3 marker."
|
|
758
|
+
echo " State the answer, whatever it is โ the gate blocks silence, not a modest value."
|
|
759
|
+
echo " Settle the TARGET CLASS first (ยง7 Q0), then the tier:"
|
|
760
|
+
echo " standpoint: not-applicable โ <what you checked to conclude no target exists>"
|
|
761
|
+
echo " standpoint: tier1b(pmh-dev) โ read the target's own files, executed nothing"
|
|
762
|
+
echo " standpoint: tier2(qasp-dev) โ ran <command> there, saw <output>"
|
|
763
|
+
echo " standpoint: DEGRADED_NOT_RUN โ target reachable, not run (<why>)"
|
|
764
|
+
echo " Append one line to: $m"
|
|
765
|
+
return 1
|
|
766
|
+
fi
|
|
767
|
+
# Duplicate guard โ same defect class the crossfamily lane closes: a marker is append-only in
|
|
768
|
+
# practice and readers take the FIRST match, so an appended correction is silently shadowed.
|
|
769
|
+
# Measured in this corpus: one marker carries `tier1` at :32 and `not-applicable` at :89.
|
|
770
|
+
if [ "$n" -gt 1 ]; then
|
|
771
|
+
echo " โ FAIL โ marker carries MORE THAN ONE 'standpoint:' line."
|
|
772
|
+
grep -nE '^[[:space:]]*standpoint:' "$m" | sed 's/^/ /'
|
|
773
|
+
echo " A later line does not supersede an earlier one โ the reader takes the first."
|
|
774
|
+
echo " Delete the stale line and leave exactly one."
|
|
775
|
+
return 1
|
|
776
|
+
fi
|
|
777
|
+
line=$(grep -m1 -E '^[[:space:]]*standpoint:' "$m" | sed -E 's/^[[:space:]]*standpoint:[[:space:]]*//')
|
|
778
|
+
case "$line" in '"'*|"'"*) quoted=" [format: value wrapped in quotes โ an auditor's grep for
|
|
779
|
+
'standpoint: tier2' misses it; drop the quotes next time]";; esac
|
|
780
|
+
line=$(printf '%s' "$line" | sed -E 's/^["'"'"']//; s/["'"'"']$//')
|
|
781
|
+
val=$(printf '%s' "$line" | sed -E 's/[[:space:]]*(โ|--).*$//' | sed -E 's/[[:space:]]+$//')
|
|
782
|
+
reason=$(printf '%s' "$line" | sed -E 's/^[^โ-]*((โ|--)[[:space:]]*)?//')
|
|
783
|
+
# Cross-axis contamination. `crossfamily:` and `standpoint:` each carry a three-way degrade
|
|
784
|
+
# triad and the doctrine says the literals are DISTINCT ON PURPOSE. Borrowing the other axis's
|
|
785
|
+
# token records the wrong axis while looking well-formed.
|
|
786
|
+
case "$val" in
|
|
787
|
+
DEGRADED_SINGLE_FAMILY*|DEGRADED_PANEL_UNUSED*|panel\(*|declined)
|
|
788
|
+
echo " โ FAIL โ '$val' belongs to the crossfamily: axis, not standpoint:."
|
|
789
|
+
echo " crossfamily = WHICH MODEL FAMILY reviewed. standpoint = WHOSE REPO was ground truth."
|
|
790
|
+
echo " standpoint's own triad: DEGRADED_NO_TARGET_ACCESS (could not) /"
|
|
791
|
+
echo " DEGRADED_NOT_RUN (did not) / UNKNOWN (did not look)."
|
|
792
|
+
return 1 ;;
|
|
793
|
+
esac
|
|
794
|
+
case "$val" in
|
|
795
|
+
tier1)
|
|
796
|
+
echo " โ
standpoint leg: tier1 (content-only โ a scoping fact, not a failure)$quoted" ;;
|
|
797
|
+
tier1b\(*\)|tier2\(*\)|tier2b\(*\)|tier3\(*\))
|
|
798
|
+
case "$val" in
|
|
799
|
+
tier2\(*\)|tier2b\(*\)|tier3\(*\))
|
|
800
|
+
if [ "${#reason}" -lt 20 ] || ! printf '%s' "$reason" \
|
|
801
|
+
| grep -qiE 'ran |run |์คํ|๋๋ ธ|ํธ์ถ|invok|call|rc=|exit |output|์ถ๋ ฅ|suite|์ค์ํธ|test|bash |grep |npm |pytest|command|์ปค๋งจ๋|arm'; then
|
|
802
|
+
echo " โ ๏ธ standpoint leg: $val โ recorded, but the grounds do not NAME an execution."
|
|
803
|
+
echo " ${val%%(*} asserts code RAN in the target. The discriminator is mechanical:"
|
|
804
|
+
echo " name the command you ran and the output you saw. If you only read files,"
|
|
805
|
+
echo " the honest rung is tier1b โ weaker on purpose, so the owed arm stays visible."
|
|
806
|
+
echo " Advisory by design (see the named residual above this function)."
|
|
807
|
+
else
|
|
808
|
+
echo " โ
standpoint leg: $val$quoted"
|
|
809
|
+
fi ;;
|
|
810
|
+
*) echo " โ
standpoint leg: $val$quoted" ;;
|
|
811
|
+
esac ;;
|
|
812
|
+
not-applicable)
|
|
813
|
+
# ยง7: asserting non-applicability without naming what was checked is indistinguishable from
|
|
814
|
+
# UNKNOWN wearing a permissive label. Measured: 6 bare instances in this corpus.
|
|
815
|
+
if [ "${#reason}" -lt 20 ]; then
|
|
816
|
+
echo " โ FAIL โ bare 'not-applicable' with no grounds on the same line."
|
|
817
|
+
echo " That is UNKNOWN wearing a permissive label. Name what you checked:"
|
|
818
|
+
echo " no named peer carries this surface AND it changes no consumer-visible behavior."
|
|
819
|
+
return 1
|
|
820
|
+
fi
|
|
821
|
+
echo " โ
standpoint leg: not-applicable$quoted" ;;
|
|
822
|
+
DEGRADED_NO_TARGET_ACCESS*|DEGRADED_NOT_RUN*|UNKNOWN*)
|
|
823
|
+
if [ "${#reason}" -lt 20 ]; then
|
|
824
|
+
echo " โ FAIL โ $val without substantive grounds on the same line."
|
|
825
|
+
echo " could-not / did-not / did-not-look are separate values on purpose; collapsing"
|
|
826
|
+
echo " them renders an unrun probe as a zero finding. Name what you probed."
|
|
827
|
+
return 1
|
|
828
|
+
fi
|
|
829
|
+
echo " โ ๏ธ standpoint leg: $val โ recorded, not silent" ;;
|
|
830
|
+
*)
|
|
831
|
+
echo " โ FAIL โ '$val' is not a member of the standpoint: enum."
|
|
832
|
+
echo " Closed enum: tier1 ยท tier1b(<h>) ยท tier2(<h>) ยท tier2b(<h>) ยท tier3(<h>) ยท"
|
|
833
|
+
echo " not-applicable ยท DEGRADED_NO_TARGET_ACCESS ยท DEGRADED_NOT_RUN ยท UNKNOWN"
|
|
834
|
+
echo " Default-deny: an unrecognised value is UNDECIDABLE, never assumed benign."
|
|
835
|
+
return 1 ;;
|
|
836
|
+
esac
|
|
837
|
+
return 0
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
# โโ thirdparty: typed value lane (2026-08-17) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
841
|
+
# Grammar: thirdparty: <VALUE>(<grounds>) ยท UNKNOWN takes no parenthetical
|
|
842
|
+
#
|
|
843
|
+
# WHAT THIS AXIS IS โ and it has TWO halves, which is what made getting it wrong so easy.
|
|
844
|
+
# The 6-axis canon (fh_three_layer_canon.md:243) defines โ by what it RECEIVES: ยซ๋ฌธ์ + ๋จ์
|
|
845
|
+
# ์ฝ๋๋ฒ ์ด์คยป, and it asks two questions:
|
|
846
|
+
# โ prior art โ has this claimed-new thing already been solved outside? checked/none-found
|
|
847
|
+
# โก harness-level โ how does my change look from ANOTHER HARNESS's repo and peer-review
|
|
848
|
+
# adversarial persona? (operator: put Sonnet in gstack, wear the gstack
|
|
849
|
+
# persona, review the qasp change โ a cross-FAMILY, harness-scoped
|
|
850
|
+
# adversarial review. FH is the GOVERNOR: it creates the situation,
|
|
851
|
+
# observes, and judges. What the 4-axis gate verifies is the OPINION
|
|
852
|
+
# that third harness produced, judged LOCALLY โ it does not put a gate
|
|
853
|
+
# on someone else's repo.)
|
|
854
|
+
# It is NOT "I talked to another session". The canonical spec
|
|
855
|
+
# (`.claude/rules/fh_4axis_gate.md`) has said so since the field shipped, and the evidence that
|
|
856
|
+
# created it was 6 presentation claims that turned out to have prior art (mutation testing,
|
|
857
|
+
# promptfoo/DeepEval, an official cross-family plugin, โฆ).
|
|
858
|
+
#
|
|
859
|
+
# WHY NOW โ the first recorded false values exist, and they are the author's own. Measured across
|
|
860
|
+
# the marker corpus 2026-08-17: 4 markers used the enum correctly; **2 wrote free prose about
|
|
861
|
+
# peer-session contact** and matched no enum member at all. That is the field-canon failure this
|
|
862
|
+
# repo already names โ normalizing a harness term into a general concept ("third party" โ "another
|
|
863
|
+
# session"). Presence was checked; the value was not; nothing caught it.
|
|
864
|
+
#
|
|
865
|
+
# SCOPE โ channel, not judgment (CLAUDE.md ยงMechanization Boundary). This asserts the RECORD is a
|
|
866
|
+
# member of the closed enum and names non-vacuous grounds. It never asserts the prior-art search
|
|
867
|
+
# was thorough, nor that what a third party told you is TRUE โ ๐ฅ that second one is deliberate and
|
|
868
|
+
# load-bearing: the axis exists to surface information you could not predict, and the truth of what
|
|
869
|
+
# comes back is adjudicated by the OTHER axes (adversarial ยท grounding ยท first-real-use), which is
|
|
870
|
+
# what the 4-axis gate is for. A wrong answer from a third party is that party's defect, possibly
|
|
871
|
+
# one to go fix; it is not something this field should be asked to certify.
|
|
872
|
+
#
|
|
873
|
+
# TRIGGER โ validate IF PRESENT. Presence stays pointer-driven (`โ=โthirdparty`), deliberately:
|
|
874
|
+
# requiring the field on every load-bearing change would force a prior-art search per commit, and
|
|
875
|
+
# over-blocking trains the override that disarms the Destructive-Op gate in this same hook.
|
|
876
|
+
#
|
|
877
|
+
# Calibration before wiring: the corpus supplied BOTH arms โ peer-authored markers 4/4 PASS,
|
|
878
|
+
# author's own malformed 2/2 BLOCK, plus 6 synthetic shapes, 0 over-blocks. An earlier draft DID
|
|
879
|
+
# over-block 3 legitimate markers by looking for the closing paren on the first line when the
|
|
880
|
+
# grounds wrap across lines; the corpus caught it. Anchor: scripts/test_marker_thirdparty_lanes.sh
|
|
881
|
+
validate_thirdparty_leg() { # $1 = marker path
|
|
882
|
+
local m="$1" line val reason n
|
|
883
|
+
n=$(grep -cE '^[[:space:]]*thirdparty:' "$m" 2>/dev/null); n=${n:-0}
|
|
884
|
+
[ "$n" -eq 0 ] && return 0 # absent is governed by the โ pointer check above, not here
|
|
885
|
+
if [ "$n" -gt 1 ]; then
|
|
886
|
+
echo " โ FAIL โ marker carries MORE THAN ONE 'thirdparty:' line."
|
|
887
|
+
grep -nE '^[[:space:]]*thirdparty:' "$m" | sed 's/^/ /'
|
|
888
|
+
echo " Readers take the first, so an appended correction is silently shadowed."
|
|
889
|
+
return 1
|
|
890
|
+
fi
|
|
891
|
+
line=$(grep -m1 -E '^[[:space:]]*thirdparty:' "$m" | sed -E 's/^[[:space:]]*thirdparty:[[:space:]]*//')
|
|
892
|
+
val=$(printf '%s' "$line" | sed -E 's/\(.*$//' | sed -E 's/[[:space:]]*(โ|--).*$//' | sed -E 's/[[:space:]]+$//')
|
|
893
|
+
# Cross-axis contamination โ standpoint's tiers and crossfamily's tokens are different axes.
|
|
894
|
+
case "$val" in
|
|
895
|
+
tier1|tier1b|tier2|tier2b|tier3|panel|declined|DEGRADED_SINGLE_FAMILY|DEGRADED_PANEL_UNUSED|DEGRADED_NO_TARGET_ACCESS)
|
|
896
|
+
echo " โ FAIL โ '$val' belongs to another axis, not thirdparty:."
|
|
897
|
+
echo " standpoint = whose repo was ground truth ยท crossfamily = which model family reviewed"
|
|
898
|
+
echo " thirdparty = was this claimed-new thing already done OUTSIDE this repo."
|
|
899
|
+
return 1 ;;
|
|
900
|
+
esac
|
|
901
|
+
case "$val" in
|
|
902
|
+
UNKNOWN)
|
|
903
|
+
echo " โ ๏ธ thirdparty leg: UNKNOWN โ ์ ๋ดค๋ค. Recorded, not silent." ;;
|
|
904
|
+
peer-review|checked|none-found|not-applicable|DEGRADED_NO_ACCESS|DEGRADED_NOT_RUN)
|
|
905
|
+
# The parenthetical may wrap across lines; only require that it OPENS with content after it.
|
|
906
|
+
case "$line" in *\(*) ;; *)
|
|
907
|
+
echo " โ FAIL โ '$val' without a (<grounds>) parenthetical."
|
|
908
|
+
echo " none-found without naming WHAT you searched is indistinguishable from not looking."
|
|
909
|
+
return 1 ;;
|
|
910
|
+
esac
|
|
911
|
+
reason=$(printf '%s' "$line" | sed -E 's/^[^(]*\(//')
|
|
912
|
+
if [ "${#reason}" -lt 12 ]; then
|
|
913
|
+
echo " โ FAIL โ '$val(...)' grounds are vacuous. Name what was searched and what came back."
|
|
914
|
+
return 1
|
|
915
|
+
fi
|
|
916
|
+
echo " โ
thirdparty leg: $val" ;;
|
|
917
|
+
*)
|
|
918
|
+
echo " โ FAIL โ '$val' is not a member of the thirdparty: enum."
|
|
919
|
+
echo " Closed enum: peer-review(<harness>/<persona> -> <target>, <what came back>) ยท"
|
|
920
|
+
echo " checked(<searched/found>) ยท none-found(<searched>) ยท"
|
|
921
|
+
echo " DEGRADED_NO_ACCESS(<why>) ยท DEGRADED_NOT_RUN(<why>) ยท UNKNOWN ยท not-applicable(<why>)"
|
|
922
|
+
echo " ๐ฅ โ3์๋๋ฉด = third-party PRIOR ART, not 'I talked to another session'. The two"
|
|
923
|
+
echo " markers that made this lane necessary both made exactly that substitution."
|
|
924
|
+
return 1 ;;
|
|
925
|
+
esac
|
|
926
|
+
return 0
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
|
|
930
|
+
|
|
720
931
|
# โโ 4์ถ ์๊ธฐ ๋์กฐ โ ยซ์ด๋ ์ถ์ ๋๋ ธ๊ณ ์ด๋ ์ถ์ ์ ๋๋ ธ๋ยป ํ์ ๊ฒ์ฌ โโโโโโโโโโโโ
|
|
721
932
|
# CLAUDE.md ยง3์ธต ์๊ธฐ ๋์กฐ๊ฐ ๋ง์ปค์ 3์ค์ ์๊ตฌํ๋ค. ๊ทธ์ค **๊ธฐ๊ณ๋ก ๋ณผ ์ ์๋ ๋ ์ค**๋ง ์ฌ๊ธฐ์
|
|
722
933
|
# ๊ฐ์ ํ๋ค. ๋๋จธ์ง(โ ์ํผ์ ยซ์ค๊ณ ์ ์ยป ์ผ๋๊ฐ)๋ **์๋ฆฌ์ ์ผ๋ก ํ์ธ ๋ถ๊ฐ**๋ผ ์ ๋ฃ๋๋ค โ
|
|
@@ -866,6 +1077,26 @@ validate_marker_axes_run() { # $1 = marker path
|
|
|
866
1077
|
echo " standpoint: not-applicable"
|
|
867
1078
|
return 1
|
|
868
1079
|
fi
|
|
1080
|
+
|
|
1081
|
+
# โ3์๋๋ฉด๋ ๊ฐ์ ํํ โ ๊ฐ์ `thirdparty:` ์๊ธฐ ํ๋๊ฐ ๋๋ฅธ๋ค (2026-08-17 ์ ์ค).
|
|
1082
|
+
# ๐ฅ ์ ์๊ธฐ ํ๋๊ฐ ํ์ํ๊ฐ: `axes-run` ํ ์นธ์ **ํ ํฐ**๋ง ๋ด๋๋ค. โ๊ฐ ์ค์ ๋ก ๋ด๋ ๊ฒ์
|
|
1083
|
+
# ยซ๋ฌด์์ ๊ฒ์ํด์ ๋ฌด์์ ์ฐพ์๋ยป ๋ผ ํ ํ ํฐ์ ์ ๋ค์ด๊ฐ๋ค. ์๋ฆฌ๊ฐ ์์ผ๋ฉด ์ ์ ํ๊ณ ,
|
|
1084
|
+
# ์ ์ ํ๋ฉด ๋ค์ ์ธ์
์ด ยซ์ด ์ถ์ ๋๋ ธ๋ยป๋ฅผ ๋ฌผ์ ์๋ ํ์ธํ ์๋ ์๋ค.
|
|
1085
|
+
# ์ค์ธก(2026-08-17): ์ด ์ถ์ ์ฒ์ ์ ๋๋ก ๋๋ฆฐ ์ธ์
์์ ๋ฐํ ์ฃผ์ฅ **6๊ฑด**์ด ์ ํ์์ฐ์
|
|
1086
|
+
# ๊ฑธ๋ ธ๋๋ฐ, ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋กํ ํ๋๊ฐ ์์๋ค.
|
|
1087
|
+
# โ ๏ธ โ ์ **๋๊ฐ์ fail-open ์ ๋ฐ๋ณตํ์ง ์๋๋ค** โ ํ์ดํ๋ฅผ ํ์๋ก ๋ณด์ง ์๊ณ
|
|
1088
|
+
# ยซโ ๊ฐ์ด thirdparty ๋ฅผ ์ฐธ์กฐํ๋๊ฐยป ๋ก ๋ํ๋ค(โ๊ฐ ๊ทธ๋ ๊ฒ ๋ซ๋ ธ๋ ์ค์ธก์ด ๋ฐ๋ก ์์ ์๋ค).
|
|
1089
|
+
# ๊ฐ์ ๋ช
์ ์์ฌ๋ ๊ทธ๋๋ก ์์ํ๋ค: ์ฐธ์กฐ์ด๊ฐ ๋ค๋ฅธ ํ ํฐ์ ์์ผ๋ฉด ์ ์กํ๋ค.
|
|
1090
|
+
if echo "$line" | grep -qE "(^|[[:space:]])โ=[^[:space:]]*thirdparty" \
|
|
1091
|
+
&& ! grep -qE '^[[:space:]]*thirdparty:[[:space:]]*[^[:space:]]' "$m" 2>/dev/null; then
|
|
1092
|
+
echo " โ MARKER โ 'โ=โthirdparty' ๋ผ๊ณ ์ ์๋๋ฐ 'thirdparty:' ์ค์ด ์๋ค"
|
|
1093
|
+
echo " ํฌ์ธํฐ๊ฐ ๊ฐ๋ฆฌํค๋ ๊ณณ์ด ๋น์ด ์์ผ๋ฉด ๊ทธ๊ฑด ๊ธฐ๋ก์ด ์๋๋ผ ์ฃฝ์ ํฌ์ธํฐ๋ค."
|
|
1094
|
+
echo " thirdparty: checked(promptfooยทDeepEval ํ์ โ ๊ฒน์นจ ์์)"
|
|
1095
|
+
echo " thirdparty: none-found(<๋ฌด์์ ๊ฒ์ํ๋>) โ ์ฐพ์๋๋ฐ ์์. ยซ์ ๋ดค๋คยป์ ๋ค๋ฅด๋ค"
|
|
1096
|
+
echo " thirdparty: UNKNOWN โ ์ ๋ดค๋ค"
|
|
1097
|
+
echo " thirdparty: not-applicable"
|
|
1098
|
+
return 1
|
|
1099
|
+
fi
|
|
869
1100
|
fi
|
|
870
1101
|
|
|
871
1102
|
line=$(grep -m1 -E '^[[:space:]]*controls:' "$m" 2>/dev/null \
|
|
@@ -1283,6 +1514,8 @@ if [ -n "$LOADBEARING" ]; then
|
|
|
1283
1514
|
fi
|
|
1284
1515
|
MARKER_LB="$REPO_ROOT/tracks/_meta/.axes_23_passed_${BRANCH_SLUG}_${TODAY}.marker"
|
|
1285
1516
|
if validate_crossfamily_leg "$MARKER_LB"; then :; else FAILED=1; fi
|
|
1517
|
+
if validate_standpoint_leg "$MARKER_LB"; then :; else FAILED=1; fi
|
|
1518
|
+
if validate_thirdparty_leg "$MARKER_LB"; then :; else FAILED=1; fi
|
|
1286
1519
|
fi
|
|
1287
1520
|
|
|
1288
1521
|
# โโ staged-blob materializer (advisory linters) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -1391,6 +1624,35 @@ if [ -f "$PL_LINT" ]; then
|
|
|
1391
1624
|
fi
|
|
1392
1625
|
fi
|
|
1393
1626
|
|
|
1627
|
+
# โโ Pre-ship read reminder (advisory) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
1628
|
+
# WHY (2026-08-17, โ3์๋๋ฉด ์ฒซ ์ค์ฌ์ฉ์ ๋ต์ต๋ถ). A third harness (gstack), wearing its own
|
|
1629
|
+
# `/plan-devex-review` persona, reviewed a peer harness's change and asked a question neither FH
|
|
1630
|
+
# nor that harness asks: **who opens this file, and what do they see in the first eight lines?**
|
|
1631
|
+
# Its finding: a human-facing report had been shipped to three surfaces where the body was 3 lines
|
|
1632
|
+
# under 21 lines of fixed template, the answer to "did it pass?" was buried in the 4th bullet, and
|
|
1633
|
+
# the last impression was "readers may skip this". The delta that shipped it was correct code.
|
|
1634
|
+
#
|
|
1635
|
+
# FH reached the same place independently the same day from a different axis โ "readability is
|
|
1636
|
+
# measured only by rendering it or by a person; a static scan catches what is ABSENT, never what is
|
|
1637
|
+
# UNREADABLE", with 12 findings and 0 caught by static scanners. **Two independent arrivals** is
|
|
1638
|
+
# why this was the one item the internalization filter accepted out of four
|
|
1639
|
+
# (`harness_incubator_doctrine.md` ยง4-c).
|
|
1640
|
+
#
|
|
1641
|
+
# ๐ฅ WHAT THIS IS NOT. It does not judge readability, and it never blocks. Judging "is this
|
|
1642
|
+
# readable" is exactly the frozen-judgment class ยงMechanization Boundary reserves for people, and a
|
|
1643
|
+
# vocabulary test for "human-facing document" over-blocked a legitimate case the same day it was
|
|
1644
|
+
# first tried. The trigger is therefore a PATH SET (what actually ships to readers), not a
|
|
1645
|
+
# vocabulary guess, and its whole output is one line of salience.
|
|
1646
|
+
_PRESHIP=$(printf '%s\n' "$STAGED" | grep -E '^(README([._a-z]*)?\.md|CHEATSHEET\.md|CATALOG\.md|docs/[^/]*\.md|templates/[^/]*\.md)$' || true)
|
|
1647
|
+
if [ -n "$_PRESHIP" ]; then
|
|
1648
|
+
echo "[Gate] reader-facing surface staged โ pre-ship read reminder (advisory)..."
|
|
1649
|
+
printf '%s\n' "$_PRESHIP" | sed 's/^/ /'
|
|
1650
|
+
echo " โ ๏ธ ์ด ํ์ผ๋ค์ ยซ์ฌ๋์ด ์ฐ๋คยป. ๋๊ฐ๊ธฐ ์ ์ ํ ๋ฒ ยซ๋
์๋ก์ยป ์ฝ์๋?"
|
|
1651
|
+
echo " ์ฒซ 8์ค์ ๊ฒฐ๋ก ์ด ์๋ ยท ๋ณธ๋ฌธ์ด ํ
ํ๋ฆฟ์ ์ ๋ฎ์ด๋ ยท ๋ง์ง๋ง ์ธ์์ด ๋ฌด์์ธ๊ฐ"
|
|
1652
|
+
echo " ๋ ์ฆ๊ฐ ์ด๋ฏธ ์๋ค: /sim-conductor A-1 (beginner cold-read) ๋๋ ์ง์ ๋ ๋ํด์ ์ฝ๊ธฐ"
|
|
1653
|
+
echo " (advisory โ ๋ง์ง ์๋๋ค. ใ์ฝ์๋๊ฐใ๋ ์ด ํ
์ด ๋ณด์ฆํ ์ ์๊ณ ๋ณด์ฆํ๋ ค ๋ค์ง๋ ์๋๋ค.)"
|
|
1654
|
+
fi
|
|
1655
|
+
|
|
1394
1656
|
# โโ Novelty-claim ledger (advisory) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
1395
1657
|
# "Ask the world before asserting novelty" is an INTENT trigger โ un-hookable, like the
|
|
1396
1658
|
# repo-go-public surface. So this does not force the asking; it makes NOT having asked visible
|
|
@@ -83,3 +83,25 @@ A pre-publish catch is a process win worth logging:
|
|
|
83
83
|
|
|
84
84
|
**Done when**: Step 0 pre-flags clean (or escalated to full scan) ยท Step 1 both gates non-LEAK ยท
|
|
85
85
|
publish executed *after* the gates passed, never before.
|
|
86
|
+
|
|
87
|
+
## Read it as a reader, once, before it goes out
|
|
88
|
+
|
|
89
|
+
Not a correctness check โ the code can be right and this still fails. Open the thing a **reader**
|
|
90
|
+
opens and look at what they see:
|
|
91
|
+
|
|
92
|
+
- [ ] Is the **conclusion in the first ~8 lines**, or is it buried under banners, metadata and
|
|
93
|
+
"how to read this table"?
|
|
94
|
+
- [ ] Does the **body survive the template**? (measured case: 3 lines of content under 21 lines of
|
|
95
|
+
fixed boilerplate)
|
|
96
|
+
- [ ] What is the **last impression**? (measured case: the final line told the reader they could
|
|
97
|
+
skip it)
|
|
98
|
+
- [ ] Does it say **what the reader should do next**?
|
|
99
|
+
|
|
100
|
+
A lens already exists โ `/sim-conductor` A-1 (`beginner` cold-read) โ or just render it and read it.
|
|
101
|
+
|
|
102
|
+
๐ฅ **Why this is a human step and not a scan**: a static check catches what is ABSENT, never what is
|
|
103
|
+
UNREADABLE. Two independent measurements arrived here the same day (2026-08-17): one harness axis
|
|
104
|
+
found 12 readability defects with **0** caught by static scanners; a third-harness review
|
|
105
|
+
(`harness_incubator_doctrine.md` ยง4-c) found a shipped report whose answer to "did it pass?" sat in
|
|
106
|
+
the fourth bullet. The pre-commit hook prints an advisory reminder when a reader-facing surface is
|
|
107
|
+
staged โ it reminds, it does not certify.
|
|
@@ -64,13 +64,41 @@ if [ "${1:-}" = "--pr" ]; then
|
|
|
64
64
|
fi
|
|
65
65
|
PR_BRANCH="$2"
|
|
66
66
|
BASE_BRANCH="${3:-main}"
|
|
67
|
-
|
|
67
|
+
# Resolve each side to a ref that actually EXISTS in this checkout.
|
|
68
|
+
#
|
|
69
|
+
# WHY (measured 2026-08-17, 5 of 5 SAMPLED CI runs โ not an exhaustive audit): a bare branch
|
|
70
|
+
# NAME does not resolve in a GitHub Actions PR checkout. actions/checkout lands on a DETACHED
|
|
71
|
+
# HEAD and creates refs/remotes/origin/*, not local branches โ so `git merge-base main <branch>`
|
|
72
|
+
# found neither side, returned empty, and this block exited 3. `fetch-depth: 0` was already set
|
|
73
|
+
# and is NOT the cause; the history was present, the NAMES were not. The workflow then rendered
|
|
74
|
+
# that instrument error as a green PASS (fixed in the same commit).
|
|
75
|
+
#
|
|
76
|
+
# ๐ฅ THE FIRST FIX FOR THIS INTRODUCED A WORSE HOLE, caught by cross-family review before it
|
|
77
|
+
# shipped. It resolved `ref -> origin/ref -> refs/remotes/origin/ref` and fell back to HEAD.
|
|
78
|
+
# `github.head_ref` is only a branch NAME, not owner-qualified, so a **fork PR whose branch is
|
|
79
|
+
# named `main`** resolved the PR side to the BASE repo's `main` โ merge-base(main, main) = main,
|
|
80
|
+
# empty diff, SKIP, green. A guard silently comparing a branch to itself is worse than one that
|
|
81
|
+
# errors. So: NO name-guessing and NO silent HEAD fallback. Callers pass something
|
|
82
|
+
# unambiguous (a SHA, or an explicit `origin/<ref>`); anything that does not resolve EXACTLY
|
|
83
|
+
# is an instrument error, and the workflow now fails closed on that.
|
|
84
|
+
_rg_resolve_ref() { # $1 = ref-ish; echoes it iff it resolves EXACTLY as given (rc=1 otherwise)
|
|
85
|
+
git rev-parse --verify --quiet "${1}^{commit}" >/dev/null 2>&1 && printf '%s' "$1"
|
|
86
|
+
}
|
|
87
|
+
_BASE_RESOLVED=$(_rg_resolve_ref "$BASE_BRANCH") || _BASE_RESOLVED=""
|
|
88
|
+
_HEAD_RESOLVED=$(_rg_resolve_ref "$PR_BRANCH") || _HEAD_RESOLVED=""
|
|
89
|
+
if [ -z "$_BASE_RESOLVED" ] || [ -z "$_HEAD_RESOLVED" ]; then
|
|
90
|
+
echo "ERROR: ref does not resolve โ base='$BASE_BRANCH'->'${_BASE_RESOLVED:-<none>}' head='$PR_BRANCH'->'${_HEAD_RESOLVED:-<none>}'" >&2
|
|
91
|
+
echo " Pass an unambiguous ref (a SHA, or origin/<branch>). Guessing is how a fork PR" >&2
|
|
92
|
+
echo " branch named 'main' silently compared the base repo's main to itself." >&2
|
|
93
|
+
exit 3
|
|
94
|
+
fi
|
|
95
|
+
BASE_REF=$(git merge-base "$_BASE_RESOLVED" "$_HEAD_RESOLVED" 2>/dev/null)
|
|
68
96
|
if [ -z "$BASE_REF" ]; then
|
|
69
|
-
echo "ERROR: cannot compute merge-base for $
|
|
97
|
+
echo "ERROR: cannot compute merge-base for $_HEAD_RESOLVED vs $_BASE_RESOLVED" >&2
|
|
70
98
|
exit 3
|
|
71
99
|
fi
|
|
72
|
-
HEAD_REF="$
|
|
73
|
-
echo "PR MODE: merge-base=$(git rev-parse --short "$BASE_REF")
|
|
100
|
+
HEAD_REF="$_HEAD_RESOLVED"
|
|
101
|
+
echo "PR MODE: merge-base=$(git rev-parse --short "$BASE_REF") base=$_BASE_RESOLVED head=$_HEAD_RESOLVED"
|
|
74
102
|
elif [ "${1:-}" = "--staged" ]; then
|
|
75
103
|
# Pre-commit context: evaluate the staged index against HEAD. On a direct-to-main
|
|
76
104
|
# workflow, --pr's merge-base(main,main)=HEAD yields an empty diff, so staged changes
|