@chrono-meta/fh-gate 1.4.91 → 1.4.92
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-plugin/marketplace.json +2 -2
- package/AGENTS.md +20 -0
- package/knowledge/shared/harness-core/fh_three_layer_canon.md +18 -4
- package/knowledge/shared/learnings/subagent_invocations_log.yaml +16 -0
- package/knowledge/shared/rules/multi_session_close_protocol.md +31 -0
- package/package.json +1 -1
- package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
- package/plugins/fh-meta/skills/frontier-digest/SKILL_detail.md +13 -0
- package/scripts/branch_claim.sh +53 -1
- package/scripts/test_branch_claim_lanes.sh +62 -1
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "fh-meta",
|
|
14
|
-
"version": "1.4.
|
|
14
|
+
"version": "1.4.92",
|
|
15
15
|
"description": "Hub meta-operations toolkit — 35 skills + 7 agents. New in 1.4.53: `fh-codex-doctor` (npm bin) — Codex adapter drift scanner; reads the documented M1/M2/M3 skill tier map + skill/agent source and reports codex-native/adapter-required/claude-native/unclassified per unit, wired into `npm test`/`prepublishOnly` (fail-closed on unclassified Claude-native primitives). New in 1.4.49: steel-quench gains Step 0.6 Verdict-Invariance Probe (groundedness axis — a load-bearing judged gate's verdict must track behavior, not rubric phrasing; measured flip-count over cross-family paraphrases; arXiv:2605.06161 Policy Invariance anchor); multi_model_sidecar_strategy §Vendor-native harness (a model is strongest in its own vendor CLI — Claude/CC, GPT/codex, Gemini/Antigravity; a universal router degrades all of them, so it stays an autocomplete/QA sidecar, never orchestration); predelete_check.sh fail-closed rewrite; memory-hygiene A-TMA anchor. New in 1.4.48: phantom-quench + steel-quench gain external frontier anchors (arXiv:2607.02052 package-hallucination; arXiv:2607.02057 prompt-coverage-adequacy); README model-flat claim reframed from a per-release point-curve to structural invariants (operation flattens across tiers; depth tier-order fixed within a generation). New in 1.4.47: onboarding step ① surfaces the Mode D companion-store session-start load in the auto-read salience anchor (previously only in the local binding + rules, so a greeting could skip the load). New in 1.4.46: context-doctor command-output axis (route to rtk/proxy for verbose CLI stdout, complementing .claudeignore; risk-gated to token-scarce envs). New in 1.4.41: context-doctor 2026 trigger vocab (context engineering/rot/collapse) + phantom-citation hardening; hub measurement-integrity-checklist (cross-model measurement pre-flight: display-name pin/reps≥3/discriminating probe). New in 1.4.40: install-wizard queryable-wiki scaffold (INDEX + session-start read + R/W/C ingest). New in 1.4.39: auto-decorrelation (cross-family verifier sidecar recruitment) + video-ingest (capability-routed video ingestion). New in 1.4.x: verify-axis check-class taxonomy (mandatory-pass/measured/judged), no-reinvention Tier-0 inventory, 7-class failure taxonomy, Destructive-Op Gate, Wave-T (Temper), tier-floor governance, Mode D Model Notice, FC consent lane, default-Sonnet guidance. New in 1.3.0: public-surface-audit, field-harvest Mode B auto-trigger, 4-axis gate scope ext. Validated cross-CLI: Claude Code, Codex, Gemini.",
|
|
16
16
|
"source": "./plugins/fh-meta"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"name": "fh-commons",
|
|
20
|
-
"version": "1.4.
|
|
20
|
+
"version": "1.4.92",
|
|
21
21
|
"description": "Project-agnostic utility skills — 4 skills (convergence-loop · deliberation · mcp-circuit-breaker · token-budget-gate) + 1 agent (quench-challenger). Domain-independent utilities transplantable into any project.",
|
|
22
22
|
"source": "./plugins/fh-commons"
|
|
23
23
|
}
|
package/AGENTS.md
CHANGED
|
@@ -55,6 +55,9 @@ Machine-readable mirror: `.claude/registry/agent_cards.json`.
|
|
|
55
55
|
passing check. Governor agreement alone is not an anchor.
|
|
56
56
|
- **Sidecar completion:** judge a sidecar only by the typed verdict from `scripts/sidecar_wait.sh`.
|
|
57
57
|
Never infer completion or emptiness by looking at its output file.
|
|
58
|
+
- **Identity grades:** the five-identity grade table is canonical only in
|
|
59
|
+
`knowledge/shared/harness-core/ship_readiness_gate.md`. Do not restate grades from memory or from
|
|
60
|
+
a session card — cite that file's current state.
|
|
58
61
|
|
|
59
62
|
> **Detail**: See `knowledge/shared/harness-core/agents_md_runtime_details.md §Architecture-and-output-routing`
|
|
60
63
|
> — layer ownership and destination routing — read before routing work across a public/private workspace pair.
|
|
@@ -84,6 +87,23 @@ Because non-Claude runtimes do not auto-load Claude path rules, apply these rule
|
|
|
84
87
|
require instrument suspicion.
|
|
85
88
|
6. **Irreversible intent:** before publish, delete, or history rewrite, read and apply the
|
|
86
89
|
Pre-Publish or Destructive-Op gate in `CLAUDE.md`. `pre-push` is only the git-side backstop.
|
|
90
|
+
7. **Self-contrast on asset touch:** the trigger for the three-layer self-contrast (process ·
|
|
91
|
+
engines · identities) is *touching an FH/PMH asset*, not being asked. Pick verification axes by
|
|
92
|
+
failure mode — running all four every time is not the rule. Record, in the existing Axes 2–3
|
|
93
|
+
marker fields: the soul line written before design (or `none`), each axis run and not run — by
|
|
94
|
+
name, and each axis's control with whether it survived. The minimum evidence that an axis ran is
|
|
95
|
+
execution output with a live control; reporting an axis as run without one, or recording only a
|
|
96
|
+
reason for not choosing it, is not compliance. The four axes are named in
|
|
97
|
+
`knowledge/shared/harness-core/fh_three_layer_canon.md` §1-a — read it before recording; §1-c
|
|
98
|
+
holds the sample limits — read it before citing. This record is self-attested and has no hook
|
|
99
|
+
behind it; it is closed by a different-family reader, not by writing it more carefully.
|
|
100
|
+
8. **Branch-surface claims:** GitHub branch protection is two independent layers — legacy
|
|
101
|
+
protection and rulesets coexist, and the strictest wins. Read both
|
|
102
|
+
`/repos/{owner}/{repo}/branches/{branch}/protection` and
|
|
103
|
+
`/repos/{owner}/{repo}/rules/branches/{branch}` before declaring any branch surface open or
|
|
104
|
+
closed. In the reference repository (forge-harness), reading the protection object alone
|
|
105
|
+
misjudged the force-push surface three times; the symmetric single-endpoint case is untested,
|
|
106
|
+
but the same failure shape applies.
|
|
87
107
|
|
|
88
108
|
> **Detail**: See `knowledge/shared/harness-core/agents_md_runtime_details.md §Mandatory-checklist-procedures`
|
|
89
109
|
> — exact supporting procedures and canonical doctrine links — read when any checklist trigger fires.
|
|
@@ -279,9 +279,19 @@ branch-claim 게이트 ① 영혼 필수 «무엇을 성공으로 볼 것
|
|
|
279
279
|
§2 에서 게이트의 `engine` 열이 이미 4엔진을 들고 있었던 것과 같은 형태 — 있는 것의 이름이다.
|
|
280
280
|
|
|
281
281
|
**그리고 이 관점은 공짜로 검출기 하나를 준다**: 어느 정체성에도 안 붙는 스킬 =
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
282
|
+
**미배정(unmapped) 유닛**이다. ⚠️ **「고아(orphan)」라고 부르지 마라 — 이 저장소에서 그 단어는
|
|
283
|
+
이미 기계화된 다른 뜻이고, 그 뜻도 하나가 아니다**: ① **참조-고아** — 인바운드 참조 0
|
|
284
|
+
(`harness-doctor` SKILL.md §Step 5 「orphan page」 → R-tier · `phantom-quench` 역방향 검사 ·
|
|
285
|
+
`harvest-loop` INDEX/FILE-ORPHAN) ② `harness-doctor` L2 상주 원장의 `orphaned/decorative` —
|
|
286
|
+
트리거/백스톱 없는 **상주 섹션**(참조와도 정체성과도 무관한 제3의 용법). 미배정은 **분류
|
|
287
|
+
문제**고 참조-고아는 **삭제 후보**라 처방이 정반대인데, 어휘를 합치면 다음 세션이 「정체성
|
|
288
|
+
미배정」을 「참조 0」으로 읽어 멀쩡히 배선된 스킬을 R-tier 로 렌더한다 (2026-08-10 스크리너
|
|
289
|
+
실측: 위 저항 3종 전부 인바운드 참조 보유 — 명령:
|
|
290
|
+
`grep -rn "token-budget-gate\|mcp-circuit-breaker\|video-ingest" CLAUDE.md knowledge/ plugins/`.
|
|
291
|
+
단 grep 스크리너 수준이고 ⓓ 되돌림 실측은 미실시, 참조 있음 ≠ 배선됨). `CLAUDE.md §Identity`
|
|
292
|
+
의 적신호 문장(*"red flags are orphaned, redundant, and decorative units"*)은 orphaned 를
|
|
293
|
+
**정의하지 않는다** — 이 문서는 그 단어를 이미 기계화된 참조-0 쪽에 **고정한다**(원문 독해가
|
|
294
|
+
아니라 어휘 결정이다). 정체성 매핑은 분류가 아니라 **자산 감사 계기**다.
|
|
285
295
|
|
|
286
296
|
🟡 **전수 대응은 미측정이다 — 눈으로 배정하지 마라.** 실측 규모는 **스킬 38 · 에이전트 8**
|
|
287
297
|
(2026-08-09). 명확한 것도 많지만 **실제로 저항하는 항목이 있다**: `video-ingest`(⑤ 증폭자인가
|
|
@@ -291,7 +301,11 @@ branch-claim 게이트 ① 영혼 필수 «무엇을 성공으로 볼 것
|
|
|
291
301
|
|
|
292
302
|
**다음 세션 후보(미착수)**: 스킬별 정체성 선언을 frontmatter 로 받고 미선언·미분류를
|
|
293
303
|
세는 검사기. 선언은 저자가 하고 계기는 **세기만** 한다 — 계기가 분류를 대신 하면
|
|
294
|
-
`[[feedback_not_found_is_not_zero_family]]` 로 되돌아간다.
|
|
304
|
+
`[[feedback_not_found_is_not_zero_family]]` 로 되돌아간다. 합법값에
|
|
305
|
+
**`identity: n/a — run-facing`** 을 포함하라 — 위 저항 3종은 사용자의 *일*이 아니라 **런 그
|
|
306
|
+
자체**(비용 · 도구 건강 · 입력 채널)를 다루는 자산이라, «사람이 얻는 기능»을 값역으로 갖는
|
|
307
|
+
정체성 축의 **대상 밖**이다(§0 의 정의상 — 얻는 게 아니라 안 죽게 하는 것).
|
|
308
|
+
이 값이 없으면 계기가 미배정을 0으로 렌더한다.
|
|
295
309
|
|
|
296
310
|
⚠️ **⑤ 의 🟢 는 범위 한정 가능성이 열려 있다**(2026-08-09 관찰, 미반영): 그 근거가 전부
|
|
297
311
|
*"사용자 산출물"* 대상에서 측정됐고, `⑤ × 하네스 자신` · `⑤ × 낳는 하네스` 는 미측정이다.
|
|
@@ -1709,3 +1709,19 @@
|
|
|
1709
1709
|
계기는 **문자열 착지만** 재고 기록의 정확성·실행 여부는 안 본다.
|
|
1710
1710
|
★ 오라벨 1건 발견: `fh_completed:1384` 의 `⑨PR 선제흡수` 는 틀렸다 — ⑨ 답변은 `ⓑ`이고
|
|
1711
1711
|
「PR 선제흡수」는 별개 지적(:1301)이다. 한 줄에 두 결정이 뭉개졌다.
|
|
1712
|
+
|
|
1713
|
+
- date: 2026-08-10
|
|
1714
|
+
session: ifkakao-fhops-pmh-sync-overnight
|
|
1715
|
+
agents_summary: "5 dispatches (consolidated): weekly-audit(claude generic, bg) · persona-innovator Mode F(bg) · quench-challenger(Axis2, sync) · company-extract(claude generic, bg) · codex gpt-5.5 sidecar(crossfamily, pmh sync tooling)"
|
|
1716
|
+
dispatch_count: 5
|
|
1717
|
+
outcome: accepted
|
|
1718
|
+
evidence: "audit file 18994B M3/S3/R5 · innovator A1 채택→canon 수리 착지 · challenger M5/S10 전건 수리(PR #311) · extract 31절 무손실(known-pair 24/24) · codex M4/S4→M 전건 수리+레인 8/8"
|
|
1719
|
+
notes: "codex payload residency scan (org-token 0, control 3) · 전 dispatch 산출이 머지/파일로 착지"
|
|
1720
|
+
|
|
1721
|
+
- date: 2026-08-10
|
|
1722
|
+
session: qasp-axready-parallel-overnight
|
|
1723
|
+
agents_summary: "8 dispatches (consolidated): Explore×3(mtm setter 배선조사 · AX완성조건 발화수집 · 핸드오프 격리 그라운딩 감사, 전부 bg) · codex gpt-5.5 sidecar×5(rer/effective 수리 적대 5라운드)"
|
|
1724
|
+
dispatch_count: 8
|
|
1725
|
+
outcome: accepted
|
|
1726
|
+
evidence: "조사① runner 5지점+정직술어 지목→#139 그대로 채택 · 조사② B1~B12 판정표→NOT READY 판정 근거 · 감사③ 핵심수치 전건 CONFIRMED+정정 8건→핸드오프 반영 · codex 5R 반증 12건 전건 수리(S→S→S/A→A/B→B 소진, #137·#138 머지)"
|
|
1727
|
+
notes: "sidecar 수치 액면 미인용 — S급 전건 governor 소스 재판정(재현 후 수리). 조사 에이전트 위치주장은 구현 중 파일 직독으로 교차확인"
|
|
@@ -34,6 +34,29 @@
|
|
|
34
34
|
2026-08-09 실측: 그 질문이 델타 8건을 꺼냈고 **PR 이 없어 `gh pr list` 로 구조적으로 안 잡히는
|
|
35
35
|
것들**이 다수였다(스펙/설계만 바뀐 것 · PR 이전 브랜치 커밋 · 외부 승인 대기 · 이월 누락).
|
|
36
36
|
|
|
37
|
+
## 1-b. 머지 직전 — no-op 확인 (pmh Axis 0 답습분 · 산문 — CLAUDE.md 마감체인 ①-b 에 붙는다)
|
|
38
|
+
|
|
39
|
+
self-mergeable PR 을 실제로 머지하기 **직전**(⑤ 카드 쓰기 이전 단계다), 반입할 내용이 실재하는지
|
|
40
|
+
**트리 비교**로 확인한다:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
[ "$(git merge-tree --write-tree <base> <head>)" = "$(git rev-parse '<base>^{tree}')" ] && echo NO-OP
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**NO-OP(트리 동일)면 머지하지 않는다** — 같은 내용이 이미 들어가 있다. merge-tree 의 충돌 종료는
|
|
47
|
+
«판정 불가»가 아니라 «델타 있음»이고, **ref 해석 불가만 판정 불가**다(not-found ≠ 0).
|
|
48
|
+
`--write-tree` 는 git ≥ 2.38. ⚠️ `git diff --stat <base>..<head>` 도 `<base>...<head>` 도 쓰지
|
|
49
|
+
마라 — «내용이 이미 다른 경로로 착지 + base 전진»인 바로 그 no-op 시나리오에서 **둘 다 비어 있지
|
|
50
|
+
않다** (2026-08-10 known-pair 실측: no-op 쌍=트리 일치 / 실델타 쌍=불일치로 가른 것은 merge-tree
|
|
51
|
+
형태뿐이었다). PR 의 열림/닫힘 · 커밋 수 · SHA · `@{u}` 거리는 전부 내용의 지표가 아니다.
|
|
52
|
+
|
|
53
|
+
기원: pmh 실측 3회 — ref 상태를 내용 상태로 오독, REST push/미러 경로에서 no-op 이중 머지 직전까지
|
|
54
|
+
감 (기록: `pmh-dev/scripts/merge_noop_check.sh` 헤더 — #61 vs #60, 트리 a170a8e5). FH 표면 실측 =
|
|
55
|
+
main 최근 200커밋 중 no-op 1건 (2026-08-09 측정, d2c7f55). **기계화 임계는 두 트리거다**
|
|
56
|
+
(`operations.md`: N≥3 **또는 같은 클래스의 타표면 재발**): (i) FH 표면 N=1 < 3 · (ii) 타표면
|
|
57
|
+
재발 — pmh→FH 는 아직 FH 재현 0. **FH 재현 1회 시 즉시 기계화** — pmh `merge_noop_check.sh` 를
|
|
58
|
+
가져오면 된다(exit 계약까지 이미 있다).
|
|
59
|
+
|
|
37
60
|
## 2. 세 가지를 각각 못 믿는다
|
|
38
61
|
|
|
39
62
|
### 2-a. 보냈다 ≠ 닿았다
|
|
@@ -48,6 +71,14 @@
|
|
|
48
71
|
→ **보낸 수가 아니라 답 온 수를 적고, 미회신 수를 카드에 명시한다.** 기계화 불가(스크립트는 회신을
|
|
49
72
|
못 본다) — `session_close_check.sh` ①-c 가 이 문장을 살리언스로 띄운다.
|
|
50
73
|
|
|
74
|
+
**외부 1차 근거 (2026-08-10, 벤더 문서 — code.claude.com/docs/en/cross-session-messaging)**:
|
|
75
|
+
크로스세션 배달은 **3값**이다 — `Delivered` / `Held`(수신자 승인 대기 — 기본 다이얼로그 경로는
|
|
76
|
+
`dialogExpiry` 초과 시 폐기되지만 **나중에 accept 가 적용되면 보류분이 릴리스**되고, `-p` 세션에선
|
|
77
|
+
계속 보류된다) / `Refused`. **도착 시 Refused 만 구조적 무음**이다(송신 측 통지 없음). 나머지
|
|
78
|
+
결과-통지 채널은 존재하되, 위 실측에서 관측과 어긋난 적이 있으므로 **신뢰층이 아니다 — 그래서
|
|
79
|
+
회신 수를 센다.** 벤더 문서는 명세지 관측이 아니다 — 위 «단정하지 마라»가 여전히 우선한다.
|
|
80
|
+
(Held-릴리스 경로는 08-09 의 «만료 통지 vs 실제 도달» 불일치를 설명할 수 있는 후보다.)
|
|
81
|
+
|
|
51
82
|
### 2-a-2. 탐지는 넓게, **통지는 좁게** — 둘을 같은 목록으로 쓰지 마라
|
|
52
83
|
2026-08-09 의 오발신은 탐지가 넓어서가 아니라 **탐지 목록을 그대로 수신자 목록으로 썼기** 때문이다
|
|
53
84
|
(원래 알려야 할 상대는 1곳이었다). 두 목록은 목적이 다르다:
|
package/package.json
CHANGED
|
@@ -12,6 +12,19 @@ load: on-demand
|
|
|
12
12
|
|
|
13
13
|
## §Collection-Bash
|
|
14
14
|
|
|
15
|
+
> **Execution form — canonical (revised 2026-08-10, operator decision ⓑ).** The default runtime path
|
|
16
|
+
> is **WebFetch (+WebSearch) against the endpoints below** — not `curl`. Measured: **13 consecutive
|
|
17
|
+
> unattended runs** executed via WebFetch while this section documented only the curl form (the digest
|
|
18
|
+
> reported the mismatch on every run — spec debt N=13). `curl` requires an allowlist entry that a
|
|
19
|
+
> default install does not carry; the bash blocks below are kept as the **endpoint/parameter spec**
|
|
20
|
+
> (query shapes, filters, item caps, refresh rules — all still authoritative), and a run applies them
|
|
21
|
+
> through WebFetch on the same URLs. If an environment *does* allow the curl form, it is equivalent —
|
|
22
|
+
> the transport is not the contract, the endpoints and criteria are.
|
|
23
|
+
>
|
|
24
|
+
> **arXiv HTTP 429 is a separate item, not part of this revision**: it is a remote rate limit
|
|
25
|
+
> (N=11 consecutive, transport-independent). On 429: back off once, then fall back to per-item
|
|
26
|
+
> `https://arxiv.org/abs/{id}` reads; record `FAILED (429)`, never 0 items.
|
|
27
|
+
|
|
15
28
|
### HackerNews (Algolia API)
|
|
16
29
|
|
|
17
30
|
```bash
|
package/scripts/branch_claim.sh
CHANGED
|
@@ -130,6 +130,35 @@ _live_peers() {
|
|
|
130
130
|
echo "$n"
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
# 🟥 «peer 없음» 인지 «못 가르는 것» 인지를 나누는 **독립 계수기**.
|
|
134
|
+
# _live_peers 는 claim 기록만 세므로 claim 하지 않은 세션을 구조적으로 못 본다. 그 구멍의
|
|
135
|
+
# 폭을 재려면 claim 과 무관한 신호가 필요하고, 이 머신에서는 세션 소켓이 그 신호다.
|
|
136
|
+
#
|
|
137
|
+
# 반환: 살아있는 «claim 안 한» 다른 CC 세션 수 · 또는 `unknown`(경로 없음 = 교차확인 불가).
|
|
138
|
+
# ⚠️ **이건 상한이지 증거가 아니다.** 소켓은 머신 전역이라 다른 레포에서 도는 세션도 센다 —
|
|
139
|
+
# 그래서 «peer 가 이 체크아웃에 있다» 를 증명하지 못한다. 증명되는 건 «구분할 수 없다» 뿐이고,
|
|
140
|
+
# 그게 정확히 이 함수가 있는 이유다. 0 도 N 도 아닌 **세 번째 값**을 만든다.
|
|
141
|
+
# ⚠️ 소켓 경로는 런타임 구현 세부라 바뀔 수 있다 — 그때는 `unknown` 으로 degrade 하고
|
|
142
|
+
# «peer 없음» 이라고 말하지 않는다(부재를 0으로 렌더하지 않는 것이 이 함수의 계약이다).
|
|
143
|
+
_unclaimed_risk() {
|
|
144
|
+
local dir me n=0 s pid live_claims
|
|
145
|
+
dir="${FH_CLAIM_SOCK_DIR:-}"
|
|
146
|
+
if [ -z "$dir" ] || ! _test_mode; then dir=/tmp/cc-socks; fi
|
|
147
|
+
[ -d "$dir" ] || { echo unknown; return; }
|
|
148
|
+
me=$(_session_pid)
|
|
149
|
+
for s in "$dir"/*.sock; do
|
|
150
|
+
[ -e "$s" ] || continue
|
|
151
|
+
pid=$(basename "$s" .sock)
|
|
152
|
+
case "$pid" in ''|*[!0-9]*) continue;; esac
|
|
153
|
+
[ "$pid" = "$me" ] && continue
|
|
154
|
+
kill -0 "$pid" 2>/dev/null && n=$((n+1))
|
|
155
|
+
done
|
|
156
|
+
# 이미 claim 한 살아있는 peer 는 _live_peers 가 세므로 여기서 뺀다 — 중복 계상 방지.
|
|
157
|
+
live_claims=$(_live_peers)
|
|
158
|
+
n=$((n - live_claims)); [ "$n" -lt 0 ] && n=0
|
|
159
|
+
echo "$n"
|
|
160
|
+
}
|
|
161
|
+
|
|
133
162
|
_override_log() { local gd; gd=$(_git_dir) || return 1; echo "$gd/fh-branch-claim-overrides.log"; }
|
|
134
163
|
|
|
135
164
|
cmd_claim() {
|
|
@@ -230,7 +259,30 @@ cmd_check() {
|
|
|
230
259
|
|
|
231
260
|
peers=$(_live_peers)
|
|
232
261
|
if [ "$peers" -eq 0 ]; then
|
|
233
|
-
|
|
262
|
+
# 🟥 «peer 없음» 과 «peer 가 claim 을 안 함» 은 다른 명제다. _live_peers 는 **claim 기록만**
|
|
263
|
+
# 세므로 claim 하지 않은 세션을 구조적으로 못 본다 — 그걸 0 으로 렌더하면 미측정이 0이 된다.
|
|
264
|
+
# 실측 2026-08-09 (라이브, N=2): peer 세션이 커밋할 때 이 분기가 «peer 없음» 을 찍었는데
|
|
265
|
+
# 그 순간 다른 세션이 살아서 메시지를 주고받고 있었다. 원인은 판정식이 아니라 **기록 부재** —
|
|
266
|
+
# 활성화 배선은 세션 시작 때 도는데, 배선보다 먼저 시작된 세션은 영원히 claim 이 없다.
|
|
267
|
+
local unclaimed; unclaimed=$(_unclaimed_risk)
|
|
268
|
+
case "$unclaimed" in
|
|
269
|
+
unknown)
|
|
270
|
+
echo " ⚠️ branch-claim: 기록('$cb')과 HEAD('$b')가 다르다 — 살아있는 peer claim 0."
|
|
271
|
+
echo " 🟡 교차 확인 불가(세션 소켓 경로 없음) — «peer 없음» 을 확인하지 못했다. 차단하지 않는다."
|
|
272
|
+
;;
|
|
273
|
+
0)
|
|
274
|
+
echo " ⚠️ branch-claim: 기록('$cb')과 HEAD('$b')가 다르다 — 살아있는 peer 세션이 없어 차단하지 않는다."
|
|
275
|
+
echo " (교차 확인: 이 머신에 다른 CC 세션도 없다)"
|
|
276
|
+
;;
|
|
277
|
+
*)
|
|
278
|
+
echo " 🟥 branch-claim: 기록('$cb')과 HEAD('$b')가 다른데 **판정할 수 없다.**"
|
|
279
|
+
echo " peer claim 0 개 ↔ 이 머신에 살아있는 다른 CC 세션 ${unclaimed} 개."
|
|
280
|
+
echo " 이 게이트는 지금 «peer 가 없다» 와 «peer 가 claim 을 안 했다» 를 **구분하지 못한다.**"
|
|
281
|
+
echo " claim 하지 않은 세션이 이 체크아웃을 같이 쓰고 있으면 지금 커밋은 남의 브랜치에 얹힌다."
|
|
282
|
+
echo " → 확인: bash scripts/branch_claim.sh show · 다른 세션에서: claim"
|
|
283
|
+
echo " ⚠️ 차단하지 않는다(커밋은 가역 표면). 다만 이건 «안전» 이 아니라 «미판정» 이다."
|
|
284
|
+
;;
|
|
285
|
+
esac
|
|
234
286
|
echo " 의도한 전환이면: bash scripts/branch_claim.sh claim"
|
|
235
287
|
return 0
|
|
236
288
|
fi
|
|
@@ -16,7 +16,11 @@ set -uo pipefail
|
|
|
16
16
|
SCRIPT="$(cd "$(dirname "$0")" && pwd)/branch_claim.sh"
|
|
17
17
|
PASS=0; FAIL=0; LIVE_PID=""
|
|
18
18
|
|
|
19
|
-
cleanup() {
|
|
19
|
+
cleanup() {
|
|
20
|
+
[ -n "$LIVE_PID" ] && kill "$LIVE_PID" 2>/dev/null
|
|
21
|
+
[ -n "${SOCK_EMPTY:-}" ] && rm -rf "$SOCK_EMPTY"
|
|
22
|
+
return 0
|
|
23
|
+
}
|
|
20
24
|
trap cleanup EXIT
|
|
21
25
|
|
|
22
26
|
_mkrepo() {
|
|
@@ -61,6 +65,14 @@ _plant_peer() { # repo branch pid
|
|
|
61
65
|
# 앵커가 배선되기 전까지(=호출부 0개) 이 사실은 드러날 수 없었다.
|
|
62
66
|
unset CLAUDE_PID
|
|
63
67
|
|
|
68
|
+
# ⚠️ 같은 이유로 **소켓 경로도 환경에서 빌리지 않는다.** _unclaimed_risk 가 기본으로 실제
|
|
69
|
+
# `/tmp/cc-socks` 를 보므로, 그대로 두면 «지금 이 머신에 CC 세션이 몇 개 떠 있나» 가 레인
|
|
70
|
+
# 판정을 바꾼다 — 실측 2026-08-09: 이 수정을 넣자 레인 ④ 가 즉시 깨졌다(실제 세션 5개).
|
|
71
|
+
# **위의 CLAUDE_PID 와 같은 결함을 수리 그 자체가 재생산한 것**이라, 같은 처방을 붙인다.
|
|
72
|
+
# 개별 레인은 필요할 때 이 값을 인라인으로 덮는다.
|
|
73
|
+
SOCK_EMPTY=$(mktemp -d "${TMPDIR:-/tmp}/bcsock0.XXXXXX")
|
|
74
|
+
export FH_CLAIM_SOCK_DIR="$SOCK_EMPTY"
|
|
75
|
+
|
|
64
76
|
echo "[branch-claim] known-pair 앵커"
|
|
65
77
|
PEERPID=$(_spawn_live)
|
|
66
78
|
|
|
@@ -226,6 +238,55 @@ _ok "⑬-c 기존 값 보존 (main 이어야 함 · 지금=$kept)" "$([ "$kept"
|
|
|
226
238
|
# 게이트가 영원히 통과한다 — 자동화가 게이트를 무장해제하는 경로다
|
|
227
239
|
rm -rf "$R"
|
|
228
240
|
|
|
241
|
+
# ── ⑭ «peer 없음» 과 «못 가름» 을 나눈다 (2026-08-09 라이브 거짓 음성 N=2 회귀) ──
|
|
242
|
+
# _live_peers 는 claim 기록만 센다 → claim 안 한 세션을 구조적으로 못 본다.
|
|
243
|
+
# 그걸 0 으로 렌더한 게 라이브 결함이었다. 세 값이 **각각 다른 문장**을 내야 한다.
|
|
244
|
+
# ⚠️ 세 팔이 rc 는 전부 0(차단 안 함)이라 **rc 로는 구분이 안 된다** — 출력으로 잡는다.
|
|
245
|
+
# 이 레인이 rc 만 봤으면 세 팔 다 통과하고 아무것도 안 잡았을 것이다.
|
|
246
|
+
_sockdir() { local d; d=$(mktemp -d "${TMPDIR:-/tmp}/bcsock.XXXXXX"); echo "$d"; }
|
|
247
|
+
|
|
248
|
+
# ⑭ 소켓엔 살아있는 세션이 있는데 claim 은 0 → 「판정할 수 없다」
|
|
249
|
+
R=$(_mkrepo); S=$(_sockdir); : > "$S/${PEERPID}.sock"
|
|
250
|
+
(cd "$R" && FH_CLAIM_TEST=1 FH_CLAIM_SESSION=me bash "$SCRIPT" claim a >/dev/null 2>&1)
|
|
251
|
+
git -C "$R" switch -q -c other
|
|
252
|
+
LANE_OUT=$(cd "$R" && FH_CLAIM_TEST=1 FH_CLAIM_SESSION=me FH_CLAIM_SOCK_DIR="$S" bash "$SCRIPT" check 2>&1); rc=$?
|
|
253
|
+
_lane "⑭ claim 0 인데 살아있는 세션 있음 → «판정할 수 없다»" 0 "$rc" \
|
|
254
|
+
"판정할 수 없다" "구분하지 못한다" "«안전» 이 아니라 «미판정»"
|
|
255
|
+
rm -rf "$R" "$S"
|
|
256
|
+
|
|
257
|
+
# ⑭-b 컨트롤: 소켓 디렉터리는 있는데 살아있는 세션이 없다 → 진짜 「peer 없음」
|
|
258
|
+
R=$(_mkrepo); S=$(_sockdir)
|
|
259
|
+
(cd "$R" && FH_CLAIM_TEST=1 FH_CLAIM_SESSION=me bash "$SCRIPT" claim a >/dev/null 2>&1)
|
|
260
|
+
git -C "$R" switch -q -c other
|
|
261
|
+
LANE_OUT=$(cd "$R" && FH_CLAIM_TEST=1 FH_CLAIM_SESSION=me FH_CLAIM_SOCK_DIR="$S" bash "$SCRIPT" check 2>&1); rc=$?
|
|
262
|
+
_lane "⑭-b 컨트롤: 살아있는 세션 0 → «peer 없음» 이라고 단정한다" 0 "$rc" \
|
|
263
|
+
"살아있는 peer 세션이 없어" "이 머신에 다른 CC 세션도 없다"
|
|
264
|
+
_ok "⑭-b′ 그 팔엔 «판정할 수 없다» 가 없다(문장이 갈린다)" \
|
|
265
|
+
"$(printf '%s' "$LANE_OUT" | grep -q "판정할 수 없다" && echo 0 || echo 1)"
|
|
266
|
+
rm -rf "$R" "$S"
|
|
267
|
+
|
|
268
|
+
# ⑭-c 소켓 경로 자체가 없다 → 0 이 아니라 「교차 확인 불가」
|
|
269
|
+
# 런타임이 소켓 경로를 바꾸면 여기로 떨어진다. 그때 «peer 없음» 이라고 말하면 안 된다.
|
|
270
|
+
R=$(_mkrepo)
|
|
271
|
+
(cd "$R" && FH_CLAIM_TEST=1 FH_CLAIM_SESSION=me bash "$SCRIPT" claim a >/dev/null 2>&1)
|
|
272
|
+
git -C "$R" switch -q -c other
|
|
273
|
+
LANE_OUT=$(cd "$R" && FH_CLAIM_TEST=1 FH_CLAIM_SESSION=me FH_CLAIM_SOCK_DIR="/nonexistent-$$" bash "$SCRIPT" check 2>&1); rc=$?
|
|
274
|
+
_lane "⑭-c 소켓 경로 부재 → «교차 확인 불가»(0 아님)" 0 "$rc" "교차 확인 불가"
|
|
275
|
+
_ok "⑭-c′ 그 팔은 «peer 없음» 이라고 단정하지 않는다" \
|
|
276
|
+
"$(printf '%s' "$LANE_OUT" | grep -q "이 머신에 다른 CC 세션도 없다" && echo 0 || echo 1)"
|
|
277
|
+
rm -rf "$R"
|
|
278
|
+
|
|
279
|
+
# ⑭-d 노브 게이팅: FH_CLAIM_TEST 없이 FH_CLAIM_SOCK_DIR 를 주면 **무시돼야** 한다.
|
|
280
|
+
# 안 그러면 프로덕션에서 소켓 경로를 갈아끼워 게이트를 무력화할 수 있다
|
|
281
|
+
# (이 파일이 이미 같은 결함으로 한 번 수리된 적이 있다 — 테스트 노브 6종 무음 우회).
|
|
282
|
+
R=$(_mkrepo); S=$(_sockdir); : > "$S/${PEERPID}.sock"
|
|
283
|
+
(cd "$R" && FH_CLAIM_TEST=1 FH_CLAIM_SESSION=me bash "$SCRIPT" claim a >/dev/null 2>&1)
|
|
284
|
+
git -C "$R" switch -q -c other
|
|
285
|
+
LANE_OUT=$(cd "$R" && FH_CLAIM_SESSION=me FH_CLAIM_SOCK_DIR="$S" bash "$SCRIPT" check 2>&1); rc=$?
|
|
286
|
+
_ok "⑭-d 노브가 FH_CLAIM_TEST 없이는 무시된다(실경로를 본다)" \
|
|
287
|
+
"$(printf '%s' "$LANE_OUT" | grep -qF -- "$S" && echo 0 || echo 1)"
|
|
288
|
+
rm -rf "$R" "$S"
|
|
289
|
+
|
|
229
290
|
echo
|
|
230
291
|
echo "[branch-claim] PASS=$PASS FAIL=$FAIL"
|
|
231
292
|
[ "$FAIL" -eq 0 ] || exit 1
|