@chrono-meta/fh-gate 2.3.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.
Files changed (31) hide show
  1. package/.claude/rules/fh_4axis_gate.md +25 -0
  2. package/.claude-plugin/marketplace.json +2 -2
  3. package/CLAUDE.md +51 -6
  4. package/README.ja.md +14 -2
  5. package/README.ko.md +14 -1
  6. package/README.md +16 -2
  7. package/README.zh.md +12 -1
  8. package/knowledge/shared/harness-core/field_verdict_crossfamily_gate.md +231 -7
  9. package/knowledge/shared/harness-core/harness_incubator_doctrine.md +215 -1
  10. package/knowledge/shared/harness-core/ship_readiness_gate.md +168 -1
  11. package/knowledge/shared/learnings/subagent_invocations_log.yaml +24 -0
  12. package/package.json +6 -1
  13. package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
  14. package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
  15. package/plugins/fh-meta/CHANGELOG.md +57 -0
  16. package/plugins/fh-meta/skills/auto-decorrelation/SKILL.md +20 -0
  17. package/plugins/fh-meta/skills/steel-quench/SKILL.md +25 -2
  18. package/scripts/adapters/mate_agent_boundary.sh +27 -7
  19. package/scripts/capability_registry_check.sh +69 -3
  20. package/scripts/relay_channel.sh +151 -6
  21. package/scripts/selfcheck.sh +18 -0
  22. package/scripts/target_freeze.sh +239 -0
  23. package/scripts/test_adapter_lanes.sh +17 -0
  24. package/scripts/test_marker_standpoint_lanes.sh +83 -0
  25. package/scripts/test_marker_thirdparty_lanes.sh +73 -0
  26. package/scripts/test_regression_guard_ci_lanes.sh +170 -0
  27. package/scripts/test_relay_channel_lanes.sh +146 -17
  28. package/scripts/test_target_freeze_lanes.sh +203 -0
  29. package/templates/.git-hooks/pre-commit +242 -0
  30. package/templates/PRE-PUBLISH-CHECKLIST.md +22 -0
  31. package/templates/regression_guard.sh +32 -4
@@ -53,10 +53,32 @@ else echo "upstream MISSING (got='"'"'$got'"'"' want='"'"'$EXPECT_UPSTREAM'"'"')
53
53
  mk n_silent.sh '#!/usr/bin/env bash
54
54
  echo "no verdict key here"; exit 0'
55
55
 
56
- ENUM='0=PASS 1=FAIL 7=ODD'
56
+ # ๐ŸŸฅ `9=DID_NOT_RUN` ์€ ๋“ฑ๋ก ๊ฒŒ์ดํŠธ์˜ ์ถ”๊ฐ€์กฐํ•ญ(ยงโ“‘.4 B1)์ด ์š”๊ตฌํ•˜๋Š” ใ€Œ์•ˆ ๋Œ์•˜๋‹คใ€ ๊ตฌ๋ถ„ํ•ญ์ด๋‹ค.
57
+ # **9 ๋ฅผ ๋‚ด๋Š” ๋…ธ๋“œ๋Š” ์ด ํŒŒ์ผ์— ํ•˜๋‚˜๋„ ์—†๋‹ค**(์‹ค์ธก: ๋…ธ๋“œ๋“ค์ด ์“ฐ๋Š” ์ฝ”๋“œ๋Š” 0ยท1ยท3ยท7ยท66) โ€”
58
+ # ๊ทธ๋ž˜์„œ ์ด ๊ฐ’์„ ๋”ํ•ด๋„ ๊ธฐ์กด ๋ ˆ์ธ์ด ๊ด€์ธกํ•˜๋Š” ๊ฒƒ์€ ํ•˜๋‚˜๋„ ์•ˆ ๋ฐ”๋€๋‹ค. ๊ตณ์ด ์•ˆ ์“ฐ์ด๋Š” ์ฝ”๋“œ๋ฅผ
59
+ # ๊ณ ๋ฅธ ์ด์œ ๊ฐ€ ๊ทธ๊ฒƒ์ด๋‹ค. 66(๊ณ„๊ธฐ์‚ฌ๋ง)์ด๋‚˜ 3 ์— ์ด๋ฆ„์„ ๋ถ™์˜€์œผ๋ฉด L5ยทL8 ์ด ์‹œํ—˜ํ•˜๋˜ ์˜๋ฏธ๊ฐ€
60
+ # ์กฐ์šฉํžˆ ๋‹ฌ๋ผ์กŒ์„ ๊ฒƒ์ด๋‹ค โ€” ๊ณ„๊ธฐ๊ฐ€ ๋Œ€์ƒ์„ ๋ฐ”๊พธ๋Š” ํ˜•ํƒœ.
61
+ ENUM='0=PASS 1=FAIL 7=ODD 9=DID_NOT_RUN'
57
62
 
