@chrono-meta/fh-gate 2.5.1 β 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/rules/fh_4axis_gate.md +26 -3
- package/.claude-plugin/marketplace.json +2 -2
- package/AGENTS.md +24 -3
- package/CATALOG.md +41 -0
- package/CLAUDE.md +77 -15
- package/README.ja.md +36 -11
- package/README.ko.md +36 -11
- package/README.md +116 -18
- package/README.zh.md +32 -11
- package/docs/USER_GUIDE.md +118 -0
- package/docs/platform_sustainability.md +174 -0
- package/knowledge/shared/GLOSSARY.md +26 -1
- package/knowledge/shared/harness-core/fh_detail_protocols.md +44 -2
- package/knowledge/shared/harness-core/field_verdict_crossfamily_gate.md +19 -5
- package/knowledge/shared/harness-core/harness_incubator_doctrine.md +129 -2
- package/knowledge/shared/harness-core/multi_model_sidecar_strategy.md +32 -0
- package/knowledge/shared/harness-core/ship_readiness_gate.md +77 -8
- package/knowledge/shared/learnings/subagent_invocations_log.yaml +110 -0
- package/knowledge/shared/rules/knowledge_layer_seam.md +1 -1
- package/package.json +17 -3
- 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 +136 -0
- package/plugins/fh-meta/agents/persona-innovator.md +170 -0
- package/plugins/fh-meta/skills/auto-decorrelation/SKILL.md +44 -0
- package/plugins/fh-meta/skills/fh/SKILL.md +31 -0
- package/plugins/fh-meta/skills/goal-quench/SKILL_detail.md +1 -1
- package/plugins/fh-meta/skills/harness-doctor/SKILL.md +7 -1
- package/plugins/fh-meta/skills/harvest-loop/SKILL.md +32 -0
- package/plugins/fh-meta/skills/public-surface-audit/SKILL_detail.md +16 -1
- package/plugins/fh-meta/skills/steel-quench/SKILL.md +97 -0
- package/scripts/adapters/fixtures/mate_agent_boundary_known_negative.md +31 -0
- package/scripts/adapters/fixtures/mate_agent_boundary_known_positive.md +56 -0
- package/scripts/adapters/peer_resolve.sh +58 -6
- package/scripts/cluster_capability_scan.sh +60 -18
- package/scripts/digest_landing_check.sh +181 -10
- package/scripts/fh_hub_identity.sh +83 -0
- package/scripts/fh_session_load.sh +53 -5
- package/scripts/fh_track_resolve.sh +114 -0
- package/scripts/field_canon_preload.sh +50 -5
- package/scripts/package_coverage_check.sh +17 -0
- package/scripts/prior_art_prompt.sh +168 -0
- package/scripts/psa_scan_lib.sh +201 -15
- package/scripts/residency_admission_check.sh +204 -0
- package/scripts/selfcheck.sh +117 -1
- package/scripts/test_adapter_lanes.sh +67 -2
- package/scripts/test_heavy_classifier_lanes.sh +144 -0
- package/scripts/test_marker_defense_lanes.sh +152 -0
- package/scripts/test_marker_soul_check_lanes.sh +211 -0
- package/scripts/test_prior_art_prompt_lanes.sh +128 -0
- package/scripts/test_psa_singlefile_lanes.sh +351 -1
- package/scripts/test_residency_admission_lanes.sh +60 -0
- package/scripts/test_track_resolve_lanes.sh +158 -0
- package/templates/.git-hooks/pre-commit +400 -4
- package/templates/.git-hooks/pre-push +17 -2
- package/templates/settings.PriorArt.snippet.json +15 -0
- package/scripts/test_satellite_publish_gate_lanes.sh +0 -339
|
@@ -356,6 +356,7 @@ else
|
|
|
356
356
|
fi
|
|
357
357
|
else
|
|
358
358
|
PSA_LEAK=0
|
|
359
|
+
PSA_DEAD=0
|
|
359
360
|
# NUL-delimited (regression caught by the refactor's own cross-family review): the pre-refactor
|
|
360
361
|
# loop read this list with `read -r -d ''`, and the rewrite dropped back to a line-oriented read.
|
|
361
362
|
# `core.quotePath=false` stops git quoting NON-ASCII names, but a path holding a backslash or a
|
|
@@ -367,7 +368,11 @@ else
|
|
|
367
368
|
added=$(git diff --cached -- "$f" 2>/dev/null | grep -E '^\+' | sed 's/^\+//' || true)
|
|
368
369
|
[ -z "$added" ] && continue
|
|
369
370
|
# Per-line pass, path-tagged so the LOW file allowlist applies.
|
|
370
|
-
|
|
371
|
+
# π₯ rc λ₯Ό Β«λΉμΒ» μΌλ‘ λκ°μ§ λ§λΌ (cross-family, 2026-08-21): 1=μ μΆ Β· 3=κ³κΈ° μ¬λ§.
|
|
372
|
+
# λμ ν©μΉλ©΄ μλ PUBLIC_SURFACE_OK κ° **κ³κΈ° μ¬λ§κΉμ§ Β«μΉμΈλ μ μΆΒ»λ‘ ν΅κ³Ό**μν¨λ€.
|
|
373
|
+
_psa_rc=0
|
|
374
|
+
printf '%s\n' "$added" | sed "s|^|$f |" | psa_scan_tagged || _psa_rc=$?
|
|
375
|
+
case "$_psa_rc" in 0) ;; 3) PSA_DEAD=1 ;; *) PSA_LEAK=1 ;; esac
|
|
371
376
|
# Split-token backstop (pre-commit ONLY β this surface is a DIFF, so a literal can be wrapped
|
|
372
377
|
# mid-word across two added lines and neither line matches). TIGHT-join, no separator, so
|
|
373
378
|
# "fh-\nbe" becomes "fh-be". Accepted residual, verified not a missed fix: two whitespace-
|
|
@@ -375,15 +380,31 @@ else
|
|
|
375
380
|
# PUBLIC_SURFACE_OK escape. A sentinel separator would close the fusion but RE-OPEN the split
|
|
376
381
|
# catch, which is the more important case since real literals have no internal whitespace.
|
|
377
382
|
joined=$(printf '%s' "$added" | tr -d '\n\r')
|
|
378
|
-
|
|
383
|
+
_psa_rc2=0
|
|
384
|
+
printf '%s\t%s\n' "$f" "$joined" | psa_scan_tagged >/dev/null 2>&1 || _psa_rc2=$?
|
|
385
|
+
[ "$_psa_rc2" = "3" ] && PSA_DEAD=1
|
|
386
|
+
if [ "$_psa_rc2" != "0" ] && [ "$_psa_rc2" != "3" ]; then
|
|
379
387
|
# Only report if the per-line pass did not already flag this file, to avoid double-reporting.
|
|
380
388
|
if ! printf '%s\n' "$added" | sed "s|^|$f |" | psa_scan_tagged >/dev/null 2>&1; then :; else
|
|
389
|
+
# π₯ **νμ μ μ© β νμ κ²½λ‘κ° μλλ€** (2026-08-21 λ°°μ 리뷰 R-1). μ΄ νμ΄νλ λΉλ§λ¨μ΄λΌ
|
|
390
|
+
# `$?` κ° `sed` κ²μ΄λ€. κ·Έλλ λλ μ΄μ λ λλ¬ μ‘°κ±΄μ΄ **μ΄λ―Έ Β«μ μΆ νμ Β»** μ΄κ³ λ°λ‘
|
|
391
|
+
# λ€μ μ€μ΄ `PSA_LEAK=1` μ 무쑰건 μΈμ°κΈ° λλ¬Έμ΄λ€. νμ μ μ `_psa_rc` 3λΆκΈ°κ° μ₯λ€.
|
|
392
|
+
# β οΈ μ΄ μ£Όμμ΄ μμΌλ©΄ λ€μ μ¬λμ΄ νμ κ²½λ‘λ‘ μ½κ³ Β«κ³ μΉλ€Β» β κ·Έκ² μ΄ μ€μ μννκ² λ§λ λ€.
|
|
381
393
|
printf '%s\t%s\n' "$f" "$joined" | psa_scan_tagged | sed 's/leak β/leak (line-split) β/'
|
|
382
394
|
PSA_LEAK=1
|
|
383
395
|
fi
|
|
384
396
|
fi
|
|
385
397
|
done < <(git -c core.quotePath=false diff --cached --name-only --no-renames --diff-filter=ACMR -z 2>/dev/null || true)
|
|
386
|
-
|
|
398
|
+
# π₯ κ³κΈ° μ¬λ§μ override λμμ΄ **μλλ€.** override λ Β«μκ³ μλ μΈκΈΒ»μ μΉμΈνλ κ²μ΄μ§
|
|
399
|
+
# Β«μ μ° νλ©΄Β»μ μΉμΈνλ κ² μλλ€. μ΄νμ 1 κ³Ό 3 μ ν©μ³μ, PUBLIC_SURFACE_OK=1 μ΄λ©΄
|
|
400
|
+
# μ€μΊλκ° μ£½μ μνλ ν΅κ³Όνλ€ β λΉκ°μ νλ©΄μμ κ°μ₯ λμ μ‘°ν©μ΄λ€.
|
|
401
|
+
if [ "${PSA_DEAD:-0}" -eq 1 ]; then
|
|
402
|
+
echo " β INSTRUMENT DEAD β the public-surface scan did NOT run (rc=3). NOT SCANNED is not clean."
|
|
403
|
+
echo " PUBLIC_SURFACE_OK does NOT cover this. Fix the scanner, then commit:"
|
|
404
|
+
echo " Β· run under bash (zsh special vars can blank PATH inside the matcher)"
|
|
405
|
+
echo " Β· confirm psa_load ran and PSA_STREAM is non-empty"
|
|
406
|
+
FAILED=1
|
|
407
|
+
elif [ "$PSA_LEAK" -eq 1 ]; then
|
|
387
408
|
if [ "${PUBLIC_SURFACE_OK:-0}" = "1" ]; then
|
|
388
409
|
echo " β οΈ public-surface hit(s) allowed by PUBLIC_SURFACE_OK=1 (conscious, reviewed intent)"
|
|
389
410
|
echo "$(date +%Y-%m-%dT%H:%M:%S) PUBLIC_SURFACE_OK override β branch $BRANCH β review suppressed hit(s) above" \
|
|
@@ -575,6 +596,86 @@ marker_recreate_hint() {
|
|
|
575
596
|
# VALUE is a closed enum; the three degrade values additionally require a non-vacuous reason
|
|
576
597
|
# on the SAME line (keeping the established one-line marker convention rather than adding a
|
|
577
598
|
# second field). Returns 0 = accepted, 1 = block.
|
|
599
|
+
validate_defense_leg() { # $1 = marker path β fires ONLY at the floor tiers (see call site)
|
|
600
|
+
# Wave 1-D defense line. Absorbed from a sibling harness 2026-08-20, but the machine is OURS:
|
|
601
|
+
# π₯ the source document claimed "the commit hook reads that line" and "pinned by
|
|
602
|
+
# test_marker_floor_lanes.sh" β measured twice with a control (`crossfamily` 21 hits), BOTH were
|
|
603
|
+
# 0 hits there. Porting the prose without building this function would have imported the phantom.
|
|
604
|
+
#
|
|
605
|
+
# WHY IT EXISTS: measured n=6 in the origin field β a floor-tier pass executes the attack angles
|
|
606
|
+
# without defect but does NOT spontaneously ask these three of its own numbers. That is a
|
|
607
|
+
# checklist gap, not a capability gap, and a harness whose behaviour depends on which model drives
|
|
608
|
+
# it is defective by sonnet_floor_doctrine.md rather than merely limited.
|
|
609
|
+
# FH-side corroboration (2026-08-20, this session, n=1): a comparison was published from two
|
|
610
|
+
# DIFFERENT package versions and read as a defect ("15 lanes vs 13 lanes β two vanished"). The
|
|
611
|
+
# λΉκ΅κ³΅μ μ± question β *were the two arms measured under the same conditions?* β asks exactly
|
|
612
|
+
# that, and the claim was retracted only because a later step happened to surface the version gap.
|
|
613
|
+
#
|
|
614
|
+
# SCOPE, and why it is not wider: `below-floor` occurs 0 times across the existing marker corpus,
|
|
615
|
+
# so gating on it alone would be a decoration that never fires. `sonnet-floor` occurs 11 times.
|
|
616
|
+
# Both are gated here; the wide reading ("any marker carrying numbers") is deliberately NOT taken β
|
|
617
|
+
# it would price this axis at a near-universal rate, which is the over-trigger Β§7 rejects.
|
|
618
|
+
#
|
|
619
|
+
# π₯ WHAT WAS NOT IMPORTED: the origin also amends its floor rule so that below-floor + this line
|
|
620
|
+
# + a crossfamily record PASSES WITHOUT the operator ack. That is a LOOSENING of an existing FH
|
|
621
|
+
# gate and it is not adopted. Here this leg is purely additive: below-floor still requires
|
|
622
|
+
# below-floor-ack, unchanged.
|
|
623
|
+
local m="$1" line r f e
|
|
624
|
+
if [ ! -f "$m" ] || ! grep -qE '^[[:space:]]*axis2-defense:[[:space:]]*[^[:space:]]' "$m"; then
|
|
625
|
+
echo " β FAIL β floor-tier marker with no 'axis2-defense:' line."
|
|
626
|
+
echo " At the floor tier the attack angles run but the three defense questions do not get"
|
|
627
|
+
echo " asked spontaneously. Ask them OF YOUR OWN findings and numbers, then record:"
|
|
628
|
+
echo " axis2-defense: reproducibility=<exact command or file:line another session runs>"
|
|
629
|
+
echo " fairness=<reps/inputs/env named for BOTH arms>"
|
|
630
|
+
echo " estimation-layer=<per number: measured | estimate | quotation;"
|
|
631
|
+
echo " for measured, what showed the instrument works here>"
|
|
632
|
+
echo " Append to: $m"
|
|
633
|
+
return 1
|
|
634
|
+
fi
|
|
635
|
+
if [ "$(grep -cE '^[[:space:]]*axis2-defense:' "$m")" -gt 1 ]; then
|
|
636
|
+
echo " β FAIL β marker carries MORE THAN ONE 'axis2-defense:' line."
|
|
637
|
+
grep -nE '^[[:space:]]*axis2-defense:' "$m" | sed 's/^/ /'
|
|
638
|
+
echo " The reader takes the FIRST, so an appended correction is silently shadowed."
|
|
639
|
+
echo " Leave exactly one line."
|
|
640
|
+
return 1
|
|
641
|
+
fi
|
|
642
|
+
# The whole value, including any continuation lines, up to the next top-level field.
|
|
643
|
+
line=$(sed -n '/^[[:space:]]*axis2-defense:/,/^[a-z0-9-]*:[[:space:]]/p' "$m" | sed -E '2,$ { /^[a-z0-9-]+:[[:space:]]/d; }' | tr '\n' ' ')
|
|
644
|
+
r=$(printf '%s' "$line" | sed -nE 's/.*reproducibility=([^ ].*)/\1/p' | sed -E 's/(fairness=|estimation-layer=).*//')
|
|
645
|
+
f=$(printf '%s' "$line" | sed -nE 's/.*fairness=([^ ].*)/\1/p' | sed -E 's/(reproducibility=|estimation-layer=).*//')
|
|
646
|
+
e=$(printf '%s' "$line" | sed -nE 's/.*estimation-layer=([^ ].*)/\1/p' | sed -E 's/(reproducibility=|fairness=).*//')
|
|
647
|
+
local missing=""
|
|
648
|
+
[ -n "$r" ] || missing="$missing reproducibility"
|
|
649
|
+
[ -n "$f" ] || missing="$missing fairness"
|
|
650
|
+
[ -n "$e" ] || missing="$missing estimation-layer"
|
|
651
|
+
if [ -n "$missing" ]; then
|
|
652
|
+
echo " β FAIL β axis2-defense: missing sub-answer(s):$missing"
|
|
653
|
+
echo " All three are required. A partially-filled form is not a defense β the question you"
|
|
654
|
+
echo " skipped is the one you did not want to answer."
|
|
655
|
+
return 1
|
|
656
|
+
fi
|
|
657
|
+
# Non-vacuity. `ok` / `yes` / `n/a` / `done` are a FILLED FORM, not an answer β the same shape the
|
|
658
|
+
# crossfamily leg rejects. Anything under 12 chars cannot name a command, a reps count, or a layer.
|
|
659
|
+
local bad=""
|
|
660
|
+
for pair in "reproducibility:$r" "fairness:$f" "estimation-layer:$e"; do
|
|
661
|
+
local k="${pair%%:*}" v="${pair#*:}"
|
|
662
|
+
v=$(printf '%s' "$v" | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//')
|
|
663
|
+
case "$(printf '%s' "$v" | tr 'A-Z' 'a-z')" in
|
|
664
|
+
ok|yes|y|n/a|na|none|done|true|-|tbd|todo) bad="$bad $k(vacuous:'$v')" ;;
|
|
665
|
+
*) [ "${#v}" -ge 12 ] || bad="$bad $k(too short:'$v')" ;;
|
|
666
|
+
esac
|
|
667
|
+
done
|
|
668
|
+
if [ -n "$bad" ]; then
|
|
669
|
+
echo " β FAIL β axis2-defense: vacuous answer(s):$bad"
|
|
670
|
+
echo " 'ok'/'yes'/'n/a' is a filled form, not an answer. Name the command, the reps, the layer."
|
|
671
|
+
echo " π₯ The hook checks PRESENCE and NON-VACUITY only. It cannot check whether the answers"
|
|
672
|
+
echo " are TRUE β that stays with the operator and the weekly audit, exactly like every other"
|
|
673
|
+
echo " marker field. Do not read a PASS here as a claim that these answers were verified."
|
|
674
|
+
return 1
|
|
675
|
+
fi
|
|
676
|
+
return 0
|
|
677
|
+
}
|
|
678
|
+
|
|
578
679
|
validate_crossfamily_leg() { # $1 = marker path
|
|
579
680
|
local m="$1" line val reason fams
|
|
580
681
|
if [ ! -f "$m" ] || ! grep -qE '^[[:space:]]*crossfamily:[[:space:]]*[^[:space:]]' "$m"; then
|
|
@@ -971,6 +1072,263 @@ validate_thirdparty_leg() { # $1 = marker path
|
|
|
971
1072
|
return 0
|
|
972
1073
|
}
|
|
973
1074
|
|
|
1075
|
+
SOUL_PRESENT_GRACE_DATE="2026-08-21"
|
|
1076
|
+
validate_soul_present_leg() { # $1 = marker path
|
|
1077
|
+
# ββ β μνΌ μ‘΄μ¬ κ²μ¬ (2026-08-21) ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
1078
|
+
# CLAUDE.md Β§μκΈ° λμ‘° has mandated an β μνΌ line (μ±κ³΅ μ μ / μ λ μ ν¨) on every marker
|
|
1079
|
+
# since 2026-08-09. MEASURED on the real corpus this day, with the instrument calibrated over
|
|
1080
|
+
# EIGHT spellings and two controls (`axes-run` and `axis2-engine` = 98/98): of 98 post-grace
|
|
1081
|
+
# markers, 61 carry it and **37 (37.8%) carry no form of it at all**.
|
|
1082
|
+
#
|
|
1083
|
+
# π₯ The first pass of that same measurement grepped only the exact `β μνΌ` string and returned
|
|
1084
|
+
# "92.9% missing". That would have been a false alarm ([[feedback_measured_scope_vs_claimed_scope]]
|
|
1085
|
+
# β hand-enumerating spellings). The union + hand-verification of one negative sample is what
|
|
1086
|
+
# makes the 37 real: `.axes_23_passed_feat_target-freeze-gate_2026-08-18.marker` was opened by
|
|
1087
|
+
# eye β a panel-reviewed, 28-lane, controls-alive marker with every OTHER field filled, and no
|
|
1088
|
+
# soul line in any spelling.
|
|
1089
|
+
#
|
|
1090
|
+
# π₯ WHY THIS IS NOT THE THING THE β μνΌ COMMENT BELOW DECLINES. That comment refuses to check
|
|
1091
|
+
# Β«was it written BEFORE designingΒ» β unverifiable from a file, correctly excluded, and this
|
|
1092
|
+
# lane does not check it. PRESENCE is a different question and is trivially decidable. The
|
|
1093
|
+
# impossible question carried the possible one out on its ticket.
|
|
1094
|
+
#
|
|
1095
|
+
# SCOPE β channel, not judgment. Asserts the field EXISTS and is not vacuous. Says nothing about
|
|
1096
|
+
# whether the success definition is good, was honoured, or was written in advance.
|
|
1097
|
+
local m="$1" n body words
|
|
1098
|
+
# Grace: only markers dated on/after the grace date are required to carry it. Retroactive
|
|
1099
|
+
# enforcement would block every in-flight branch, and that over-block trains `--no-verify`,
|
|
1100
|
+
# which would disarm the Destructive-Op gate living in this same hook.
|
|
1101
|
+
local mdate
|
|
1102
|
+
mdate=$(printf '%s' "$m" | sed -nE 's/.*_([0-9]{4}-[0-9]{2}-[0-9]{2})\.marker$/\1/p')
|
|
1103
|
+
# π₯ An unparseable date used to `return 0` β fail-OPEN. The hook itself builds this filename
|
|
1104
|
+
# as `..._${TODAY}.marker`, so a name without a date is unexpected, not old. Unknown is not
|
|
1105
|
+
# exempt ([[feedback_not_found_is_not_zero_family]]); it falls through to the presence check.
|
|
1106
|
+
[ -z "$mdate" ] || { [ "$mdate" \< "$SOUL_PRESENT_GRACE_DATE" ] && return 0; }
|
|
1107
|
+
# π₯ agy (claims axis): the comment above says the MEASUREMENT swept eight spellings; this
|
|
1108
|
+
# regex carries FOUR alternatives (β μνΌ Β· a `soul:` key Β· μ±κ³΅[ ]?μ μ Β· μ λ μ ν¨). Both are
|
|
1109
|
+
# true and they are different instruments β but writing only the first invites reading the
|
|
1110
|
+
# second's coverage off it ([[feedback_rule_misdescribes_its_own_machine]]). The four are the
|
|
1111
|
+
# ones the corpus actually uses as a FIELD; the other four measured spellings appeared only
|
|
1112
|
+
# inside prose, which this leg deliberately excludes.
|
|
1113
|
+
# π₯ The soul detector must NOT read the `soul-check:` line itself. codex's alignment fix widened
|
|
1114
|
+
# the pattern and G8 flipped to PASS: `soul-check: reflected(β¦μ±κ³΅μ μβ¦)` matched as its own
|
|
1115
|
+
# evidence β self-referential FP ([[feedback_lane_runner_self_referential_fp]]). The claim can
|
|
1116
|
+
# never be its own ground. Same reason comment/prose lines are excluded: a marker that merely
|
|
1117
|
+
# SAYS μ±κ³΅ μ μ in narration has not declared one.
|
|
1118
|
+
# π₯ A DECLARATION, not a MENTION. First real use (isolated clone, 2026-08-21) broke this on
|
|
1119
|
+
# attempt #1: this very delta's marker says Β«change: λ§μ»€μ β μνΌ μ¬λ‘―μ μλΉμ²κ° 0μ΄λ κ²μβ¦Β»
|
|
1120
|
+
# in a prose FIELD VALUE, and the leg reported "β μνΌ present: 10 words" for a marker that
|
|
1121
|
+
# declares nothing. Excluding `#` and `soul-check:` was not enough β any field whose value
|
|
1122
|
+
# discusses the axis satisfies a substring test, and markers ABOUT this axis always will.
|
|
1123
|
+
# 39 lanes, codex (diff axis) and agy (claims axis) all missed it; running it once did not
|
|
1124
|
+
# ([[feedback_adversarial_review_not_substitute_for_first_use]]).
|
|
1125
|
+
# So the token must sit at LINE START as the key (a leading markdown `#` is allowed β the real
|
|
1126
|
+
# corpus writes Β«## β μνΌ β μ€κ³ μ μ μ΄ β¦Β» as a heading, and P7 flipped to BLOCK when the first
|
|
1127
|
+
# version of this regex forbade it. The known-pair caught that in one run).
|
|
1128
|
+
# Prose inside a field VALUE can no longer vote.
|
|
1129
|
+
_soul_src=$(grep -E '^[[:space:]]*#*[[:space:]]*(β μνΌ|soul|μ λ μ ν¨|μ±κ³΅ ?μ μ)[[:space:]]*[:β-]' "$m" 2>/dev/null \
|
|
1130
|
+
| grep -vE '^[[:space:]]*soul-check[[:space:]]*:')
|
|
1131
|
+
n=$(printf '%s\n' "$_soul_src" | grep -cE 'β μνΌ|^[[:space:]]*soul:|μ±κ³΅ ?μ μ|μ λ μ ν¨'); n=${n:-0}
|
|
1132
|
+
if [ "$n" -eq 0 ]; then
|
|
1133
|
+
echo " β FAIL β marker carries no β μνΌ line (μ±κ³΅ μ μ / μ λ μ ν¨)."
|
|
1134
|
+
echo " CLAUDE.md Β§μκΈ° λμ‘° requires it on every marker. Measured 2026-08-21: 37 of 98"
|
|
1135
|
+
echo " post-grace markers had none β including panel-reviewed ones with every other"
|
|
1136
|
+
echo " field filled. Nothing read the field, so nothing noticed."
|
|
1137
|
+
echo " Write, before the design: μ±κ³΅ μ μ = Β«β¦Β». μ λ μ ν¨ = Β«β¦Β»."
|
|
1138
|
+
echo " π₯ This checks PRESENCE, not provenance. Β«Was it written firstΒ» stays unverifiable"
|
|
1139
|
+
echo " and unchecked β see the β μνΌ comment below. Presence is not that question."
|
|
1140
|
+
return 1
|
|
1141
|
+
fi
|
|
1142
|
+
# Non-vacuity β counted in WORDS, not characters (Korean corpus; ${#} is locale-dependent).
|
|
1143
|
+
body=$(awk '/β μνΌ|^[[:space:]]*soul:|μ±κ³΅ ?μ μ|μ λ μ ν¨/{if (!f) f=1}
|
|
1144
|
+
f && /^[[:space:]]*$/{exit}
|
|
1145
|
+
f && seen && /^[[:space:]]*[A-Za-z][A-Za-z0-9_-]*:/{exit}
|
|
1146
|
+
f{print; seen=1}' <<< "$_soul_src")
|
|
1147
|
+
words=$(printf '%s' "$body" | wc -w | tr -d ' '); words=${words:-0}
|
|
1148
|
+
# π₯ A DECLARED ABSENCE IS A VALUE, not a gap. CLAUDE.md Β§μκΈ° λμ‘° explicitly permits
|
|
1149
|
+
# Β«μμΌλ©΄ `μμ`Β», and the first version of this leg BLOCKED a bare `soul: μμ` (2 words) while
|
|
1150
|
+
# PASSING a 33-word paragraph whose content was Β«I did not write oneΒ». That punishes the honest
|
|
1151
|
+
# form and rewards padding β [[feedback_unreachable_done_when_trains_evasion]] exactly. Found by
|
|
1152
|
+
# a peer session's first real use of this gate (forge-harness-59, 2026-08-21); self-detection 0,
|
|
1153
|
+
# and 41 lanes + two cross-family panels had all passed.
|
|
1154
|
+
# Same shape as the sibling axes' `UNKNOWN`: recorded, not silent, and never free of a record.
|
|
1155
|
+
# π₯ `$body` β this leg's own local. The first version read `$soul_body`, which is the OTHER
|
|
1156
|
+
# leg's variable name; unset here, so the case never matched and the fix was inert while the
|
|
1157
|
+
# lane suite stayed green ([[feedback_unedited_parts_of_my_own_file]] Β· half-fix).
|
|
1158
|
+
_sbare=$(printf '%s' "$body" | sed -E 's/^[[:space:]]*#*[[:space:]]*(β μνΌ|soul|μ λ μ ν¨|μ±κ³΅ ?μ μ)[[:space:]]*[:β-]*//' | tr -d '[:space:].Β·]')
|
|
1159
|
+
case "$_sbare" in
|
|
1160
|
+
μμ|μμβ|none|None|NONE|n/a|N/A|-|ν΄λΉμμ)
|
|
1161
|
+
echo " β οΈ β μνΌ: μμ β declared absent. Recorded, not silent."
|
|
1162
|
+
echo " π₯ A marker that declares no success definition cannot carry"
|
|
1163
|
+
echo " soul-check: reflected(...) β there is nothing to reflect against."
|
|
1164
|
+
return 0 ;;
|
|
1165
|
+
esac
|
|
1166
|
+
if [ "$words" -lt 6 ]; then
|
|
1167
|
+
echo " β FAIL β β μνΌ line is vacuous ($words word(s)). Name the success definition AND"
|
|
1168
|
+
echo " the μ λ μ ν¨. A one-word placeholder trains the ritual this field exists to avoid."
|
|
1169
|
+
return 1
|
|
1170
|
+
fi
|
|
1171
|
+
echo " β
β μνΌ present: $words word(s)"
|
|
1172
|
+
return 0
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
validate_soul_check_leg() { # $1 = marker path
|
|
1176
|
+
# ββ β 격리 κ·ΈλΌμ΄λ© β μ¬μ μ μΈμΌλ‘ νμ₯ (2026-08-21) βββββββββββββββββββββββββ
|
|
1177
|
+
# π₯ NOT a seventh axis. Classified against `fh_three_layer_canon.md Β§1-a-2`, whose
|
|
1178
|
+
# discriminator is explicit: Β«μΆμ Β«μΌλ§λ μ λμ μΈκ°Β»λ‘ κ°λ¦¬μ§ μλλ€. «무μμ λ°μλκ°Β»λ‘
|
|
1179
|
+
# κ°λ¦°λ€.Β» β 격리 κ·ΈλΌμ΄λ© receives Β«μ μκ° μ΄ λ¬Έμ₯ + μ§κΈμ νΈλ¦¬Β» β which is, character for
|
|
1180
|
+
# character, what this leg receives (β μνΌ is μ μκ° μ΄ λ¬Έμ₯; the delta is μ§κΈμ νΈλ¦¬).
|
|
1181
|
+
# The tense differs (μ¬μ μ μΈ vs μ¬ν μ£Όμ₯) and tense is a posture, not an axis, exactly as
|
|
1182
|
+
# that section says adversarialness is (Β«μ λμ±μ μμΈμ§ μΆμ΄ μλλ€Β»).
|
|
1183
|
+
# Precedent, in the canon itself: the author attributed 5 findings to β and two independent
|
|
1184
|
+
# cross-family classifiers reclassified 3 of them β Β«κ·Έ μ
μ μΆμ΄ νμνλ κ² μλλΌ λ€λ₯Έ μΆμ΄
|
|
1185
|
+
# λμΉ κ²μ΄λ€Β». Minting β would have been that mistake a second time.
|
|
1186
|
+
# β β's γλ°λ κ²γ widens to Β«μ μκ° μ΄ λ¬Έμ₯(μ¬ν μ£Όμ₯ λ° **μ¬μ μ μΈ**)Β», and `soul-check:`
|
|
1187
|
+
# is β's own field in the same shape `standpoint:` is β's. The six axes stay six.
|
|
1188
|
+
# WHY: CLAUDE.md Β§μκΈ° λμ‘° has required an `β μνΌ` line (μ±κ³΅ μ μ / μ λ μ ν¨) on every
|
|
1189
|
+
# marker since 2026-08-09, and it is written 4/4 on the real corpus. NOTHING has ever read it
|
|
1190
|
+
# back. Measured this day with a control: `crossfamily` = 21 hits in this hook, `β μνΌ`/`soul`
|
|
1191
|
+
# = 0 lines of consuming code anywhere in templates/ or scripts/; `fh_4axis_gate.md` does not
|
|
1192
|
+
# even name the field. That is the half-externalization shape β a slot with zero consumers,
|
|
1193
|
+
# which always reports "done" because presence is doing the judging.
|
|
1194
|
+
#
|
|
1195
|
+
# π₯ The reason this hook gave for NOT checking the field answers a DIFFERENT question. Line
|
|
1196
|
+
# ~1058 declines it because Β«did you write it BEFORE designingΒ» is unverifiable from a file β
|
|
1197
|
+
# true, and correctly excluded. But Β«was it then SATISFIEDΒ» is a separate question that was
|
|
1198
|
+
# never asked, and it rode out on the first one's ticket.
|
|
1199
|
+
#
|
|
1200
|
+
# SCOPE β channel, not judgment (CLAUDE.md Β§Mechanization Boundary). This lane asserts
|
|
1201
|
+
# properties of the RECORD: single Β· closed enum Β· non-vacuous Β· not wearing another axis's
|
|
1202
|
+
# tokens Β· **and consistent with the β μνΌ line in the same file**. It never asserts the
|
|
1203
|
+
# read-back reached the right conclusion. A fabricated `reflected(...)` with a fluent reason
|
|
1204
|
+
# PASSES β that hole is Β§4-b's (cross-family reads the marker), not this lane's, and it is
|
|
1205
|
+
# named here rather than papered over.
|
|
1206
|
+
local m="$1" line val reason n soul_n soul_body
|
|
1207
|
+
n=$(grep -cE '^[[:space:]]*soul-check:' "$m" 2>/dev/null); n=${n:-0}
|
|
1208
|
+
if [ "$n" -eq 0 ]; then
|
|
1209
|
+
# π₯ A near-miss key reads as "absent" and takes the grace exit β silent, and the author
|
|
1210
|
+
# believes they recorded it. Absent is legal; MISSPELLED is not.
|
|
1211
|
+
if grep -qE '^[[:space:]]*(soul[ _-]?check[[:space:]]*:|soulcheck:|soul-checks:)' "$m" 2>/dev/null \
|
|
1212
|
+
&& ! grep -qE '^[[:space:]]*soul-check:' "$m" 2>/dev/null; then
|
|
1213
|
+
echo " β FAIL β a line looks like a soul-check key but is not exactly 'soul-check:'."
|
|
1214
|
+
grep -nE '^[[:space:]]*(soul[ _-]?check[[:space:]]*:|soulcheck:|soul-checks:)' "$m" | sed 's/^/ /'
|
|
1215
|
+
return 1
|
|
1216
|
+
fi
|
|
1217
|
+
return 0 # genuinely absent β adoption is incremental, same as thirdparty:
|
|
1218
|
+
fi
|
|
1219
|
+
if [ "$n" -gt 1 ]; then
|
|
1220
|
+
echo " β FAIL β marker carries MORE THAN ONE 'soul-check:' line."
|
|
1221
|
+
grep -nE '^[[:space:]]*soul-check:' "$m" | sed 's/^/ /'
|
|
1222
|
+
echo " Readers take the first, so an appended correction is silently shadowed."
|
|
1223
|
+
return 1
|
|
1224
|
+
fi
|
|
1225
|
+
line=$(grep -m1 -E '^[[:space:]]*soul-check:' "$m" | sed -E 's/^[[:space:]]*soul-check:[[:space:]]*//')
|
|
1226
|
+
val=$(printf '%s' "$line" | sed -E 's/\(.*$//' | sed -E 's/[[:space:]]*(β|--).*$//' | sed -E 's/[[:space:]]+$//')
|
|
1227
|
+
|
|
1228
|
+
# Cross-axis contamination β the other three axes ask different questions.
|
|
1229
|
+
case "$val" in
|
|
1230
|
+
tier1|tier1b|tier2|tier2b|tier3|panel|declined|checked|none-found|peer-review|\
|
|
1231
|
+
DEGRADED_SINGLE_FAMILY|DEGRADED_PANEL_UNUSED|DEGRADED_NO_TARGET_ACCESS|DEGRADED_NO_ACCESS)
|
|
1232
|
+
echo " β FAIL β '$val' belongs to another axis, not soul-check:."
|
|
1233
|
+
echo " standpoint = whose repo was ground truth Β· crossfamily = which model family reviewed"
|
|
1234
|
+
echo " thirdparty = was this already done outside Β· soul-check = was THIS marker's own"
|
|
1235
|
+
echo " β μνΌ (μ±κ³΅ μ μ / μ λ μ ν¨) read back against the delta."
|
|
1236
|
+
return 1 ;;
|
|
1237
|
+
esac
|
|
1238
|
+
|
|
1239
|
+
# Is there an β μνΌ line in this same marker, and does it carry content?
|
|
1240
|
+
# π₯ The soul detector must NOT read the `soul-check:` line itself. codex's alignment fix widened
|
|
1241
|
+
# the pattern and G8 flipped to PASS: `soul-check: reflected(β¦μ±κ³΅μ μβ¦)` matched as its own
|
|
1242
|
+
# evidence β self-referential FP ([[feedback_lane_runner_self_referential_fp]]). The claim can
|
|
1243
|
+
# never be its own ground. Same reason comment/prose lines are excluded: a marker that merely
|
|
1244
|
+
# SAYS μ±κ³΅ μ μ in narration has not declared one.
|
|
1245
|
+
# π₯ A DECLARATION, not a MENTION. First real use (isolated clone, 2026-08-21) broke this on
|
|
1246
|
+
# attempt #1: this very delta's marker says Β«change: λ§μ»€μ β μνΌ μ¬λ‘―μ μλΉμ²κ° 0μ΄λ κ²μβ¦Β»
|
|
1247
|
+
# in a prose FIELD VALUE, and the leg reported "β μνΌ present: 10 words" for a marker that
|
|
1248
|
+
# declares nothing. Excluding `#` and `soul-check:` was not enough β any field whose value
|
|
1249
|
+
# discusses the axis satisfies a substring test, and markers ABOUT this axis always will.
|
|
1250
|
+
# 39 lanes, codex (diff axis) and agy (claims axis) all missed it; running it once did not
|
|
1251
|
+
# ([[feedback_adversarial_review_not_substitute_for_first_use]]).
|
|
1252
|
+
# So the token must sit at LINE START as the key (a leading markdown `#` is allowed β the real
|
|
1253
|
+
# corpus writes Β«## β μνΌ β μ€κ³ μ μ μ΄ β¦Β» as a heading, and P7 flipped to BLOCK when the first
|
|
1254
|
+
# version of this regex forbade it. The known-pair caught that in one run).
|
|
1255
|
+
# Prose inside a field VALUE can no longer vote.
|
|
1256
|
+
_soul_src=$(grep -E '^[[:space:]]*#*[[:space:]]*(β μνΌ|soul|μ λ μ ν¨|μ±κ³΅ ?μ μ)[[:space:]]*[:β-]' "$m" 2>/dev/null \
|
|
1257
|
+
| grep -vE '^[[:space:]]*soul-check[[:space:]]*:')
|
|
1258
|
+
soul_n=$(printf '%s\n' "$_soul_src" | grep -cE 'β μνΌ|^[[:space:]]*soul:|μ±κ³΅ ?μ μ|μ λ μ ν¨'); soul_n=${soul_n:-0}
|
|
1259
|
+
# Body = the β μνΌ block only: stop at a blank line OR at the next ASCII field key. An earlier
|
|
1260
|
+
# draft ran the range to EOF, so a marker reading Β«β μνΌ: μμΒ» followed by any other field was
|
|
1261
|
+
# scored as having content β the fixture G9 caught it.
|
|
1262
|
+
# π₯ Counted in WORDS, not characters. `${#var}` on Korean text is bytes-or-chars depending on
|
|
1263
|
+
# locale, and this corpus is Korean β a char threshold silently changes meaning per machine
|
|
1264
|
+
# β οΈ Honest caveat, measured: `wc -w` is NOT a human word count on Korean either (Β«β μνΌ: μμ£ΌΒ»
|
|
1265
|
+
# reports 3, not 2). It is stable per-machine and monotonic, which is all a floor needs β but
|
|
1266
|
+
# every threshold here was calibrated by RUNNING it, never by reasoning about word counts.
|
|
1267
|
+
# ([[feedback_instrument_vs_target_and_budget]], λ¨μ νΌλ). Word count is locale-independent.
|
|
1268
|
+
soul_body=$(awk '/β μνΌ|^[[:space:]]*soul:|μ±κ³΅ ?μ μ|μ λ μ ν¨/{if (!f) f=1}
|
|
1269
|
+
f && /^[[:space:]]*$/{exit}
|
|
1270
|
+
f && seen && /^[[:space:]]*[A-Za-z][A-Za-z0-9_-]*:/{exit}
|
|
1271
|
+
f{print; seen=1}' "$m" 2>/dev/null \
|
|
1272
|
+
| sed -E '1s/^[[:space:]]*(β μνΌ|soul)[^[:alnum:]Β«]*//')
|
|
1273
|
+
soul_words=$(printf '%s' "$soul_body" | wc -w | tr -d ' ')
|
|
1274
|
+
soul_words=${soul_words:-0}
|
|
1275
|
+
|
|
1276
|
+
case "$val" in
|
|
1277
|
+
UNKNOWN)
|
|
1278
|
+
echo " β οΈ soul-check leg: UNKNOWN β μ λ΄€λ€. Recorded, not silent." ;;
|
|
1279
|
+
reflected|violated|DEGRADED_NOT_RUN|DEGRADED_NO_SOUL|not-applicable)
|
|
1280
|
+
case "$line" in *\(*) ;; *)
|
|
1281
|
+
echo " β FAIL β '$val' without a (<grounds>) parenthetical."
|
|
1282
|
+
echo " A read-back that names neither WHAT read it nor WHAT came back is"
|
|
1283
|
+
echo " indistinguishable from not running it."
|
|
1284
|
+
return 1 ;;
|
|
1285
|
+
esac
|
|
1286
|
+
# π₯ Measure INSIDE the parens. The old form took everything after the first `(`, so
|
|
1287
|
+
# `reflected() β <long tail>` passed on text that was never grounds (codex, diff axis).
|
|
1288
|
+
reason=$(printf '%s' "$line" | sed -E 's/^[^(]*\(//; s/\)[^)]*$//')
|
|
1289
|
+
# π₯ agy (claims axis, 2026-08-21): this leg's own comment above condemns `${#var}` on
|
|
1290
|
+
# Korean text and then used `${#reason}` twelve lines later β [[feedback_citing_a_rule_is_not_obeying_it]],
|
|
1291
|
+
# verbatim. Inherited from the sibling legs, which does not excuse it here. Word count.
|
|
1292
|
+
_rwords=$(printf '%s' "$reason" | wc -w | tr -d ' '); _rwords=${_rwords:-0}
|
|
1293
|
+
if [ "$_rwords" -lt 3 ]; then
|
|
1294
|
+
echo " β FAIL β '$val(...)' grounds are vacuous. Name what read the β μνΌ line back,"
|
|
1295
|
+
echo " against what, and what came back."
|
|
1296
|
+
return 1
|
|
1297
|
+
fi
|
|
1298
|
+
# ββ the consistency check that makes this lane non-decorative ββββββββββββββ
|
|
1299
|
+
# Two fields in ONE record, mechanically decidable. Not a conclusion.
|
|
1300
|
+
case "$val" in
|
|
1301
|
+
reflected|violated)
|
|
1302
|
+
if [ "$soul_n" -eq 0 ] || [ "$soul_words" -lt 6 ]; then
|
|
1303
|
+
echo " β FAIL β '$val' claims the β μνΌ line was read back, but this marker has"
|
|
1304
|
+
echo " no β μνΌ line with content (found: $soul_n line(s), $soul_words word(s))."
|
|
1305
|
+
echo " π₯ Reflecting against an absent success-definition is the exact"
|
|
1306
|
+
echo " half-externalization this lane exists to catch. Write β μνΌ first,"
|
|
1307
|
+
echo " or record DEGRADED_NO_SOUL(<why>)."
|
|
1308
|
+
return 1
|
|
1309
|
+
fi ;;
|
|
1310
|
+
DEGRADED_NO_SOUL)
|
|
1311
|
+
if [ "$soul_n" -gt 0 ] && [ "$soul_words" -ge 6 ]; then
|
|
1312
|
+
echo " β FAIL β 'DEGRADED_NO_SOUL' says there is nothing to reflect against, but"
|
|
1313
|
+
echo " this marker DOES carry an β μνΌ line with content ($soul_n line(s))."
|
|
1314
|
+
echo " A recorded absence contradicted by the same record is worse than silence."
|
|
1315
|
+
return 1
|
|
1316
|
+
fi ;;
|
|
1317
|
+
esac
|
|
1318
|
+
echo " β
soul-check leg: $val" ;;
|
|
1319
|
+
*)
|
|
1320
|
+
echo " β FAIL β '$val' is not a member of the soul-check: enum."
|
|
1321
|
+
echo " Closed enum: reflected(<what came back β μ΄κΈλ¨ μ¬λΆ. NOT just who reviewed:
|
|
1322
|
+
that is crossfamily's column, and duplicating it double-counts one event>) Β·"
|
|
1323
|
+
echo " violated(<which half broke β μ±κ³΅μ μ λ―Έλ¬ / μ λμν¨ μλ° β and what was done>) Β·"
|
|
1324
|
+
echo " DEGRADED_NOT_RUN(<why>) Β· DEGRADED_NO_SOUL(<why>) Β· UNKNOWN Β· not-applicable(<why>)"
|
|
1325
|
+
echo " π₯ 'violated' is a legal value ON PURPOSE. A recorded violation is worth more"
|
|
1326
|
+
echo " than a hidden one, and a lane that only admits success trains erasure."
|
|
1327
|
+
return 1 ;;
|
|
1328
|
+
esac
|
|
1329
|
+
return 0
|
|
1330
|
+
}
|
|
1331
|
+
|
|
974
1332
|
|
|
975
1333
|
|
|
976
1334
|
# ββ 4μΆ μκΈ° λμ‘° β Β«μ΄λ μΆμ λλ Έκ³ μ΄λ μΆμ μ λλ ΈλΒ» νμ κ²μ¬ ββββββββββββ
|
|
@@ -1292,7 +1650,27 @@ if [ "$GATE_MODE" = "full" ]; then
|
|
|
1292
1650
|
MARKER="$MARKER_DIR/.axes_23_passed_${BRANCH_SLUG}_${TODAY}.marker"
|
|
1293
1651
|
|
|
1294
1652
|
if [ -f "$MARKER" ]; then
|
|
1295
|
-
|
|
1653
|
+
# Wave 1-D defense leg β floor tiers only (sonnet-floor / below-floor), on ANY full-gate marker.
|
|
1654
|
+
# π₯ It was first wired inside the LOAD-BEARING block below, which made the real condition
|
|
1655
|
+
# "load-bearing AND floor tier" while the skill documented "floor tiers only" β a SKILL.md
|
|
1656
|
+
# change at sonnet-floor would have passed with no defense line. Found by cross-family review
|
|
1657
|
+
# (codex/gpt-5.5, 2026-08-20); self-detection 0. The scope the doctrine states is the scope
|
|
1658
|
+
# the hook now has.
|
|
1659
|
+
_DEFENSE_OK=1
|
|
1660
|
+
if grep -qE '^[[:space:]]*floor-status:[[:space:]]*(sonnet-floor|below-floor)' "$MARKER"; then
|
|
1661
|
+
validate_defense_leg "$MARKER" || _DEFENSE_OK=0
|
|
1662
|
+
fi
|
|
1663
|
+
# β μ¬μ μ μΈ (β μνΌ) β scope is EVERY marker (CLAUDE.md Β§μκΈ° λμ‘° mandates β μνΌ on all of them), so it
|
|
1664
|
+
# belongs HERE and not in the load-bearing block below.
|
|
1665
|
+
# π₯ It was first wired there, exactly like `validate_defense_leg` above β the real condition
|
|
1666
|
+
# would have been "load-bearing AND has a marker" while the doctrine says every marker. The
|
|
1667
|
+
# note recording that precedent is ~8 lines above this one and was in the file at the time;
|
|
1668
|
+
# the same defect was made one day later anyway ([[feedback_unedited_parts_of_my_own_file]]).
|
|
1669
|
+
# Caught by the operator asking Β«λ°°μ μλ£λμλ?Β», not by a lane and not by cross-family.
|
|
1670
|
+
_SOUL_OK=1
|
|
1671
|
+
validate_soul_present_leg "$MARKER" || _SOUL_OK=0
|
|
1672
|
+
validate_soul_check_leg "$MARKER" || _SOUL_OK=0
|
|
1673
|
+
if [ "$_DEFENSE_OK" -eq 1 ] && [ "$_SOUL_OK" -eq 1 ] && validate_marker_floor "$MARKER" && validate_marker_axes_run "$MARKER"; then
|
|
1296
1674
|
echo " β
PASS (marker confirmed + floor/axes fields valid:"
|
|
1297
1675
|
echo " .axes_23_passed_${BRANCH_SLUG}_${TODAY}.marker)"
|
|
1298
1676
|
else
|
|
@@ -1827,6 +2205,24 @@ PTR_EOF
|
|
|
1827
2205
|
fi
|
|
1828
2206
|
fi
|
|
1829
2207
|
|
|
2208
|
+
# ββ μμ£Ό μ μ
κ²μ΄νΈ (mandatory-pass β blocks) ββββββββββββββββββββββββββββββββ
|
|
2209
|
+
# μ μ¬κΈ° μλ (2026-08-20 μ€μΈ‘): κ°λμ μ μΆμ΄ μλλΌ **μ μ
** λ¬Έμ λ€.
|
|
2210
|
+
# CLAUDE.md 23,839(05-26) β 70,758(07-20 Β«11.9% κ°λΒ» μ§ν) β 144,138(08-20)
|
|
2211
|
+
# 07-20 μ΄ν 31μΌ +73,380 = μ½ 2,367 bytes/μΌ. κ°μ κΈ°κ° μ΄λΈλ μ΄μ
CUT νμ **0건**.
|
|
2212
|
+
# μ΅λ κ°λ ν보 μ (22k)μ‘°μ°¨ μ μ
9μΌμΉ β λκ°λ λ¬Έλ§ λΉκΈ°λ©΄ ꡬ쑰μ μΌλ‘ μ§λ€.
|
|
2213
|
+
# κ²μ¬νλ κ²μ **κΈ°λ‘μ μμ±**μ΄μ§ κ²°λ‘ μ΄ μλλ€(Β§Mechanization Boundary):
|
|
2214
|
+
# γμ΄ λ¬Έλ¨μ΄ μμ£Όν κ°μΉκ° μλγλ₯Ό νμ νμ§ μκ³ γμ μκ° μ νμ μ μΈνλγλ§ λ³Έλ€.
|
|
2215
|
+
# Anchor: scripts/test_residency_admission_lanes.sh (λ μΈ 10 Β· 컨νΈλ‘€ 2 Β· κ°λ 7 μ μ λλλ¦Ό 격리)
|
|
2216
|
+
RESID="$REPO_ROOT/scripts/residency_admission_check.sh"
|
|
2217
|
+
if [ -f "$RESID" ]; then
|
|
2218
|
+
echo "[Residency] μμ£Ό μ μ
κ²μ΄νΈ..."
|
|
2219
|
+
resid_out=$(bash "$RESID" "$REPO_ROOT" 2>&1); resid_rc=$?
|
|
2220
|
+
printf '%s\n' "$resid_out" | sed 's/^/ /'
|
|
2221
|
+
if [ "$resid_rc" -ne 0 ]; then FAILED=1; fi
|
|
2222
|
+
else
|
|
2223
|
+
echo " β οΈ μμ£Ό μ μ
κ²μ΄νΈ λ―Έμ€μΉ (scripts/residency_admission_check.sh) β skipped, not passed"
|
|
2224
|
+
fi
|
|
2225
|
+
|
|
1830
2226
|
# ββ Verdict βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
1831
2227
|
echo ""
|
|
1832
2228
|
echo "ββββββββββββββββββββββββββββββββββββββββββββββ"
|
|
@@ -357,10 +357,25 @@ PPRANGES
|
|
|
357
357
|
[ "${PUBLIC_SURFACE_OK:-0}" = "1" ] || exit 1
|
|
358
358
|
fi
|
|
359
359
|
if [ "${_pp_count:-0}" -gt 0 ]; then
|
|
360
|
-
|
|
360
|
+
# π₯ rc λ₯Ό Β«λΉμΒ» μΌλ‘ λκ°μ§ λ§λΌ (cross-family, 2026-08-21): 1=μ μΆ Β· 3=κ³κΈ° μ¬λ§.
|
|
361
|
+
# ν©μΉλ©΄ μλ PUBLIC_SURFACE_OK κ° **κ³κΈ° μ¬λ§κΉμ§ μΉμΈ**νλ€ β push λ λΉκ°μμ΄λ€.
|
|
362
|
+
_pp_rc=0
|
|
363
|
+
printf '%s\n' "$_pp_added" | awk '
|
|
361
364
|
/^\+\+\+ b\// { f = substr($0, 7); next }
|
|
362
365
|
/^\+/ { print f "\t" substr($0, 2) }
|
|
363
|
-
' | psa_scan_tagged
|
|
366
|
+
' | psa_scan_tagged || _pp_rc=$?
|
|
367
|
+
if [ "$_pp_rc" = "3" ]; then
|
|
368
|
+
echo ""
|
|
369
|
+
echo "ββββββββββββββββββββββββββββββββββββββββββββββ"
|
|
370
|
+
echo " β FH Pre-Publish Gate (pre-push) β INSTRUMENT DEAD, scan did NOT run"
|
|
371
|
+
echo "ββββββββββββββββββββββββββββββββββββββββββββββ"
|
|
372
|
+
echo " rc=3 = NOT SCANNED. That is not Β«no token foundΒ» β nothing was measured."
|
|
373
|
+
echo " PUBLIC_SURFACE_OK does NOT cover this: an override approves a KNOWN mention,"
|
|
374
|
+
echo " never an unmeasured surface. Fix the scanner first (run under bash; psa_load)."
|
|
375
|
+
echo "ββββββββββββββββββββββββββββββββββββββββββββββ"
|
|
376
|
+
exit 1
|
|
377
|
+
fi
|
|
378
|
+
if [ "$_pp_rc" = "0" ]; then
|
|
364
379
|
echo " β
FH Pre-Publish: no operator-private token in the TEXT added by ${_pp_count} commit(s)"
|
|
365
380
|
echo " (not covered: annotated-tag messages Β· binary blobs β named residuals)"
|
|
366
381
|
else
|