@chrono-meta/fh-gate 1.4.93 → 1.4.95

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.
@@ -11,13 +11,13 @@
11
11
  "plugins": [
12
12
  {
13
13
  "name": "fh-meta",
14
- "version": "1.4.93",
14
+ "version": "1.4.95",
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.93",
20
+ "version": "1.4.95",
21
21
  "description": "Project-agnostic utility skills — 5 skills (convergence-loop · deliberation · mcp-circuit-breaker · token-budget-gate · ko-tech-writer) + 1 agent (quench-challenger). Domain-independent utilities transplantable into any project.",
22
22
  "source": "./plugins/fh-commons"
23
23
  }
package/CLAUDE.md CHANGED
@@ -598,7 +598,7 @@ Proposal format: `"I see [X]. Want me to run /[skill] to [one-line description]?
598
598
  | "delete the branch", "브랜치 삭제", "브랜치 정리", "clean up branches", "force-push", "rewrite history", "지워도 돼?" (destructive intent — **proactive**, fire *before* the action) | **Destructive-Op Gate** (see above → enumerate → recover → destroy; `templates/predelete_check.sh`) |
599
599
  | **"새 기능 검증해줘", "test this feature", "이 TC 확인해줘" — verifying the user's PRODUCT/feature (not FH itself)** | **Route to the mapped field harness first** (Cross-Project Skill Bus / registry) — the field harness owns product verification. The harness-verification rows in this table (`verify-bidirectional` · `prompt-regression` · `sim-conductor` · `pipeline-conductor`) verify the *harness*, and must not shadow a product-verification ask (a field project's *harness assets* — its skills/rules — still use those FH verification rows) |
600
600
  | "지난주에 뭐 했지", "what did we do last week", "예전에 이거 한 적 있나" (recall intent) | **CATALOG-first recall** — read `CATALOG.md`, identify candidates by tag/date, then open only those files. Never scan session files one by one |
601
- | "add this MCP server", "mount this MCP", "mcp.json에 추가", "connect this tool server" (external-MCP mount intent — **proactive**, fire *before* first tool call; mount intent only — a failing/erroring mounted server is `/mcp-circuit-breaker`'s row above) | `templates/.claude/rules/mcp_tool_gating.md` (name-keyed ask/allow table — never trust server annotations or names; fill §3 at mount time) |
601
+ | "add this MCP server", "mount this MCP", "mcp.json에 추가", "connect this tool server" (external-MCP mount intent — **proactive**, fire *before* first tool call; mount intent only — a failing/erroring mounted server routes to `/mcp-circuit-breaker` via its own skill description; its table row was removed in the 2026-07-17 row diet, so this parenthetical no longer points "above") | `templates/.claude/rules/mcp_tool_gating.md` (name-keyed ask/allow table — never trust server annotations or names; fill §3 at mount time) |
602
602
  | "did my rule change break anything", "regression check", "test harness changes" | `/prompt-regression` |
603
603
  | "review for the team", "CTO review", "decision-maker", "share with leadership", "approval deck" | `/apex-review` |
604
604
  | "run full pipeline", "verify everything", "end-to-end sweep", "chain all verifications" | `/pipeline-conductor` |
@@ -353,10 +353,31 @@ Split honestly by moment; only one of the three is closable today.
353
353
 
354
354
  - **Registration moment — reachable.** The reviewer is inside the registry when it applies. Pointers
355
355
  from `.claude/registry/README.md` and `fh_detail_protocols.md §1-c` put the M1–M5 bar in front of
356
- them. **Named residual, not built**: a `scripts/capability_registry_check.sh` that validates the
357
- schema and runs each declared M4 pair would make registration *measured* rather than reviewed. It
358
- does not exist. Until it does, M1–M5 is a reviewed bar, and this file says so rather than implying a
359
- floor it does not have.
356
+ them. ~~**Named residual, not built**: a `scripts/capability_registry_check.sh`…~~ **built
357
+ 2026-08-11.** `scripts/capability_registry_check.sh` validates the schema (closed key list an
358
+ unknown key is a failure, never an ignore) and **runs each declared M4 pair**, so registration is
359
+ now *measured* on those axes rather than reviewed. Known-pair calibrated, 7 lanes, BLOCK/PASS
360
+ symmetric. Two capabilities are registered through it (`fh_psa_leak.cap` · `fh_degrade_verdict.cap`).
361
+
362
+ 🟥 **What the checker measures, and the axis it provably does NOT — learned by being bitten.**
363
+ M1–M5 answer *"is the declaration well-formed, and does the instrument separate a case whose
364
+ answer we already know?"*. They do **not** answer *"is the declaration true"*, and one axis made
365
+ that concrete the same day it was built: a capability declaring **`writes: read-only`** passed all
366
+ five criteria — executable entry, closed enum with a did-not-run value, `judge: mechanical`, M4
367
+ known-pair green ×2, valid `requires_cwd` — and its entry point then **`rm -rf`'d this repo's
368
+ `scripts/` directory** on a no-argument invocation (a cleanup `trap` whose variable was reassigned
369
+ to a real path after the trap was installed). Tracked files were recovered by `git checkout`;
370
+ three untracked new scripts were not, and the recovery checkout also reverted an unrelated
371
+ in-flight edit. Nothing in the bar could have caught it: M4 exercised the two *declared* arms, and
372
+ the destructive path was the *undeclared* default arm.
373
+ - **Partial fix applied** (entry-point discipline, both probes): the cleanup variable is never
374
+ reassigned, the scan target is a separate variable, and the trap re-checks that the path it is
375
+ about to delete is under a temp root. Calibrated with a canary file in an isolated repo.
376
+ - **Structural fix, not built**: run the M4 pair under a read-only mount / sandbox and *observe*
377
+ whether a write is attempted. Until that exists, `writes:` (and `reversibility:`) are the
378
+ **registrant's claim**, and the checker prints them as such rather than implying it verified
379
+ them. A bar that silently accepts an unverifiable axis is how a `read-only` capability deletes a
380
+ directory with every light green.
360
381
  - **Call moment — salience-only, no mechanical floor exists.** No hook can observe "a session is about
361
382
  to compose a capability call"; the trigger is intent, exactly like the Instrument-Calibration rule.
362
383
  The strongest available lever is structural: §ⓑ.3 makes the merged constraint set **step 2 of the
@@ -1797,3 +1797,36 @@
1797
1797
  outcome: accepted
1798
1798
  evidence: "Explore 지도가 «R-10.1=하위호환 unknown 레인·P7 는 그 경로 못 탐·R-4.3 은 sourced 가능» 3판정을 사전 특정 — 구현이 그 지도 그대로 감. codex 10R: S10·A7·B9 중 수용 21·기각 2(스펙 근거), R10 «신규 없음» CONVERGED. 라이브 자기적발 1(전제 미형성 FAIL 이 이슈 오발화 — 1차 주행에서 즉시 실측). 머지 #146(7cb45ac), 검출 1→4"
1799
1799
  notes: "R5~R8 네 라운드가 전부 R3 증거-짝 수리의 후속 결함(«수리가 결함의 주된 출처» 4연쇄 실측) · R9 는 codex 가 내 회귀 레인의 공허성(.opine 계약 미준수로 단언 미실행)을 적발 — 레인 자체가 검증 대상이라는 사례"
1800
+
1801
+ - date: 2026-08-11
1802
+ session: qasp-axready-night-autonomous
1803
+ agents_summary: "7 열거 + 훅 집계 9 (consolidated): ① Explore 필드 정본 읽기(MTM/2막 계약/발화 자격) ② Explore 데이터-플레인 오라클 삽입지점 설계(bg) ③ codex gpt-5.5 1R (야간 5커밋 diff 적대 감사) ④ fh-meta:beginner 콜드리드 1차(기술문서 삽입 초안) ⑤ general-purpose 독립 2차 지각 QA(PDF 8쪽, 200dpi 크롭) ⑥ fh-meta:beginner 콜드리드 2차(수리본 재검 — 1차 후 고친 것을 다시 읽힌 라운드) ⑦ general-purpose@sonnet 블라인드 sim(ko-tech-writer 보강본, 심은 결함 3종)"
1804
+ dispatch_count: 9
1805
+ dispatch_count_note: "훅 집계 9 · 내가 이름으로 열거 가능한 것 7. 차이 2건은 귀속 미상이라 지어내지 않는다 — 세션 로그를 뒤져 채우는 대신 미상으로 남긴다(없는 엔트리를 만드는 것이 빠진 엔트리보다 나쁘다). 초판은 3으로 적혀 있었고 마감 체크 ④-e 가 9를 찍어 드러났다."
1806
+ outcome: accepted
1807
+ evidence: "④⑤⑥ 는 기술문서 축(같은 세션 후속) — ⑥ 이 **1차 콜드리드 수리본을 다시 읽힌 라운드**이고 거기서 «반례 흔한 단정 + 배치 오류» 가 나와 §7→§6 이동으로 이어졌다(그 경험이 스킬 Step 5 재콜드리드 규율의 근거다). ⑦ sim = 심은 결함 3/3 검출·오탐 0. ① 정본 Explore 가 «MTM=블박+화박 동시 실행, verdict 불변, 표기 4상태» 를 정본 인용으로 확정 — 이후 2-arm 실측(판정 51/51 동일 · mtm_cited 17)이 그 계약과 일치함을 확인하는 근거가 됐다. 일반 개념(«화이트박스 모드») 정규화를 사전 차단. ② 설계 Explore 가 relations.py 5곳·triage.py:258 닫힌 어휘·sourced 레인 선점 함정·데이터 리더 부재(known-positive 컨트롤 동반)를 특정 — 이번 세션은 그 능력을 안 지었으나(잔여 S1) 지도는 그대로 유효. ③ codex 1R: S/A/B/C 4축 반증 중 **헤드라인 반증 1건 수용** — 「품절 배지 검출」 주장이 로케일 축 오귀속임을 App.tsx/en.ts 근거로 지적, governor 가 소스 재확인 후 철회하고 attribution_risk 를 기계에 실었다(86dc8bf). 추가로 C-1(미생성 사유가 stdout 전용) 수용·수리, 잔여 4건은 명명"
1808
+ notes: "cross-family 가 **내 커밋 메시지의 주장** 을 반증한 사례 — 코드 결함이 아니라 «주장의 귀속» 이 틀린 경우라 레인·적대검증·되돌림 셋 다 못 잡았을 축이다([[feedback_grounding_audit_of_own_record]] 형). 자력 적발 0. 반대로 생성기 1차 산출의 오탐 공장 성질은 **손검사로 자력 적발**했다 — 기계 감사와 육안 표본이 서로 다른 결함을 잡았다"
1809
+
1810
+ - date: 2026-08-11
1811
+ session: plugin-reship-campaign-mtier-precheck
1812
+ agents_summary: "3 dispatches (parallel): fh-meta:challenger×3 — pmh-dev M티어 8스킬 선-적대검토 (배치1 게이트3종 · 배치2 감사/차단2종 · 배치3 오케스트레이션3종)"
1813
+ dispatch_count: 3
1814
+ outcome: accepted
1815
+ evidence: "M 22건·S 12건 회수, governor 소스 재검증 spot-check 9건 전건 CONFIRMED (marketplace plugin.json skills 키 부재 실측 · PSA 2층 패턴 스펙불일치 · MCB 계기 위치 stale · sidecar MEMORY.md 팬텀 · field-harvest 의무체인 Done When 미배선 등). 공통 M = 8스킬 전부 Done When check-class 0 (known-positive 컨트롤: 타 11스킬 히트) — 센서스 triage 와 일치. 배치3이 경계규칙(local_pmh_context.md:7 재인라인 금지) 발굴 → synced 7종 수리는 FH 상류 라우팅 확정"
1816
+ notes: "센서스(기계 grep triage) → 병렬 적대검토 → governor 재검증의 3단이 전수조사 표준 후보. 배치1이 known-pair 캘리브레이션을 자발 수행(BSD grep 오탐 후보를 스스로 기각) — challenger 에 계기 규율이 전이된 사례"
1817
+
1818
+ - date: 2026-08-11
1819
+ session: qasp-m1-tc-coordinates
1820
+ agents_summary: "1 dispatch: codex gpt-5.5 1R — M1 diff(좌표 3축·생성기 확장·entry NO_FORM·node 가드) 적대 감사, 낙관 방향(정상 FAIL/결함 PASS/BLOCKED 소실) 특정 지시"
1821
+ dispatch_count: 1
1822
+ outcome: accepted
1823
+ evidence: "5 findings 전건 수용·수리: F1(S — NO_FORM 이 «레시피 타깃 부재»를 «벽 부재»로 오판, SSO/타언어 벽 반례 → expect 증인 결박 + ENTRY_NO_FORM_UNVERIFIED fail-closed) · F2(venue 경유 basis 가 EVIDENCE_SPAN_SECTION 으로 오표기 — 시험관 선택이 기획서 지정으로 읽힘 → basis 분리) · F3(importer 가 generated_from 폐기 — 귀속위험이 하류 미결박 → 보존+report per_tc 표면화, exit 결박 잔여) · F4(무스코프 롤카운트 전역 오염/상쇄 → UNSCOPED_ROLE_COUNT typed, within= 잔여) · F5(부정 표시문 열거 추출 — codex 가 .venv 로 재현 실증 → 부정문 가드 양쪽). 수리 후 레인 69 + 전량 2,805 passed · 라이브 재검증 R9_1 BLOCKED→FAIL(typed)"
1824
+ notes: "F5 는 감사자가 처방 전에 재현을 직접 실행해 보인 사례(«짧은 함수 재현은 .venv 로 확인») — finding 수용의 소스검증 비용을 감사자가 선지불. F1 은 governor 의 «전원 부재 = 벽 부재» 가정을 반례(SSO/한국어 벽)로 무너뜨림 — 증인(positive witness) 요구가 처방으로 그대로 채택됨"
1825
+
1826
+ - date: 2026-08-11
1827
+ session: qasp-m1b-finding-identity
1828
+ agents_summary: "1 dispatch: codex gpt-5.5 1R — M1-b diff(finding 정체성 P6/P4→P7 관통) 적대 감사, **오귀속 방향** 지시(틀린 finding/화면 결박)"
1829
+ dispatch_count: 1
1830
+ outcome: accepted
1831
+ evidence: "4 findings 전건 수용·수리·되돌림 검증: F3(S — `references` 가 이미 5곳에서 4의미로 쓰임(finding_id·BVA source_defect_id·StateMatrix_combo_N·조인 id·row id) → 값만 보고 finding 해석 시 SM/BVA TC 오귀속, 감사자가 함수 실행으로 /orders 주입 재현 → reference_kind 선언 자격) · F2(S — sibling 위반은 행 쌍인데 조인이 첫 행에서 break, row1=/customers·row2=/orders 가 /customers 로 결박 → distinct 2+ 면 scalar 비움) · F1(S — 저신뢰 AREA_LABEL 이 선언된 유래를 이김 → 유예+충돌 typed, 유예가 좌표 손실이면 후퇴라 복원 경로 동반) · F4(M — 색인 raw / 소비 strip 불일치로 'dup'/'dup ' 미검출). 수리 4건 **각각 되돌려 정확히 그 앵커 1개씩 적색** 확인 후 커밋. 재생성 무회귀(app_id 40/51 · basis 분포 동일) · 전량 2,819 passed"
1832
+ notes: "감사자가 «일반 import 는 fitz/docx 부재로 막혀 P7·surface_inventory 를 직접 로드해 함수 단위로 확인» 이라고 **계기 한계를 스스로 명시**했다. 자체 적발 1건 — F3 게이트가 F 계열의 정당한 직결까지 막아 app_id 40→39(자격 게이트 도입은 기존 통과 경로에도 선언을 요구한다는 표준 회귀). peer 세션(로컬 다른 축)이 같은 라운드에 **두 축을 더** 잡았다: ⓐ 내 신규 앵커가 장식(생산자 배선 되돌려도 16 전건 초록 — 호출부 우회) ⓑ DISPLAY_MAPPING 의 case-insensitive 부분수리 함정(en.ts 직독 확인: 6종 중 5종만 통과). ★**갈림의 원인은 계열이 아니라 «무엇을 받았느냐» 다**(peer 정정 수용): codex 에게는 **diff** 를 줬고 → 코드 경로 결함 4건, peer 에게는 **내 보고서(주장)** 가 갔고 → 장치·주장 결함 4건. 계열이 같아도 diff 를 받았으면 같은 것을 봤을 것이다. 재사용 가능한 형태는 «탈상관 축은 누가 보느냐만이 아니라 **무엇을 받느냐**» — 적대검증을 아무리 붙여도 **전부 diff 를 주면 주장 축은 계속 빈다**([[feedback_grounding_audit_of_own_record]] 와 같은 축). peer 도 그 넷을 설계해서 잡은 게 아니라 **주장을 읽는 채널에 있었기 때문**이라고 자기 몫을 정직하게 낮췄다 — «피어 세션은 장치 결함을 잡는다» 가 아니라 «**주장을 받은 쪽**이 장치 결함을 잡는다» 가 맞는 모델이고, 그 차이가 다음에 누구에게 무엇을 보낼지를 바꾼다. 🟥**적발 비율을 지표로 읽지 마라**: 이 세션은 자력 1 · 외부 8 인데 그 8 = **codex(diff) 4 + peer(주장) 4** — 즉 «내가 못 봤다» 가 아니라 «**두 축을 열었다**» 다. 분모(어떤 채널을 몇 개 붙였나)를 안 적으면 리뷰를 많이 붙일수록 자력 비율이 나빠 보이고, 그 수를 성과로 읽는 순간 최적해가 «리뷰를 덜 붙이는 것» 이 된다(peer 지적). 채널을 같이 적어야 위의 «무엇을 받았느냐» 구분이 그 숫자 안에서 다시 접히지 않는다"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrono-meta/fh-gate",
3
- "version": "1.4.93",
3
+ "version": "1.4.95",
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": [
@@ -85,6 +85,7 @@
85
85
  "templates/goal-quench-hook-setup.md",
86
86
  "templates/.claude/rules/session.md",
87
87
  "scripts/below_floor_scan.sh",
88
+ "scripts/capability_registry_check.sh",
88
89
  "scripts/chamber_run.sh",
89
90
  "scripts/fh_env_delta_scan.sh",
90
91
  "scripts/substrate_jump_detector.sh",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fh-commons",
3
- "version": "1.4.93",
3
+ "version": "1.4.95",
4
4
  "engines": {
5
5
  "claudeCode": ">=1.0.0"
6
6
  },
@@ -60,12 +60,31 @@ model: sonnet
60
60
  | 용어-머리 «**X** — 설명» | 글머리 기호 뒤 `** — ` | «**X**: 설명» 콜론형 |
61
61
  | 콜론 나열투 («관측된 분업은: …») | 서술어 없는 `:` 도입 | 완결 문장으로 («…분업은 ~라는 것입니다») |
62
62
  | 조각문 («걸리는 층.») | 서술어 없는 마침 | 서술어 복원 («걸리는 층입니다») |
63
+ | 소유 직역 「~을 갖다/가지다」 | `grep -E "을 갖\|를 갖\|을 가지\|를 가지"` | 존재문으로 (**«이전 상태를 갖지 않는다» → «이전 상태가 없다»**). 영어 `have`의 직역이고, 한국어는 소유보다 존재로 말한다. 소유가 실제 논점인 자리(권한·지분)는 남긴다 |
63
64
  | 내부 조어·은어 | 팀 밖에서 안 쓰는 단어 (예: 렌더하다, 초록/빨강을 CI 결과 뜻으로) | 청중 어휘로 평문화. 단, **화면이 어휘를 받치면**(실제 빨간 화면을 가리키는 발화) 유지 가능 — 근거를 적는다 |
64
65
  | 레지스터 드리프트 | 습니다↔해요↔평어 혼재 | 표본 기준 단일화 |
65
66
 
66
- 클래스의 성격이 갈린다: 앞 줄(줄표·용어-머리·콜론·조각문)은 **기계 검출 가능**(grep
67
- 후보 + 눈 판정)이고, 뒤 두 줄(내부 조어·레지스터)은 **판정 클래스**다 — Done When에서
68
- 서로 다른 check-class로 채점된다. 의도 보존으로 제외하는 항목은 **명명된 사유**(열거
67
+ 클래스의 성격이 갈린다: 앞 다섯 줄(줄표·용어-머리·콜론·조각문·소유 직역)은 **기계 검출
68
+ 가능**(grep 후보 + 눈 판정)이고, 뒤 두 줄(내부 조어·레지스터)은 **판정 클래스**다 —
69
+ Done When에서 서로 다른 check-class로 채점된다.
70
+
71
+ ### Step 2-b — 부분 개고 전용: 기존 본문과의 리듬 대조
72
+
73
+ **문서 전체를 새로 쓰는 경우가 아니라 기존 문서에 문단·문장을 끼워 넣는 경우**, 위 클래스가
74
+ 전부 0이어도 **삽입분만 이질적으로 읽힐 수 있다.** 클래스 목록은 «틀린 표현»을 잡지
75
+ «튀는 표현»을 못 잡기 때문이다. 삽입·개고분에 한해 두 가지를 **수치로** 대조한다:
76
+
77
+ 1. **문장 길이 분포** — 본문 평문 문장의 길이 중앙값·상위 10%를 먼저 구하고, 삽입분의 각
78
+ 문장이 상위 10% 안에 드는지 본다. 넘으면 끊는다. (본문에 더 긴 문장이 있어도 근거가
79
+ 못 된다 — 그건 그 문단의 리듬이지 삽입분의 것이 아니다.)
80
+ 2. **어휘 반복** — 삽입분 안에서, 그리고 **직전·직후 문장까지 포함해** 같은 어간이 3회
81
+ 이상 연달아 나오는지. 특히 같은 글자가 뜻이 다른 채로 반복되면(«검사를 더 **붙**여도 …
82
+ 지점 쪽에 **붙**습니다») 읽는 눈이 걸린다.
83
+
84
+ > 실측(2026-08-11, 기술문서 v2.1 작업): 클래스 스캔 잔여 0인 삽입분이 본문 112문장 중
85
+ > 유일한 소유 직역 1건을 들여왔고, 문장 길이 **124자**(본문 중앙값 40자 · 상위 10% 78자),
86
+ > 「내려가」 앞 문장 포함 4회 연속 · 「붙」 한 문장 안 2회였다. 셋 다 기존 클래스 목록으로는
87
+ > 안 잡혔고, **읽는 사람이 «톤이 다르다»로 먼저 알아챘다.** 의도 보존으로 제외하는 항목은 **명명된 사유**(열거
69
88
  매달기·리듬 결속·화면 받침)에 한하며, 제외 목록은 Step 5 콜드리드 짝에게 **통독이 끝난
70
89
  뒤** 별도 패스로 전달해 대조받는다 — 사전 전달은 첫-읽기 눈을 오염시키고, 자기가 쓴
71
90
  사유만으로 무제한 제외하지 않는다.
@@ -126,17 +145,30 @@ model: sonnet
126
145
  격리 수준이었는지 이력에 적는다.** 에이전트 디스패치는 호출 세션의 권한·도구를 따른다
127
146
  (이 스킬의 allowed-tools 밖의 일이다). 강격리도 비저자 짝도 구할 수 없으면 해당 judged
128
147
  조건을 **미충족으로 명시**하고 발행 판단자에게 넘긴다 — 자기 검토로 대체하지 않는다.
148
+ - 🟥 **재콜드리드 — 지적을 반영해 고쳐 썼으면 고친 것을 다시 읽힌다.** 콜드리드 1회는
149
+ *초안*을 검증할 뿐이고, **발행되는 것은 수리본**이다. 수리는 결함의 주된 출처라서
150
+ «지적 반영»과 «통과»는 다른 사건이다. 본문이 바뀌었으면 라운드를 하나 더 돈다.
151
+ **정지 조건은 라운드 수가 아니라 변경이다** — 그 라운드에 **새 지적이 없거나, 있어도
152
+ 본문을 안 고쳤을 때** 닫는다. 고쳤으면 아직 안 닫힌 것이다. 라운드마다 «지적 n건 · 반영
153
+ n건 · 본문 변경 여부»를 이력에 적는다.
154
+ > 실측(2026-08-11): 1차 콜드리드가 초안의 삽입 지점 오류를 잡았고, **그 수리가 새 결함
155
+ > 3종**(소유 직역 · 문장 길이 124자 · 어휘 반복)을 낳았다. 재콜드리드를 안 돌아 그대로
156
+ > 발행될 뻔했고, 읽는 사람이 «톤이 다르다»로 먼저 잡았다. 돌려 보니 문체보다 큰 것까지
157
+ > 나왔다 — 삽입분의 주장이 **반례가 흔한 단정**이었고(«검사를 아무리 늘려도 잡히지
158
+ > 않는다»), 자리도 닫는 절이 아니라 한계 절이 맞았다. 문장 다듬기 라운드가 **주장 범위와
159
+ > 배치**를 되돌렸다.
129
160
 
130
161
  ## Done When
131
162
 
132
163
  | 조건 | check-class |
133
164
  |---|---|
134
165
  | 대상 매체 실물 표본 확인 기록이 산출물 이력에 존재 — 불가 시 **시도 내용·사유 포함** 명기(시도 기록 없는 «불가»는 불충족) | mandatory-pass |
135
- | Step 2 **기계 클래스**(줄표·용어-머리·콜론·조각문) 스캔 잔여 0 — 컨트롤(알려진 양성 1건) 동반, 제외는 명명 사유와 함께 목록화 | measured |
166
+ | Step 2 **기계 클래스**(줄표·용어-머리·콜론·조각문·소유 직역) 스캔 잔여 0 — 컨트롤(알려진 양성 1건) 동반, 제외는 명명 사유와 함께 목록화 | measured |
136
167
  | Step 2 **판정 클래스**(내부 조어·레지스터) 통과 | judged — 짝: 콜드리드 통독(통독 후 제외 목록 대조) |
168
+ | **부분 개고인 경우**(기존 문서에 삽입·수정): Step 2-b 리듬 대조 — 삽입분 각 문장이 본문 길이 상위 10% 이내 ∧ 어간 3회 연속 반복 0. 전문 신규 집필이면 N/A 명기 | measured |
137
169
  | 본문 수치 전건(기계 추출 분모 기준)이 라벨(창 병기/재측정 대상/미확인) 동반 | measured |
138
170
  | 렌더 산출물(HTML/PDF/이미지 — 파생 압축 자산 포함)이 존재하는 경우 전 페이지 지각 확인 — md-only 산출이면 N/A 명기 | judged — 짝: **독립 2차 지각 패스**(다른 눈이 같은 렌더를 봄) |
139
- | 발행 콜드리드 통독 완료 — 격리 수준(강/약) 이력 기록. Step 5 미적용 레지스터(낭독 대본)는 N/A 명기 | judged — 짝: 격리 검토(Step 5 정의) |
171
+ | 콜드리드가 **변경 없음으로 닫혔다**마지막 라운드에 새 지적이 없거나, 있어도 본문을 안 고쳤다. 지적을 반영해 고쳤으면 그 수리본으로 한 라운드 더. 라운드별 «지적 n · 반영 n · 본문 변경 여부» + 격리 수준(강/약) 이력 기록. Step 5 미적용 레지스터(낭독 대본)는 N/A 명기 | judged — 짝: 격리 검토(Step 5 정의) |
140
172
  | 정직 수위 분리(Step 3)가 적용됨 | judged — 짝: 발행 승인자 확인 또는 격리 적대 검토. 둘 다 불가하면 **미충족으로 명시 보고**(자기 검토로 대체 금지) |
141
173
 
142
174
  ## 역할 경계 (중복 아님을 명시)
@@ -53,10 +53,20 @@ Default thresholds:
53
53
  Identify the failing tool and failure mode:
54
54
 
55
55
  ```bash
56
- # Check MCP server config
57
- cat .claude/settings.json 2>/dev/null | grep -A5 '"mcpServers"' || echo "No MCP config found"
56
+ # MCP mounts live in SEVERAL scopes — check all that this shell can see. Project settings.json is
57
+ # usually NOT one of them (it commonly holds hooks only), so a single-file read renders "no config"
58
+ # on a session with live MCP servers — a wrong-location instrument, not a measurement.
59
+ for f in .mcp.json .claude/settings.json .claude/settings.local.json; do
60
+ [ -e "$f" ] && { echo "== $f =="; grep -A5 '"mcpServers"' "$f" 2>/dev/null || echo "(present, no mcpServers key)"; }
61
+ done
62
+ [ -e ~/.claude.json ] && grep -c '"mcpServers"' ~/.claude.json | xargs echo "user-scope ~/.claude.json mcpServers blocks:"
58
63
  ```
59
64
 
65
+ An empty result above means **"no project/user-scope config found"** — never "no MCP mounted":
66
+ plugin-provided and remotely-managed servers appear in no local file. The live mount evidence is the
67
+ failing tool call itself (`mcp__{server}__{tool}` in this session); classify from that name even when
68
+ every config read comes back empty.
69
+
60
70
  Classify failure type:
61
71
 
62
72
  | Type | Symptom | Likely Cause |
@@ -98,6 +108,10 @@ Failure type: {TYPE} | Consecutive failures: {N}
98
108
  Further calls to this tool are blocked until circuit resets.
99
109
  ```
100
110
 
111
+ (Blocking is **session-level discipline** — this skill's protocol, not a mechanical hook; no
112
+ PreToolUse gate enforces it. State it that way if asked — a protocol honestly labeled beats a
113
+ phantom enforcement claim.)
114
+
101
115
  ---
102
116
 
103
117
  ### Step 3. Log Circuit State
@@ -106,17 +120,16 @@ Write state to session-local file (in-memory is insufficient — logs survive /c
106
120
 
107
121
  ```bash
108
122
  mkdir -p .claude/mcp_circuit/
109
- # Append to circuit log
110
- ```
111
-
112
- Log entry format:
113
- ```yaml
123
+ grep -qxF '.claude/mcp_circuit/' .gitignore 2>/dev/null \
124
+ || echo "NOTE: add '.claude/mcp_circuit/' to .gitignore — session state must not become a tracked file (the ignored-but-committed class public-surface-audit Step 3c hunts)"
125
+ cat >> .claude/mcp_circuit/circuit_log.yaml << EOF
114
126
  - tool: {tool-name}
115
- state: OPEN
127
+ state: OPEN # OPEN | HALF-OPEN | CLOSED — same enum as Done When
116
128
  failure_type: {TYPE}
117
129
  failure_count: {N}
118
130
  tripped_at: {ISO-8601}
119
131
  reset_at: null
132
+ EOF
120
133
  ```
121
134
 
122
135
  ---
@@ -133,7 +146,8 @@ Present the relevant fallback options ranked by effort (at least 3):
133
146
  | **3 — Pause and retry** | Wait for server recovery (HALF-OPEN probe after cooldown) | Transient failure (TIMEOUT, RATE_LIMIT) |
134
147
 
135
148
  > **Gating carries over to the substitute** (cross-ref the external-MCP tool-gating rule
136
- > `mcp_tool_gating.md`). A REST/API or
149
+ > `templates/.claude/rules/mcp_tool_gating.md` template path; if installed live, your repo's
150
+ > `.claude/rules/` copy). A REST/API or
137
151
  > workflow-automation tool adopted under Priority 1b is still an external-action surface: classify its
138
152
  > calls under the same ask/allow tiers — reads are `allow (untrusted-read)` only after behavior
139
153
  > confirmation; any write / send / delete / permission-change stays `ask`. Trading a gated MCP mount for
@@ -197,10 +211,13 @@ Recommendations:
197
211
 
198
212
  ## Done When
199
213
 
200
- - Failure pattern classified (type + count)
201
- - Circuit state logged (OPEN / HALF-OPEN / CLOSED)
202
- - At least 3 fallback alternatives proposed when circuit is OPEN
203
- - Recovery probe offered with reset path
214
+ - Failure pattern classified (type + count) — *judged* (adversarial pairing: an `UNKNOWN`
215
+ classification must state which Step 1 config reads ran and came back empty — "couldn't determine"
216
+ without the read evidence is not a classification)
217
+ - Circuit state logged to `.claude/mcp_circuit/circuit_log.yaml` (OPEN / HALF-OPEN / CLOSED) —
218
+ *mandatory-pass*
219
+ - At least 3 fallback alternatives proposed when circuit is OPEN — *measured* (count ≥3)
220
+ - Recovery probe offered with reset path — *mandatory-pass*
204
221
 
205
222
  ---
206
223
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: token-budget-gate
3
- description: Estimates token cost before a multi-step task and outputs a Green/Yellow/Red gate verdict. Tracks actual vs. estimated after completion for calibration. Triggers on "token budget", "how much will this cost", "will this be expensive", "estimate tokens", before long multi-agent tasks.
3
+ description: Estimates token cost before a multi-step task and outputs a 4-tier Green/Yellow/Orange/Red gate verdict. Records actual vs. estimated after completion when a mechanical usage source exists. Triggers on "token budget", "how much will this cost", "will this be expensive", "estimate tokens", before long multi-agent tasks.
4
4
  user-invocable: true
5
5
  allowed-tools: ["Read", "Bash"]
6
6
  model: sonnet
@@ -76,6 +76,14 @@ Base estimates per task type:
76
76
 
77
77
  Apply dimension multipliers from Step 1 to the base estimate.
78
78
 
79
+ **Calibration override (read leg)**: if `.claude/token_calibration/log.tsv` exists and holds ≥1 row
80
+ for this task type, derive the base as `median(actual_i / mult_i)` over its rows — each row's `mult`
81
+ column records the dimension-multiplier product that was in effect, so dividing recovers a
82
+ dimension-neutral base. **Never use raw `actual` as the base**: actual already contains that run's
83
+ multipliers, and re-applying this run's multipliers on top double-counts scope. The table above is the
84
+ uncalibrated default; measured local data outranks it. State which source the estimate used
85
+ (`base: table (uncalibrated)` or `base: calibration log, n={rows}`).
86
+
79
87
  **Final formula:**
80
88
  ```
81
89
  Estimated = base × file_multiplier × agent_multiplier × iteration_multiplier
@@ -131,35 +139,46 @@ Scope reduction options table (ORANGE/RED):
131
139
 
132
140
  ---
133
141
 
134
- ### Step 5. Post-Task Calibration (optional)
142
+ ### Step 5. Post-Task Calibration (offered, not required)
135
143
 
136
144
  After task completion, if user says "how much did that cost" or "calibrate":
137
145
 
146
+ **`Actual` must come from a mechanical source** — the user pasting `/cost` output, or `budget.spent()`
147
+ in a Workflow context. A model cannot read its own token consumption by introspection; a reconstructed
148
+ number written into the log poisons every future estimate that reads it (the read leg in Step 2).
149
+ No mechanical source available → render `Actual: UNMEASURED` and **write no row**.
150
+
138
151
  ```
139
152
  ## Calibration
140
153
 
141
154
  Estimated: ~16K tokens
142
- Actual: ~{actual}K tokens
143
- Error: {+/-N}%
144
-
145
- Calibration note saved → improves next estimate for this task type.
155
+ Actual: ~{actual}K tokens (source: /cost paste | budget.spent() | UNMEASURED)
156
+ Error: {+/-N}% (omit when UNMEASURED)
146
157
  ```
147
158
 
148
- Write calibration data:
159
+ Write calibration data (only when Actual is mechanically sourced):
149
160
  ```bash
150
161
  mkdir -p .claude/token_calibration/
151
- # Append: task_type, estimated, actual, date
162
+ # mult = this run's dimension-multiplier product (Step 1) — stored so the read leg can divide it
163
+ # back out; a log without it makes every future estimate double-count scope.
164
+ printf '%s\t%s\t%s\t%s\t%s\n' "{task_type}" "{estimated_k}" "{actual_k}" "{mult}" "$(date +%F)" \
165
+ >> .claude/token_calibration/log.tsv
152
166
  ```
153
167
 
154
- Calibration data improves future estimates for the same task type (no model training — local record only).
168
+ Recorded rows feed the Step 2 calibration override for the same task type (no model training — local record only).
155
169
 
156
170
  ---
157
171
 
158
172
  ## Done When
159
173
 
160
- - Gate verdict output (GREEN/YELLOW/ORANGE/RED) with estimated cost breakdown
161
- - For ORANGE/RED: scope reduction options presented and user decision recorded
162
- - Calibration offered after task completion (optional, not mandatory)
174
+ - Gate verdict output (GREEN/YELLOW/ORANGE/RED) with estimated cost breakdown, naming the estimate's
175
+ base source (table-uncalibrated or calibration log) *mandatory-pass*
176
+ - For ORANGE/RED: scope reduction options presented and user decision recorded — *mandatory-pass*
177
+ - Any calibration row written this run carries a mechanically-sourced `actual` (pasted `/cost` or
178
+ `budget.spent()`); no mechanical source → `Actual: UNMEASURED`, no row — *mandatory-pass*
179
+
180
+ (Post-task calibration is an offer in Step 5, not a completion condition — a completion condition
181
+ marked "optional" is not a completion condition.)
163
182
 
164
183
  ---
165
184
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fh-meta",
3
- "version": "1.4.93",
3
+ "version": "1.4.95",
4
4
  "engines": {
5
5
  "claudeCode": ">=1.0.0"
6
6
  },
@@ -79,7 +79,22 @@ Classify each commit using the following criteria (commit message + changed-file
79
79
 
80
80
  Output the candidate list (format in §ModeA-Blocks): scanned count, FH absorption candidates with type/location/impact stars, field-only skipped count, then ask `[all / select number / skip]`. If 0 candidates, report "no absorption candidates" and exit.
81
81
 
82
- ## Step 4. PR Creation (upon user approval)
82
+ ## Step 3.5. contention-layer Collision Gate (mandatory when candidates ≥ 1)
83
+
84
+ Run `contention-layer` on the confirmed candidate list **before any PR is created** — a new pattern
85
+ must be checked for collision with existing skill role clusters before registration. Skip only when
86
+ Step 3 found 0 candidates.
87
+
88
+ | contention-layer result | Behavior |
89
+ |---|---|
90
+ | No collision | Proceed to Step 4 |
91
+ | Collision / blocked registration | **No PR** — verdict `FAIL` per the enum below |
92
+ | Role collision needing a human call | **No PR** — verdict `ESCALATE` |
93
+
94
+ (This step previously existed only as a line *below* Done When — a cold executor reading Steps 0–4
95
+ had no occasion to see it before `gh pr create`. It is now in the execution path it gates.)
96
+
97
+ ## Step 4. PR Creation (upon user approval, after Step 3.5)
83
98
 
84
99
  Create a `harvest/{project}-{date}` branch in FH, apply approved patterns, commit, push, and `gh pr create` (bash + PR body checklist in §ModeA-Blocks).
85
100
 
@@ -215,24 +230,32 @@ One pass per session; never blocks the Mode B commit.
215
230
 
216
231
  **Mode A (Pattern Harvest)**:
217
232
  ```
218
- All stages Step 0~4 complete
219
- + Step 3 harvest candidate list output (N candidates + M field-only)
220
- + Upon user approval, Step 4 PR creation complete (gh pr create executed)
221
- + When 0 candidates, "no absorption candidates" reported then exit
233
+ All stages through the exit point complete (Step 0~4; or
234
+ Step 0~3 + the 0-candidate exit, which is a valid terminal) — mandatory-pass
235
+ + Step 3 harvest candidate list output (N candidates + M field-only) — measured (counts)
236
+ + Step 3.5 contention-layer ran on the confirmed list (or its
237
+ 0-candidate skip recorded) and its verdict gated Step 4 — mandatory-pass
238
+ + Upon user approval, Step 4 PR creation complete (gh pr create ran) — mandatory-pass
239
+ + When 0 candidates, "no absorption candidates" reported then exit — mandatory-pass
222
240
  ```
223
241
 
224
242
  **Mode B (Session Log)**:
225
243
  ```
226
- All Steps 0-B ~ 5-B executed
227
- + Step 0-B.1 detection-skip ledger applied (already-logged commits filtered)
228
- + Session markdown file generated from git log (or "all commits already logged" exit)
229
- + Hub commit created (no auto-push)
230
- + Confirmation output with push offer
244
+ All Steps 0-B ~ 5-B executed — mandatory-pass
245
+ + Step 0-B.1 detection-skip ledger applied (already-logged filtered) — mandatory-pass
246
+ + Session markdown file generated from git log
247
+ (or "all commits already logged" exit) — mandatory-pass
248
+ + Hub commit created (no auto-push) — mandatory-pass
249
+ + Confirmation output with push offer — mandatory-pass
231
250
  ```
232
251
 
252
+ The harvest-worthiness classification itself (Steps 1–2) is *judged* — its adversarial pairing is
253
+ Step 3.5 `contention-layer` plus the PR-side `asset-placement-gate`; no candidate registers on the
254
+ author's judgment alone.
255
+
233
256
  Verdict: PASS (harvest candidates output and PR created, or 0 candidates confirmed) | CONDITIONAL_PASS (candidates found but PR pending user approval; or Mode B commit created, push pending) | FAIL (contention-layer blocked candidate registration; or hub path not found) | ESCALATE (role collision with existing skill requires human decision)
234
257
 
235
- **→ Mandatory next: `contention-layer`** — run immediately after Step 3 candidate list is confirmed, before Step 4 PR creation. New patterns must be checked for collision with existing skill role clusters before registration. Skip only when 0 candidates found.
258
+ **→ Mandatory next: `contention-layer`** — executed as Step 3.5 above (kept here in closed-chain form for chain auditors: run after Step 3 candidate confirmation, before Step 4 PR creation; skip only at 0 candidates).
236
259
 
237
260
  ## Linked Skills
238
261
 
@@ -84,7 +84,7 @@ Result: **PASS** / **PARTIAL** / **FAIL**
84
84
  ### Check 3 — Maintenance Signals
85
85
 
86
86
  ```bash
87
- cd "$REPO_PATH" 2>/dev/null || cd "$(pwd)"
87
+ cd "$REPO_PATH" 2>/dev/null || { echo "ABORT: cannot cd to $REPO_PATH — refusing to measure the current repo in its place"; exit 1; }
88
88
  git log -1 --format="Last commit: %ar (%ad)" --date=short 2>/dev/null
89
89
  ls CHANGELOG* 2>/dev/null && echo "CHANGELOG found" || echo "No CHANGELOG"
90
90
  git tag -l 2>/dev/null | tail -5
@@ -101,47 +101,66 @@ Result: **ACTIVE** / **STALE** (60–180 days) / **ABANDONED** (180+ days)
101
101
  ### Check 4 — Duplication / Conflict Detection
102
102
 
103
103
  ```bash
104
- # list skills in current repo (directory-based)
105
- find "$REPO_PATH" -name "SKILL.md" 2>/dev/null | xargs -I{} dirname {} | xargs -I{} basename {}
106
- # compare with existing FH skills (if FH_DIR is set)
107
- [ -n "$FH_DIR" ] && ls "$FH_DIR/plugins/fh-meta/skills/" 2>/dev/null
108
- # cross-check registered skills in plugin.json (SoT)
109
- [ -n "$FH_DIR" ] && python3 -c "
110
- import json, sys
111
- with open('$FH_DIR/plugins/fh-meta/.claude-plugin/plugin.json') as f:
112
- d = json.load(f)
113
- skills = [s['name'] for s in d.get('skills', [])]
114
- print('plugin.json registered skills (' + str(len(skills)) + '):', ', '.join(skills))
115
- " 2>/dev/null || echo "plugin.json parse failed (FH_DIR not set or path error)"
104
+ # list skills in target repo (directory-based — skills are directory-registered in this ecosystem;
105
+ # plugin.json carries no skills array, so a manifest read is NOT a skill list. An earlier version
106
+ # of this check read `plugin.json["skills"]`, a key that never exists, so it rendered every healthy
107
+ # repo as STALE a broken instrument, calibrated against this very repo.)
108
+ find "$REPO_PATH" -name "SKILL.md" 2>/dev/null | xargs -I{} dirname {} | xargs -I{} basename {} | sort > /tmp/_mkt_target_skills.txt
109
+ cat /tmp/_mkt_target_skills.txt
110
+ # compare with hub skills — SKIPPED must be visible, never silent. The readability test matters:
111
+ # FH_DIR set-but-wrong yields an empty ls through the pipe, which reads as "0 overlaps" — a silent
112
+ # skip wearing a pass. Set-but-unreadable is its own labeled state.
113
+ if [ -n "$FH_DIR" ] && [ -d "$FH_DIR/plugins" ]; then
114
+ ls "$FH_DIR"/plugins/*/skills/ 2>/dev/null | grep -v ':$' | grep -v '^$' | sort > /tmp/_mkt_hub_skills.txt
115
+ comm -12 /tmp/_mkt_target_skills.txt /tmp/_mkt_hub_skills.txt | sed 's/^/NAME-OVERLAP: /'
116
+ elif [ -n "$FH_DIR" ]; then
117
+ echo "SKIPPED: FH_DIR set but $FH_DIR/plugins unreadable — hub cross-check NOT run (not a CLEAN signal)"
118
+ else
119
+ echo "SKIPPED: FH_DIR unset — hub cross-check NOT run (this is not a CLEAN signal)"
120
+ fi
116
121
  ```
117
122
 
118
- **Duplication verdict**: If directory-based list and plugin.json list don't match → **STALE** warning.
123
+ **Duplication verdict**: name overlap with hub skills → **OVERLAP**/**CONFLICT** by role comparison.
124
+ Hub cross-check skipped → report `CLEAN (target-internal only — hub cross-check SKIPPED)`, never bare CLEAN.
119
125
 
120
126
  | Criterion | Check |
121
127
  |---|---|
122
- | No name conflict with existing FH skills | name comparison |
128
+ | No name conflict with existing FH skills | name comparison (or visible SKIPPED) |
123
129
  | No functional duplication | description keyword comparison |
124
- | plugin.json list matches directory list | cross-check (SoT consistency) |
125
130
 
126
131
  Result: **CLEAN** / **OVERLAP** (N candidates) / **CONFLICT** (direct conflict)
127
132
 
128
133
  ### Check 5 — Public Safety
129
134
 
135
+ **Primary path (no-reinvention)**: when `public-surface-audit` is installed, run it against
136
+ `$REPO_PATH` and map its verdict — `LEAK` → **BLOCKED** · `REVIEW` → **WARNING** · `CLEAN` → **SAFE**
137
+ · `NOT_CONFIGURED` → **WARNING(NOT_CONFIGURED)** (pattern source absent — not a clean bill). That
138
+ skill is the real token scanner; this check does not re-implement it. The `NOT_CONFIGURED` qualifier
139
+ survives into the Step 2 aggregate — see the 🟢 rule there (an unmeasured public surface must not be
140
+ absorbed into an ignorable ⚠️).
141
+
142
+ **Fallback (screening-grade only)** — when public-surface-audit is not installed:
143
+
130
144
  ```bash
131
- # detect hardcoded internal domains
132
- grep -r "<your-ghe-url>\|internal-domain\|sandbox\|internal-api" \
145
+ # placeholder-literal screening — catches template residue, NOT real internal hostnames or secrets
146
+ grep -r "<your-ghe-url>\|internal-domain\|internal-api" \
133
147
  "$REPO_PATH" --include="*.md" --include="*.json" --include="*.yaml" -l 2>/dev/null | head -10
134
- # sensitive information exposure
135
- grep -r "API_KEY\s*=\|SECRET\s*=\|PASSWORD\s*=" \
136
- "$REPO_PATH" --include="*.md" --include="*.json" -l 2>/dev/null | head -5
148
+ # sensitive information exposure (assignment shapes only)
149
+ grep -rE "API_KEY\s*=|SECRET\s*=|PASSWORD\s*=" \
150
+ "$REPO_PATH" --include="*.md" --include="*.json" --include="*.yaml" --include="*.yml" \
151
+ --include="*.sh" --include="*.env*" -l 2>/dev/null | head -5
137
152
  # license
138
153
  ls "$REPO_PATH"/LICENSE* 2>/dev/null && echo "LICENSE found" || echo "No LICENSE"
139
154
  ```
140
155
 
156
+ Fallback results are always labeled `(screening-grade — placeholder patterns; not a hostname/secret
157
+ scanner)`. A go-public action still owes the Pre-Publish Surface Gate's full chain regardless of a
158
+ SAFE here — this check screens listing readiness, it does not clear publication.
159
+
141
160
  | Criterion | Check |
142
161
  |---|---|
143
- | No hardcoded internal domains (or clearly marked as internal-only) | grep |
144
- | No sensitive information exposed | grep |
162
+ | No hardcoded internal domains (or clearly marked as internal-only) | public-surface-audit (or screening-grade grep, labeled) |
163
+ | No sensitive information exposed | public-surface-audit (or screening-grade grep, labeled) |
145
164
  | LICENSE file exists | ls |
146
165
 
147
166
  Result: **SAFE** / **WARNING** (N items to review) / **BLOCKED** (sensitive info exposed)
@@ -161,10 +180,14 @@ marketplace-gate — Listing Suitability Verdict
161
180
  Check 4 Duplication detection: ✅ CLEAN / ⚠️ OVERLAP({N}) / ❌ CONFLICT
162
181
  Check 5 Public safety : ✅ SAFE / ⚠️ WARNING({N}) / ❌ BLOCKED
163
182
 
164
- Overall verdict:
165
- 🟢 Recommended for listing — 0 FAIL + 0 BLOCKED
166
- 🟡 Conditional listing — ≤1 FAIL + 0 BLOCKED
167
- 🔴 Listing on hold 2+ FAIL or 1+ BLOCKED
183
+ Overall verdict — counted over the ❌-class {FAIL, ABANDONED, CONFLICT, BLOCKED}
184
+ (each check has its own vocabulary; the aggregate counts the ❌ column, not the token "FAIL" —
185
+ an ABANDONED or CONFLICT is a failure even though its word differs):
186
+ 🟢 Recommended for listing 0 ❌-class results, AND Check 5 does not carry the
187
+ NOT_CONFIGURED qualifier (an unmeasured surface caps
188
+ the verdict at 🟡 — unmeasured ≠ pass)
189
+ 🟡 Conditional listing — exactly 1 ❌-class result, and it is not BLOCKED
190
+ 🔴 Listing on hold — 2+ ❌-class results, or any BLOCKED
168
191
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
169
192
  ```
170
193
 
@@ -182,12 +205,17 @@ marketplace-gate — Listing Suitability Verdict
182
205
  ## Done When
183
206
 
184
207
  ```
185
- All steps 0–2 completed
186
- + Full 5-point check results output (Check 1–5 individual verdicts)
187
- + Overall verdict output (🟢 Recommended / 🟡 Conditional / 🔴 On hold)
208
+ All steps 0–2 completed — mandatory-pass
209
+ + Full 5-point check results output (Check 1–5 individual
210
+ verdicts, skipped legs rendered as visible SKIPPED) — mandatory-pass
211
+ + Overall verdict output (🟢/🟡/🔴) counted over the ❌-class — measured (❌-class count)
212
+ + Before any 🟢 Recommended verdict: phantom-quench ran over
213
+ the target's citations/URLs/path refs; phantom refs found
214
+ → verdict auto-downgrades to 🟡 Conditional — mandatory-pass
188
215
  ```
189
216
 
190
- **→ Mandatory before 🟢 Recommended verdict: `phantom-quench`** forward axis check on all citations, external URLs, and file path references in the asset being reviewed. A 🟢 verdict without phantom-quench is incomplete. If phantom-quench finds phantom refs → verdict downgrades to 🟡 Conditional automatically.
217
+ (The phantom-quench leg sits inside Done When on purpose an earlier version stated it below the
218
+ fence, so the fence alone could be satisfied without it.)
191
219
 
192
220
  > When `agent-composer` receives a "comprehensive marketplace listing audit" request,
193
221
  > recommend: Wave 0 `fact-checker` → Wave 1 `marketplace-gate` + `hub-persona-auditor` in parallel.