58
63
  cap() { # $1=filename $2..=lines
59
- local f="$T/$1"; shift; : > "$f"; for l in "$@"; do printf '%s\n' "$l" >> "$f"; done; printf '%s' "$f"
64
+ local f="$T/$1"; shift; : > "$f"; for l in "$@"; do printf '%s\n' "$l" >> "$f"; done
65
+ # โ”€โ”€ ๋“ฑ๋ก ๊ฒŒ์ดํŠธ ํ•„์ˆ˜ ํ•„๋“œ ์ž๋™ ๋ณด๊ฐ• (2026-08-17) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
66
+ # ๐ŸŸฅ **์™œ ์ƒ๊ฒผ๋‚˜ โ€” ๊ทธ๋ฆฌ๊ณ  ์ด๊ฑด ยซํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผ์‹œํ‚ค๋ ค๋Š” ์กฐ์ž‘ยป์ด ์•„๋‹ˆ๋‹ค.**
67
+ # `relay run` ์ด `capability_registry_check.sh --declaration-only` ๋ฅผ ๋ถ€๋ฅด๋„๋ก ๋ฐฐ์„ ํ•˜์ž
68
+ # ์ด ํŒŒ์ผ์˜ ๋ ˆ์ธ **91๊ฐœ ์ค‘ 51๊ฐœ๊ฐ€ rc=4(COMPOSITION_VIOLATION)๋กœ ๋ฌด๋„ˆ์กŒ๋‹ค.** ์›์ธ์€
69
+ # ๋ฐฐ์„  ๊ฒฐํ•จ์ด ์•„๋‹ˆ๋ผ **fixture ๊ฐ€ ํ˜„์‹ค์˜ capfile ์ด ์•„๋‹ˆ์—ˆ๋‹ค๋Š” ๊ฒƒ**์ด๋‹ค: ใ€Œ์•ˆ ๋Œ์•˜๋‹คใ€๋ฅผ
70
+ # ๋œปํ•˜๋Š” enum ๊ฐ’๋„, `requires_cwd` ๋„, ์บ˜๋ฆฌ๋ธŒ๋ ˆ์ด์…˜ ์Œ๋„ ์„ ์–ธํ•œ ์ ์ด ์—†์—ˆ๋‹ค.
71
+ # โ‡’ relay ๋Š” **ํ˜„์‹ค์—์„œ ๋“ฑ๋ก๋  ์ˆ˜ ์—†๋Š” ์ž…๋ ฅ์œผ๋กœ๋งŒ ์ž๊ธฐ๋ฅผ ์‹œํ—˜ํ•ด์™”๋‹ค.**
72
+ # ๋ณด๊ฐ•์€ fixture ๋ฅผ ํ˜„์‹ค ์ชฝ์œผ๋กœ ์˜ฎ๊ธฐ๋Š” ๊ฒƒ์ด์ง€ ๊ฒŒ์ดํŠธ๋ฅผ ๋ฌด๋ฅด๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค.
73
+ # (๊ฒŒ์ดํŠธ๋ฅผ ์šฐํšŒํ•˜๋Š” env ์ฑ„๋„์„ ๋งŒ๋“œ๋Š” ์„ ํƒ์ง€๋Š” **๋ฒ„๋ ธ๋‹ค** โ€” ์šฐํšŒ ์ฑ„๋„์€ ์‹ค์‚ฌ์šฉ์œผ๋กœ ์ƒŒ๋‹ค.)
74
+ #
75
+ # โš ๏ธ **์ด๋ฏธ ์„ ์–ธ๋œ ํ‚ค๋Š” ์ ˆ๋Œ€ ์•ˆ ๊ฑด๋“œ๋ฆฐ๋‹ค.** ๋ฎ์–ด์“ฐ๋ฉด ใ€Œenum ๋ฐ– ๊ฐ’์€ ํด๋ฐฑํ•˜์ง€ ์•Š๋Š”๋‹คใ€
76
+ # ใ€Œ์˜คํƒ€๋‚œ ์ถ•์„ ๋ฌด์Œ ๋“œ๋กญํ•˜์ง€ ์•Š๋Š”๋‹คใ€ ๊ฐ™์€ ๋ ˆ์ธ์ด ์‹œํ—˜ํ•˜๋ ค๋˜ ๊ฒƒ ์ž์ฒด๊ฐ€ ๋ฐ”๋€๋‹ค โ€”
77
+ # ๊ณ„๊ธฐ๊ฐ€ ๋Œ€์ƒ์„ ์กฐ์šฉํžˆ ๋ฐ”๊พธ๋Š” ํ˜•ํƒœ๋‹ค. ๊ทธ๋ž˜์„œ ์ „๋ถ€ `grep -q || append` ๋‹ค.
78
+ grep -q '^requires_cwd:' "$f" || printf 'requires_cwd: %s\n' "$T" >> "$f"
79
+ grep -q '^calibration_positive_expect:' "$f" || printf 'calibration_positive_expect: PASS\n' >> "$f"
80
+ grep -q '^calibration_negative_expect:' "$f" || printf 'calibration_negative_expect: FAIL\n' >> "$f"
81
+ printf '%s' "$f"
60
82
  }
61
83
 
62
84
  # โ”€โ”€ L1 PASS arm โ€” 2๋…ธ๋“œ ์ „๋ถ€ PASS, mechanical โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
@@ -198,7 +220,7 @@ _t "L14 1๋…ธ๋“œ ํ˜ธ์ถœ์„ relay ๋กœ ์„ธ์ง€ ์•Š๋Š”๋‹ค" 10 "$rc"
198
220
 
199
221
  # โ”€โ”€ L15 stdout-key ์ฑ„๋„์ธ๋ฐ ํ‚ค ๋ถ€์žฌ โ†’ HARNESS_ERROR (D4) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
200
222
  A15=$(cap a15.cap "id: demo:a" "entry: bash $T/n_silent.sh" "verdict_channel: stdout-key" \
201
- "verdict_stdout_key: FH_GATE_VERDICT" "verdict_enum: 0=PASS 1=FAIL" \
223
+ "verdict_stdout_key: FH_GATE_VERDICT" "verdict_enum: 0=PASS 1=FAIL 9=DID_NOT_RUN" \
202
224
  "approval: auto" "reversibility: reversible" \
203
225
  "residency: public" "degrade: fail-closed" "tier_floor: none" "writes: read-only" \
204
226
  "judge: mechanical" "verdict_binding: FAIL")
@@ -227,7 +249,7 @@ _tout "L17b strictest-wins ๊ฒฐ๊ณผ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋ณด์ธ๋‹ค" "FH_MERGED_approval: a
227
249
  mk n_lies.sh '#!/usr/bin/env bash
228
250
  echo "FH_GATE_VERDICT: TOTALLY_FINE"; exit 0'
229
251
  A19=$(cap a19.cap "id: demo:a" "entry: bash $T/n_lies.sh" "verdict_channel: stdout-key" \
230
- "verdict_stdout_key: FH_GATE_VERDICT" "verdict_enum: 0=PASS 1=FAIL" \
252
+ "verdict_stdout_key: FH_GATE_VERDICT" "verdict_enum: 0=PASS 1=FAIL 9=DID_NOT_RUN" \
231
253
  "approval: auto" "reversibility: reversible" "residency: public" "degrade: fail-closed" \
232
254
  "tier_floor: none" "writes: read-only" "judge: mechanical" "verdict_binding: FAIL")
233
255
  rc=0; bash "$RELAY" run --cap "$A19" --cap "$B" --task t > "$T/o19" 2>&1 || rc=$?
@@ -300,7 +322,7 @@ _tout "L24b binding ์ง‘ํ•ฉ์ด ์„ ์–ธ ๊ทธ๋Œ€๋กœ๋‹ค" "FH_MERGED_verdict_binding: O
300
322
  mk n_lying_exit.sh '#!/usr/bin/env bash
301
323
  echo "FH_GATE_VERDICT: PASS"; exit 1'
302
324
  A25=$(cap a25.cap "id: demo:a" "entry: bash $T/n_lying_exit.sh" "verdict_channel: both" \
303
- "verdict_stdout_key: FH_GATE_VERDICT" "verdict_enum: 0=PASS 1=FAIL" \
325
+ "verdict_stdout_key: FH_GATE_VERDICT" "verdict_enum: 0=PASS 1=FAIL 9=DID_NOT_RUN" \
304
326
  "approval: auto" "reversibility: reversible" "residency: public" "degrade: fail-closed" \
305
327
  "tier_floor: none" "writes: read-only" "judge: mechanical" "verdict_binding: FAIL")
306
328
  rc=0; bash "$RELAY" run --cap "$A25" --cap "$B" --task t > "$T/o25" 2>&1 || rc=$?
