@chrono-meta/fh-gate 1.4.98 → 2.0.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.
@@ -8,10 +8,10 @@
8
8
 
9
9
  | What | Count | Notes |
10
10
  |---|---:|---|
11
- | Active skills | **33** | 29 in `fh-meta` + 4 in `fh-commons`; 3 deprecated redirect stubs not counted |
11
+ | Active skills | **40** | 35 in `fh-meta` + 5 in `fh-commons`; **0** deprecated redirect stubs currently exist, so that exclusion is a no-op today |
12
12
  | Agent definitions | **8** | `challenger`, `quench-challenger`, `fact-checker`, `hub-persona-auditor`, `persona-innovator`, `beginner`, `main-player`, `expert` |
13
- | Operating rules | **6** | `.claude/rules/*.md` — mapping, modes, sync, sister-asset, operations |
14
- | Knowledge docs | **23** | `knowledge/` — 6-axis framework, compounding loop, runtime flow, dialogue playbook |
13
+ | Operating rules | **1** | `.claude/rules/*.md` — `fh_4axis_gate.md`. The drop from 6 is **not** deletion: the others were relocated to `knowledge/shared/rules/` so they stop loading on every session, and this path now holds only the path-scoped gate |
14
+ | Knowledge docs | **57** | `knowledge/` — 6-axis framework, compounding loop, runtime flow, dialogue playbook, and the harness-core canon |
15
15
  | Plugins | **2** | `fh-meta` (meta-harness) + `fh-commons` (project-agnostic) |
16
16
  | Self-gate | **1** | 4-axis pre-commit hook (backward / adversarial / forward / record) |
17
17
 
@@ -19,12 +19,17 @@
19
19
 
20
20
  | Metric | Value |
21
21
  |---|---|
22
- | First commit → latest | **2026-05-26 → 2026-06-06** (12 days) |
23
- | Commits | **224** |
24
- | Merged PRs | **66** |
22
+ | First commit → latest | **2026-05-26 → 2026-08-15** (81 days) |
23
+ | Commits | **768** |
24
+ | Merged PRs | **372** |
25
25
 
26
- > Read honestly: this is *velocity*, not *maturity*. A 12-day-old project is early. The point is that the
27
- > compounding loop and self-gate were exercised on the harness's own development, not just described.
26
+ > ⚠️ **Counted 2026-08-15; a pace table is stale the day after it is written.** The previous version of
27
+ > this block sat at "12 days / 224 commits / 66 PRs" for two months and read as current, because nothing
28
+ > in it said when it was measured. Re-run the commands below rather than trusting the numbers above —
29
+ > and if you update them, update this date in the same edit.
30
+
31
+ > Read honestly: this is *velocity*, not *maturity*. The point is that the compounding loop and self-gate
32
+ > were exercised on the harness's own development, not just described.
28
33
 
29
34
  ## External artifacts (verifiable links)
30
35
 
@@ -106,10 +111,14 @@ rather than only synthetic ones.
106
111
  <sub>Reproduce the counts:</sub>
107
112
 
