@chrono-meta/fh-gate 3.5.0 → 3.6.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.
@@ -11,25 +11,25 @@
11
11
  "plugins": [
12
12
  {
13
13
  "name": "fh-meta",
14
- "version": "3.5.0",
14
+ "version": "3.6.0",
15
15
  "description": "New in 2.2.0: BREAKING (gate): chamber step 6 now reads ACTUAL.md, not BUDGET.md — an in-flight chamber run whose actual cost sits in BUDGET.md blocks until the ACTUAL: line moves to tracks/_chamber/<slug>/ACTUAL.md (the runner prints the path). Why: BUDGET.md's pre-verdict hash IS the ordering witness, and step 6 hard-blocked until that same file changed, so every run that reached COMPLETE necessarily mutated a witnessed artifact and verify returned TAMPERED — the chamber's promotion condition was unsatisfiable by construction, not by strictness. Two roles (immutable witness / post-verdict calibration sink) had collided in one file; each was correct alone, so neither side's code showed the conflict. Also: ko-tech-writer Step 2/4-b scans are now calibration-backed (known-pair fixtures + reproducible command, shipped) — discrimination is proven, 'zero residue' is explicitly NOT; chamber lane suite 12 -> 33 including the runner x witness seam no test covered; chamber_run.sh now teaches the two-commit discipline (gate hashes and verdict hash must land in separate commits/PRs — it previously advised the opposite). New in 2.1.0: BREAKING (gate): `crossfamily: declined` in an Axes 2-3 marker now requires grounds naming a record path that RESOLVES on disk — bare `declined`, and `declined` justified by author judgment, are blocked at commit. Remedy: cite where the operator decision lives (e.g. `.. — operator declined sidecars, per knowledge/shared/rules/operational_adaptation.md`), or use `DEGRADED_PANEL_UNUSED` if a panel was reachable and you chose not to recruit it — which is what author judgment actually is. `declined` was the only enum value with no grounds requirement; a cross-family review then broke the first (vocabulary-grep) fix three ways — self-validating on the value's own token, vacuous keyword passes, and over-blocking real declinations in natural prose — so the check asserts a resolvable record instead of words. Also: standpoint axis gains `tier1b` (a STATIC read of a target repo, executed nothing) plus a decide-in-order procedure, after blind floor-tier sims graded pure cold-reads as `tier2` three rounds running; steel-quench Wave 1's sixth angle (gate-locality) gains the output-template row it never had, so a mandatory angle stops being structurally unreportable; verify-bidirectional gains category 5 (prescriptive doctrine statement); Sister Asset Protocol gains an active-adoption trigger; new resident doctrine — Mechanization Boundary, Local Execution First, Skeleton-not-Muscle, Expedition track, and this package's versioning policy. Hub meta-operations toolkit — 35 skills + 7 agents. New in 2.0.1: harness-doctor cadence hook, portability lint wired into pre-commit, branch_claim.sh claim-count-vs-tree-count warning, louder confidentiality-scan fail-open notice, fh-gate.sh missing-package.json survival, identity ① reclassified 🟢 (cross-harness adapters + relay argument channel). 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": "3.5.0",
20
+ "version": "3.6.0",
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
  },
24
24
  {
25
25
  "name": "fh-qp",
26
- "version": "3.5.0",
26
+ "version": "3.6.0",
27
27
  "description": "QP (Quality Platform) — the generic edition of a field QA harness's Prepare→Automation→Regression loop as an FH plugin: 4 skills (qp router · qp-plan · qp-run · qp-regress) + qp_tools.sh (target-class · adapter-probe · mask · surface-reach · mtm-check · run-verbs, typed exit codes) + a zero-domain-constant profile slot + 29 known-pair lanes. Drives web targets through the session's Playwright MCP and desktop targets through computer-use MCP (mobile deferred); calls a registered qasp typed capability when one exists (strictest-wins) — none is registered today, so the MCP fallback is the first edition. Verdict contract: a MACHINE closure requires a recorded assertion; a failed first step is attributed BLOCKED, not FAIL; surface_reach counts every TC in the denominator. Born as chamber run #18 (EMIT, 2026-09-05).",
28
28
  "source": "./plugins/fh-qp"
29
29
  },
30
30
  {
31
31
  "name": "fh-preprep",
32
- "version": "3.5.0",
32
+ "version": "3.6.0",
33
33
  "description": "preprep — a presentation-preparation harness. One CLI over 17 lanes (7 built-in + 10 module) that reads a declared surface manifest and checks a talk's assets against each other: canonical terms, jargon glosses, retired vocabulary, inter-slide dependencies, pacing, get/put round-trips, adjacent duplication, slide relations, geometry, attribute consistency, fonts, diagrams, slide refs, and screen-parity between manuscript and deck. Verdict contract: exit 0 pass / 1 findings / 2 cannot-judge — a surface that could not be read is UNMEASURED, never 0. Promoted out of fh-commons 2026-09-13 after the skill grew past one role (routing / lane modules / fixtures).",
34
34
  "source": "./plugins/fh-preprep"
35
35
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrono-meta/fh-gate",
3
- "version": "3.5.0",
3
+ "version": "3.6.0",
4
4
  "description": "FH runtime adapters — run FH governance, skills, and agents via Claude or Codex with machine-parseable gates.",
5
5
  "homepage": "https://github.com/chrono-meta/forge-harness#readme",
6
6
  "bugs": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fh-commons",
3
- "version": "3.5.0",
3
+ "version": "3.6.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": "3.5.0",
3
+ "version": "3.6.0",
4
4
  "engines": {
5
5
  "claudeCode": ">=1.0.0"
6
6
  },
@@ -1,5 +1,30 @@
1
1
  # forge-harness (fh-meta) Changelog
2
2
 
3
+ ### [3.6.0] — 2026-09-13 — 사용 원장의 동의를 «첫 실행» 에서 받는다 (3상태)
4
+
5
+ `preprep` 과 필드 하네스 진입점이 **쓰이는 순간** 한 번 묻는다. 공지는 읽는 사람과 쓰는 사람이
6
+ 다르기 때문이다(운영자 결정).
7
+
8
+ | `FH_USAGE_LEDGER` | 뜻 | 행동 |
9
+ |---|---|---|
10
+ | 미설정 | 아직 안 정함 | 매 실행 **stderr** 3줄 안내 |
11
+ | `<경로>` | 동의 | 조용 + 기록 |
12
+ | `off` | **명시적 거절** | 조용 + 기록 없음 |
13
+
14
+ 🟥 **`BREAKING (gate)` 는 없다** — 게이트 수용이 깨지지 않는다. 켜든 끄든 도구 동작과
15
+ **종료코드가 완전히 같고**, 그 동일성을 레인이 직접 잰다(FH ⑫-6 · qasp ⑥-5).
16
+ 그래서 안내 문구도 「안 켜면 제대로 안 돈다」라고 **쓰지 않았다** — 그건 거짓이고, 거짓으로 받은
17
+ 동의는 그 동의로 얻은 숫자의 값도 같이 떨어뜨린다. 쓰는 이유는 참인 쪽이다:
18
+ **「당신이 어디서 막히는지 우리가 못 본다」**.
19
+
20
+ - 안내는 **stderr 전용**이다. `stdout` 은 판정 출력이고 기계가 읽는다 — 한 줄도 안 끼운다.
21
+ - 나가는 문이 거짓말이 아니라 `off` 다. **거절도 기록**이므로 정한 사람에게는 다시 안 묻는다.
22
+ - `$HOME` 앵커를 문구에 박았다(`export FH_USAGE_LEDGER="$HOME/.fh-usage"`) — 레포가 어디에
23
+ 있든 안내 문구가 모두에게 **글자 그대로 같다**.
24
+
25
+ ⚠️ 명명된 잔여: 안내가 실제로 전환을 만드는지 **미측정**이고, **원장 자신이 그걸 못 잰다**
26
+ (안 켠 사람은 기록이 없다 — 구조적 사각).
27
+
3
28
  ### [3.5.0] — 2026-09-13 — preprep 를 자기 플러그인으로 승격 (`fh-preprep`)
4
29
 
5
30
  **BREAKING (gate):** 스킬 경로가 **이동한다**.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fh-preprep",
3
- "version": "3.5.0",
3
+ "version": "3.6.0",
4
4
  "engines": {
5
5
  "claudeCode": ">=1.0.0"
6
6
  },
@@ -43,9 +43,48 @@ _ENV_AUTHOR = "FH_USAGE_AUTHOR"
43
43
  _ENV_SALT = "FH_USAGE_SALT"
44
44
 
45
45
 
46
+ _OFF = ("off", "no", "none", "0", "false", "decline", "거절")
47
+
48
+
46
49
  def _dir():
47
50
  d = os.environ.get(_ENV_DIR, "").strip()
48
- return d or None
51
+ if not d or d.lower() in _OFF:
52
+ return None
53
+ return d
54
+
55
+
56
+ def notice(stream=None):
57
+ """미설정이면 «아직 안 정했다» 로 보고 안내를 낸다. 정하면 조용해진다.
58
+
59
+ 🟥 **세 상태를 가른다** — 이 저장소의 동의 규율 그대로다(부재 ≠ 승인, 그리고 거절도 기록이다):
60
+ · 미설정 아직 안 정함 → 매 실행 안내 (정할 때까지 계속 뜬다)
61
+ · 경로 지정 동의 → 조용, 기록 시작
62
+ · `off` 명시적 거절 → 조용, 기록 안 함
63
+
64
+ 🟥 **안내는 stderr 로만 간다.** stdout 은 이 도구의 판정 출력이고 기계가 읽는다 —
65
+ 거기에 한 줄이라도 끼면 부르는 쪽의 파싱이 깨진다(관측이 대상을 바꾸는 것과 같은 부류).
66
+ 🟥 **문구는 참인 이유만 쓴다.** 이 변수가 없어도 도구는 **완전히 같게** 동작하고 종료코드도
67
+ 같다(레인이 직접 잰다). 그러니 «제대로 안 돈다» 는 거짓이다 — 못 보는 것은 **우리 관측**이지
68
+ 그 사람의 도구가 아니다. 거짓으로 받은 동의는 그 동의로 얻은 숫자의 값도 같이 떨어뜨린다.
69
+ """
70
+ import sys as _sys
71
+ raw = os.environ.get(_ENV_DIR, "").strip()
72
+ if raw: # 경로든 off 든 — 정했으면 조용
73
+ return False
74
+ out = stream if stream is not None else _sys.stderr
75
+ try:
76
+ out.write(
77
+ "\n[usage] 이 도구가 얼마나 쓰이는지 아직 못 재고 있습니다 — 기본값이 꺼짐이라 그렇습니다.\n"
78
+ " 켜기: export FH_USAGE_LEDGER=\"$HOME/.fh-usage\"\n"
79
+ " 끄기: export FH_USAGE_LEDGER=off (그러면 이 안내가 사라집니다)\n"
80
+ " 기록되는 것: 시각(분)·도구명·진입점·실행시간·종료코드·익명 ID.\n"
81
+ " 기록 안 되는 것: 인자·경로·파일명·티켓번호·URL·사용자명·호스트명.\n"
82
+ " ⚠️ 켜든 끄든 도구 동작과 종료코드는 **완전히 같습니다**. 못 보는 것은\n"
83
+ " 여러분의 도구가 아니라 «여러분이 어디서 막히는지» 입니다.\n\n")
84
+ out.flush()
85
+ except Exception:
86
+ return False
87
+ return True
49
88
 
50
89
 
51
90
  def _salt(d):
@@ -171,6 +210,7 @@ def observe(harness, entry, fn, *args, **kwargs):
171
210
  🟥 이 함수는 판정을 만들지도 바꾸지도 않는다. 종료코드는 `fn` 이 정하고 여기는 읽기만 한다.
172
211
  그래서 게이트 코드가 아니다 — 관측이 판정을 바꾸는 순간 그 숫자는 아무 의미가 없다.
173
212
  """
213
+ notice()
174
214
  t0, c0 = time.monotonic(), time.process_time()
175
215
  rc, outcome = None, "ERROR"
176
216
  try:
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fh-qp",
3
- "version": "3.5.0",
3
+ "version": "3.6.0",
4
4
  "engines": {
5
5
  "claudeCode": ">=1.0.0"
6
6
  },
@@ -235,6 +235,47 @@ printf '%s' "$Z1" | /usr/bin/grep -q "비저자 표본 ≥0" \
235
235
  && ok "재사용률도 같은 분기에서 강등된다" \
236
236
  || ng "🟥 재사용률이 「표본 0」을 확정값으로 낸다"
237
237
 
238
+
239
+ echo "== ⑫ 첫 실행 안내 — 세 상태가 갈리나 (미정 / 동의 / 거절) =="
240
+ # 🟥 이 저장소의 동의 규율 그대로다: 부재 ≠ 승인, 그리고 **거절도 기록이다**.
241
+ # 미설정은 «아직 안 정함» 이라 계속 뜨고, `off` 는 «싫다» 라 조용해진다. 나가는 문이
242
+ # 거짓말(«도구가 고장난다»)이 아니라 `off` 여야 동의의 값이 안 떨어진다.
243
+ _N_OUT(){ ( cd "$PREPREP" && env $1 python3 preprep.py 2>/dev/null ); }
244
+ _N_ERR(){ ( cd "$PREPREP" && env $1 python3 preprep.py 2>&1 >/dev/null ); }
245
+
246
+ if printf '%s' "$(_N_ERR "-u FH_USAGE_LEDGER")" | /usr/bin/grep -q '\[usage\]'; then
247
+ ok "⑫-1 미설정 → stderr 에 안내 (아직 안 정한 사람에게는 계속 뜬다)"
248
+ else
249
+ ng "⑫-1 미설정인데 안내가 없다 — 아무도 켤 이유를 못 듣는다"
250
+ fi
251
+ if printf '%s' "$(_N_OUT "-u FH_USAGE_LEDGER")" | /usr/bin/grep -q '\[usage\]'; then
252
+ ng "🟥 ⑫-2 안내가 **stdout 을 오염**시킨다 — 부르는 쪽의 판정 파싱이 깨진다"
253
+ else
254
+ ok "⑫-2 stdout 은 깨끗하다 (안내는 stderr 전용 — 관측이 대상을 안 바꾼다)"
255
+ fi
256
+ if printf '%s' "$(_N_ERR "FH_USAGE_LEDGER=off")" | /usr/bin/grep -q '\[usage\]'; then
257
+ ng "🟥 ⑫-3 `off` 로 거절했는데도 계속 뜬다 — 거절이 기록으로 안 받아들여진다"
258
+ else
259
+ ok "⑫-3 off → 조용 (명시적 거절도 «정한 것»이다)"
260
+ fi
261
+ D12="$TMPROOT/notice"; mkdir -p "$D12"
262
+ if printf '%s' "$(_N_ERR "FH_USAGE_LEDGER=$D12")" | /usr/bin/grep -q '\[usage\]'; then
263
+ ng "🟥 ⑫-4 경로를 줬는데도 안내가 뜬다"
264
+ else
265
+ ok "⑫-4 경로 지정 → 조용 + 기록 시작 (행 $(rows "$D12"))"
266
+ fi
267
+ D12b="$TMPROOT/notice_off"; mkdir -p "$D12b"
268
+ ( cd "$PREPREP" && FH_USAGE_LEDGER=off python3 preprep.py >/dev/null 2>&1 )
269
+ [ -z "$(ls -A "$D12b" 2>/dev/null)" ] && ok "⑫-5 off 는 행을 안 남긴다 (거절이 곧 무동작)" \
270
+ || ng "🟥 ⑫-5 off 인데 뭔가 남았다"
271
+ ( cd "$PREPREP" && env -u FH_USAGE_LEDGER python3 preprep.py >/dev/null 2>&1 ); _r1=$?
272
+ ( cd "$PREPREP" && FH_USAGE_LEDGER=off python3 preprep.py >/dev/null 2>&1 ); _r2=$?
273
+ ( cd "$PREPREP" && FH_USAGE_LEDGER="$D12" python3 preprep.py >/dev/null 2>&1 ); _r3=$?
274
+ if [ "$_r1" = "$_r2" ] && [ "$_r2" = "$_r3" ]; then
275
+ ok "⑫-6 종료코드가 세 상태에서 동일 ($_r1) — «안 켜면 제대로 안 돈다» 는 **거짓**이다"
276
+ else
277
+ ng "🟥 ⑫-6 상태마다 종료코드가 다르다 (미정=$_r1 off=$_r2 켜짐=$_r3)"
278
+ fi
238
279
  echo
239
280
  echo "════════════════════════════════════════"
240
281
  printf ' PASS %s FAIL %s SKIP %s\n' "$PASS" "$FAIL" "$SKIP"