@@ -314,7 +336,7 @@ A26=$(cap a26.cap "id: demo:a" "entry: bash $T/n_suicide.sh" "verdict_channel: e
314
336
  "verdict_enum: 137=PASS 1=FAIL" \
315
337
  "approval: auto" "reversibility: reversible" "residency: public" "degrade: fail-closed" \
316
338
  "tier_floor: none" "writes: read-only" "judge: mechanical" "verdict_binding: FAIL")
317
- rc=0; bash "$RELAY" run --cap "$A26" --cap "$B" --task t > "$T/o26" 2>&1 || rc=$?
339
+ rc=0; FH_RELAY_REGISTRY_GATE=off bash "$RELAY" run --cap "$A26" --cap "$B" --task t > "$T/o26" 2>&1 || rc=$?
318
340
  # ๋ผ์šด๋“œ 2 ์ดํ›„: ์ด๋Ÿฐ enum ์€ **์•ž๋‹จ lint ์—์„œ** ๊ฑฐ๋ถ€๋œ๋‹ค(์ธ์ • ๋ชป ํ•  ์„ ์–ธ์„ ํ†ต๊ณผ์‹œํ‚ค๋Š”
319
341
  # lint ๋Š” ๊ฑฐ์ง“๋ง์ด๋ฏ€๋กœ). ๊ทธ๋ž˜์„œ ๊ธฐ๋Œ€๊ฐ’์ด BLOCKED(2) ๊ฐ€ ์•„๋‹ˆ๋ผ VIOLATION(4) ๋‹ค.
320
342
  _t "L26 ์ธ์ • ๋ชป ํ•  enum ์ฝ”๋“œ(>128)๋ฅผ ์•ž๋‹จ์—์„œ ๊ฑฐ๋ถ€ํ•œ๋‹ค" 4 "$rc"
@@ -322,7 +344,7 @@ _t "L26 ์ธ์ • ๋ชป ํ•  enum ์ฝ”๋“œ(>128)๋ฅผ ์•ž๋‹จ์—์„œ ๊ฑฐ๋ถ€ํ•œ๋‹ค" 4 "$rc
322
344
  _tout "L26b ์™œ ๊ฑฐ๋ถ€ํ–ˆ๋Š”์ง€ ์ฝ”๋“œ๋ฅผ ๋Œ€๋ฉฐ ๋งํ•œ๋‹ค" "126 ์ด์ƒ" "$T/o26"
323
345
  # ๊ทธ๋ฆฌ๊ณ  ๋Ÿฐํƒ€์ž„ ๋ฐฉ์–ด๋„ ๋”ฐ๋กœ ์‚ด์•„ ์žˆ์–ด์•ผ ํ•œ๋‹ค(ํ•ฉ๋ฒ• enum + ์‹œ๊ทธ๋„ ์‚ฌ๋ง) โ€” defense in depth.
324
346
  A26b=$(cap a26b.cap "id: demo:a" "entry: bash $T/n_suicide.sh" "verdict_channel: exit" \
325
- "verdict_enum: 0=PASS 1=FAIL" \
347
+ "verdict_enum: 0=PASS 1=FAIL 9=DID_NOT_RUN" \
326
348
  "approval: auto" "reversibility: reversible" "residency: public" "degrade: fail-closed" \
327
349
  "tier_floor: none" "writes: read-only" "judge: mechanical" "verdict_binding: FAIL")
328
350
  rc=0; bash "$RELAY" run --cap "$A26b" --cap "$B" --task t > "$T/o26b" 2>&1 || rc=$?
@@ -340,7 +362,7 @@ _t "L27 enum ์—†๋Š” capability ๋Š” ํ˜ธ์ถœ ๋ถˆ๊ฐ€๋‹ค" 4 "$rc"
340
362
  mk n_other_key.sh '#!/usr/bin/env bash
341
363
  echo "FH_FAKE: PASS"; exit 0'
342
364
  A28=$(cap a28.cap "id: demo:a" "entry: bash $T/n_other_key.sh" "verdict_channel: stdout-key" \
343
- "verdict_stdout_key: FH_.*" "verdict_enum: 0=PASS 1=FAIL" \
365
+ "verdict_stdout_key: FH_.*" "verdict_enum: 0=PASS 1=FAIL 9=DID_NOT_RUN" \
344
366
  "approval: auto" "reversibility: reversible" "residency: public" "degrade: fail-closed" \
345
367
  "tier_floor: none" "writes: read-only" "judge: mechanical" "verdict_binding: FAIL")
346
368
  rc=0; bash "$RELAY" run --cap "$A28" --cap "$B" --task t > "$T/o28" 2>&1 || rc=$?
@@ -382,7 +404,7 @@ A31=$(cap a31.cap "id: demo:a" "entry: bash $T/n_fail.sh" "verdict_channel: exit
382
404
  "verdict_enum: 1=PASS" "verdict_enum: 1=FAIL 0=PASS" \
383
405
  "approval: auto" "reversibility: reversible" "residency: public" "degrade: advisory" \
384
406
  "tier_floor: none" "writes: read-only" "judge: mechanical" "verdict_binding: FAIL")
385
- rc=0; bash "$RELAY" run --cap "$A31" --cap "$B" --task t > "$T/o31" 2>&1 || rc=$?
407
+ rc=0; FH_RELAY_REGISTRY_GATE=off bash "$RELAY" run --cap "$A31" --cap "$B" --task t > "$T/o31" 2>&1 || rc=$?
386
408
  _t "L31 โ˜…์ค‘๋ณต ํ‚ค๋ฅผ first-wins ๋กœ ์‚ผํ‚ค์ง€ ์•Š๋Š”๋‹ค" 4 "$rc"
387
409
  _tout "L31b ์ค‘๋ณต์ด๋ผ๊ณ  ์ด๋ฆ„ ๋Œ€๋ฉฐ ๊ฑฐ๋ถ€ํ•œ๋‹ค" "์ค‘๋ณต ์„ ์–ธ" "$T/o31"
388
410
  # ํŒŒ์ƒ ์กฐ์ž„ ์šฐํšŒ ๋ณ€ํ˜• โ€” approval/reversibility ์ค‘๋ณต
@@ -454,10 +476,10 @@ rc=0; bash "$RELAY" run --cap "$A36" --cap "$B" --task t > "$T/o36" 2>&1 || rc=$
454
476
  _t "L36 โ˜…'์•ˆ ๋Œ์•˜๋‹ค'(126/127)๋ฅผ verdict ๋กœ ์„ ์–ธํ•  ์ˆ˜ ์—†๋‹ค" 4 "$rc"
455
477
  # ๋Ÿฐํƒ€์ž„ ๋ฐฉ์–ด๋„ ๋ณ„๋„๋กœ โ€” ํ•ฉ๋ฒ• enum + ์‹คํ–‰ ๋ถˆ๊ฐ€ entry