108
113
  ```bash
109
- # active skills (excludes deprecated redirect stubs)
110
- for d in plugins/*/skills/*/; do grep -qi "DEPRECATED merged\|redirect stub\|moved to" "$d/SKILL.md" || echo "$d"; done | wc -l
111
- # agents
112
- ls .claude/agents/*.md plugins/*/agents/*.md | wc -l
114
+ # active skills. NOTE: the old recipe here grepped each SKILL.md for "redirect stub"/"deprecated"
115
+ # and returned 38, because phantom-quench and hub-cc-pr-reviewerboth live merely MENTION those
116
+ # words in their prose. A body-text grep cannot tell "I am a stub" from "I detect stubs". There are
117
+ # currently zero stubs, so count the files and re-introduce an exclusion only when one exists, in
118
+ # frontmatter where it can be matched on a field rather than on a phrase.
119
+ find plugins -name SKILL.md | wc -l
120
+ # agents (there is no .claude/agents/ in this repo — that path is for field projects)
121
+ find plugins -path '*/agents/*.md' | wc -l
113
122
  # knowledge docs
114
123
  find knowledge -name '*.md' | wc -l
115
124
  # pace
@@ -354,3 +354,39 @@ the retrospective standpoint review of this section itself) is enough to mechani
354
354
  not enough to claim the standpoint axis's uplift is calibrated in the same statistical sense family
355
355
  diversity is. Record accordingly: `standpoint:` entries accumulate toward that bar, they do not
356
356
  presuppose it already cleared.
357
+
358
+ ---
359
+
360
+ ### §7-b Standpoint **acquisition** — where the third party comes from (operator decision, 2026-08-15)
361
+
362
+ §7 above says *whose ground truth the check ran against*. It never said **where that standpoint
363
+ comes from when you don't have one**. In practice the roster was whatever repos happened to be on
364
+ disk, which silently caps the axis at "harnesses we already cloned".
365
+
366
+ **Operator decision (2026-08-15)**: when a change is judged **large-and-irreversible**, propose
367
+ cluster mode (skip the prompt for an operator who already granted standing consent) → use a local
368
+ repo if one fits → **if none does, search (GitHub etc.), clone, and run the standpoint review
369
+ against the clone** → produce the result. The clone step is covered by the same standing setup
370
+ consent as dispatch; it is not a separate ask.
371
+
372
+ **Selection criterion — not any repo is a standpoint.** The 2026-08-15 run measured this: the
373
+ third-party review that paid was against a repo carrying its **own written discipline**
374
+ (`CLAUDE.md` + `ETHOS.md` with explicit anti-patterns and a "search before building" rule). A repo
375
+ with code but no articulated discipline yields a code dump, not a standpoint — there is nothing to
376
+ judge *from*. Require an articulated canon (rules/ethos/contributing with normative statements)
377
+ before spending a clone on it.
378
+
379
+ 🟥 **Read, never execute.** A standpoint review **reads** the target's canon and code. Cloning an
380
+ unfamiliar repo and *running* it is a different risk class and is not part of this protocol —
381
+ the 2026-08-15 runs (mate, gstack) were read-only, and that is the shape that generalizes.
382
+ Residency applies unchanged: nothing from a company-origin surface is sent outward to obtain a
383
+ standpoint.
384
+
385
+ **What the acquisition step does NOT settle** (named, not deferred):
386
+ - **Uplift is unmeasured for acquired standpoints.** The 2026-08-15 evidence used repos already on
387
+ disk. Whether a *newly cloned* repo yields comparable findings is **untested** — the selection
388
+ criterion above is reasoned from one case, not calibrated.
389
+ - **Cost is real and the yield is thin.** Blind external classification of that run put the
390
+ third-party axis's *exclusive* yield at **2 of 15** findings. Both were boundary-crossing
391
+ (a rule the other project had already abandoned; another repo importing the changed file) —
392
+ which is why it earns its place on large-and-irreversible surfaces and nowhere else.
@@ -1997,3 +1997,24 @@
1997
1997
  ★**The phantom-quench audit flagged a claim PHANTOM by reading a stale illustrative comment as live ground truth** — the checker's own header names an old example subject (directional_diff_gate.sh) instead of the real live-computed one (relay_channel.sh). Re-derived ground truth by RUNNING the checker's own Python detection logic directly rather than trusting either the audit's reading or the comment: confirmed the diff's own claim was TRUE, but the investigation surfaced a genuinely separate, real bug — lane_runner_check.sh's self-test detector scans a subject's OWN file among its 'runners' search and misreads directional_diff_gate.sh's usage-example comment as a real caller (self-referential false positive), so that subject silently reads as wired when nothing actually calls it. Recorded, not fixed this session (different file/job) — tracks/_meta/fh_signal_2026-08-15_selfcheck-wiring.md.
1998
1998
  ⚠️ **A same-session self-correction on the codex dispatch itself**: an operator-preferred lighter codex tier (gpt-5.6-luna) was believed to have silently fallen back to a bare 'gpt-5' based on a self-report identity probe ('what model are you'). The operator flagged this as surprising given the CLI's own model picker showed it as a valid current tier. A live control (pinning a clearly-different, smaller model and repeating the same probe) got the identical 'gpt-5' self-report, while the tool's own `model:` banner line was correct every time — proving the self-report probe itself is not a discriminating identity signal for this CLI, not that the pin failed. The R2 dispatch had already redirected to gpt-5.5-xhigh before this was caught, so the cross-family result stands (a clean pass on a stronger model is not weakened by this), but the diagnosis that motivated the redirect was wrong and is corrected in the Axis-2 marker.
1999
1999
  🟥 **Tally discrepancy, named not forced**: the mechanical SubagentStop tally counted 16 dispatches machine-wide for 2026-08-15; this entry accounts for 3 substantive + 3 diagnostic = 6. The remaining ~10 are unattributed — a live peer session shared this exact checkout for most of the day (confirmed via ListAgents/SendMessage during this session's own branch-claim conflict) and very likely contributed some of them, but the tally has no per-spawn attribution (same structural gap this log already named on 2026-08-13), so the split is not reconstructable after the fact. Recorded as unknown rather than estimated to fit."
2000
+
2001
+ - date: 2026-08-15
2002
+ session: fh-parallel-59b59c27 (Opus 5 1M, bg, A축 lane_runner 자기참조 오탐 + directional_diff_gate 배선 → README 3층 정본 반영)
2003
+ agents_summary: "4 dispatches: codex gpt-5.5×1 (cross-family adversarial on the working-tree diff, live tool execution) · fh-meta:challenger×1 (Axis 2 adversarial, isolated, static-trace only — Bash not in its tool set) · general-purpose×1 (Axis 3 phantom-quench grounding audit over the same diff) · Explore×1 (README.md staleness sweep vs measured repo reality)."
2004
+ dispatch_count: 4
2005
+ outcome: accepted
2006
+ evidence: "codex R1 → 2A+1B, all source-grounded and closed: `calibration passed (0 pairs)` certifies a suite whose lanes were all deleted (reproduced by neutralizing all 23 `t` lanes → rc=0 + PASS); a usage banner satisfied the substring match; no timeout while both neighbouring blocks had one. fh-meta:challenger R2 → 4A+6B, the decisive one falsifying this delta's own evidence: L14/L15 both put the self-reference on a `#` line, so comment-stripping alone satisfied them and the self-exclusion guard could be reverted with 16/16 still green — verified by executing the revert, not by reading. Closed with lane L16 (non-comment self-reference); guard present → 17/17, removed → exactly L16 red. Grounding audit → 31 claims, 0 phantoms, CONDITIONAL_PASS, 3 B closed (chief among them a factually wrong `$_LANE_TO` attribution that would have told the next editor a `set -u` dependency was safe to delete). Explore sweep → README roster enumerates 37 of 40 skills and 4 of 8 agents, model-tier list pinned to an older generation and naming a tier the source artifact deliberately anonymizes, and docs/OUTPUT_EVIDENCE.md drifted hard (skills 33 vs 40, rules 6 vs 1, knowledge 23 vs 57, '12-day-old project' vs 81 days, commits 224 vs 767)."
2007
+ notes: "★**Self-caught 0 of 7 A-grade.** Two different reviewer engines produced DISJOINT A-grade sets — codex found the verdict-predicate family, the challenger found the unanchored-guard family — which is the concrete argument for two legs over one leg twice, on the same diff, same day.
2008
+ ★**The challenger had no Bash** and said so plainly rather than implying execution ('아래는 전부 정적 추적이고, 실행 확인은 0건'). Its decisive finding was still correct, and the governor executed the revert to confirm it — sidecar finding as evidence candidate, verdict closed by a mechanical anchor.
2009
+ 🟥 **Self-inflicted, self-caught**: the Explore sweep was dispatched and README.md was then edited while it ran — `[[feedback_audit_target_must_be_frozen]]` reproduced. The agent itself flagged the file changing under it (494 → 572 lines). Its verdicts on the edited region were discarded; only the counts/links/translation-drift findings were used, and the translation port was deferred until the English file is committed and frozen."
2010
+
2011
+ - date: 2026-08-16
2012
+ session: fh-parallel-59b59c27 (Opus 5 1M, bg — v1.4.99 재출하 축, 08-15 밤에서 날짜 넘김)
2013
+ agents_summary: "6 dispatches: general-purpose×1 (Pre-Publish 코드 보안 패스, 출하 델타의 실행 표면) · codex gpt-5.5×3 (cross-family R2/R3/R4 — 각각 직전 수리를 과녁으로, R4 는 앞 라운드가 닫은 것을 명시해 재보고를 막음) · fh-meta:persona-innovator×1 (운영자 지시 자율주행, Mode F) · fh-meta:beginner×1 (README 새 절 콜드리드, 08-15 분)."
2014
+ dispatch_count: 6
2015
+ outcome: accepted
2016
+ evidence: "보안 패스가 **BLOCK** 을 냈고 그게 옳았다 — `fh_node_check.sh` 의 consent 게이트가 파일 전체 verdict + raw grep 을 클래스 조인 대신 써서, `revoked` 로 적힌 클래스가 소비자 머신에서 자동 fast-forward 를 통과했다(거버너가 컨트롤 동반 재현 후 수리). R2 는 그 수리에서 4건을 냈고 **전부 새 게이트의 ON 스위치가 조용히 꺼지는 형태**(공백만 이름·argv[1] 한정 파싱·미지 옵션이 경로로·요약이 판정과 모순). R3 는 2건, 그중 A급은 `tr -d` 가 트림이 아니라 squeeze 라 **다른 이름이 실제 클래스의 판정을 받는** 신원 붕괴 — 게이트가 막으려던 그 일을 게이트가 하고 있었다. R4 는 **새 S/A 0**(수렴), 남은 B 1건은 근거와 함께 소스에 명시 잔여. innovator 는 7건이 **하나의 관계**(proxy 가 target 보다 약함)임을 짚고 **그 이름이 사흘 전 메모리에 이미 있다**(`feedback_instrument_vs_target_and_budget`)는 것, 그리고 외부에서 가져올 것은 도구가 아니라 `partially rotten` 이라는 **상태값**이라는 것을 냈다."
2017
+ notes: "★**자력 적발 0 / 7건.** 검출기가 호출부 0을 10/10으로 셈 · 새 가드에 앵커 없음 · consent 게이트가 클래스를 안 봄 · 그 레인이 두 번 장식 · ON 스위치 4건 · 「바운드」가 아무것도 안 묶음 · 프로브가 실행조차 안 됐는데 초록. 리뷰어·컨트롤·되돌림·CI 가 잡았다.
2018
+ ★**라운드를 거듭할수록 과녁을 직전 수리로 옮긴 것이 값을 냈다** — R2·R3 의 A급은 원래 결함이 아니라 **그 결함을 고치려고 내가 쓴 코드**에 있었다. R4 에 「앞 라운드가 닫은 것」을 명시해 준 것도 효과가 있었다(재보고 0, 새 축에서만 1건).
2019
+ ⚠️ **innovator 는 자기 근거의 한계를 스스로 붙였고 그걸 지우지 않았다** — 7건을 재검증하지 못했고(편집 금지+Bash 없음) task card 진술을 그대로 받았다고 명시. 제안 1의 수율은 미측정이라고 못박았고, 손검증 불가한 EMSE 수치는 **인용을 거부**했다. 사이드카가 자기 표본 한계를 먼저 선언한 사례로 남긴다.
2020
+ 🟥 **거버너 쪽 실책 2건**: ⓐ staleness 감사기를 띄워 놓고 그 사이 대상 파일을 편집(`feedback_audit_target_must_be_frozen` 재생산, 자력 적발) ⓑ 되돌림 프로브의 적용확인 단언이 인용부호에서 깨져 **프로브가 실행되지 않았는데 스위트는 초록**이었다 — 단언이 없었으면 「되돌려도 초록」을 「앵커 살아있음」으로 오독했을 것이다."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrono-meta/fh-gate",
3
- "version": "1.4.98",
3
+ "version": "2.0.0",
4
4
  "description": "FH runtime adapters — run FH governance, skills, and agents via Claude or Codex with machine-parseable gates.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -27,7 +27,7 @@
