@chrono-meta/fh-gate 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/capabilities/adapters/gstack-content-safety.cap +110 -0
- package/.claude/capabilities/adapters/mate-agent-boundary.cap +110 -0
- package/.claude/capabilities/adapters/qasp-new-code-anchor.cap +116 -0
- package/.claude/capabilities/degrade-direction-scan.cap +24 -0
- package/.claude/capabilities/public-surface-scan.cap +24 -0
- package/.claude/rules/fh_4axis_gate.md +77 -1
- package/.claude-plugin/marketplace.json +3 -3
- package/AGENTS.md +25 -3
- package/CHEATSHEET.md +17 -1
- package/CLAUDE.md +241 -5
- package/README.ja.md +55 -20
- package/README.ko.md +86 -20
- package/README.md +57 -20
- package/README.zh.md +49 -19
- package/knowledge/shared/harness-core/capability_composition_contract.md +68 -0
- package/knowledge/shared/harness-core/fh_three_layer_canon.md +122 -5
- package/knowledge/shared/harness-core/field_verdict_crossfamily_gate.md +115 -3
- package/knowledge/shared/harness-core/harness_incubator_doctrine.md +83 -1
- package/knowledge/shared/harness-core/harness_terminal_correlation_and_recommendations.md +255 -0
- package/knowledge/shared/harness-core/harness_verification_core_extended.md +1 -1
- package/knowledge/shared/harness-core/ship_readiness_gate.md +234 -11
- package/knowledge/shared/learnings/subagent_invocations_log.yaml +128 -0
- package/knowledge/shared/rules/sister_asset_protocol.md +12 -0
- package/package.json +12 -3
- package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-commons/agents/quench-challenger.md +6 -1
- package/plugins/fh-meta/.claude-plugin/plugin.json +2 -2
- package/plugins/fh-meta/CHANGELOG.md +60 -0
- package/plugins/fh-meta/skills/auto-decorrelation/SKILL.md +14 -2
- package/plugins/fh-meta/skills/steel-quench/SKILL_detail.md +13 -0
- package/plugins/fh-meta/skills/verify-bidirectional/SKILL.md +34 -1
- package/scripts/adapters/gstack_content_safety.sh +186 -0
- package/scripts/adapters/mate_agent_boundary.sh +120 -0
- package/scripts/adapters/peer_resolve.sh +99 -0
- package/scripts/adapters/qasp_new_code_anchor.sh +159 -0
- package/scripts/branch_claim.sh +14 -0
- package/scripts/capability_effect_probe.sh +340 -20
- package/scripts/capability_registry_check.sh +271 -20
- package/scripts/chamber_run.sh +24 -3
- package/scripts/chamber_witness.sh +25 -0
- package/scripts/cluster_capability_scan.sh +629 -0
- package/scripts/fh-gate.sh +16 -1
- package/scripts/fh_session_load.sh +70 -0
- package/scripts/package_coverage_check.sh +10 -0
- package/scripts/portability_lint.sh +257 -0
- package/scripts/prepublish_scope_note.sh +139 -0
- package/scripts/relay_channel.sh +70 -7
- package/scripts/selfcheck.sh +167 -5
- package/scripts/test_adapter_lanes.sh +296 -0
- package/scripts/test_capability_entrypoint_shipping.sh +8 -1
- package/scripts/test_fh_gate_regressions.sh +21 -9
- package/scripts/test_marker_axes_run_lanes.sh +87 -3
- package/scripts/test_marker_crossfamily_lanes.sh +31 -1
- package/scripts/test_relay_channel_lanes.sh +76 -0
- package/templates/.git-hooks/pre-commit +239 -12
- package/templates/subagent-tally-hook.json +2 -2
package/scripts/relay_channel.sh
CHANGED
|
@@ -97,7 +97,12 @@ AXES_SET="verdict_binding"
|
|
|
97
97
|
# 선언이 VIOLATION 으로 거부된다 — 2026-08-11 실측: registry_check 를 통과한 capfile 2개가
|
|
98
98
|
# relay 에서 8건 VIOLATION. 같은 스펙에 대해 두 계기가 서로 다른 스키마를 든
|
|
99
99
|
# divergent-normalizer 이고, 관대함이 갈리면 한쪽만 통과하는 입력이 생긴다.
|
|
100
|
-
|
|
100
|
+
# 🟥 **2026-08-16 재발.** 위 문단이 이 함정을 2026-08-11 사례로 적어놨는데, `summary`/`tags` 가
|
|
101
|
+
# `capability_registry_check.sh` 에만 추가되고 여기 안 들어와 **같은 파일이 같은 함정을 두 번**
|
|
102
|
+
# 밟았다 — 출하된 `.claude/capabilities/*.cap` 2개가 `✅ REGISTRABLE` 인데 relay 에서
|
|
103
|
+
# `⛔ COMPOSITION_VIOLATION` 이었다(등록되지만 **부를 수 없는** 선언). 아래 교차 레인이
|
|
104
|
+
# 그 재발을 잡는 앵커다 — 산문 경고는 이미 있었고 막지 못했다.
|
|
105
|
+
META_KEYS="id entry requires_cwd summary tags verdict_channel verdict_enum verdict_stdout_key upstream_argv echoes_upstream calibration_positive_args calibration_positive_expect calibration_negative_args calibration_negative_expect"
|
|
101
106
|
|
|
102
107
|
_die() { printf '❌ %s\n' "$*" >&2; exit "$RC_HARNESS"; }
|
|
103
108
|
_violation() { printf '⛔ COMPOSITION_VIOLATION — %s\n' "$*" >&2; VIOLATED=1; }
|
|
@@ -378,7 +383,7 @@ _sha() { # stdin → sha256 hex (macOS/linux 양쪽)
|
|
|
378
383
|
# 반환: 전역 NODE_VERDICT / NODE_EXIT / NODE_OUT_FILE
|
|
379
384
|
# 파이프로 verdict 를 읽지 않는다(D6) — 출력은 파일로 받고 exit status 를 직접 취한다.
|
|
380
385
|
_invoke_node() {
|
|
381
|
-
local f="$1" task="$2" upstream="$3" outdir="$4" n="$5"
|
|
386
|
+
local f="$1" task="$2" upstream="$3" outdir="$4" n="$5" node_args="${6:-}"
|
|
382
387
|
local entry cwd chan enum key argv_extra rc line code name
|
|
383
388
|
entry=$(_cap_get "$f" entry)
|
|
384
389
|
cwd=$(_cap_get "$f" requires_cwd)
|
|
@@ -387,6 +392,19 @@ _invoke_node() {
|
|
|
387
392
|
key=$(_cap_get "$f" verdict_stdout_key); [ -z "$key" ] && key="FH_GATE_VERDICT"
|
|
388
393
|
|
|
389
394
|
[ -n "$entry" ] || { NODE_VERDICT="HARNESS_ERROR"; NODE_EXIT=$RC_HARNESS; return 0; }
|
|
395
|
+
# ★`requires_cwd: SELF` = «이 선언을 품은 레포». 해석은 `capability_registry_check.sh` ·
|
|
396
|
+
# `capability_effect_probe.sh` 와 **같은 규칙**(git 레포 루트)이어야 한다 — 규칙이 갈리면
|
|
397
|
+
# 두 계기가 서로 다른 트리를 재고, 그건 이 파일이 §divergent-normalizer 로 이름 붙인 결함이다.
|
|
398
|
+
# 🟥 이 값이 없으면 **tracked 선언이 구조적으로 uncallable** 하다: `SELF` 는 공개표면에
|
|
399
|
+
# 운영자 홈 절대경로를 안 싣기 위해 도입된 형태라, 하네스 자기선언은 전부 이 형태를 쓴다.
|
|
400
|
+
# 실측(2026-08-16): SELF 두 노드 → `NOT_CALLABLE … HARNESS_ERROR` · 절대경로 두 노드 → 진짜 판정.
|
|
401
|
+
if [ "$cwd" = "SELF" ]; then
|
|
402
|
+
cwd="$(git -C "$(dirname "$f")" rev-parse --show-toplevel 2>/dev/null)"
|
|
403
|
+
if [ -z "$cwd" ]; then
|
|
404
|
+
printf 'ℹ️ NOT_CALLABLE: requires_cwd: SELF 인데 선언이 git 레포 안에 없다 → dispatch 폴백\n' >&2
|
|
405
|
+
NODE_VERDICT="HARNESS_ERROR"; NODE_EXIT=$RC_HARNESS; return 0
|
|
406
|
+
fi
|
|
407
|
+
fi
|
|
390
408
|
if [ -n "$cwd" ] && [ ! -d "$cwd" ]; then
|
|
391
409
|
printf 'ℹ️ NOT_CALLABLE: requires_cwd 부재(%s) → dispatch 폴백 대상\n' "$cwd" >&2
|
|
392
410
|
NODE_VERDICT="HARNESS_ERROR"; NODE_EXIT=$RC_HARNESS; return 0
|
|
@@ -406,8 +424,10 @@ _invoke_node() {
|
|
|
406
424
|
argv_extra="--upstream-verdict $upstream"
|
|
407
425
|
fi
|
|
408
426
|
|
|
427
|
+
# 순서: entry → **노드 인자** → 결박 플래그. 노드 인자를 결박 플래그 뒤에 붙이면
|
|
428
|
+
# 위치 인자를 읽는 진입점이 `--upstream-verdict` 를 자기 인자로 삼는다.
|
|
409
429
|
( [ -n "$cwd" ] && cd "$cwd"; RELAY_TASK="$task" RELAY_UPSTREAM_VERDICT="$upstream" \
|
|
410
|
-
$entry $argv_extra ) > "$NODE_OUT_FILE" 2>&1
|
|
430
|
+
$entry $node_args $argv_extra ) > "$NODE_OUT_FILE" 2>&1
|
|
411
431
|
rc=$?
|
|
412
432
|
NODE_EXIT=$rc
|
|
413
433
|
|
|
@@ -489,11 +509,54 @@ _invoke_node() {
|
|
|
489
509
|
|
|
490
510
|
# ── run ───────────────────────────────────────────────────────────────────────
|
|
491
511
|
do_run() {
|
|
492
|
-
local task="" recdir="" ack=0 n=0 upstream="" rc final=$RC_PASS
|
|
493
|
-
CAPS=()
|
|
512
|
+
local task="" recdir="" ack=0 n=0 upstream="" rc final=$RC_PASS _ci
|
|
513
|
+
CAPS=(); CAPARGS=(); CAPARGS_SET=()
|
|
494
514
|
while [ $# -gt 0 ]; do
|
|
495
515
|
case "$1" in
|
|
496
|
-
--cap) shift; CAPS+=("$1") ;;
|
|
516
|
+
--cap) shift; CAPS+=("$1"); CAPARGS+=("") ;;
|
|
517
|
+
# ── 노드별 호출 인자 채널 ────────────────────────────────────────────────
|
|
518
|
+
# 🟥 **왜 이게 필요한가(실측 2026-08-16)**: 호출 시점 인자를 요구하는 능력은
|
|
519
|
+
# relay 를 통과할 방법이 아예 없었다. pmh-dev 의 `merge-noop-check` 는
|
|
520
|
+
# «무엇을 무엇에 반입하나» 가 인자인데, 채널이 없어 **항상 ARGS→HARNESS_ERROR
|
|
521
|
+
# →BLOCKED** 로 떨어졌다. 방향은 fail-closed 로 옳았지만 **신호가 0** 이었고,
|
|
522
|
+
# 그건 정체성 ①의 «재사용이 실재하고 호출 가능한가» 를 구조적으로 못 만족시킨다.
|
|
523
|
+
# ⚠️ **결박은 «가장 최근 --cap»** 이다. 위치 인덱스로 짝지으면 순서가 어긋났을 때
|
|
524
|
+
# 조용히 다른 노드에 붙는다 — 그건 계기가 대상을 바꾸는 형태다.
|
|
525
|
+
# ⚠️ `--cap` 없이 먼저 오면 **fail-closed**: 어디 붙일지 모르는 인자를 버리지 않는다.
|
|
526
|
+
--cap-args)
|
|
527
|
+
shift
|
|
528
|
+
[ "${#CAPS[@]}" -gt 0 ] || _die "--cap-args 는 --cap 뒤에 와야 한다(어느 노드의 인자인지 결박 불가)"
|
|
529
|
+
_ci=$(( ${#CAPS[@]} - 1 ))
|
|
530
|
+
# 🟥 **중복 지정은 fail-closed**(cross-family 지목 + 재현, 2026-08-16). 초판은 같은
|
|
531
|
+
# `--cap`에 `--cap-args` 를 두 번 주면 **앞 값이 조용히 사라지고** 정상 실행됐다
|
|
532
|
+
# (`--cap A --cap-args FIRST --cap-args SECOND` → 노드가 `ARGS=[SECOND]`).
|
|
533
|
+
# 무음 폐기는 이 레포가 반복해서 이름 붙인 결함이다 — 의미가 append 인지 replace 인지
|
|
534
|
+
# **정하지 않은 채 하나를 버리느니** 막는다.
|
|
535
|
+
[ -z "${CAPARGS_SET[$_ci]:-}" ] || _die "같은 --cap 에 --cap-args 가 두 번 왔다 — 무음 폐기 대신 막는다(의미가 append 인지 replace 인지 정의되지 않았다)"
|
|
536
|
+
# 🟥 **결박 플래그와의 충돌은 fail-closed**(cross-family 지목 + 재현). 진입점은
|
|
537
|
+
# `$entry $node_args $argv_extra` 순으로 단어분해 실행되므로, `upstream_argv: yes` 를
|
|
538
|
+
# 선언한 노드에 `--cap-args --` 를 주면 그 노드의 옵션 파서가 `--` 에서 멈추고
|
|
539
|
+
# 뒤따르는 `--upstream-verdict <v>` 를 **위치 인자로 삼는다** = 인과 결박이 조용히 죽는다.
|
|
540
|
+
# 순서를 뒤집으면 위치 인자를 읽는 진입점이 깨지므로, 안전한 순서가 없다.
|
|
541
|
+
# ⇒ 두 계약을 **동시에 요구하지 않게** 한다. 결박은 `RELAY_UPSTREAM_VERDICT` env 로
|
|
542
|
+
# 항상 전달되므로 이 노드도 결박 자체를 잃지는 않는다.
|
|
543
|
+
if [ "$(_cap_get "${CAPS[$_ci]}" upstream_argv 2>/dev/null)" = "yes" ]; then
|
|
544
|
+
_die "이 노드는 upstream_argv: yes 다 — argv 결박과 --cap-args 를 같이 쓸 수 없다(둘의 인자 순서가 정의되지 않았다). env RELAY_UPSTREAM_VERDICT 로 결박을 받아라"
|
|
545
|
+
fi
|
|
546
|
+
# 셸 메타문자·제어문자 거부. ⚠️ **이건 «명령 실행 우회» 방어가 아니다** — 진입점은
|
|
547
|
+
# `eval` 이 아니라 단어분해로 돌고 전역 `set -f` 라 `;` `|` `$()` 글롭 틸드 중괄호는
|
|
548
|
+
# 연산자/확장으로 재해석되지 않는다. 이 가드가 막는 것은 **인자 경계의 조용한 변형**이다.
|
|
549
|
+
# 초판 주석은 "프로브와 같은 규칙" 이라고 적었는데 **과잉주장이었다**: 개행이 통과해
|
|
550
|
+
# 한 인자가 두 인자로 쪼개졌다(`$'GO\nSECOND'` → 노드가 `ARGC=2`). 개행/탭/제어문자를
|
|
551
|
+
# 추가로 막고, 문구를 사실에 맞춘다.
|
|
552
|
+
case "$1" in
|
|
553
|
+
*[\;\|\&\`\$\(\)\<\>]*|*'..'/*) _die "--cap-args 에 셸 메타문자/상위경로: $1" ;;
|
|
554
|
+
esac
|
|
555
|
+
case "$1" in
|
|
556
|
+
*[$'\n\t\r\v\f']*) _die "--cap-args 에 개행/탭/제어문자 — 인자 경계가 조용히 바뀐다" ;;
|
|
557
|
+
esac
|
|
558
|
+
CAPARGS[$_ci]="$1"; CAPARGS_SET[$_ci]=1
|
|
559
|
+
;;
|
|
497
560
|
--task) shift; task="$1" ;;
|
|
498
561
|
--record) shift; recdir="$1" ;;
|
|
499
562
|
--ack) ack=1 ;;
|
|
@@ -539,7 +602,7 @@ do_run() {
|
|
|
539
602
|
|
|
540
603
|
for f in "${CAPS[@]}"; do
|
|
541
604
|
n=$((n+1))
|
|
542
|
-
_invoke_node "$f" "$task" "$upstream" "$recdir" "$n"
|
|
605
|
+
_invoke_node "$f" "$task" "$upstream" "$recdir" "$n" "${CAPARGS[$((n-1))]:-}"
|
|
543
606
|
printf 'FH_NODE%d_ID: %s\n' "$n" "$(_cap_get "$f" id)"
|
|
544
607
|
printf 'FH_NODE%d_VERDICT: %s\n' "$n" "$NODE_VERDICT"
|
|
545
608
|
printf 'FH_NODE%d_EXIT: %s\n' "$n" "$NODE_EXIT"
|
package/scripts/selfcheck.sh
CHANGED
|
@@ -276,6 +276,86 @@ else
|
|
|
276
276
|
fail=1
|
|
277
277
|
fi
|
|
278
278
|
|
|
279
|
+
# gate-anchor harness — the SAME defect the block above documents, still open six days later.
|
|
280
|
+
# MEASURED 2026-08-16 (weekly_audit_2026-08-16.md 🟥-2): scripts/gate_anchor_check.sh has shipped in
|
|
281
|
+
# package.json files[] since 2026-08-10 (b18a2bf, PR #323) with ZERO callers — not selfcheck, not
|
|
282
|
+
# prepublishOnly, not .git-hooks/, not .github/, not npm scripts. Its only other mention is a comment
|
|
283
|
+
# line in a .cap adapter, which is not a call. So the one instrument this repo owns for asking
|
|
284
|
+
# "is the gate green for the RIGHT REASON?" was itself never green, never red, never run.
|
|
285
|
+
#
|
|
286
|
+
# 🟥 The detector did not see it: lane_runner_check.sh reports 46/46 wired · self-test 13/13, because
|
|
287
|
+
# this file is neither a lane suite nor a --self-test subject — it is OUTSIDE the detector's field of
|
|
288
|
+
# view, exactly like directional_diff_gate on 2026-08-15, and it survived that repair.
|
|
289
|
+
#
|
|
290
|
+
# ⚠️ Do NOT use "documented in a .md" as the wiring test: publish_freshness_check.sh has no reference
|
|
291
|
+
# in any TRACKED doc (knowledge/, docs/) and IS wired (prepublishOnly), and it blocked a publish on
|
|
292
|
+
# 2026-08-16. (An earlier draft of this line said "zero .md references" flat; that is false — three
|
|
293
|
+
# gitignored tracks/*.md files mention it. The thesis survives, the number did not, and Axis 3 caught
|
|
294
|
+
# it.) Call sites are the evidence; documentation is not. The first scan for this finding also missed
|
|
295
|
+
# templates/.git-hooks/* because those files carry no extension — scope corrected twice.
|
|
296
|
+
#
|
|
297
|
+
# Exit contract (from the script header): 0 = every known-pair held · 1 = a pair COLLAPSED (real
|
|
298
|
+
# defect) · 10 = harness error OR hooks-not-installed.
|
|
299
|
+
#
|
|
300
|
+
# 🟥 10 IS TWO DIFFERENT PROPOSITIONS AND THIS BLOCK MUST SPLIT THEM. gate_anchor_check.sh reaches
|
|
301
|
+
# `exit 10` from FOUR places, and only ONE of them is a legitimate consumer state:
|
|
302
|
+
# :43 cwd/script repo mismatch → harness defect
|
|
303
|
+
# :99 mktemp failed → harness defect
|
|
304
|
+
# :133 _fixture_fail (git init/seed/remote/origin-main) → harness defect
|
|
305
|
+
# :339 NOT_INSTALLED > 0 → legitimate: a package-mode consumer has no hooks
|
|
306
|
+
# Folding all four into a quiet SKIP is fail-open: a fixture collapse in THIS repo (a git default
|
|
307
|
+
# -branch policy change, a restricted TMPDIR, forced commit.gpgsign) would render byte-identically to
|
|
308
|
+
# a consumer who simply has no hooks. That is the "unmeasured → clean" move this file legislates
|
|
309
|
+
# against — and worse, the --self-test loop 200 lines below already routes rc=10 to `fail=1` saying
|
|
310
|
+
# "it could not measure, so its verdicts prove nothing". Same file, same code, opposite verdict.
|
|
311
|
+
# (Adversarial review 2026-08-16 found this. ⚠️ An earlier draft of this comment cited `:524` for
|
|
312
|
+
# it; the standpoint review measured the real sites — the loop dispatches at :528 and routes
|
|
313
|
+
# rc=10 to fail=1 at :562, and the quoted sentence lives at :429/:475. `:524` is none of them, so
|
|
314
|
+
# a reviewer following it lands on prose. A stale line number in the comment that argues against
|
|
315
|
+
# stale instruments is this commit's own defect class, recorded rather than quietly corrected.)
|
|
316
|
+
#
|
|
317
|
+
# The discriminator is mechanical, not a guess: the three harness-defect paths use plain `echo
|
|
318
|
+
# "❌ harness-error: …"`, while the NOT-INSTALLED path goes through `say()` which `--quiet` suppresses.
|
|
319
|
+
# Verified 2026-08-16: running the script from an unrelated git repo with --quiet returns rc=10 AND
|
|
320
|
+
# prints `harness-error` to the captured stream. So harness-error-in-output → FAIL; rc=10 without it
|
|
321
|
+
# → genuine NOT-INSTALLED → SKIP, labelled UNMEASURED and loud.
|
|
322
|
+
#
|
|
323
|
+
# ⚠️ NAMED RESIDUAL, not closed here: in CI this lane is structurally UNMEASURED. No workflow sets
|
|
324
|
+
# core.hooksPath (grep: 0 hits in .github/workflows/), so validate.yml's selfcheck step always lands
|
|
325
|
+
# on the NOT-INSTALLED arm — and `validate` is this repo's only required status check. A permanently
|
|
326
|
+
# SKIPPED lane in the one required check is worse than a permanently red one, because SKIP does not
|
|
327
|
+
# catch the eye. Fixing it is one line in validate.yml (set core.hooksPath before the selfcheck step)
|
|
328
|
+
# and is deliberately NOT bundled here — see the handoff. Until then the only real measurement arm is
|
|
329
|
+
# the author's machine, which is the exact shape portability_lint.sh was written to condemn.
|
|
330
|
+
if [ ! -f scripts/gate_anchor_check.sh ]; then
|
|
331
|
+
_absent_subject_verdict "gate-anchor harness" "scripts/gate_anchor_check.sh" || fail=1
|
|
332
|
+
else
|
|
333
|
+
# Capture the status BEFORE any display filter — $? after a pipe is the filter's, and a failed
|
|
334
|
+
# check would read as 0 (the repo's own pipe-verdict guard fired on this exact shape twice while
|
|
335
|
+
# this block was being written).
|
|
336
|
+
_ga_out="$(bash scripts/gate_anchor_check.sh --quiet 2>&1)"; _ga_rc=$?
|
|
337
|
+
case "$_ga_rc" in
|
|
338
|
+
0) echo "PASS gate-anchor harness (every known-pair held — gate is green for the right reason)" ;;
|
|
339
|
+
1) echo "FAIL gate-anchor harness: a known-pair COLLAPSED — a gate is green for the wrong reason"
|
|
340
|
+
[ -n "$_ga_out" ] && printf '%s\n' "$_ga_out" | sed 's/^/ /'
|
|
341
|
+
fail=1 ;;
|
|
342
|
+
10) if printf '%s' "$_ga_out" | grep -q 'harness-error'; then
|
|
343
|
+
echo "FAIL gate-anchor harness: the anchor could not MEASURE (harness-error, rc=10) — a"
|
|
344
|
+
echo " collapsed fixture is a defect in the instrument, not a consumer's missing hook."
|
|
345
|
+
printf '%s\n' "$_ga_out" | sed 's/^/ /'
|
|
346
|
+
fail=1
|
|
347
|
+
else
|
|
348
|
+
echo "SKIP gate-anchor harness: UNMEASURED (hooks not installed, rc=10)"
|
|
349
|
+
echo " Absence is not a pass — this run did NOT establish that the gates are sound."
|
|
350
|
+
[ -n "$_ga_out" ] && printf '%s\n' "$_ga_out" | sed 's/^/ /'
|
|
351
|
+
fi ;;
|
|
352
|
+
*) echo "FAIL gate-anchor harness: undeclared exit code $_ga_rc — the contract is 0/1/10, so an"
|
|
353
|
+
echo " unknown status is a harness defect, not a pass."
|
|
354
|
+
[ -n "$_ga_out" ] && printf '%s\n' "$_ga_out" | sed 's/^/ /'
|
|
355
|
+
fail=1 ;;
|
|
356
|
+
esac
|
|
357
|
+
fi
|
|
358
|
+
|
|
279
359
|
# count_check's README format override is a mandatory-pass gate whose pattern is caller-supplied.
|
|
280
360
|
# Same subject-present/anchor-gone shape as the block above: if the guard ships without its known
|
|
281
361
|
# pair, a template that defeats the gate (a rendered newline turns the pattern into an OR search)
|
|
@@ -320,14 +400,49 @@ fi
|
|
|
320
400
|
# how a fresh CI checkout gets approximated, so the check was absent from exactly the tree used
|
|
321
401
|
# to reason about CI. scripts/test_package_coverage_lanes.sh pins the predicate across all four
|
|
322
402
|
# tree shapes plus a known pair.
|
|
403
|
+
# ── FH 내장 어댑터 레인 ────────────────────────────────────────────────────
|
|
404
|
+
# 어댑터(`.claude/capabilities/adapters/*.cap` + `scripts/adapters/*.sh`)는 남의 하네스 능력을
|
|
405
|
+
# FH 안에서 호출하는 **기본 경로**다(운영자 결정 2026-08-16 — 남의 레포에 선언을 심지 않는다).
|
|
406
|
+
# 🟥 배선이 없으면 이 레인은 «만들고 배선 안 함» 이 된다 — 같은 날 `cluster_capability_scan.sh`
|
|
407
|
+
# 가 비재귀 글롭이라 어댑터를 통째로 못 보던 것을 이 세션이 실측으로 잡았고, 그 회귀를 막는
|
|
408
|
+
# 레인(L16/L16b)이 그 파일 self-test 안에 있다. 어댑터 자체의 레인은 여기서 돈다.
|
|
409
|
+
if [ ! -f scripts/adapters/peer_resolve.sh ]; then
|
|
410
|
+
_absent_subject_verdict "test_adapter_lanes.sh" "scripts/adapters/peer_resolve.sh" || fail=1
|
|
411
|
+
elif [ -f scripts/test_adapter_lanes.sh ]; then
|
|
412
|
+
if ! bash scripts/test_adapter_lanes.sh; then
|
|
413
|
+
fail=1
|
|
414
|
+
fi
|
|
415
|
+
else
|
|
416
|
+
_absent_subject_verdict "test_adapter_lanes.sh" "scripts/test_adapter_lanes.sh" || fail=1
|
|
417
|
+
fi
|
|
418
|
+
|
|
323
419
|
if [ ! -f scripts/package_coverage_check.sh ]; then
|
|
324
420
|
_absent_subject_verdict "test_package_coverage_lanes.sh" "scripts/package_coverage_check.sh" || fail=1
|
|
325
421
|
elif [ -f scripts/test_package_coverage_lanes.sh ]; then
|
|
326
|
-
|
|
422
|
+
if ! bash scripts/test_package_coverage_lanes.sh; then
|
|
327
423
|
fail=1
|
|
328
424
|
fi
|
|
329
|
-
|
|
330
|
-
|
|
425
|
+
# The CHECKER's semantics are deliberately fail-closed and must not be softened: given a git
|
|
426
|
+
# checkout with no package.json it reports UNMEASURED, never "clean", and its own lane L4 pins
|
|
427
|
+
# exactly that. Applicability is the CALLER's judgment, and it is made mechanically here.
|
|
428
|
+
#
|
|
429
|
+
# A repo that ships no npm surface at all is not "unmeasured npm coverage" — it has no npm
|
|
430
|
+
# coverage to measure. Measured 2026-08-16 in a sibling harness that carries this suite verbatim
|
|
431
|
+
# and is not an npm package: this was a PERMANENT red, on every run forever. A gate that can
|
|
432
|
+
# never go green is not strict, it is a gate people learn to ignore — and this suite is the
|
|
433
|
+
# mandatory-pass surface, so that habit is expensive.
|
|
434
|
+
#
|
|
435
|
+
# Note the asymmetry, because it is the whole point: this repo HAS an npm surface, so if its
|
|
436
|
+
# package.json ever went missing the checker would fire and SHOULD — that is a real defect here.
|
|
437
|
+
# The guard below distinguishes "the surface is absent by nature" from "the surface is missing",
|
|
438
|
+
# which is the distinction `not found ≠ 0` exists to protect.
|
|
439
|
+
if [ -f package.json ] || [ -d bin ]; then
|
|
440
|
+
if ! bash scripts/package_coverage_check.sh; then
|
|
441
|
+
fail=1
|
|
442
|
+
fi
|
|
443
|
+
else
|
|
444
|
+
echo "SKIP package-coverage — NOT APPLICABLE: this repo ships no npm surface"
|
|
445
|
+
echo " (checked mechanically: package.json absent, bin/ absent — not a claim, a test)"
|
|
331
446
|
fi
|
|
332
447
|
else
|
|
333
448
|
echo "FAIL test_package_coverage_lanes.sh: package_coverage_check.sh present but its anchor is missing"
|
|
@@ -489,7 +604,16 @@ done
|
|
|
489
604
|
# and capability_registry_check does not: its terminal line is a genuine VERDICT
|
|
490
605
|
# ("publish_freshness 캘리브레이션 통과/실패"), not a Korean test-case title, so a future rename of
|
|
491
606
|
# any single lane cannot flip a real PASS into a false "dispatcher missing?" here.
|
|
492
|
-
|
|
607
|
+
# portability_lint joined 2026-08-16 (weekly_audit 🟧-3). Same qualification as publish_freshness_check:
|
|
608
|
+
# its terminal line is a genuine verdict ("portability-lint 캘리브레이션 통과/실패"), emitted once in
|
|
609
|
+
# the summary position, not a Korean test-case title — so renaming any single lane cannot flip a real
|
|
610
|
+
# PASS into a false "dispatcher missing?" here. It earns the slot for a second reason: its own
|
|
611
|
+
# self-test caught THREE defects in it before it ever shipped (a `|` field separator colliding with
|
|
612
|
+
# regex alternation, a line-window too narrow to see the next-line guard, and a rule that flagged its
|
|
613
|
+
# own prescription), and a hand-check then removed two false-positive classes that had inflated its
|
|
614
|
+
# first repo-wide count by 40% (50 → 30). An instrument that has never been able to fail is not
|
|
615
|
+
# calibrated; this one has failed, been fixed, and still discriminates (10 pass / 0 fail).
|
|
616
|
+
for _subj in compaction_probe judgment_circuit_lint novelty_claim_check chamber_witness digest_landing_check publish_freshness_check portability_lint; do
|
|
493
617
|
if [ ! -f "scripts/$_subj.sh" ]; then
|
|
494
618
|
_absent_subject_verdict "$_subj --self-test" "scripts/$_subj.sh" || fail=1
|
|
495
619
|
else
|
|
@@ -528,6 +652,24 @@ for _subj in compaction_probe judgment_circuit_lint novelty_claim_check chamber_
|
|
|
528
652
|
echo " so its verdicts prove nothing about $_subj. Not a lane failure, not a pass either."
|
|
529
653
|
fail=1; continue ;;
|
|
530
654
|
esac
|
|
655
|
+
# 🟥 portability_lint 만 **전체 줄 + 비영 카운트**를 요구한다 (적대검증 2026-08-16).
|
|
656
|
+
# 아래 substring 게이트는 «`캘리브레이션` 이라는 글자가 출력 어딘가에 있나» 만 본다. 그래서
|
|
657
|
+
# 그 스크립트의 `RULES` 를 통째로 비우고 `_ck` 호출을 전부 지워도
|
|
658
|
+
# `portability-lint 캘리브레이션 통과: 0 pass / 0 fail` 이 나오고 rc=0 → **PASS** 다.
|
|
659
|
+
# 이건 이 파일이 directional_diff_gate 에 대해 이미 명시적으로 방어한 결함
|
|
660
|
+
# (*"a suite with nothing left in it certifies itself"*)이고, 새 subject 만 약한 게이트에
|
|
661
|
+
# 들어갔다. 저자의 되돌림 프로브(«디스패처 제거 → FAIL»)는 **디스패처 존재**만 입증하고
|
|
662
|
+
# **레인 생존**은 입증하지 않는다 — 더 강한 뮤턴트(레인 삭제)는 안 돌았고, 돌았으면 초록이었다.
|
|
663
|
+
if [ "$_subj" = portability_lint ]; then
|
|
664
|
+
case "$_st_out" in
|
|
665
|
+
*"portability-lint 캘리브레이션 통과: "[1-9]*" pass / 0 fail"*) : ;;
|
|
666
|
+
*) echo "FAIL $_subj: --self-test 가 «비영 pass + 0 fail» 종단 verdict 를 내지 않았다"
|
|
667
|
+
echo " (레인이 전멸해도 substring 게이트는 통과한다 — 그래서 여기만 전체 줄을 본다)"
|
|
668
|
+
printf '%s\n' "$_st_out" | tail -3 | sed 's/^/ /'
|
|
669
|
+
fail=1; _st_rc=0 ;;
|
|
670
|
+
esac
|
|
671
|
+
continue
|
|
672
|
+
fi
|
|
531
673
|
case "$_st_out" in
|
|
532
674
|
*캘리브레이션*) : ;;
|
|
533
675
|
*) echo "FAIL $_subj: --self-test produced no calibration verdict (dispatcher missing?)"
|
|
@@ -557,6 +699,24 @@ else
|
|
|
557
699
|
fail=1
|
|
558
700
|
fi
|
|
559
701
|
|
|
702
|
+
# cluster_capability_scan — wired 2026-08-16 (정체성 ①-(a) cluster-wizard).
|
|
703
|
+
# 종단 판정줄이 `… 캘리브레이션 통과/실패: N PASS / M FAIL` 이라 위 `_subj` 루프의
|
|
704
|
+
# 캘리브레이션 게이트를 **진짜 판정줄로** 만족한다 — 테스트 케이스 제목이 아니다.
|
|
705
|
+
# 그래서 이 subject 는 그 루프에 넣어도 안전하지만, 루프는 `scripts/<name>.sh` 존재를
|
|
706
|
+
# 전제로 SKIP/FAIL 을 가르므로 여기 직접 디스패치로 둔다(다른 두 capability 검사기와 같은 형태).
|
|
707
|
+
# 🟥 exit code 만 보지 않는다 — 레인이 전멸해도 0 이 나온다.
|
|
708
|
+
if [ ! -f scripts/cluster_capability_scan.sh ]; then
|
|
709
|
+
echo "FAIL cluster_capability_scan.sh: missing — it ships via package.json files[], so absence is deletion, not package mode"
|
|
710
|
+
fail=1
|
|
711
|
+
elif _out=$(bash scripts/cluster_capability_scan.sh --self-test < /dev/null 2>&1) \
|
|
712
|
+
&& printf '%s\n' "$_out" | grep -qE '캘리브레이션 통과: [1-9][0-9]* PASS / 0 FAIL'; then
|
|
713
|
+
echo "PASS cluster_capability_scan.sh --self-test ($(printf '%s\n' "$_out" | grep -oE '[0-9]+ PASS / [0-9]+ FAIL' | tail -1))"
|
|
714
|
+
else
|
|
715
|
+
echo "FAIL cluster_capability_scan.sh: --self-test failed or produced no terminal verdict line"
|
|
716
|
+
_show_failure "$_out"
|
|
717
|
+
fail=1
|
|
718
|
+
fi
|
|
719
|
+
|
|
560
720
|
# capability_effect_probe — wired 2026-08-16. `lane_runner_check.sh` had been flagging it as an
|
|
561
721
|
# embedded --self-test subject with ZERO dispatchers, and that warning was load-bearing: the same
|
|
562
722
|
# day, two SECURITY regression anchors were added to this suite (L8 args injection · L9 the git
|
|
@@ -752,7 +912,9 @@ else
|
|
|
752
912
|
fail=1
|
|
753
913
|
fi
|
|
754
914
|
|
|
755
|
-
# marker axes-run lanes — 훅의
|
|
915
|
+
# marker axes-run lanes — 훅의 축 자기대조 형식 검사(§CLAUDE.md 3층 자기 대조)의 앵커.
|
|
916
|
+
# ⚠️ 「4축」이라고 적혀 있었는데 2026-08-17 부로 **6축 분기가 생겼다**(마커 날짜 >= 그 날이면
|
|
917
|
+
# 기호 키 ⓐ~ⓕ 요구, 미만이면 옛 ASCII 넷). 산문 층이라 레인은 안 깨지고 조용히 stale 이었다.
|
|
756
918
|
# subject 부재를 FAIL 로 두는 이유는 branch-claim 블록과 같다: 이 subject 는 pre-commit 훅
|
|
757
919
|
# 자체이고 files[] 에 있으므로 «정당한 부재» 가 없다. 부재 = 삭제다.
|
|
758
920
|
if [ ! -f templates/.git-hooks/pre-commit ]; then
|