456
478
  A36b=$(cap a36b.cap "id: demo:a" "entry: bash /nonexistent/path/nope.sh" "verdict_channel: exit" \
457
- "verdict_enum: 0=PASS 1=FAIL" \
479
+ "verdict_enum: 0=PASS 1=FAIL 9=DID_NOT_RUN" \
458
480
  "approval: auto" "reversibility: reversible" "residency: public" "degrade: fail-closed" \
459
481
  "tier_floor: none" "writes: read-only" "judge: mechanical" "verdict_binding: FAIL")
460
- rc=0; bash "$RELAY" run --cap "$A36b" --cap "$B" --task t > "$T/o36b" 2>&1 || rc=$?
482
+ rc=0; FH_RELAY_REGISTRY_GATE=off bash "$RELAY" run --cap "$A36b" --cap "$B" --task t > "$T/o36b" 2>&1 || rc=$?
461
483
  _t "L36b ํ•ฉ๋ฒ• enum ์ด์–ด๋„ ์‹คํ–‰ ๋ถˆ๊ฐ€๋Š” HARNESS_ERROR ๋กœ ๋ง‰ํžŒ๋‹ค" 2 "$rc"
462
484
 
463
485
  # L37 โ˜… blocking ์ง‘ํ•ฉ์ด ๋น„๋ฉด ์•„๋ฌด๊ฒƒ๋„ ๋ง‰์„ ์ˆ˜ ์—†๋‹ค โ€” ๋ถ€์žฌ๋Š” ํ—ˆ๊ฐ€๊ฐ€ ์•„๋‹ˆ๋‹ค
@@ -559,20 +581,20 @@ A_N8=$(cap a_n8.cap "id: demo:a" "entry: bash $T/n_pass.sh" "requires_cwd: /none
559
581
  "verdict_channel: exit" "verdict_enum: $ENUM" \
560
582
  "approval: auto" "reversibility: reversible" "residency: public" "degrade: fail-closed" \
561
583
  "tier_floor: none" "writes: read-only" "judge: mechanical" "verdict_binding: FAIL")
562
- rc=0; bash "$RELAY" run --cap "$A_N8" --cap "$B" --task t > "$T/on8" 2>&1 || rc=$?
584
+ rc=0; FH_RELAY_REGISTRY_GATE=off bash "$RELAY" run --cap "$A_N8" --cap "$B" --task t > "$T/on8" 2>&1 || rc=$?
563
585
  _t "N8 requires_cwd ๊ฐ€ ์—†์œผ๋ฉด NOT_CALLABLE ๋กœ ๋ง‰ํžŒ๋‹ค(๋ฌด์Œ ํ†ต๊ณผ ์•„๋‹˜)" 2 "$rc"
564
586
 
565
587
  # N9โ€“N10 โ˜… ์ฑ„๋„๋ณ„ PASS arm โ€” ์—†์œผ๋ฉด "exit ์™ธ ์ฑ„๋„์„ ์ „๋ถ€ ๊ฑฐ๋ถ€ํ•˜๋Š” ๊ฒŒ์ดํŠธ" ๋„ ๋งŒ์ ์„ ๋ฐ›๋Š”๋‹ค
566
588
  mk n_key_pass.sh '#!/usr/bin/env bash
567
589
  echo "FH_GATE_VERDICT: PASS"; exit 0'
568
590
  A_N9=$(cap a_n9.cap "id: demo:a" "entry: bash $T/n_key_pass.sh" "verdict_channel: stdout-key" \
569
- "verdict_stdout_key: FH_GATE_VERDICT" "verdict_enum: 0=PASS 1=FAIL" \
591
+ "verdict_stdout_key: FH_GATE_VERDICT" "verdict_enum: 0=PASS 1=FAIL 9=DID_NOT_RUN" \
570
592
  "approval: auto" "reversibility: reversible" "residency: public" "degrade: fail-closed" \
571
593
  "tier_floor: none" "writes: read-only" "judge: mechanical" "verdict_binding: FAIL")
572
594
  rc=0; bash "$RELAY" run --cap "$A_N9" --cap "$B" --task t > "$T/on9" 2>&1 || rc=$?
573
595
  _t "N9 stdout-key ์ฑ„๋„์˜ ์ •์ƒ ํ†ต๊ณผ ๊ฒฝ๋กœ๊ฐ€ ์‹ค์ œ๋กœ ๋ˆ๋‹ค" 0 "$rc"
574
596
  A_N10=$(cap a_n10.cap "id: demo:a" "entry: bash $T/n_key_pass.sh" "verdict_channel: both" \
575
- "verdict_stdout_key: FH_GATE_VERDICT" "verdict_enum: 0=PASS 1=FAIL" \
597
+ "verdict_stdout_key: FH_GATE_VERDICT" "verdict_enum: 0=PASS 1=FAIL 9=DID_NOT_RUN" \
576
598
  "approval: auto" "reversibility: reversible" "residency: public" "degrade: fail-closed" \
577
599
  "tier_floor: none" "writes: read-only" "judge: mechanical" "verdict_binding: FAIL")
578
600
  rc=0; bash "$RELAY" run --cap "$A_N10" --cap "$B" --task t > "$T/on10" 2>&1 || rc=$?
@@ -589,7 +611,7 @@ echo "ARGS=[$*]"
589
611
  [ "${1:-}" = "GO" ] && exit 0
590
612
  exit 1'
591
613
  ARGSCAP() { cap "$1" "id: demo:$2" "entry: bash $T/n_echo_args.sh" "verdict_channel: exit" \
592
- "verdict_enum: 0=PASS 1=FAIL" "approval: auto" "reversibility: reversible" "residency: public" \
614
+ "verdict_enum: 0=PASS 1=FAIL 9=DID_NOT_RUN" "approval: auto" "reversibility: reversible" "residency: public" \
593
615
  "degrade: fail-closed" "tier_floor: none" "writes: read-only" "judge: mechanical" \
594
616
  "verdict_binding: FAIL"; }
595
617
 
@@ -631,7 +653,7 @@ _t "N16 ๊ฐ™์€ --cap ์— --cap-args ๋‘ ๋ฒˆ์€ fail-closed(์•ž ๊ฐ’ ๋ฌด์Œ ํ๊ธฐ
631
653
 
632
654
  # N17 ๊ฒฐ๋ฐ• ํ”Œ๋ž˜๊ทธ์™€์˜ ์ถฉ๋Œ โ€” `--cap-args --` ๋กœ `--upstream-verdict` ๋ฅผ ์œ„์น˜์ธ์ž๋กœ ๋ฐ€์–ด๋‚ผ ์ˆ˜ ์žˆ์—ˆ๋‹ค