27
27
  "prepare": "chmod +x bin/fh-gate.js bin/fh-run.js bin/fh-goal.js bin/fh-codex-doctor.js scripts/fh-gate.sh scripts/fh-run.sh scripts/fh-goal.sh",
28
28
  "postinstall": "node scripts/postinstall_notice.js",
29
29
  "test": "bash scripts/selfcheck.sh",
30
- "prepublishOnly": "bash scripts/version_lockstep_check.sh && bash scripts/selfcheck.sh && bash scripts/package_coverage_check.sh --vs-tarball && bash scripts/public_surface_scan_files.sh",
30
+ "prepublishOnly": "bash scripts/publish_freshness_check.sh && bash scripts/version_lockstep_check.sh && bash scripts/selfcheck.sh && bash scripts/package_coverage_check.sh --vs-tarball && bash scripts/public_surface_scan_files.sh",
31
31
  "release": "bash scripts/public_surface_scan_files.sh && npm publish"
32
32
  },
33
33
  "engines": {
@@ -72,6 +72,7 @@
72
72
  "scripts/test_lane_runner_lanes.sh",
73
73
  "scripts/test_version_lockstep_lanes.sh",
74
74
  "scripts/package_coverage_check.sh",
75
+ "scripts/publish_freshness_check.sh",
75
76
  "scripts/lane_runner_check.sh",
76
77
  "scripts/test_package_coverage_lanes.sh",
77
78
  "scripts/test_fh_gate_regressions.sh",
@@ -92,6 +93,7 @@
92
93
  "templates/.claude/rules/session.md",
93
94
  "scripts/below_floor_scan.sh",
94
95
  "scripts/capability_registry_check.sh",
96
+ "scripts/capability_effect_probe.sh",
95
97
  "scripts/chamber_run.sh",
96
98
  "scripts/fh_env_delta_scan.sh",
97
99
  "scripts/substrate_jump_detector.sh",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fh-commons",
3
- "version": "1.4.98",
3
+ "version": "2.0.0",
4
4
  "engines": {
5
5
  "claudeCode": ">=1.0.0"
6
6
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fh-meta",
3
- "version": "1.4.98",
3
+ "version": "2.0.0",
4
4
  "engines": {
5
5
  "claudeCode": ">=1.0.0"
6
6
  },
@@ -10,6 +10,135 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
10
10
 
11
11
  ## Plugin Level
12
12
 
13
+ ### [2.0.0] — 2026-08-16
14
+
15
+ **BREAKING — M6 로 인해 종전 통과하던 capability 등록이 거부될 수 있다. 그리고 그 M6 는 1.4.99 에서 출하되지 않았다.**
16
+
17
+ - 🔴 **BREAKING: `capability_registry_check.sh` 에 M6(선언 진위 관측)가 실제로 도달한다.**
18
+ M1–M5 를 전부 통과하던 capfile 이 `writes:` 선언과 실제 부작용이 다르면 이제 **REJECTED** 된다.
19
+ major 인 이유는 이것 하나다 — 소비자의 초록이 빨강이 될 수 있다. `^1.4.x` 범위가 이 변경을 자동
20
+ 수용하지 않도록 minor 가 아니라 major 로 낸다. 🟥 **M6 를 opt-in 으로 낮추는 선택지는 거부했다**:
21
+ 비가역 표면(등록 → 이후 자동 호출)의 fail-closed 축을 끌 수 있게 만들면 그건 floor 가 아니다.
22
+ - 🔴 **fix: `scripts/capability_effect_probe.sh` 가 `files[]` 에 없어 출하되지 않았다.**
23
+ M6 를 배선한 커밋(`a48e644`)이 그 M6 가 호출하는 프로브를 출하 목록에 넣지 않았다. 실측 결과
24
+ 1.4.99 tarball 의 `capability_registry_check.sh` 에는 **M6 가 0회** 등장한다 — 소비자는 깨진 M6 가
25
+ 아니라 **M6 자체를 못 받았다**(출하본이 main 보다 뒤처져 있었다). 고치지 않고 재출하했다면 그때
26
+ 프로브 부재 → fail-closed → **모든 등록 거부**가 됐다.
27
+ - **feat: 형제 의존 출하 검사** — `test_capability_entrypoint_shipping.sh` 가 세 번째 방향을 얻었다.
28
+ 출하되는 셸 스크립트가 `${0%/*}/x.sh` 형태로 부르는 **형제 파일**도 `files[]` 에 있어야 한다.
29
+ 기존 두 방향은 `*_capability.sh` **진입점 규약**에만 걸려 있어 이번 결함에 구조적으로 눈이 멀었고,
30
+ `package_coverage_check.sh` 는 참조 정규식이 `scripts/` 로 **시작하는 리터럴**만 봐서 런타임에
31
+ 조립되는 경로를 볼 수 없다. 이 클래스의 4번째 발생이라 앞단에 기계로 세웠다.
32
+ - **fix: script-dir 해석이 symlink 를 통과한다** — `${0%/*}` 는 심링크 경유 호출에서 링크의
33
+ 디렉토리를 가리켜 M6 가 «프로브 부재» 로 fail-closed 했다(있는데 없다고 말한다). `BASH_SOURCE` +
34
+ symlink 해소로 교체.
35
+ - **feat: publish 신선도 게이트** — `prepublishOnly` 가 «출하 트리가 커밋된 main 인가» 를 본다.
36
+ 이번 사고와 2026-08-13 의 «남의 미커밋 초안 출하» 가 같은 표면의 두 발생이다.
37
+ - **fix: `sync-to-be.sh` 락 경합 경로가 rc=64 로 죽었다** — `log()` 가 호출부보다 아래에 정의돼
38
+ macOS `/usr/bin/log` 로 떨어졌고, `set -euo pipefail` 이 의도한 `exit 0` 도착 전에 스크립트를
39
+ 죽였다. 경합 경로는 **경합할 때만** 실행되므로 병렬 세션 둘이 붙기 전까지 출하된 채로 있었다.
40
+
41
+ ### [1.4.99] — 2026-08-15
42
+
43
+ **fix: 검출기가 «호출부 0» 을 «10/10 초록» 으로 보고하고 있었다 — 그리고 이 릴리스는 #388 의 첫 출하이기도 하다**
44
+
45
+ - 🔴 **`lane_runner_check.sh` 가 거짓 초록을 생산했다.** 이 도구의 존재 이유가 «호출자 없는 레인
46
+ 찾기» 인데, `has_selftest_runner()` 가 subject **자기 파일**을 `runners` 에 포함하고
47
+ `selftest_dispatched()` 가 주석줄을 안 걸렀다. 그래서 `directional_diff_gate.sh` 가 **자기 usage
48
+ 주석**(`# bash scripts/directional_diff_gate.sh --self-test`)을 호출부로 오독당해, 실제 호출부가
49
+ **0개**인데 리포트는 `self-test: 10/10 wired` 를 찍었다. 같은 파일의 `has_runner()` /
50
+ `runner_dispatches()` 는 그 두 가드를 **이미** 갖고 있었다 — 몰라서가 아니라 **한 술어에만
51
+ 적용**돼 있었다. 검출기 수리와 그 부채의 실제 배선을 **한 커밋에** 실었다(따로 실으면 한쪽은
52
+ «새로 보이는데 여전히 안 도는 부채», 다른 쪽은 «거짓으로 평평한 카운트» 가 된다).
53
+ 🟥 **참값은 6일 전부터 기록돼 있었다** — 2026-08-09 핸드오프가 컨트롤까지 붙여 «호출자 0» 을 재고
54
+ *"오신뢰를 하나 더한 상태"* 라고 진단해 뒀는데, 그 6일 동안 검출기는 10/10 을 보고했고 이 건이
55
+ 닫힌 경로는 그 문서가 아니라 **검출기 자신의 오탐을 쫓던 세션**이다.
56
+ - **`selfcheck.sh` 의 판정은 종료코드만으로 하지 않는다.** 새 `directional_diff_gate --self-test`
57
+ 블록은 **종결 판정줄을 통째로**(`^✅ calibration passed (N pairs)$`) 요구하고 **N > 0** 을 강제한다.
58
+ cross-family 라운드가 준 구체 입력: 그 subject 는 *실패* 수가 0이면 **레인 수와 무관하게** 통과줄을
59
+ 찍으므로, 레인을 전부 지우면 `calibration passed (0 pairs)` + `exit 0` 이 나온다 — **삭제가 조용한
60
+ PASS 로 렌더**된다. 부분문자열 매치였다면 usage 배너도 게이트를 만족시켰다. `rc=124`(timeout kill)는
61
+ «레인 실패» 가 아니라 **HARNESS ERROR** 로 라우팅한다.
62
+ - **회귀 앵커 7개 신설** — `test_lane_runner_lanes.sh` 14→17, `test_selfcheck_state_lanes.sh` 44→48.
63
+ L16 이 특히 load-bearing 하다: 같은 세션의 적대검증이 **처음 넣은 자기제외 가드에 앵커가 없음**을
64
+ 지목했고(레인 둘 다 픽스처의 자기참조를 `#` 주석으로 써서, 가드를 되돌려도 16/16 초록이었다),
65
+ 비-주석 픽스처를 넣고서야 «가드 있으면 17/17 · 없으면 정확히 L16 만 적색» 이 됐다.
66
+ - **문서: README 가 정본 이전 프레이밍을 싣고 있었다.** `① Assemble / ② Forge / ③ Sidecar /
67
+ ④ Self-evolving loop` 는 2026-08-09 정식화 **이전** 판으로, 5대 정체성과 절반 겹치면서 **⑤ 증폭자와
68
+ ② 인큐베이터가 통째로 빠져** 있었다. 3층 정본(5정체성 → 4엔진 → 3단 공정 + 4축 검증 + standpoint)
69
+ 으로 교체하고 **번역 3종(ko/zh/ja)까지 같은 릴리스에서** 맞췄다. 등급표는 복제하지 않는다 —
70
+ `ship_readiness_gate.md` 가 유일 정본이고, 이 페이지는 4개 언어라 복제하면 사본이 4개가 된다.
71
+ - **수치 청소(전부 재측정)**: 로스터가 스킬 37/40 · 에이전트 4/8 만 열거하던 것 → 40/40 · 8/8 ·
72
+ `docs/OUTPUT_EVIDENCE.md` 스킬 33→40 · 규칙 6→1(삭제가 아니라 `knowledge/shared/rules/` 로 이전) ·
73
+ 지식 23→57 · 「12일차 · 커밋 224 · PR 66」→ **81일 · 768 · 372**, 그리고 **측정 날짜를 박았다** —
74
+ 이 표가 두 달간 현재처럼 읽힌 원인은 숫자가 아니라 «언제 쟀는지 안 적힌 것» 이었다.
75
+ 그 문서의 재현 recipe 는 SKILL.md 본문에서 "deprecated"/"redirect stub" 을 grep 해 **살아있는 스킬
76
+ 2개**(`phantom-quench` · `hub-cc-pr-reviewer`)를 stub 으로 오분류하고 38 을 뱉고 있었다 — 본문 grep 은
77
+ «내가 stub 이다» 와 «나는 stub 을 검출한다» 를 구분하지 못한다. 교체했다.
78
+ - 🟥 **이 릴리스는 #388 의 첫 출하이기도 하다.** `v1.4.98` 태그가 `#387` 에서 잘렸고 `#388`
79
+ (consent-gated auto fast-forward)은 그 뒤에 머지돼 **레지스트리에 한 번도 나간 적이 없다.**
80
+ 실측 대조: 출하된 1.4.98 tarball 의 `scripts/fh_node_check.sh` 는 **243줄**로 consent 게이트 arm 이
81
+ 통째로 없고, main 은 **282줄**이다. 1.4.98 항목이 «버전을 안 올려 런타임에 도달 못 한 규칙» 을
82
+ 기록했는데 **같은 얼굴이 태그 절단 지점에서 재발**했다. 다만 이번엔 계기가 잡았다 —
83
+ `session_close_check.sh` ④-b 가 «마지막 태그 이후 출하자산 변경» 을 발화했고, 이번엔 그 출력을
84
+ 표시필터로 자르지 않았다.
85
+ - **공개면 정리**: `README.zh.md` 가 마지막 두 줄에 `</content>` `</invoke>` 도구 잔재 태그를
86
+ **공개 상태로** 싣고 있었다(이전 커밋부터). 제거.
87
+
88
+ 🟥 **출하 직전 보안 패스가 이 릴리스를 한 번 막았고, 그래서 이 항목이 더 있다.** `npm publish` 전
89
+ 게이트로 돌린 코드 보안 리뷰가 **BLOCK** 을 냈다 — `scripts/fh_node_check.sh` 의 consent 게이트가
90
+ **클래스를 조인하지 않았다.** 두 조건이 각각 이랬다: ① `consent_registry_check.sh` 를 인자 없이 돌린
91
+ rc=0 — 그건 «레지스트리와 grant 들이 well-formed 하고 floor join 이 성립» 이라는 **파일 전체 속성**이라
92
+ **무관한 클래스 하나만 유효하게 승인돼 있어도 0** 이 난다 ② UAP **파일 전체**에 대한 raw grep 이라
93
+ `granted` 와 `revoked` 를 구분하지 못하고, 히트가 기계 판독 영역인 frontmatter 안인지 산문 문단인지도
94
+ 보지 않는다. **컨트롤 동반 재현**: 무관한 클래스 1건 유효 승인 + 대상 클래스는 산문에
95
+ ` repo-freshness-autopull: 안 쓰기로 했다` 한 줄 → 두 조건 통과 → **자동 fast-forward 실행**, 그리고
96
+ 배너는 운영자에게 *"standing consent"* 가 있다고 말한다. 망가진 쪽이 하필 **철회 경로**였고, 그건
97
+ `absent ≠ granted` floor 가 지키려던 바로 그 자리다.
98
+ - **수리**: `consent_registry_check.sh --require-class NAME` 신설 — 그 클래스가 **활성·등록·미만료
99
+ grant 로 전 항목을 통과했을 때만 0**, 아니면 3(=계속 물어라). 파일 전체 위반은 여전히 1 이 이긴다
100
+ («판정 불가 == 불허»는 그 스크립트 자신의 규칙이다). 조인 대상은 `validated`(per-grant 검사 **전에**
101
+ 증가하는 카운터)가 아니라 **모든 검사를 통과한 이름 집합**이다. 플래그를 안 주면 동작은 종전과 동일.
102
+ - **없던 컨트롤을 채웠다** — `lane10-f WRONG-CLASS`. 기존 거부 arm 셋(c/d/e)은 전부 **파일 전체 판정을
103
+ 깨는 방식**으로 거부를 만들어서(grant 블록 삭제 · 리스 과거화 · 발산), **클래스 조인 축을 하나도
104
+ 건드리지 않았다.** 새 arm 은 «다른 클래스는 유효 승인 + 이 클래스는 산문에만» 상태에서 거부되는지를
105
+ 보고, **동시에 「그 순간 파일 전체 검사가 여전히 0인지」를 같이 단언**한다 — 그 두 번째 단언이 없으면
106
+ lane10-c 와 같은 이유로 통과해 축이 또 안 돌아간다.
107
+ 🟥 그 레인도 **처음엔 장식이었다**: 되돌림 프로브를 돌리니 수리를 되돌려도 초록이었다(앞 arm 이 픽스처를
108
+ 발산 상태로 남겨 consent 와 무관하게 ff 가 거부됐다). 전제조건을 리셋·단언하도록 고쳤고, 그 리셋이
109
+ **tracked UAP 편집을 되돌린다**는 두 번째 함정도 순서를 뒤집어 닫았다. 최종: 수리 있으면 24/24,
110
+ 되돌리면 **정확히 lane10-f 만** 적색.
111
+ - **그 수리 자체도 cross-family 가 4건 깎았다** — 전부 **fail-open** 이었다: ⓐ `--require-class ' '`
112
+ (공백만)이 bash 의 `-z` 를 통과한 뒤 python 이 strip 해서 빈 문자열이 되어 **좁히기가 조용히 꺼졌다**
113
+ ⓑ 플래그를 경로 **뒤에** 쓰면 무시되고 옛 파일 전체 판정이 나왔다 ⓒ 미지 옵션이 경로로 삼켜졌다
114
+ ⓓ 클래스 거부인데 요약줄은 `PASS` 를 찍어 **판정과 모순**했다(이 파일 자신이 「요약이 판정과 모순되면
115
+ 거짓 초록」이라고 적어 둔 규칙). 넷 다 fail-closed 로 닫고 레인 10개를 붙였다(88 → 98).
116
+ - **라운드 3 이 또 두 건을 깎았고 하나는 내가 만든 A급이었다** — 첫 수리가 `tr -d '[:space:]'` 로
117
+ 이름의 **모든 공백을 삭제**해서 `--require-class 'repo-freshness-auto pull'` 이 실제 클래스로 접혀
118
+ **다른 이름을 물었는데 다른 클래스의 판정을 받았다**(신원 붕괴). 지금은 **트림만** 하고 내부 공백이
119
+ 남으면 **고쳐주지 않고 거부**한다 — 신원을 조용히 수선하는 것이 바로 잘못된 클래스가 승인되는 경로다.
120
+ 같은 라운드가 인자 재조립도 깎았다: 개행 구분 문자열 방식이 **argv 보존이 아니라** 개행 포함 경로를
121
+ 쪼개고 빈 위치인자를 드롭해 **UAP 를 레지스트리 자리로 승격**시켰다 → bash 배열로 교체. 레인 +4(98 → 102).
122
+ - **라운드 4 = 새 S/A 0** (수렴). 남은 B 1건은 **명시 잔여로 소스에 적었다**: 영-폭 결합자 같은 보이지
123
+ 않는 문자가 클래스 이름으로 허용된다. 같은 라운드 컨트롤이 **거짓 조인은 없음**을 보였고(`ok` vs
124
+ `o<ZWJ>k` → 3, NFC/NFD 불일치 → 3), 악용에는 레지스트리와 UAP **둘 다** 쓰기 권한이 필요한데 그건
125
+ 동의의 원천 자체라 권한 상승이 아니다. 올바른 수리는 NFC 정규화 + format/control 문자 거부이지
126
+ 비-ASCII 일괄 거부가 아니다(이 하네스는 언어 중립이고 한글 클래스명은 정당하다) — 릴리스 중에 넣을
127
+ 줄이 아니라 자기 known-pair 를 갖는 설계 결정이다.
128
+ - **머지 시간 바운드는 넣었다가 뺐다** — cross-family 실측이 그 워치독이 **git 을 안 묶는다**를 보였다
129
+ (`perl alarm 2` 로 감싼 ff 머지가 느린 smudge 필터 앞에서 **7.9초 걸리고 rc=0**). 안 묶는 장치를
130
+ 「바운드」라는 이름으로 남기면 이 릴리스가 고치려는 거짓 초록을 내가 다시 만드는 것이라 제거했다.
131
+ 🟥 **파급이 더 있다**: 같은 워치독이 그 위 **fetch** 도 감싸고 있고 그건 이 변경 이전부터다 —
132
+ 즉 fetch 가 실제로 묶이는지는 이제 **가정이 아니라 UNVERIFIED** 다. 소스에 잔여로 적었다.
133
+ - **머지에 자기 예산을 줬다**(`FH_NODE_GIT_MERGE_DEADLINE`, 기본 5s). fetch 는 워치독이 있는데 머지는
134
+ 없어서, fetch 가 예산 8s 를 다 쓰면 10s 훅 예산 안에서 큰 fast-forward 가 워킹트리 갱신 도중 죽을 수
135
+ 있었다. **실소요는 안 쟀다 — 바운드지 초과했다는 주장이 아니다.**
136
+
137
+ **남은 잔여(명시)**: 주석 필터는 줄-접두 전용이라 heredoc/문자열 안의 디스패치는 여전히 계수된다 ·
138
+ subject **발견** 쪽은 아직 주석을 읽는다(«언급 ≠ 선언» 이 runner 쪽에만 강제된다) ·
139
+ `selfcheck.sh` 의 인접 두 루프는 `rc=124` 를 라우팅하지 않는다(하나는 무음 실패, 다른 하나는
140
+ «dispatcher missing?» 이라는 확신에 찬 오진) — Added-Scope Gate 로 분리해 신호로 이월.
141
+
13
142
  ### [1.4.98] — 2026-08-15
14
143
 
15
144
  **fix: 1.4.97 이후 쌓인 출하면 36파일을 실제로 배포한다 — 특히 «있는데 실행에 도달 못 하던» 검증 축 하나**