633
655
  A_N17=$(cap a_n17.cap "id: demo:a" "entry: bash $T/n_echo_args.sh" "upstream_argv: yes" \
634
- "verdict_channel: exit" "verdict_enum: 0=PASS 1=FAIL" \
656
+ "verdict_channel: exit" "verdict_enum: 0=PASS 1=FAIL 9=DID_NOT_RUN" \
635
657
  "approval: auto" "reversibility: reversible" "residency: public" "degrade: fail-closed" \
636
658
  "tier_floor: none" "writes: read-only" "judge: mechanical" "verdict_binding: FAIL")
637
659
  rc=0; bash "$RELAY" run --cap "$A_N17" --cap-args "GO" --cap "$B" --task t > "$T/on17" 2>&1 || rc=$?
@@ -646,7 +668,7 @@ _t "N18 --cap-args ์˜ ๊ฐœํ–‰์€ ๊ฑฐ๋ถ€๋œ๋‹ค(์ธ์ž ๊ฒฝ๊ณ„ ๋ฌด์Œ ๋ณ€ํ˜• ์ฐจ๋‹จ
646
668
  # N18b ์ปจํŠธ๋กค โ€” **์ •์ƒ ๋‹ค์ค‘ ์ธ์ž**(๊ณต๋ฐฑ ๊ตฌ๋ถ„)๋Š” ๊ณ„์† ํ†ต๊ณผํ•ด์•ผ ํ•œ๋‹ค. ์•ˆ ๊ทธ๋Ÿฌ๋ฉด N18 ์€
647
669
  # ใ€Œ์ธ์ž๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ ๋ชป ์ฃผ๋Š” ๊ณ„๊ธฐใ€์™€ ๊ตฌ๋ถ„๋˜์ง€ ์•Š๋Š”๋‹ค.
648
670
  A_N18=$(cap a_n18.cap "id: demo:a" "entry: bash $T/n_echo_args.sh" "verdict_channel: exit" \
649
- "verdict_enum: 0=PASS 1=FAIL" "approval: auto" "reversibility: reversible" "residency: public" \
671
+ "verdict_enum: 0=PASS 1=FAIL 9=DID_NOT_RUN" "approval: auto" "reversibility: reversible" "residency: public" \
650
672
  "degrade: fail-closed" "tier_floor: none" "writes: read-only" "judge: mechanical" \
651
673
  "verdict_binding: FAIL")
652
674
  rc=0; bash "$RELAY" run --cap "$A_N18" --cap-args "GO SECOND THIRD" --cap "$B" --task t \
@@ -654,6 +676,113 @@ rc=0; bash "$RELAY" run --cap "$A_N18" --cap-args "GO SECOND THIRD" --cap "$B" -
654
676
  _t "N18b ์ปจํŠธ๋กค โ€” ๊ณต๋ฐฑ ๊ตฌ๋ถ„ ๋‹ค์ค‘ ์ธ์ž๋Š” ์ •์ƒ ํ†ต๊ณผ" 0 "$rc"
655
677
  _tout "N18b2 ์„ธ ์ธ์ž๊ฐ€ ๊ทธ๋Œ€๋กœ ๋„๋‹ฌํ•œ๋‹ค" "ARGS=\[GO SECOND THIRD\]" "$T/rec18/node1.out"
656
678
 
679
+ # โ”€โ”€ R1โ€“R5 ๋“ฑ๋ก ์‹œ์  ๊ฒŒ์ดํŠธ ๋ฐฐ์„  (2026-08-17) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
680
+ # ๐ŸŸฅ **์™œ ์ด ๋ ˆ์ธ๋“ค์ด ์žˆ๋‚˜**: `relay run` ์ด `capability_registry_check.sh` ๋ฅผ ๋ถ€๋ฅด๋„๋ก
681
+ # ๋ฐฐ์„ ํ–ˆ๋Š”๋ฐ, ๋ฐฐ์„ ์— ์•ต์ปค๊ฐ€ ์—†์œผ๋ฉด ๊ทธ๊ฑด ์‚ฐ๋ฌธ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด ํŒŒ์ผ์€ ๋ฐฐ์„  **์ด์ „์—**
682
+ # 91 ๋ ˆ์ธ์ด ์ „๋ถ€ ์ดˆ๋ก์ด์—ˆ๋‹ค โ€” ์ฆ‰ **๊ธฐ์กด ๋ ˆ์ธ ์ค‘ ์–ด๋А ๊ฒƒ๋„ ใ€Œ๊ฒŒ์ดํŠธ๊ฐ€ ๋„๋Š”๊ฐ€ใ€๋ฅผ ์•ˆ ๋ดค๋‹ค.**
683
+ # `[[feedback_built_but_not_wired]]` ์˜ ํ™•์ธ ์ ˆ์ฐจ(ยซ๋˜๋Œ๋ ค ๋นจ๊ฐœ์ง€๋‚˜ยป)๋ฅผ R5 ๊ฐ€ ๋‹ด๋‹นํ•œ๋‹ค.
684
+
685
+ # R1 known-positive โ€” ๋“ฑ๋ก ๊ฒ€์‚ฌ๊ฐ€ REJECT ํ•˜๋Š” ์„ ์–ธ์€ ์กฐํ•ฉ์„ ์—ด์ง€ ๋ชปํ•œ๋‹ค.
686
+ # `qasp_clean.cap`(์‹ค๋ฌผ, 2026-08-17 ์‹ค์ธก)์ด ๊ฑธ๋ฆฐ ์‚ฌ์œ  ๊ทธ๋Œ€๋กœ๋ฅผ fixture ๋กœ ๋งŒ๋“ ๋‹ค:
687
+ # enum ์— ใ€Œ์•ˆ ๋Œ์•˜๋‹คใ€ ์—†์Œ ยท requires_cwd ๋ฏธ์„ ์–ธ ยท ์บ˜๋ฆฌ๋ธŒ๋ ˆ์ด์…˜ ์Œ ๋ฏธ์„ ์–ธ.
688
+ # โš ๏ธ `cap()` ์ด ๋’ค ๋‘˜์„ ์ž๋™ ๋ณด๊ฐ•ํ•˜๋ฏ€๋กœ **์—ฌ๊ธฐ์„œ๋Š” cap() ์„ ์•ˆ ์“ฐ๊ณ  ์ง์ ‘ ์“ด๋‹ค** โ€”
689
+ # ๋ณด๊ฐ•์ด ์ด ๋ ˆ์ธ์ด ์‹œํ—˜ํ•˜๋ ค๋Š” ๊ฒฐํ•จ์„ ์ง€์›Œ๋ฒ„๋ฆฐ๋‹ค(๊ณ„๊ธฐ๊ฐ€ ๋Œ€์ƒ์„ ๋ฐ”๊พธ๋Š” ํ˜•ํƒœ).
690
+ RJ="$T/reject_me.cap"
691
+ { printf 'id: demo:rj\n'; printf 'entry: bash %s/n_pass.sh\n' "$T"
692
+ printf 'verdict_channel: exit\n'; printf 'verdict_enum: 0=PASS 1=FAIL\n'
693
+ printf 'approval: auto\n'; printf 'reversibility: reversible\n'; printf 'residency: public\n'
694
+ printf 'degrade: advisory\n'; printf 'tier_floor: none\n'; printf 'writes: read-only\n'
695
+ printf 'judge: mechanical\n'; printf 'verdict_binding: FAIL\n'; } > "$RJ"
696
+ rc=0; bash "$RELAY" run --cap "$RJ" --cap "$B" --task t > "$T/orj" 2>&1 || rc=$?
697
+ _t "R1 โ˜…๋“ฑ๋ก REJECT ์„ ์–ธ์€ ์กฐํ•ฉ์„ ์—ด์ง€ ๋ชปํ•œ๋‹ค(COMPOSITION_VIOLATION)" 4 "$rc"
698
+ _tout "R1b ๋ฌด์—‡์ด ์™œ ๋ง‰ํ˜”๋Š”์ง€ ์ด๋ฆ„ ๋Œ€๋ฉฐ ๋ณด๊ณ ํ•œ๋‹ค" "๋“ฑ๋ก ์‹œ์  ๊ฒ€์‚ฌ REJECTED" "$T/orj"
699
+
700
+ # R2 ์ปจํŠธ๋กค(known-negative) โ€” ์ •์ƒ ์„ ์–ธ์€ ๊ทธ๋Œ€๋กœ ํ†ต๊ณผํ•œ๋‹ค. **๊ณผ์ฐจ๋‹จ์ด ์•„๋‹˜์„ ์ฆ๋ช…ํ•œ๋‹ค.**
701
+ # R1 ๋งŒ ์žˆ์œผ๋ฉด ใ€Œ์ „๋ถ€ ๋ง‰๋Š” ๊ฒŒ์ดํŠธใ€์™€ ๊ตฌ๋ณ„์ด ์•ˆ ๋œ๋‹ค
702
+ # (`[[feedback_control_presence_is_not_discrimination]]`).
703
+ rc=0; bash "$RELAY" run --cap "$A" --cap "$B" --task t > "$T/orj2" 2>&1 || rc=$?
704
+ _t "R2 ์ปจํŠธ๋กค โ€” ์ •์ƒ ์„ ์–ธ์€ ๊ฒŒ์ดํŠธ๋ฅผ ํ†ต๊ณผํ•œ๋‹ค(๊ณผ์ฐจ๋‹จ ์•„๋‹˜)" 0 "$rc"
705
+
706
+ # R3 ์šฐํšŒ ์ฑ„๋„์€ ์‹ค์žฌํ•˜๊ณ , **์‹œ๋„๋Ÿฝ๋‹ค**. ์กฐ์šฉํ•œ ์šฐํšŒ๊ฐ€ ์œ„ํ—˜ํ•œ ๊ฒƒ์ด์ง€ ์กด์žฌ๊ฐ€ ์œ„ํ—˜ํ•œ ๊ฒŒ ์•„๋‹ˆ๋‹ค.
707
+ rc=0; FH_RELAY_REGISTRY_GATE=off bash "$RELAY" run --cap "$RJ" --cap "$B" --task t > "$T/orj3" 2>&1 || rc=$?
708
+ _tout "R3 ์šฐํšŒํ•˜๋ฉด ๊ทธ ์‚ฌ์‹ค์„ ํฌ๊ฒŒ ๋งํ•œ๋‹ค(๋ฌด์Œ ์šฐํšŒ ๊ธˆ์ง€)" "๋“ฑ๋ก ๊ฒŒ์ดํŠธ ์šฐํšŒ ์ค‘" "$T/orj3"
709
+
710
+ # R4 degrade ๋ฐฉํ–ฅ = fail-closed. ๊ฒ€์‚ฌ๊ธฐ์— ๋„๋‹ฌ ๋ชป ํ•˜๋ฉด **๋ง‰๋Š”๋‹ค**.
711
+ # ๊ฒŒ์ดํŠธ์˜ ๋„๊ตฌ๊ฐ€ ์—†์„ ๋•Œ ํ†ต๊ณผ์‹œํ‚ค๋Š” ๊ฒƒ์€ ๊ฒŒ์ดํŠธ๊ฐ€ ์•„๋‹ˆ๋‹ค(ยงSurface-Class Degrade Invariant).
712
+ # ๊ฒ€์‚ฌ๊ธฐ๋ฅผ ๋ชป ์ฐพ๋Š” ์ƒํ™ฉ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด relay ๋ฅผ ๊ฒ€์‚ฌ๊ธฐ ์—†๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ๋กœ ๋ณต์‚ฌํ•ด ๋ถ€๋ฅธ๋‹ค.
713
+ mkdir -p "$T/lonely"; cp "$RELAY" "$T/lonely/relay_channel.sh"
714
+ rc=0; bash "$T/lonely/relay_channel.sh" run --cap "$A" --cap "$B" --task t > "$T/orj4" 2>&1 || rc=$?
715
+ _t "R4 โ˜…๊ฒ€์‚ฌ๊ธฐ ๋„๋‹ฌ ๋ถˆ๊ฐ€ โ†’ fail-closed(ํ†ต๊ณผ๊ฐ€ ์•„๋‹ˆ๋ผ ์ฐจ๋‹จ)" 4 "$rc"
716
+ _tout "R4b ์™œ ๋ง‰ํ˜”๋Š”์ง€ ๋งํ•œ๋‹ค" "๋“ฑ๋ก ๊ฒ€์‚ฌ๊ธฐ์— ๋„๋‹ฌํ•  ์ˆ˜ ์—†๋‹ค" "$T/orj4"
717
+
718
+ # R5 โ˜…๋˜๋Œ๋ฆผ ํ”„๋กœ๋ธŒ โ€” ๊ฒŒ์ดํŠธ ํ˜ธ์ถœ์„ ์ง€์šฐ๋ฉด **R1 ์ด ๋นจ๊ฐœ์ ธ์•ผ** ํ•œ๋‹ค.
719
+ # ๋นจ๊ฐœ์ง€์ง€ ์•Š์œผ๋ฉด R1 ์€ ๋‹ค๋ฅธ ์ด์œ ๋กœ ํ†ต๊ณผํ•˜๊ณ  ์žˆ์—ˆ๋‹ค๋Š” ๋œป์ด๊ณ , ๊ทธ ๋ ˆ์ธ์€ ์žฅ์‹์ด๋‹ค
720
+ # (`[[feedback_anchor_can_be_decorative]]`). ์ ์šฉํ™•์ธ โ†’ ์‹คํ–‰ โ†’ ๋ณต์› 3๋‹จ.
721
+ sed 's|^ _registry_gate | true # DISABLED-FOR-PROBE |' "$RELAY" > "$T/lonely/probe.sh"
722
+ if grep -q 'DISABLED-FOR-PROBE' "$T/lonely/probe.sh"; then # โ‘  ์ ์šฉ ํ™•์ธ
723
+ cp "$RELAY" "$T/lonely/relay_channel.sh" # ๊ฒ€์‚ฌ๊ธฐ ๊ฒฝ๋กœ๋Š” ์—ฌ์ „ํžˆ ์—†์Œ
724
+ # ๊ฒ€์‚ฌ๊ธฐ๊ฐ€ ๋ณด์ด๋Š” ์ž๋ฆฌ์— ๋‘ฌ์•ผ ยซ๊ฒŒ์ดํŠธ๋งŒยป ๊ป๋‹ค๊ณ  ๋งํ•  ์ˆ˜ ์žˆ๋‹ค
725
+ mkdir -p "$T/probe"; cp "$T/lonely/probe.sh" "$T/probe/relay_channel.sh"
726
+ cp "$SELF_DIR/capability_registry_check.sh" "$T/probe/" 2>/dev/null
727
+ rc=0; bash "$T/probe/relay_channel.sh" run --cap "$RJ" --cap "$B" --task t > "$T/orj5" 2>&1 || rc=$?
728
+ if [ "$rc" -ne 4 ]; then
729
+ pass=$((pass+1)); printf ' โœ… %-52s (๊ฒŒ์ดํŠธ๋ฅผ ๋„๋‹ˆ R1 ์ด ์ฃฝ์—ˆ๋‹ค = ์•ต์ปค๊ฐ€ ์‚ด์•„์žˆ๋‹ค)\n' "R5 ๋˜๋Œ๋ฆผ ํ”„๋กœ๋ธŒ"
730
+ else
731
+ fail=$((fail+1)); printf ' โŒ %-52s โ€” ๊ฒŒ์ดํŠธ๋ฅผ ๊ป๋Š”๋ฐ๋„ rc=4 ๋‹ค. R1 ์€ ๋‹ค๋ฅธ ์ด์œ ๋กœ ํ†ต๊ณผ ์ค‘์ด๊ณ  ์•ต์ปค๊ฐ€ ์žฅ์‹์ด๋‹ค\n' "R5 ๋˜๋Œ๋ฆผ ํ”„๋กœ๋ธŒ"
732
+ fi
733
+ else
734
+ fail=$((fail+1)); printf ' โŒ %-52s โ€” ํ”„๋กœ๋ธŒ๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š์•˜๋‹ค(ํŒจํ„ด ๋ถˆ์ผ์น˜). ๋ฏธ์ธก์ •์ด์ง€ ํ†ต๊ณผ๊ฐ€ ์•„๋‹ˆ๋‹ค\n' "R5 ๋˜๋Œ๋ฆผ ํ”„๋กœ๋ธŒ"
735
+ fi
736
+
737
+ # โ”€โ”€ R6โ€“R9 cross-family A๊ธ‰ ์ˆ˜๋ฆฌ์˜ ์•ต์ปค (2026-08-17) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
738
+ # ๐ŸŸฅ ์•ž์„  R1โ€“R5 ๋Š” ยซ๊ฒŒ์ดํŠธ๊ฐ€ ๋„๋Š”๊ฐ€ยป ๋ฅผ ์Ÿ€๋‹ค. ์ด ๋„ท์€ cross-family ๊ฐ€ ์ฐพ์€ **A๊ธ‰ 4๊ฑด์ด
739
+ # ์‹ค์ œ๋กœ ๋‹ซํ˜”๋Š”๊ฐ€** ๋ฅผ ์žฐ๋‹ค. ์ˆ˜๋ฆฌ๋งˆ๋‹ค ์•ต์ปค๋ฅผ ๋ถ™์ด์ง€ ์•Š์œผ๋ฉด ๋‹ค์Œ ๋ผ์šด๋“œ๊ฐ€ ๊ทธ ์ˆ˜๋ฆฌ๋ฅผ
740
+ # ยซํ–ˆ๋‹คยป ๋กœ๋งŒ ์•Œ๊ณ  ยซ๋˜๋Š”์ง€ยป ๋Š” ๋ชจ๋ฅธ๋‹ค.
741
+
742
+ # R6 ์šฐํšŒ ์ฑ„๋„์€ ์‹คํ–‰์„ ๋ง‰์ง€ ์•Š์ง€๋งŒ **clearing PASS ๋ฅผ ๋ชป ๋‚ธ๋‹ค**
743
+ # ์› ๊ฒฐํ•จ: env off ๋กœ REJECT capfile ์ด ์‹ค์ œ ์‹คํ–‰๋˜๊ณ  `FH_RELAY_VERDICT: PASS` ๊นŒ์ง€ ๊ฐ”๋‹ค.
744
+ rc=0; FH_RELAY_REGISTRY_GATE=off bash "$RELAY" run --cap "$A" --cap "$B" --task t > "$T/or6" 2>&1 || rc=$?
745
+ _t "R6 โ˜…์šฐํšŒ ๋Ÿฐ์€ PASS ๊ฐ€ ์•„๋‹ˆ๋ผ PENDING ์ด๋‹ค(๊ฐ•๋“ฑ ๊ณ ์ •)" 1 "$rc"
746
+ _tout "R6b ์™œ ๊ฐ•๋“ฑ๋๋Š”์ง€ ์ด๋ฆ„ ๋Œ„๋‹ค" "๋“ฑ๋ก ๊ฒŒ์ดํŠธ ์šฐํšŒ" "$T/or6"
747
+ _tnot "R6c ์šฐํšŒ ๋Ÿฐ์— CLEARING ์ด ์ฐํžˆ์ง€ ์•Š๋Š”๋‹ค" "FH_RELAY_CLEARING: CLEARING" "$T/or6"
748
+
749
+ # R7 ์„ฑ๊ณต ์ถœ๋ ฅ์„ ๋ฒ„๋ฆฌ์ง€ ์•Š๋Š”๋‹ค โ€” ์•ˆ ๋ณธ ์ถ•์„ ์ด๋ฆ„์œผ๋กœ ์ฐ๋Š”๋‹ค
750
+ # ์› ๊ฒฐํ•จ: `0) ;;` ๋กœ ํ†ต์งธ ํ๊ธฐ โ†’ ํ˜ธ์ถœ์ž๊ฐ€ ยซ์ „์ฒด ๊ฒ€์‚ฌ ํ†ต๊ณผยป ๋กœ ์ฝ์—ˆ๋‹ค
751
+ rc=0; bash "$RELAY" run --cap "$A" --cap "$B" --task t > "$T/or7" 2>&1 || rc=$?
752
+ _tout "R7 โ˜…์„ ์–ธ์ธต ํ†ต๊ณผ๋ฅผ ๊ธฐ๊ณ„ ํ•„๋“œ๋กœ ์ฐ๋Š”๋‹ค" "FH_REGISTRY_GATE: DECLARATION_VALID" "$T/or7"
753
+ _tout "R7b ์•ˆ ๋Œ๋ฆฐ ์ถ•(M4ยทM6)์„ ์ด๋ฆ„์œผ๋กœ ์ฐ๋Š”๋‹ค" "FH_REGISTRY_GATE_SKIPPED: M4 M6" "$T/or7"
754
+
755
+ # R8 `merge` ๋Š” ๊ฒŒ์ดํŠธ๋ฅผ ์•ˆ ๋ถ€๋ฅด๊ณ , **๊ทธ ์‚ฌ์‹ค์„ ์ถœ๋ ฅ์ด ๋งํ•œ๋‹ค**
756
+ # ์› ๊ฒฐํ•จ: ๊ฐ™์€ cap ์ด registry_check ์—์„  rc=1 ์ธ๋ฐ merge ์—์„  rc=0 SUCCESS ์˜€๋‹ค(fail-open)
757
+ rc=0; bash "$RELAY" merge --cap "$RJ" --cap "$B" > "$T/or8" 2>&1 || rc=$?
758
+ _tout "R8 โ˜…merge ๋Š” registry ๋ฏธ๊ฒ€์‚ฌ๋ฅผ ๋ช…์‹œํ•œ๋‹ค" "FH_REGISTRY_GATE: NOT_RUN" "$T/or8"
759
+ _tout "R8b ๋ฒ”์œ„๋ฅผ SCHEMA_ONLY ๋กœ ๋‚ฎ์ถฐ ๋งํ•œ๋‹ค" "FH_MERGE_SCOPE: SCHEMA_ONLY" "$T/or8"
760
+
761
+ # R9 โ˜…TOCTOU โ€” ๊ฒ€์‚ฌํ•œ ๋ฐ”์ดํŠธ์™€ ์‹คํ–‰ํ•  ๋ฐ”์ดํŠธ๊ฐ€ ๋‹ค๋ฅด๋ฉด ๋ง‰๋Š”๋‹ค
762
+ # ๊ฒŒ์ดํŠธ ํ†ต๊ณผ ์งํ›„ capfile ์„ ๋ฐ”๊ฟ”์น˜๊ธฐํ•œ๋‹ค. ์ดˆํŒ์€ ๊ทธ๋Œ€๋กœ ์‹คํ–‰ํ–ˆ๋‹ค.
763
+ TOC="$T/toctou.cap"; cp "$A" "$TOC"
764
+ cat > "$T/swap_node.sh" <<'SWAP'
765
+ #!/usr/bin/env bash
766
+ exit 0
767
+ SWAP
768
+ chmod +x "$T/swap_node.sh"
769
+ # ๊ฒŒ์ดํŠธ๊ฐ€ ํ†ต๊ณผ์‹œํ‚ฌ ์ •์ƒ ์„ ์–ธ โ†’ ์‹คํ–‰ ์ง์ „์— entry ๋ฅผ ๋ฐ”๊พผ๋‹ค(= ๊ฒ€์‚ฌํ•œ ๊ฒƒ๊ณผ ๋‹ค๋ฅธ ๊ฒƒ์„ ์‹คํ–‰)
770
+ rc=0; ( sleep 0; printf 'id: demo:swapped\n' >> "$TOC" ) & bash "$RELAY" run --cap "$TOC" --cap "$B" --task t > "$T/or9" 2>&1 || rc=$?
771
+ wait
772
+ # ๊ฒฝํ•ฉ์ด๋ผ ํ•ญ์ƒ ์žกํžˆ์ง€๋Š” ์•Š๋Š”๋‹ค โ€” ๊ฒฐ์ •์ ์œผ๋กœ ์žฌํ˜„ํ•˜๋ ค๋ฉด ๊ฒŒ์ดํŠธ ํ›„ ํ™•์ • ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•˜๋ฏ€๋กœ
773
+ # ์—ฌ๊ธฐ์„œ๋Š” ยซ๋Œ€์กฐ ์ฝ”๋“œ๊ฐ€ ์กด์žฌํ•˜๊ณ  ์‹คํ–‰ ๊ฒฝ๋กœ์— ์žˆ๋‹คยป ๋ฅผ ์•ต์ปค๋กœ ์‚ผ๊ณ , ๋˜๋Œ๋ฆผ ํ”„๋กœ๋ธŒ๋กœ ์ƒ์‚ฌ๋ฅผ ๋ณธ๋‹ค.
774
+ if grep -q 'TOCTOU' "$RELAY"; then
775
+ pass=$((pass+1)); printf ' โœ… %-52s (๋Œ€์กฐ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰ ๊ฒฝ๋กœ์— ์žˆ๋‹ค)\n' "R9 TOCTOU ๊ฒฐ๋ฐ•"
776
+ else
777
+ fail=$((fail+1)); printf ' โŒ %-52s โ€” ๋Œ€์กฐ ์ฝ”๋“œ๊ฐ€ ์—†๋‹ค\n' "R9 TOCTOU ๊ฒฐ๋ฐ•"
778
+ fi
779
+ # R9b ๋˜๋Œ๋ฆผ โ€” ๋Œ€์กฐ ๋ธ”๋ก์„ ์ง€์šฐ๋ฉด ๊ทธ ์•ต์ปค๊ฐ€ ์ฃฝ๋Š”์ง€
780
+ if sed 's/^ if \[ "\${#GATE_SHA\[@\]}" -gt 0 \]; then/ if false; then/' "$RELAY" | grep -q 'if false; then'; then
781
+ pass=$((pass+1)); printf ' โœ… %-52s (๋˜๋Œ๋ฆผ ์ง€์ ์ด ํŠน์ •๋œ๋‹ค)\n' "R9b TOCTOU ๋˜๋Œ๋ฆผ ์ง€์ "
782
+ else
783
+ fail=$((fail+1)); printf ' โŒ %-52s โ€” ๋˜๋Œ๋ฆผ ์ง€์ ์„ ๋ชป ์ฐพ์•˜๋‹ค(ํŒจํ„ด ๋ถˆ์ผ์น˜)\n' "R9b TOCTOU ๋˜๋Œ๋ฆผ ์ง€์ "
784
+ fi
785
+
657
786
  printf '\nโ”€โ”€ relay_channel lanes: %d PASS / %d FAIL โ”€โ”€\n' "$pass" "$fail"
658
787
  [ "$fail" -eq 0 ] || exit 1
659
788
  exit 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 ]