@chrono-meta/fh-gate 2.5.1 โ†’ 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/.claude/rules/fh_4axis_gate.md +26 -3
  2. package/.claude-plugin/marketplace.json +2 -2
  3. package/AGENTS.md +24 -3
  4. package/CATALOG.md +41 -0
  5. package/CLAUDE.md +77 -15
  6. package/README.ja.md +36 -11
  7. package/README.ko.md +36 -11
  8. package/README.md +116 -18
  9. package/README.zh.md +32 -11
  10. package/docs/USER_GUIDE.md +118 -0
  11. package/docs/platform_sustainability.md +174 -0
  12. package/knowledge/shared/GLOSSARY.md +26 -1
  13. package/knowledge/shared/harness-core/fh_detail_protocols.md +44 -2
  14. package/knowledge/shared/harness-core/field_verdict_crossfamily_gate.md +19 -5
  15. package/knowledge/shared/harness-core/harness_incubator_doctrine.md +129 -2
  16. package/knowledge/shared/harness-core/multi_model_sidecar_strategy.md +32 -0
  17. package/knowledge/shared/harness-core/ship_readiness_gate.md +77 -8
  18. package/knowledge/shared/learnings/subagent_invocations_log.yaml +110 -0
  19. package/knowledge/shared/rules/knowledge_layer_seam.md +1 -1
  20. package/package.json +17 -3
  21. package/plugins/fh-commons/.claude-plugin/plugin.json +1 -1
  22. package/plugins/fh-meta/.claude-plugin/plugin.json +1 -1
  23. package/plugins/fh-meta/CHANGELOG.md +136 -0
  24. package/plugins/fh-meta/agents/persona-innovator.md +170 -0
  25. package/plugins/fh-meta/skills/auto-decorrelation/SKILL.md +44 -0
  26. package/plugins/fh-meta/skills/fh/SKILL.md +31 -0
  27. package/plugins/fh-meta/skills/goal-quench/SKILL_detail.md +1 -1
  28. package/plugins/fh-meta/skills/harness-doctor/SKILL.md +7 -1
  29. package/plugins/fh-meta/skills/harvest-loop/SKILL.md +32 -0
  30. package/plugins/fh-meta/skills/public-surface-audit/SKILL_detail.md +16 -1
  31. package/plugins/fh-meta/skills/steel-quench/SKILL.md +97 -0
  32. package/scripts/adapters/fixtures/mate_agent_boundary_known_negative.md +31 -0
  33. package/scripts/adapters/fixtures/mate_agent_boundary_known_positive.md +56 -0
  34. package/scripts/adapters/peer_resolve.sh +58 -6
  35. package/scripts/cluster_capability_scan.sh +60 -18
  36. package/scripts/digest_landing_check.sh +181 -10
  37. package/scripts/fh_hub_identity.sh +83 -0
  38. package/scripts/fh_session_load.sh +53 -5
  39. package/scripts/fh_track_resolve.sh +114 -0
  40. package/scripts/field_canon_preload.sh +50 -5
  41. package/scripts/package_coverage_check.sh +17 -0
  42. package/scripts/prior_art_prompt.sh +168 -0
  43. package/scripts/psa_scan_lib.sh +201 -15
  44. package/scripts/residency_admission_check.sh +204 -0
  45. package/scripts/selfcheck.sh +117 -1
  46. package/scripts/test_adapter_lanes.sh +67 -2
  47. package/scripts/test_heavy_classifier_lanes.sh +144 -0
  48. package/scripts/test_marker_defense_lanes.sh +152 -0
  49. package/scripts/test_marker_soul_check_lanes.sh +211 -0
  50. package/scripts/test_prior_art_prompt_lanes.sh +128 -0
  51. package/scripts/test_psa_singlefile_lanes.sh +351 -1
  52. package/scripts/test_residency_admission_lanes.sh +60 -0
  53. package/scripts/test_track_resolve_lanes.sh +158 -0
  54. package/templates/.git-hooks/pre-commit +400 -4
  55. package/templates/.git-hooks/pre-push +17 -2
  56. package/templates/settings.PriorArt.snippet.json +15 -0
  57. package/scripts/test_satellite_publish_gate_lanes.sh +0 -339
@@ -202,6 +202,50 @@ re-checks** it against the artifact (phantom-quench back-trace) before accepting
202
202
  source-grounded is **dropped, not judged**. No sidecar-only verdict (no weak-local-judge regression).
203
203
  Local 4090 = **canary tier** (evidence-of, never terminal verdict).
204
204
 
205
+ ๐ŸŸฅ **What you SEND decides which axis you get โ€” put the MARKER in the payload, not just the diff**
206
+ (2026-08-19). Sidecars have been receiving the *diff* only. That buys a review of the **code** and
207
+ buys nothing on the **record**: the Axes 2+3 marker is self-attested, and this repo has already
208
+ written down that the closing move is *"cross-family reading that marker"* โ€” then never wired the
209
+ marker into the thing that recruits cross-family. Measured twice: (a) a release marker's false
210
+ `not-applicable` passed the new typed lanes untouched, because **form was correct**; (b) this skill's
211
+ own dispatch on 2026-08-19 sent a diff and no marker, so the round could not have caught a false
212
+ axis claim even in principle.
213
+
214
+ **So the payload is two parts**: the frozen diff **and** the marker for this change โ€” **as it stands
215
+ at dispatch time**: `axes-run:` ยท `controls:` ยท `standpoint:` ยท `thirdparty:` ยท `residual:`.
216
+
217
+ ๐ŸŸฅ **`crossfamily:` is NOT in the payload โ€” it is this round's OUTPUT.** Step 6 below says every rung
218
+ *emits* that value and "the rung is not done until its verdict is recorded", so requiring it in the
219
+ thing you send is circular: you would be shipping a field this dispatch has not produced yet. Send the
220
+ marker with that line **absent or `UNKNOWN`**, and fill it from the result. (Caught by a blind
221
+ floor-tier sim of this very paragraph, 2026-08-19 โ€” the first draft listed `crossfamily:` among the
222
+ payload fields and no reader could have satisfied it.) Ask the sidecar one extra
223
+ question: *"does the record match the diff โ€” is any axis claimed that the diff shows no trace of, and
224
+ is any residual missing that the diff implies?"*
225
+
226
+ โš ๏ธ **Scope, deliberately narrow.** This asks whether the record is **consistent with the artifact**.
227
+ It does NOT ask the sidecar to score honesty โ€” ยซis this marker truthfulยป is a *conclusion*, and
228
+ ยงMechanization Boundary forbids freezing that into machinery. Consistency is checkable from two
229
+ documents; honesty is not.
230
+ โš ๏ธ **You cannot observe that the sidecar READ it โ€” say so rather than implying otherwise.** Putting the
231
+ marker in the payload and the sidecar actually using it are different events, and nothing here
232
+ distinguishes them: a silently-ignored marker looks exactly like a marker that was read and raised
233
+ nothing. The cheap partial anchor is to require the return to **quote one marker line it checked** โ€”
234
+ a reply that quotes nothing did not demonstrably read it. That is evidence-of-reading, not proof, and
235
+ it does not close ยงMechanization Boundary's named self-attestation residual. Three independent
236
+ readers flagged this same gap on the day the paragraph was written (a peer session, and two blind
237
+ floor-tier sims), which is why it is stated here instead of left to the reader to notice.
238
+
239
+ โš ๏ธ **Prose, not a check** โ€” measured recurrence is 2, below this repo's own Nโ‰ฅ3 bar
240
+ (`[[feedback_mechanize_at_repetition_prose_before]]`). On the third occurrence, mechanize it here.
241
+ โš ๏ธ **Residency still governs**: a marker can name company assets. Sanitize before any external-family
242
+ dispatch, exactly as with the diff โ€” the marker is not exempt because it is metadata.
243
+
244
+ *Origin*: sister-asset read of `raphaelchristi/harness-evolver`'s `harness-critic` agent, whose whole
245
+ role is auditing the **evaluator** rather than the artifact. Its detection signatures (score jumps,
246
+ suspiciously fast convergence) do **not** port โ€” FH markers carry no score โ€” but the *target* does.
247
+ Full assessment incl. what was deliberately not imported: `tracks/_audit/proposal_2026-08-19_sister_harness-evolver.md`.
248
+
205
249
  **Target freeze โ€” a prior drop reason, checked before any of the above (2026-08-17).** Grounding a
206
250
  finding against *today's* tree proves nothing if the sidecar reviewed *yesterday's*. Pin before
207
251
  dispatch and verify on return:
@@ -46,6 +46,37 @@ the session card and CATALOG, exactly as the greeting path would.
46
46
  End with "pick a door, say a phrase, or just state your task". Do not auto-run anything โ€” this
47
47
  command is a map, not a dispatcher.
48
48
 
49
+
50
+ ## Step 3.5 โ€” ๊ฐ€์ด๋“œ ยท Q&A (๐Ÿ“– ๋ฌธ์„ ๊ณจ๋ž์„ ๋•Œ๋งŒ ์ง„์ž…)
51
+
52
+ ๐ŸŸฅ **Q&A ๋Š” net-new ๊ธฐ๋Šฅ์ด ์•„๋‹ˆ๋‹ค.** FH ์— ๋Œ€ํ•ด ๋ฌป๊ณ  ๋‹ตํ•˜๋Š” ๊ฒƒ์€ ์ด๋ฏธ ๋œ๋‹ค(CLAUDE.md ๊ฐ€ ์ƒ์ฃผ๋ผ
53
+ ์„ธ์…˜์ด ๋ฌธยท๊ฒŒ์ดํŠธยท์Šคํ‚ฌ์„ ์•ˆ๋‹ค). ์ด ์ ˆ์ด ๋”ํ•˜๋Š” ๊ฒƒ์€ **ํ•˜๋‚˜๋ฟ**์ด๋‹ค โ€” ใ€Œ๋ฌด์—‡์„ ๊ทผ๊ฑฐ๋กœ ๋‹ตํ•˜๋‚˜,
54
+ ๊ทธ๋ฆฌ๊ณ  ์—†์œผ๋ฉด ์—†๋‹ค๊ณ  ๋งํ•œ๋‹คใ€๋Š” **๊ณ„์•ฝ**. ๊ทธ๋ž˜์„œ ์ƒˆ ์Šคํ‚ฌ์„ ๋งŒ๋“ค์ง€ ์•Š๊ณ  ์—ฌ๊ธฐ ๋ถ™์ธ๋‹ค.
55
+
56
+ **โ“ ๊ฐ€์ด๋“œ** โ€” `docs/USER_GUIDE.md` ์˜ **๊ฒฝ๋กœ์™€ 3์ค„ ๋ชฉ์ฐจ**๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค. ์Šน์ธํ•˜๋ฉด ํ”Œ๋žซํผ opener ๋ฅผ
57
+ ์ œ์•ˆํ•œ๋‹ค(`uname -s`: Darwinโ†’`open` ยท Linuxโ†’`xdg-open` ยท MINGW/MSYSโ†’`start`). opener ๊ฐ€ ์—†์œผ๋ฉด
58
+ ์กฐ์šฉํžˆ ๊ฑด๋„ˆ๋›ด๋‹ค โ€” ๊ฒฝ๋กœ๋Š” ์ด๋ฏธ ๋‚˜๊ฐ”์œผ๋ฏ€๋กœ ์†์‹ค์ด ์—†๋‹ค.
59
+ ๐ŸŸฅ **์ „๋ฌธ ์ธ๋ผ์ธ ์ถœ๋ ฅ ๊ธˆ์ง€.** ์ž๋™ ์‹คํ–‰๋„ ์•ˆ ํ•œ๋‹ค.
60
+
61
+ **โ“‘ Q&A** โ€” 1๋ฌธ 1๋‹ต. ๊ทผ๊ฑฐ๋Š” ์•„๋ž˜ ์ฝ”ํผ์Šค **์•ˆ์—์„œ๋งŒ** ์ฐพ๋Š”๋‹ค:
62
+
63
+ ```
64
+ 1 docs/USER_GUIDE.md ์‚ฌ์šฉ๋ฒ• ยท FAQ
65
+ 2 CHEATSHEET.md ๋ช…๋ น ยท ํŠธ๋ฆฌ๊ฑฐ ๋ฌธ๊ตฌ
66
+ 3 knowledge/shared/GLOSSARY.md ์šฉ์–ด
67
+ 4 README.md ยงGet started/ยงLearn more ์„ค์น˜ ยท ์ง„์ž… ๊ฒฝ๋กœ
68
+ 5 CATALOG.md ใ€Œ์˜ˆ์ „์— ๋ญ ํ–ˆ์ง€ใ€
69
+ 6 ์„ค์น˜๋œ SKILL.md frontmatter ใ€Œ๋ฌด์Šจ ์Šคํ‚ฌ ์žˆ์–ดใ€
70
+ ```
71
+
72
+ **degrade โ€” ์ฝ”ํผ์Šค์— ์—†์œผ๋ฉด**
73
+ ๐ŸŸฅ **์ง€์–ด๋‚ด์ง€ ์•Š๋Š”๋‹ค.** ใ€Œ๋ชป ์ฐพ์Œ โ€” ์ฝ”ํผ์Šค N๊ฐœ๋ฅผ ๋ดค๊ณ  ์—ฌ๊ธฐ์—” ์—†๋‹คใ€ + **๋‹ค์Œ ํ•œ ๊ฑธ์Œ**(์–ด๋А ํŒŒ์ผ์„
74
+ ์—ด์ง€ ยท `/install-doctor` ๊ฐ™์€ ์‹ค์ œ ์ง„๋‹จ ๊ฒฝ๋กœ)์„ ์ค€๋‹ค. ใ€Œ์•„๋งˆ ์ด๋Ÿด ๊ฒƒ์ด๋‹คใ€ ํ˜•ํƒœ์˜ ๋‹ต์€ **๊ธˆ์ง€**๋‹ค.
75
+ ์ด๊ฑด ยงInstrument Calibration ์˜ ยซ๋ฏธ์ธก์ •์„ 0์œผ๋กœ ๋ Œ๋”ํ•˜์ง€ ์•Š๋Š”๋‹คยป ์™€ ๊ฐ™์€ ๊ทœ์œจ์ด๋‹ค.
76
+ ๋‹ต๋งˆ๋‹ค `file:line` ์„ ๋‹จ๋‹ค โ€” ๊ทผ๊ฑฐ ์—†๋Š” ๋ฌธ์žฅ์€ ํŒฌํ…€์ด๋‹ค.
77
+
78
+ โš ๏ธ ์ฝ”ํผ์Šค ๋ฐ– ์งˆ๋ฌธ(๋„๋ฉ”์ธ ์ž‘์—… ยท ์ฝ”๋“œ)์€ ์ด ์ ˆ์ด ์•„๋‹ˆ๋ผ ํ‰์†Œ๋Œ€๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค. Q&A ๋Š” **FH ์‚ฌ์šฉ๋ฒ•**์šฉ์ด๋‹ค.
79
+
49
80
  ## Done When
50
81
 
51
82
  | Condition | Check class |
@@ -214,7 +214,7 @@ fi
214
214
  No Stop hook is required. After the Codex goal/session completes, resolve changed files with git and run:
215
215
 
216
216
  ```bash
217
- FH_BACKEND=codex npx @chrono-meta/fh-gate "{changed-files}" quick codex-goal
217
+ FH_BACKEND=codex npx --package @chrono-meta/fh-gate fh-gate "{changed-files}" quick codex-goal
218
218
  ```
219
219
 
220
220
  For high-stakes or external-facing work, use `full` instead of `quick`. Treat `BLOCKED` or `ESCALATE` the same as the Claude path: fix and re-run the gate, or surface the decision to the user.
@@ -149,7 +149,13 @@ running tool itself). Full procedure: `knowledge/shared/harness-core/measurement
149
149
  `M-n ยท <verbatim row text or its threshold> ยท <measured value>`. If you cannot quote the row, **you do not
150
150
  have a finding** โ€” downgrade to an observation. Origin (2026-07-20, instrument defect n+4, the *fourth* in
151
151
  a single run): a run fired `M-1 ยท CLAUDE.md 816 lines โ€” exceeds the FH threshold of 500`. The string `500`
152
- does not occur anywhere in this file (grep: 0 hits), and the meta-harness row it claimed to read says raw
152
+ did not occur anywhere in this file at the time (a later grep finds 2 โ€” both in this narrative, added by
153
+ this very post-mortem; the self-referential claim drifted and is corrected here rather than re-asserted).
154
+ ๐ŸŸฅ **And that grep was scoped wrong**: it searched this file only. The 500 was NOT invented from nothing โ€”
155
+ `docs/platform_sustainability.md` carried it as a live standard until 2026-08-20, contradicting this row.
156
+ The lesson is not "the run hallucinated" but **"the absence check was run against the wrong corpus"**
157
+ ([[feedback_absence_measurement_needs_control]] โ€” a no-hit grep is evidence only when its scope covers
158
+ where the thing would actually live). The meta-harness row it claimed to read says raw
153
159
  line count is **"Not a verdict."** So the run invented a threshold *and* fired a row this skill explicitly
154
160
  disables for meta-harnesses โ€” the exact recurrence of the 2026-07-15 inversion documented below, which had
155
161
  already been patched *in the skill*. The patch held; the **run** ignored it. A verdict grounded in a
@@ -54,6 +54,38 @@ Session end
54
54
  โ”‚ edit-manifest VERIFY: check pending predictions in edit_manifest.yaml
55
55
  โ”‚ memory-hygiene scan: staleness check on memory/*.md entries (skip if < 7 days)
56
56
  โ”‚
57
+ [Step 0-d] Session Retrospective (close_retro ๊ฐ€ granted ์ผ ๋•Œ๋งŒ)
58
+ ๐ŸŸฅ ์ƒˆ ์Šคํ‚ฌ์„ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค โ€” ํšŒ๊ณ  ์‚ฐ์ถœ์€ ๊ทธ๋Œ€๋กœ Step 2(contention-layer) โ†’ Step 3 โ†’ 3.5(๋“ฑ๊ธ‰)
59
+ ๋กœ **์ด๋ฏธ ์žˆ๋Š” ํŒŒ์ดํ”„๋ผ์ธ**์„ ํƒ„๋‹ค. ใ€Œ๊ฐœ์„ ํฌ์ธํŠธ ์ •๋ฆฌใ€๊ฐ€ ๊ฑฐ๊ธฐ์„œ ๊ณต์งœ๋กœ ๋ถ™๋Š”๋‹ค.
60
+
61
+ ์ง„์ž… ์กฐ๊ฑด (๊ธฐ๊ณ„์ , ์ด ์ˆœ์„œ๋กœ):
62
+ 1. `tracks/_meta/user_adaptation_profile.md` frontmatter ์˜ `close_retro`
63
+ granted โ†’ ์‹คํ–‰
64
+ declined โ†’ ๊ฑด๋„ˆ๋›ด๋‹ค. **๋‹ค์‹œ ๋ฌป์ง€ ์•Š๋Š”๋‹ค**(operational_adaptation.md no-re-nag)
65
+ unset / UAP ๋ถ€์žฌ / ephemeral โ†’ **์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ , ์ง€์–ด๋‚ด์ง€๋„ ์•Š๋Š”๋‹ค.**
66
+ ์šด์˜์ž ๋งฅ๋ฝ์ด๋ฉด(= `CLAUDE.local.md` ์กด์žฌ, `psa_detect_operator_context()` ์™€ ๊ฐ™์€
67
+ ํŒ๋ณ„์ž) **์ตœ์ดˆ ๋งˆ๊ฐ 1ํšŒ๋งŒ** ์ œ์•ˆํ•˜๊ณ  ๋‹ต์„ UAP ์— ๊ธฐ๋กํ•œ๋‹ค. ๊ทธ ์ด์ƒ ๋ฌป์ง€ ์•Š๋Š”๋‹ค.
68
+ ๐ŸŸฅ `CLAUDE.local.md` ์กด์žฌ๊ฐ€ ํŒ๋ณ„์ž์ธ ์ด์œ ๋Š” ๊ทธ๊ฒƒ์ด **gitignored** ๋ผ fresh cloneยทCI ์ฒดํฌ์•„์›ƒยท
69
+ ์›ŒํฌํŠธ๋ฆฌ ์–ด๋””์—๋„ ์•ˆ ๋”ฐ๋ผ์˜ค๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ธฐ๊ฐ๋œ ํ›„๋ณด: ใ€Œtracks/_meta ๊ฐ€ ๋น„์–ด์žˆ์ง€ ์•Š์Œใ€ โ€”
70
+ `.gitkeep` ์ด tracked ๋ผ **fresh clone ์ด ๋งŒ์กฑ์‹œํ‚จ๋‹ค**(psa_scan_lib.sh ์ฃผ์„์˜ ์‹ค์ธก).
71
+ ๐ŸŸฅ **์šด์˜์ž ๋งฅ๋ฝ์ด๋ผ๊ณ  ์ž๋™ granted ๊ฐ€ ์•„๋‹ˆ๋‹ค.** ํŒŒ์ผ์ด ์กด์žฌํ•ด์„œ ์Šน์ธ๋˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ
72
+ **์šด์˜์ž ๋ฐœํ™”๊ฐ€ ์ธ์šฉ๋ผ ๊ธฐ๋ก๋˜๋Š” ์ˆœ๊ฐ„** ์Šน์ธ๋œ๋‹ค. ์ „์ž๋Š” ์„ธ์…˜์ด ์ž๊ธฐ ๊ถŒํ•œ์„ ๋„“ํžˆ๋Š” ํ˜•ํƒœ๋‹ค.
73
+
74
+ ์‚ฐ์ถœ: ์˜ค๋Š˜์ž `tracks/_meta/fh_signal_{date}_{source}.md` ์— `retro: close` ๋ฅผ ๋‹ฌ๊ณ 
75
+ `## Session Retrospective` 4ํ•„๋“œ๋ฅผ ์ฑ„์šด๋‹ค(ํ˜•์‹ ์ •๋ณธ = fh_detail_protocols.md).
76
+ ใ€Œ์—†์Œใ€ยทใ€Œ0ใ€๋„ ์œ ํšจํ•œ ๊ฐ’์ด๊ณ , **๋น„์šฐ๋Š” ๊ฒƒ๋งŒ ์•ˆ ๋œ๋‹ค.**
77
+
78
+ Done When:
79
+ + ์˜ค๋Š˜์ž signal ํŒŒ์ผ์ด ์กด์žฌํ•˜๊ณ  `retro: close` ๋ฅผ ๋‹ฌ๊ณ  ์žˆ๋‹ค โ€” mandatory-pass
80
+ + Session Retrospective 4ํ•„๋“œ๊ฐ€ ์ „๋ถ€ ์ฑ„์›Œ์กŒ๋‹ค โ€” mandatory-pass
81
+ + ์ •์ • ๊ฑด์ˆ˜๋Š” **์„ธ์—ˆ์ง€ ํšŒ์ƒํ•˜์ง€ ์•Š์•˜๋‹ค** โ€” measured
82
+ + FH Registration Candidate ๊ฐ€ ๋น„๋ฉด ๊ทธ ์ด์œ ๊ฐ€ ํ•œ ์ค„ ์ ํ˜€ ์žˆ๋‹ค โ€” mandatory-pass
83
+
84
+ โš ๏ธ `session_close_check.sh` ์— ๊ธฐ๊ณ„ ๊ฒ€์‚ฌ๋ฅผ **๋ถ™์ด์ง€ ์•Š๋Š”๋‹ค.** ๊ฐ’์‹ธ๊ฒŒ ๋ถ™์ผ ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ๋Š”
85
+ ยซ์˜ค๋Š˜์ž signal ์ด ์žˆ๋‚˜ยป ์ธ๋ฐ ๊ทธ๊ฑด *์กด์žฌ*๋งŒ ์žฌ๊ณ  *ํšŒ๊ณ ํ–ˆ๋‚˜*๋Š” ๋ชป ์žฐ๋‹ค โ€” CLAUDE.md ๊ฐ€ โ‘ฃ ๋ฉ”๋ชจ๋ฆฌ
86
+ ์œ„์ƒ์— ๋Œ€ํ•ด ๊ฐ™์€ ์ด์œ ๋กœ ๊ฒ€์‚ฌ๋ฅผ ๋บ๋‹ค(ยซa check that can be satisfied without doing the work
87
+ is a decorationยป). ๊ฐ™์€ ํŒ๋‹จ์„ ๊ทธ๋Œ€๋กœ ์ ์šฉํ•œ๋‹ค.
88
+
57
89
  [Step 0] Regression Guard
58
90
  โ”‚ Check: does anything from this session conflict with or regress a validated skill?
59
91
  โ”‚ โ†’ Regression detected: flag, route to contention-layer
@@ -25,9 +25,24 @@ if [ -r "scripts/psa_scan_lib.sh" ]; then
25
25
  # Feed every tracked file as path<TAB>line, the stream psa_scan_tagged consumes. Sourcing the lib
26
26
  # without these calls is a no-op scan โ€” measured on this repo (PSA_STREAM stayed unset), so the
27
27
  # calls are spelled out here rather than pointed at.
28
+ # ๐ŸŸฅ rc ๋ฅผ ๋ฐ˜๋“œ์‹œ ๋ฐ›์•„๋ผ (2026-08-21 ๋ฐฐ์„  ๋ฆฌ๋ทฐ S-1). ์ด ํŒŒ์ดํ”„๋Š” **์›๋ž˜ ๋šซ๋ ธ๋˜ ๋ฐ”๋กœ ๊ทธ ์ง„์ž…์ **์ด๊ณ ,
29
+ # ๋ฐ”๋กœ ์•„๋ž˜ coverage ์กฐ๊ฑด์ด `$?` ๋ฅผ ๋ฎ์œผ๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ ์•ˆ ๋ฐ›์œผ๋ฉด ๊ณ„์•ฝ์ด ์†Œ์‹ค๋œ๋‹ค.
30
+ # ๊ณ„์•ฝ: 0=์‹ ๊ณ ํ•  ๊ฒƒ ์—†์Œ ยท 1=์œ ์ถœ(์ด๋ฏธ ์ธ์‡„๋จ) ยท 3=NOT SCANNED(๊ณ„๊ธฐ ์‚ฌ๋ง)
31
+ # โš ๏ธ `_psa_rc=0` ์€ ๋ฐ˜๋“œ์‹œ `while` **๋ฐ–**์— ๋‘”๋‹ค โ€” ์•ˆ์— ๋‘๋ฉด ๋ฃจํ”„ ๋ณธ๋ฌธ์ด๋ผ ๋งค ์ค„ ์ดˆ๊ธฐํ™”๋˜๊ณ 
32
+ # ํŒŒ์ดํ”„ ์„œ๋ธŒ์…ธ์— ๊ฐ‡ํžŒ๋‹ค(์ดˆํŒ์ด ๊ทธ๋ ‡๊ฒŒ ๋„ฃ์—ˆ๊ณ  `bash -n` ์€ ํ†ต๊ณผํ–ˆ๋‹ค. ๋ฌธ๋ฒ•์€ ๋งž๊ณ  ์˜๋ฏธ๊ฐ€ ํ‹€๋ฆฐ๋‹ค).
33
+ _psa_rc=0
28
34
  while IFS= read -r f; do
29
35
  awk -v p="$f" '{printf "%s\t%s\n", p, $0}' "$f" 2>/dev/null
30
- done < /tmp/_psa_tracked.txt | psa_scan_tagged
36
+ done < /tmp/_psa_tracked.txt | psa_scan_tagged || _psa_rc=$?
37
+ # ๐ŸŸฅ 3 ์€ ยซ๊นจ๋—ยป ์ด ์•„๋‹ˆ๋ผ ยซ์•ˆ ์Ÿ€๋‹คยป ๋‹ค. ์—ฌ๊ธฐ์„œ ๋ฉˆ์ถฐ์•ผ ํ•œ๋‹ค โ€” ์ด ์Šคํ‚ฌ์€ publish ์ง์ „์—
38
+ # Pre-Publish Gate ๊ฐ€ 1๋ฒˆ์œผ๋กœ ์ฒด์ด๋‹ํ•˜๋Š” ๋ Œ์ฆˆ์ด๊ณ , ๊ทธ ์ž๋ฆฌ์—์„œ ๋ฏธ์ธก์ •์„ ํ†ต๊ณผ์‹œํ‚ค๋ฉด
39
+ # ์•„๋ž˜ coverage ์ค„์ด ยซdefaults-only ๋กœ๋Š” ์Šค์บ”ํ–ˆ๋‹คยป ๋Š” ์ธ์ƒ๊นŒ์ง€ ์–น๋Š”๋‹ค.
40
+ if [ "$_psa_rc" -eq 3 ]; then
41
+ echo "โ›” INSTRUMENT DEAD: the scanner did not run (rc=3). NOT SCANNED is not clean."
42
+ echo " Fix first โ€” run under bash (zsh special vars can blank PATH inside the matcher),"
43
+ echo " and confirm psa_load ran (PSA_STREAM non-empty). Do NOT report a verdict from this run."
44
+ exit 3
45
+ fi
31
46
  [ "$PSA_OVERRIDE_PRESENT" -eq 1 ] \
32
47
  || echo "coverage: defaults-only โ€” operator literals NOT CONFIGURED (identity/company classes UNSCANNED)"
33
48
  else
@@ -84,6 +84,54 @@ External CLIs available: [yes/no โ†’ Wave 5 available]
84
84
 
85
85
  ---
86
86
 
87
+ ## Step 0.35 โ€” Org Constraint Load (์กฐ์ง ์ œ์•ฝ ์ ์žฌ)
88
+
89
+ **์กฐ์ง ์ œ์•ฝ์„ ๋ชจ๋ฅด๋Š” ์ ๋Œ€ ๊ฒ€์ฆ์€ ํ—›๋ฐฉ์„ ์นœ๋‹ค** โ€” ์กฐ์ง์ด ์ด๋ฏธ ๊ฒฐ์ •ํ•œ ๊ฒƒ์„ ๊ณต๊ฒฉํ•˜๊ฑฐ๋‚˜, ์กฐ์ง ์ •์ฑ…
90
+ ํ•˜์—์„œ๋งŒ ์„ฑ๋ฆฝํ•˜๋Š” ์‹ค์ œ ๊ณต๊ฒฉ์„ ๋†“์นœ๋‹ค. ๊ณต๊ฒฉ ๊ฐ๋„๋ฅผ ์ •ํ•˜๊ธฐ **์ „์—** ์กฐ์ง์ธต์„ ์ฝ๋Š”๋‹ค.
91
+ ๊ณ„์•ฝ: `knowledge/shared/rules/knowledge_layer_seam.md` (์ด ์Šคํ‚ฌ์ด **2ํ˜ธ ์†Œ๋น„์ž**;
92
+ 1ํ˜ธ๋Š” `phantom-quench` Step 2-O).
93
+
94
+ **์ ˆ์ฐจ**: ์ง„์ž… ์ธ๋ฑ์Šค(`knowledge/{org}/INDEX.md` ยท `index.md` ยท `README.md` ยท `readme.md` โ€”
95
+ ํŒ์ •๊ธฐ์™€ ๊ฐ™์€ ํ›„๋ณด ์ง‘ํ•ฉ) โ†’ ๋Œ€์ƒ๊ณผ **๊ด€๋ จ๋œ** ์ •์ฑ…ยท์šฉ์–ดยท๋„๋ฉ”์ธ ์‚ฌ์‹ค๋งŒ ๋กœ๋“œ. ์ „์ˆ˜ ์Šค์บ” ๊ธˆ์ง€(๊ณ„์•ฝ K2).
96
+ ๋ถ€์žฌ๋ฉด **ยซ์กฐ์ง ์ œ์•ฝ ๋ฏธ์ƒยป์œผ๋กœ ๋ช…์‹œํ•˜๊ณ  ์ง„ํ–‰** โ€” ์—†๋Š” ์ œ์•ฝ์„ ์ถ”๋ก ์œผ๋กœ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค.
97
+ ๐ŸŸฅ `not found` ๋Š” ยซ์ œ์•ฝ ์—†์Œยป ์ด ์•„๋‹ˆ๋‹ค. ๋ฏธ์ƒ์€ ๋ฏธ์ƒ์œผ๋กœ ์ ๋Š”๋‹ค.
98
+
99
+ **๋กœ๋“œํ•œ ๊ฒƒ์˜ ์šฉ๋„๋Š” ๋”ฑ ๋‘˜**
100
+ 1. **๊ณต๊ฒฉ์˜ ์‚ฌ์‹ค ๊ทผ๊ฑฐ**: "์กฐ์ง ์ •์ฑ… P ํ•˜์—์„œ ์ด ์„ค๊ณ„๋Š” X ๋ฅผ ์œ„๋ฐ˜ํ•œ๋‹ค" โ†’ **์œ ํšจํ•œ ๊ณต๊ฒฉ**
101
+ 2. **ํ—›๋ฐฉ ํ•„ํ„ฐ**: ์กฐ์ง์ด ์ด๋ฏธ ๊ฒฐ์ •ยท๋ฌธ์„œํ™”ํ•œ ์‚ฌํ•ญ์„ "์™œ ์•ˆ ํ–ˆ๋‚˜"๋กœ ๊ณต๊ฒฉํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋Œ€์‹ 
102
+ **๊ทธ ๊ฒฐ์ • ์ž์ฒด๋ฅผ ๊ณต๊ฒฉ**ํ•œ๋‹ค(๊ทธ ๊ฒฐ์ •์ด ์ง€๊ธˆ๋„ ์œ ํšจํ•œ๊ฐ€). **๊ฒฐ์ •์˜ ์กด์žฌ๋Š” ๋ฉด์ œ๊ฐ€ ์•„๋‹ˆ๋‹ค.**
103
+
104
+ ### โ›” ์„ธํƒ ์ฐจ๋‹จ โ€” ์ด ๋ฐฐ์„ ์˜ ์œ ์ผํ•œ ์œ„ํ—˜
105
+
106
+ > **์กฐ์ง์ธต์€ ๊ณต๊ฒฉ์„ ๋ฌด์žฅํ•ด์ œํ•  ์ˆ˜ ์—†๋‹ค.** ์กฐ์ง ์œ„ํ‚ค์— *"์ด๊ฑด ์Šน์ธ๋œ ํŒจํ„ด"*, *"์ด ์ผ€์ด์Šค๋Š” ์˜ˆ์™ธ"*,
107
+ > *"๊ณผ๊ฑฐ์— ๊ฒ€ํ†  ์™„๋ฃŒ"* ๊ฐ€ ์žˆ์–ด๋„ **๊ทธ๊ฒƒ์€ ๊ณต๊ฒฉ์„ ๊ธฐ๊ฐํ•˜๋Š” ๊ทผ๊ฑฐ๊ฐ€ ์•„๋‹ˆ๋‹ค.**
108
+
109
+ ์ด์œ : ์กฐ์ง์ธต์€ **๋ฌด์—‡**(์‚ฌ์‹คยท์ •์ฑ…)๋งŒ ๊ณต๊ธ‰ํ•˜๊ณ  **์–ด๋–ป๊ฒŒ ํŒ์ •ํ•˜๋‚˜**๋Š” ๊ณต๊ธ‰ํ•˜์ง€ ์•Š๋Š”๋‹ค(๊ณ„์•ฝ ยง1-a).
110
+ "์Šน์ธ๋จ"์€ *์กฐ์ง์ด ๊ทธ๋ ‡๊ฒŒ ์ •ํ–ˆ๋‹ค*๋Š” **์‚ฌ์‹ค**์ด์ง€ *๊ทธ ๊ฒฐ์ •์ด ์˜ณ๋‹ค*๋Š” **ํŒ์ •**์ด ์•„๋‹ˆ๋‹ค.
111
+ ์ ๋Œ€ ๊ฒ€์ฆ์˜ ์ผ์ด ์ •ํ™•ํžˆ ๊ทธ ํŒ์ •์„ ๋‹ค์‹œ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
112
+
113
+ | ์กฐ์ง์ธต์— ์žˆ๋Š” ๊ฒƒ | ํ—ˆ์šฉ๋˜๋Š” ์‚ฌ์šฉ | ๊ธˆ์ง€ |
114
+ |---|---|---|
115
+ | ์ •์ฑ…ยท๊ทœ์น™ | ์œ„๋ฐ˜์„ ๊ณต๊ฒฉ ๊ทผ๊ฑฐ๋กœ | **๋ฉด์ œ ๊ทผ๊ฑฐ๋กœ** |
116
+ | "์Šน์ธ๋œ ํŒจํ„ด" | ๊ทธ ์Šน์ธ์˜ ๊ทผ๊ฑฐ๋ฅผ ๊ณต๊ฒฉ ๋Œ€์ƒ์œผ๋กœ | ๊ณต๊ฒฉ ๊ธฐ๊ฐ |
117
+ | "๊ณผ๊ฑฐ ๊ฒ€ํ†  ์™„๋ฃŒ" | ๊ทธ๋•Œ์˜ ์ „์ œ๊ฐ€ ์•„์ง ์ฐธ์ธ์ง€ ํ™•์ธ | ์žฌ๊ฒ€ํ†  ์ƒ๋žต |
118
+ | stale ํŽ˜์ด์ง€(`review_after` ๊ฒฝ๊ณผ) | **์ œ์•ฝ์œผ๋กœ ์“ฐ์ง€ ์•Š๋Š”๋‹ค** โ€” ๋ฏธ์ƒ ์ฒ˜๋ฆฌ | ์ตœ์‹ ์œผ๋กœ ๊ฐ€์ • |
119
+
120
+ **๋ณด๊ณ  ์˜๋ฌด**: ์กฐ์ง์ธต ๋•Œ๋ฌธ์— ๊ณต๊ฒฉ์„ ์กฐ์ •ํ–ˆ์œผ๋ฉด(๊ฐ๋„ ์ถ”๊ฐ€ ยท ํ—›๋ฐฉ ์ œ๊ฑฐ) **๋ฌด์—‡์„ ์™œ ์กฐ์ •ํ–ˆ๋Š”์ง€
121
+ Wave 1 ์ถœ๋ ฅ์— ๋‚จ๊ธด๋‹ค.** ์กฐ์šฉํ•œ ์กฐ์ •์€ ๊ฒ€์ฆ ๋ฒ”์œ„ ์ถ•์†Œ์™€ ๊ตฌ๋ณ„๋˜์ง€ ์•Š๋Š”๋‹ค.
122
+
123
+ **๋ฐ˜์ถœ ๊ธˆ์ง€**(K1-s): cross-provider/cross-family ์ฑŒ๋ฆฐ์ €์— ์กฐ์ง์ธต **์›๋ฌธ์„ ๋„˜๊ธฐ์ง€ ์•Š๋Š”๋‹ค** โ€”
124
+ ๋„˜์–ด๊ฐ€๋Š” ๊ฒƒ์€ **sanitized ์ œ์•ฝ ์š”์•ฝ**๋ฟ์ด๋‹ค. (CLAUDE.md ยงField-Harness Diagnostic ์˜ residency
125
+ ๊ทœ์น™๊ณผ ๊ฐ™์€ floor ์ด๊ณ , ์ด ์Šคํ‚ฌ์ด ๊ทธ๊ฒƒ์„ ๋А์Šจํ•˜๊ฒŒ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค.)
126
+
127
+ > **์ถœ์ฒ˜**: ์› ํ•„๋“œ(sibling harness)์˜ ์„ ๋ก€๋ฅผ ์ด์‹ํ–ˆ๋‹ค. FH ์ž๊ธฐ ์ •๋ณธ์ด ์ด ์ž๋ฆฌ๋ฅผ **๋ช…์‹œ์ ์œผ๋กœ
128
+ > ใ€Œ๋ฏธ๋ฐฐ์„  โ€” ์•ฝ์†์ด ์•„๋‹ˆ๋ผ ํ›„๋ณดใ€**๋กœ ์ ์–ด๋‘๊ณ  ์žˆ์—ˆ๋‹ค(`knowledge_layer_seam.md` ยง0 ๋ฐฐ์„  ํ˜„ํ™ฉ).
129
+ > ์ด ์ ˆ์ด ๊ทธ ํ–‰์„ ํ›„๋ณด์—์„œ ๋ฐฐ์„ ์œผ๋กœ ์˜ฎ๊ธด๋‹ค.
130
+ > ๐ŸŸฅ **์ด์‹ํ•œ ๊ฒƒ์€ ์ ˆ์ฐจ์ด์ง€ ๊ทธ์ชฝ์˜ ๊ธฐ๊ณ„-์ฃผ์žฅ์ด ์•„๋‹ˆ๋‹ค** โ€” ์›๋ณธ์—๋Š” ํ›…์ด ์ด ์ ˆ์„ ๊ฐ•์ œํ•œ๋‹ค๋Š”
131
+ > ์ทจ์ง€์˜ ์„œ์ˆ ์ด ๋”ธ๋ ค ์žˆ์—ˆ์œผ๋‚˜, ์‹ค์ธกํ•˜๋‹ˆ ๊ทธ ํ›… ๋ ˆ์ธ์ด **์กด์žฌํ•˜์ง€ ์•Š์•˜๋‹ค**(`axis2-defense` ํ›… ํžˆํŠธ 0,
132
+ > ์ปจํŠธ๋กค `crossfamily` 21). ๊ทธ๋ž˜์„œ **๊ฐ•์ œ ์„œ์ˆ ์€ ์•ˆ ๊ฐ€์ ธ์™”๋‹ค.** ์ด ์ ˆ์€ ์˜ค๋Š˜ ๊ธฐ์ค€
133
+ > **์‚ด๋ฆฌ์–ธ์Šค ์ธต์ด๊ณ  ๊ธฐ๊ณ„ ๋ฐ”๋‹ฅ์ด ์—†๋‹ค** โ€” ๊ทธ๋ ‡๊ฒŒ ์ ๋Š” ๊ฒƒ์ด ํŒฌํ…€์„ ๋“ค์—ฌ์˜ค์ง€ ์•Š๋Š” ์œ ์ผํ•œ ๋ฐฉ๋ฒ•์ด๋‹ค.
134
+
87
135
  ## Step 0.4 โ€” Specialized Reviewer Discovery
88
136
 
89
137
  For the target artifact, scan installed agents for a domain-specific adversarial reviewer:
@@ -211,6 +259,55 @@ A finding here is a real-code attack (Wave 1 execution principle) โ€” cite the e
211
259
 
212
260
  ---
213
261
 
262
+ ## Wave 1-D โ€” Defense Questions (floor tiers ยท mechanically required)
263
+
264
+ Three questions, asked of **your own findings and numbers**, before Wave 1 is done. They are written
265
+ out rather than left to judgment because that is exactly what makes them portable: measured n=6 in
266
+ the origin field, a floor-tier pass executes the attack angles above without defect but **does not
267
+ spontaneously ask these three**, while a stronger tier does. That is a **checklist gap, not a
268
+ capability gap** โ€” and by `sonnet_floor_doctrine.md` a harness whose behaviour depends on which model
269
+ is driving is *defective*, not merely limited.
270
+
271
+ | # | Question | What a real answer looks like |
272
+ |:---:|---|---|
273
+ | **์žฌํ˜„์„ฑ** | Can another session reproduce this verdict from the same inputs? | The exact command, or `file:line`, another session runs. "It's reproducible" is not an answer. |
274
+ | **๋น„๊ต๊ณต์ •์„ฑ** | Were the two arms measured under the same conditions? | reps, inputs and environment named for **both** arms. An asymmetry you found and left in place counts โ€” say so. |
275
+ | **์ถ”์ •์ธต์œ„** | Is each number a measurement, an estimate, or a quotation? | Which, per number โ€” and for a measurement, what showed the instrument works **on this target** (known-pair). |
276
+
277
+ **Where the answers go** โ€” one line in the Axes 2+3 marker:
278
+
279
+ ```
280
+ axis2-defense: reproducibility=<โ€ฆ> fairness=<โ€ฆ> estimation-layer=<โ€ฆ>
281
+ ```
282
+
283
+ **Enforcement, stated exactly.** `templates/.git-hooks/pre-commit` โ†’ `validate_defense_leg()` runs
284
+ this **at the floor tiers only** (`floor-status: sonnet-floor` or `below-floor`), and checks
285
+ **presence ยท completeness ยท non-vacuity**: all three sub-answers must exist and `ok`/`yes`/`n/a` is
286
+ rejected as a filled form rather than an answer. Fixtures: `scripts/test_marker_defense_lanes.sh`
287
+ (17 lanes: known-pairs both directions, two over-block controls, four prescription assertions, and a
288
+ call-site pin โ€” because a suite that extracts the function and calls it directly stays green when
289
+ the hook stops calling it, which is precisely the failure being imported against).
290
+ ๐ŸŸฅ **It cannot check whether the answers are TRUE.** That stays with the operator and the weekly
291
+ audit, exactly like every other marker field โ€” do not read the hook's PASS as verification.
292
+
293
+ **Why the trigger is narrow.** `below-floor` occurs **0 times** across the existing marker corpus, so
294
+ gating on it alone would be a decoration that never fires; `sonnet-floor` occurs 11. The wide reading
295
+ ("any marker carrying numbers") is deliberately **not** taken โ€” pricing this axis at a near-universal
296
+ rate is the over-trigger `field_verdict_crossfamily_gate.md ยง7` rejects, and a field required
297
+ everywhere becomes a rubber stamp.
298
+
299
+ > **Provenance, and what was deliberately NOT imported.** Absorbed from a sibling harness
300
+ > (2026-08-20). That document additionally amends its floor rule so a below-floor pass carrying this
301
+ > line **plus** a crossfamily record passes **without the operator ack**. ๐ŸŸฅ That is a *loosening* of
302
+ > an existing FH gate and **was not adopted** โ€” here the leg is purely additive and `below-floor`
303
+ > still requires `below-floor-ack`, unchanged.
304
+ > ๐ŸŸฅ The same document asserted its own hook read this field and its own fixture suite pinned it.
305
+ > Measured twice with a control (`crossfamily` โ†’ 21 hits in the same files): **both were 0**. The
306
+ > prose was portable; the machine was not. Everything claimed in this section's *Enforcement*
307
+ > paragraph was built here, and the fixture file named there is the receipt.
308
+
309
+ ---
310
+
214
311
  ## Wave 2 โ€” Defense Principles
215
312
 
216
313
  **3 Defense Principles**: (1) Reinforce with external cases via WebSearch โ€” "unique to us" or "structural pattern"?
@@ -0,0 +1,31 @@
1
+ # [CALIBRATION FIXTURE โ€” known-negative] ์ •๋ณด๊ฒฝ๊ณ„ ๋ถˆ๋ณ€์‹์ด **๋น ์ง„** ํ•ฉ์„ฑ ๋ช…์„ธ์„œ
2
+
3
+ > ๐ŸŸฅ **์ด๊ฒƒ์€ ์‹คํ–‰๋˜๋Š” ์—์ด์ „ํŠธ ์ •์˜๊ฐ€ ์•„๋‹ˆ๋‹ค.** `scripts/blackbox_invariant_check.sh` ์˜
4
+ > ์บ˜๋ฆฌ๋ธŒ๋ ˆ์ด์…˜ ์Œ ์ค‘ **๋ง‰ํ˜€์•ผ ํ•˜๋Š” ์ชฝ**(expect = FAIL, exit 1)์ด๋‹ค. ์ง์€
5
+ > `agent_boundary_known_positive.md`.
6
+ >
7
+ > ์ด ํŒŒ์ผ์ด ํ•„์š”ํ•œ ์ด์œ : ์–‘์„ฑ๋งŒ ์„ธ์šฐ๋ฉด ใ€Œ๋ถˆ๋ณ€์‹์ด ์‚ด์•„์žˆ๋‹คใ€์™€ ใ€Œ๊ฒ€์‚ฌ๊ธฐ๊ฐ€ ์•„๋ฌด๊ฒƒ๋„ ์•ˆ ๋ณธ๋‹คใ€๊ฐ€
8
+ > ๊ฐ™์€ ์ดˆ๋ก์œผ๋กœ ๋ณด์ธ๋‹ค. ์Œ์„ฑ arm ์€ ๊ณ„๊ธฐ์— **ํŒ๋ณ„๋ ฅ์ด ์žˆ๋Š”์ง€**๋ฅผ ์žฌ๋Š” ์ž๋ฆฌ๋‹ค.
9
+ >
10
+ > **์˜๋„์ ์œผ๋กœ ์œ„๋ฐ˜ํ•œ๋‹ค** โ€” ๋ถˆ๋ณ€์‹ ๋ฌธ์žฅ๋“ค์„ ์ „๋ถ€ ๋บ๋‹ค. ์‹ค ์‹๋ณ„์ž๋Š” ๋‹ด์ง€ ์•Š๋Š”๋‹ค(๊ทธ๊ฑด T1 ์ด
11
+ > ์žก๋Š” ๋ณ„๊ฐœ์˜ ์œ„๋ฐ˜์ด๊ณ , ์—ฌ๊ธฐ์„œ ๊ทธ๊ฒƒ๊นŒ์ง€ ํŠธ๋ฆฌ๊ฑฐํ•˜๋ฉด ์–ด๋А ์ถ•์ด ๋ง‰์•˜๋Š”์ง€ ํ๋ ค์ง„๋‹ค).
12
+
13
+ ## ์ž…๋ ฅ ์ธํ„ฐํŽ˜์ด์Šค
14
+
15
+ ์™ธ๋ถ€ findings ๋ฅผ ๋ฐ›๋Š”๋‹ค. `finding_id` ๋Š” ๋ถˆํˆฌ๋ช… ์‹๋ณ„์ž๋‹ค(์–ดํœ˜ ์ œ์•ฝ ์—†์Œ).
16
+ `location` ์€ ํŠน์ • ์ ‘๋‘์–ด๊ฐ€ ๋ณด์ด๋ฉด ํ๊ธฐํ•œ๋‹ค(denylist).
17
+
18
+ ## ํˆฌ์˜ ๊ทœ์น™
19
+
20
+ ์ฃผ์ž… ํŒŒ์ดํ”„๊ฐ€ ๋ฏผ๊ฐ ์ •๋ณด๋ฅผ ์ง€์šด๋‹ค. ๋ฐ›์€ ์ž…๋ ฅ์€ ๊ทธ๋Œ€๋กœ ๋‹ด์•„ ํ•˜์œ„๋กœ ์ „๋‹ฌํ•œ๋‹ค.
21
+ ๊ธˆ์ง€ ๋Œ€์ƒ์€ ์ด ๋ ˆํฌ ๋ฐ–์˜ ํŒŒ์ผ์ด๋‹ค.
22
+
23
+ ## ์•ˆ์ „ ๊ทœ์น™
24
+
25
+ 1. ํŒ์ • ๊ทผ๊ฑฐ๋Š” ์ธ์šฉ ๊ฐ€๋Šฅํ•œ ์ฆ๊ฑฐ๋กœ ๋‚จ๊ธด๋‹ค.
26
+ 2. ์ฆ๊ฑฐ ์—†๋Š” ์ถ”์ •์€ ์‹ ๋ขฐ๋„๋ฅผ ์˜ฌ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.
27
+ 3. ์‹คํŒจ๋ฅผ ์„ฑ๊ณต์œผ๋กœ ์‚ผํ‚ค์ง€ ์•Š๋Š”๋‹ค.
28
+ 4. ์žฌํ˜„ ์ ˆ์ฐจ ์—†์ด ์ˆ˜์ •์•ˆ์„ ์ œ์‹œํ•˜์ง€ ์•Š๋Š”๋‹ค.
29
+ 5. ๋„๋ฉ”์ธ ๋ฐ์ดํ„ฐ๋Š” ๋กœ๊ทธ์— ๋‚จ๊ธฐ์ง€ ์•Š๋Š”๋‹ค.
30
+ 6. ReadยทGrepยทGlob ์œผ๋กœ ๊ธˆ์ง€๋œ ์†Œ์Šค๋ฅผ ์—ด์ง€ ์•Š๋Š”๋‹ค.
31
+ 7. ์‹ ๋ขฐ๋„๋Š” ๊ทผ๊ฑฐ์˜ ๊ฐ•๋„์— ๋”ฐ๋ผ ์ •ํ•œ๋‹ค.
@@ -0,0 +1,56 @@
1
+ # [CALIBRATION FIXTURE โ€” known-positive] ์ •๋ณด๊ฒฝ๊ณ„ ๋ถˆ๋ณ€์‹ ํ•ฉ์„ฑ ๋ช…์„ธ์„œ
2
+
3
+ > ๐ŸŸฅ **์ด๊ฒƒ์€ ์‹คํ–‰๋˜๋Š” ์—์ด์ „ํŠธ ์ •์˜๊ฐ€ ์•„๋‹ˆ๋‹ค.** `scripts/blackbox_invariant_check.sh` ์˜
4
+ > ์บ˜๋ฆฌ๋ธŒ๋ ˆ์ด์…˜ ์Œ ์ค‘ **ํ†ต๊ณผํ•ด์•ผ ํ•˜๋Š” ์ชฝ**(expect = PASS, exit 0)์ด๋‹ค. ์ง์€
5
+ > `agent_boundary_known_negative.md` ์ด๊ณ  ๊ทธ์ชฝ์€ ๋ง‰ํ˜€์•ผ ํ•œ๋‹ค(expect = FAIL, exit 1).
6
+ >
7
+ > **์™œ ์‹ค๋ฌผ ๋ช…์„ธ์„œ๋ฅผ ์“ฐ์ง€ ์•Š๋‚˜**: ์‹ค๋ฌผ์€ ํŒ€์ด ๊ณ„์† ๊ณ ์น˜๋Š” ์‚ด์•„์žˆ๋Š” ๋ฌธ์„œ๋ผ, ๊ทธ๊ฒƒ์„ ์–‘์„ฑ
8
+ > arm ์œผ๋กœ ์“ฐ๋ฉด ๊ณ„๊ธฐ์˜ ํŒ์ •์ด ๋‚จ์˜ ํŽธ์ง‘์— ๋”ฐ๋ผ ์กฐ์šฉํžˆ ๋’ค์ง‘ํžŒ๋‹ค(๊ฐ์‚ฌ ๋Œ€์ƒ์€ ์–ผ๋ ค์•ผ ํ•œ๋‹ค).
9
+ > ์‹ค๋ฌผ ๋ช…์„ธ์„œ๋Š” ์ธ์ž ์—†๋Š” ๊ธฐ๋ณธ ๋Œ€์ƒ์œผ๋กœ ๊ณ„์† ๊ฒ€์‚ฌ๋œ๋‹ค โ€” ์ด ํŒŒ์ผ์ด ๊ทธ๊ฒƒ์„ ๋Œ€์ฒดํ•˜์ง€ ์•Š๋Š”๋‹ค.
10
+ >
11
+ > ์‹๋ณ„์ž๋Š” ์ „๋ถ€ ํ•ฉ์„ฑ์ด๋‹ค. ์‹ค์ œ ์„œ๋น„์Šคยท๋ ˆํฌยทํ•„๋“œ ์ด๋ฆ„์€ ํ•œ ๊ฑด๋„ ๋‹ด์ง€ ์•Š๋Š”๋‹ค.
12
+
13
+ ## ์ž…๋ ฅ ์ธํ„ฐํŽ˜์ด์Šค
14
+
15
+ ์™ธ๋ถ€ ์ •์ ๋ถ„์„์—์„œ ์˜จ findings ๋ฅผ ๋ฐ›๋˜, ์ด ์—์ด์ „ํŠธ๋Š” ๊ทธ findings ์˜ **์›๋ณธ ์†Œ์Šค**๋ฅผ ์ ˆ๋Œ€
16
+ ์ฝ์ง€ ์•Š๋Š”๋‹ค. ์ฃผ์ž… ์ธก์ด ํˆฌ์˜ํ•œ ๊ฒฐ๊ณผ๋งŒ ๋ณธ๋‹ค.
17
+
18
+ - `finding_id` โ€” ๋ถˆํˆฌ๋ช… ์‹๋ณ„์ž. ์–ดํœ˜ ์ œ์•ฝ: `^[a-f0-9]{6,64}$` ๋งŒ ํ—ˆ์šฉํ•œ๋‹ค.
19
+ ์ž์œ ๋ฌธ์ž์—ด์ด๋ฉด ๊ฒฝ๋กœยท๋ฐœ์ทŒ๋ฅผ ์‹ค์–ด ๋‚˜๋ฅด๋Š” ์šฐํšŒ ์šด๋ฐ˜ ์ฑ„๋„์ด ๋œ๋‹ค.
20
+ - `location` โ€” allowlist ์ •๊ทœ์‹ `^<FE-REDACTED>::[0-9]+$` ๋งŒ ํ—ˆ์šฉํ•œ๋‹ค.
21
+ denylist(โ€œํŠน์ • ์ ‘๋‘์–ด๊ฐ€ ๋ณด์ด๋ฉด ํ๊ธฐโ€)๋Š” ์ƒ๋Œ€๊ฒฝ๋กœยท`file:///` ๋“ฑ์œผ๋กœ ๋šซ๋ฆฐ๋‹ค.
22
+
23
+ ## ํˆฌ์˜ ๊ทœ์น™
24
+
25
+ **ํˆฌ์˜์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์ฃผ์ฒด๋Š”** ์ฃผ์ž… ์ธก(ํŒŒ์ดํ”„๋ผ์ธ)์ด๋‹ค. ์ˆ˜ํ–‰ ์ฃผ์ฒด๋ฅผ ์ ์ง€ ์•Š์œผ๋ฉด redact ๊ทœ์น™์ด
26
+ ์ˆ˜ํ–‰์ž ์—†๋Š” ๊ณต์ˆ˜ํ‘œ๊ฐ€ ๋œ๋‹ค. ๋”ฐ๋ผ์„œ ์ด ์—์ด์ „ํŠธ๋Š” ์ž…๋ ฅ์„ **"๊ทธ๋Œ€๋กœ" ์ „๋‹ฌํ•˜์ง€ ์•Š๋Š”๋‹ค** โ€”
27
+ ํˆฌ์˜๋œ ํ˜•ํƒœ๋งŒ ํ•˜์œ„๋กœ ๋„˜๊ธด๋‹ค.
28
+
29
+ ๊ธˆ์ง€ ๋Œ€์ƒ์€ **์œ„์น˜๊ฐ€ ์•„๋‹ˆ๋ผ ์ถœ์ฒ˜**๋กœ ์ •์˜ํ•œ๋‹ค. ๋ ˆํฌ ์•ˆ์— ๋ณต์‚ฌ๋œ ์‚ฌ๋ณธ, ๋นŒ๋“œ ์‚ฐ์ถœ๋ฌผ,
30
+ `sourcesContent` ๋ฅผ ๋‹ด์€ ์†Œ์Šค๋งต ์ „๋ถ€ ๋™์ผํ•˜๊ฒŒ ๊ธˆ์ง€๋‹ค. "๋ ˆํฌ ๋ฐ–"์œผ๋กœ ์ •์˜ํ•˜๋ฉด ๋ ˆํฌ ์•ˆ
31
+ ์‚ฌ๋ณธ์ด ํ†ต์งธ๋กœ ๋ฉด์ œ๋œ๋‹ค.
32
+
33
+ ### ํ˜„์žฌ ๋ฐฐ์„  ์ƒํƒœ
34
+
35
+ ์ด ํˆฌ์˜ ํŒŒ์ดํ”„๋Š” **์•„์ง ์ฝ”๋“œ๋กœ ๊ตฌํ˜„๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค.** ๋ฏธ๊ตฌํ˜„ ํ†ต์ œ๋ฅผ ๊ตฌํ˜„๋œ ๊ฒƒ์ฒ˜๋Ÿผ ์ ์œผ๋ฉด
36
+ ๊ทธ ์ž์ฒด๊ฐ€ ๋‚™๊ด€ ๋ฐฉํ–ฅ ๊ฒฐํ•จ์ด๋‹ค.
37
+
38
+ ๊ทธ๋ž˜์„œ **์Šฌ๋กฏ์€ ํ˜„์žฌ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.** ์ •์˜๋˜์ง€ ์•Š์€ ์ ˆ์ฐจ์— must-never ๋ถˆ๋ณ€์‹์„ ์–น์ง€
39
+ ์•Š๊ธฐ ์œ„ํ•ด, ํ™œ์„ฑํ™” **๊ฒฝ๋กœ๋ฅผ ๋‘์ง€ ์•Š๋Š”๋‹ค.**
40
+
41
+ ## ์•ˆ์ „ ๊ทœ์น™
42
+
43
+ 1. ํŒ์ • ๊ทผ๊ฑฐ๋Š” ์ธ์šฉ ๊ฐ€๋Šฅํ•œ ์ฆ๊ฑฐ๋กœ ๋‚จ๊ธด๋‹ค.
44
+ 2. ์ฆ๊ฑฐ ์—†๋Š” ์ถ”์ •์€ ์‹ ๋ขฐ๋„๋ฅผ ์˜ฌ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.
45
+ 3. ์‹คํŒจ๋ฅผ ์„ฑ๊ณต์œผ๋กœ ์‚ผํ‚ค์ง€ ์•Š๋Š”๋‹ค.
46
+ 4. ์žฌํ˜„ ์ ˆ์ฐจ ์—†์ด ์ˆ˜์ •์•ˆ์„ ์ œ์‹œํ•˜์ง€ ์•Š๋Š”๋‹ค.
47
+ 5. ๋„๋ฉ”์ธ ๋ฐ์ดํ„ฐ๋Š” ๋กœ๊ทธ์— ๋‚จ๊ธฐ์ง€ ์•Š๋Š”๋‹ค.
48
+ 6. **๋ฉ”๋ชจ๋ฆฌ ์“ฐ๊ธฐ ๊ธˆ์ง€** โ€” **์ถœ์ฒ˜๊ฐ€ ํ™”์ดํŠธ๋ฐ•์Šค๋ฉด ์ €์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค.** ์ €์žฅ๋œ ๊ฐ’์€ ๋‹ค์Œ ํ˜ธ์ถœ์—
49
+ ์ž๋™ ์ฃผ์ž…๋˜๋ฏ€๋กœ, ์ฝ์ง€ ์•Š์„ ๊ธฐํšŒ์กฐ์ฐจ ์—†์ด ์„ธ์…˜ ๊ฒฝ๊ณ„๋ฅผ ๋„˜์–ด ์˜ค์—ผ์ด ์ง€์†๋œ๋‹ค.
50
+ 7. ์ƒํ•œ ๋ถˆ๋ณ€์‹: ์™ธ๋ถ€ findings ๊ทผ๊ฑฐ๋งŒ ์žˆ๊ณ  ๋Ÿฐํƒ€์ž„ ์ฆ๊ฑฐ(XML)๊ฐ€ ์—†์œผ๋ฉด ์‹ ๋ขฐ๋„๋Š” MEDIUM ์„
51
+ ๋„˜์ง€ ๋ชปํ•œ๋‹ค. ์ด ์กฐํ•ญ์€ **๋‹ค๋ฅธ ๋ชจ๋“  ์กฐํ•ญ์— ์šฐ์„ **ํ•œ๋‹ค.
52
+ 8. ๊ธˆ์ง€๋œ ์†Œ์Šค์˜ **๋Šฅ๋™ ํƒ์ƒ‰**์„ ํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ด ๊ธˆ์ง€๋Š” **๋„๊ตฌ ๋ฌด๊ด€**์ด๋‹ค โ€” ReadยทGrepยทGlob
53
+ ๋ฟ ์•„๋‹ˆ๋ผ `Bash` ๋ฅผ ๊ฒฝ์œ ํ•œ ์—ด๋žŒ๋„ ๊ฐ™์€ ์œ„๋ฐ˜์ด๋‹ค. ๋„๊ตฌ๋ฅผ ์—ด๊ฑฐํ•˜๋Š” ํ˜•ํƒœ๋กœ ์ ์œผ๋ฉด ์—ด๊ฑฐ๋˜์ง€
54
+ ์•Š์€ ๊ฒฝ๋กœ๊ฐ€ ๊ทธ๋Œ€๋กœ ์šฐํšŒ๋กœ๊ฐ€ ๋œ๋‹ค.
55
+ ํ•„์ˆ˜ ์ฑ„๋„(๋ฆฌํฌํŠธยท์ฒจ๋ถ€)์—์„œ **์กฐ์šฐ**ํ•œ ๊ฒฝ์šฐ๋Š” ๋ณ„๊ฐœ๋‹ค: ์—ด๊ธฐ ์ „์—๋Š” ์•Œ ์ˆ˜ ์—†์œผ๋ฏ€๋กœ
56
+ "์ฝ์ง€ ๋งˆ๋ผ"๋Š” ์ดํ–‰ ๋ถˆ๊ฐ€๋Šฅํ•œ ์˜๋ฌด๊ฐ€ ๋œ๋‹ค. ์กฐ์šฐ ์‹œ์—๋Š” ์ค‘๋‹จํ•˜๊ณ  ๋ณด๊ณ ํ•œ๋‹ค.
@@ -35,6 +35,38 @@
35
35
  # rc=3 NO_ROOTS โ€” ๋ฃจํŠธ ๋ชฉ๋ก ์ž์ฒด๊ฐ€ ๋น„์—ˆ๋‹ค(์ „์ œ ํŒŒ์†. ใ€Œ์—†๋‹คใ€๊ฐ€ ์•„๋‹ˆ๋‹ค)
36
36
  # stderr : rc!=0 ์ผ ๋•Œ ํ•œ ์ค„ ์‚ฌ์œ 
37
37
 
38
+ # โ”€โ”€ ๋‹จ์ผ ์†Œ์Šค ๋ฐฐ์„  (2026-08-21 F-1) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
39
+ # ๐ŸŸฅ ์œ„ ์ฃผ์„์ด ยซ์Šค์บ๋„ˆ์™€ ๊ฐ™์€ ์ˆœ์„œ์—ฌ์•ผ ํ•œ๋‹คยป ๊ณ  **๊ด€๋ก€๋กœ** ์ ์–ด ๋‘์—ˆ๋˜ ๊ทธ ์ค‘๋ณต์„ ์—ฌ๊ธฐ์„œ ๋‹ซ๋Š”๋‹ค.
40
+ # `scripts/fh_track_resolve.sh` ๊ฐ€ track ์ด๋ฆ„ โ†’ ๋ ˆํฌ ๋ฃจํŠธ ํ•ด์„์˜ ๋‹จ์ผ ์†Œ์Šค์ด๊ณ ,
41
+ # ์•„๋ž˜ `FH_PROJECTS_HOME` ํŒ”์€ ๊ทธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ถ€๋ฅธ๋‹ค.
42
+ #
43
+ # โš ๏ธ **`FH_CLUSTER_ROOTS` ํŒ”์€ ๋‹ซํžˆ์ง€ ์•Š๋Š”๋‹ค โ€” ์กฐํšŒ ๋ชจ๋ธ์ด ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.**
44
+ # ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ยซ๋ฃจํŠธ ํ•˜๋‚˜ ๋ฐ‘์˜ `$root/$c`ยป ๋ฅผ ๋ฌป๋Š”๋‹ค. ์ด ํŒ”์€ ยซ์ž„์˜ ์ ˆ๋Œ€๊ฒฝ๋กœ ๋ชฉ๋ก์˜
45
+ # `basename` ์ด ๋ณ„์นญ์ธ๊ฐ€ยป ๋ฅผ ๋ฌป๋Š”๋‹ค. ์ด๋ฆ„๋งŒ ๊ฐ™๊ณ  ์˜๋ฏธ๊ฐ€ ๋‹ค๋ฅธ ๋‘˜์„ ์–ต์ง€๋กœ ํ•ฉ์น˜๋ฉด ๊ทธ๊ฑด
46
+ # ์ด ์ˆ˜๋ฆฌ๊ฐ€ ๋ง‰์œผ๋ ค๋Š” ๊ฒฐํ•จ๊ณผ ๊ฐ™์€ ์ข…๋ฅ˜๋‹ค. ๊ทธ๋ž˜์„œ ๊ทธ ํŒ”์€ ์ž๊ธฐ ์—ด๊ฑฐ๋ฅผ ์œ ์ง€ํ•œ๋‹ค
47
+ # (โ‡’ `fh_peer_alias_candidates` ๋Š” ๋‚จ๋Š”๋‹ค. ๋ถ€์žฌ ๋ฉ”์‹œ์ง€์™€ ๊ฐ•๋“ฑ ํด๋ฐฑ๋„ ์ด๊ฑธ ์“ด๋‹ค).
48
+ #
49
+ # ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์—†์œผ๋ฉด **์ˆ˜๋ฆฌ ์ด์ „ ๋™์ž‘**์œผ๋กœ ๊ฐ•๋“ฑํ•œ๋‹ค โ€” ๋‹ค๋ฅธ ์„ธ ์†Œ๋น„์ž์™€ ๊ฐ™์€ ํ˜•ํƒœ๋‹ค.
50
+ # (ํด๋ฐฑ์€ ์˜๋„์ ์œผ๋กœ ์˜› ๊ตฌํ˜„ ๊ทธ๋Œ€๋กœ๋‹ค. ๊ณต๋ฐฑ ๊ฒฐํ•จ๊นŒ์ง€ ํฌํ•จํ•ด์„œ ยซ์ˆ˜๋ฆฌ ์ด์ „ยป ์ด๋‹ค.)
51
+ _FH_TRLIB="$(cd -P "$(dirname "${BASH_SOURCE[0]:-$0}")/.." 2>/dev/null && pwd)/fh_track_resolve.sh"
52
+ # shellcheck source=scripts/fh_track_resolve.sh
53
+ [ -f "$_FH_TRLIB" ] && . "$_FH_TRLIB"
54
+ type fh_resolve_track_root >/dev/null 2>&1 || fh_resolve_track_root() {
55
+ local n="$1" root="$2" c hits="" first="" nh
56
+ for c in "$n" "${n}-dev" "$(printf '%s' "$n" | tr '_' '-')"; do
57
+ [ -d "$root/$c" ] || continue
58
+ case " $hits " in *" $c "*) continue ;; esac
59
+ hits="$hits $c"; [ -n "$first" ] || first="$c"
60
+ done
61
+ nh=$(printf '%s' "$hits" | wc -w | tr -d ' ')
62
+ if [ "${nh:-0}" -gt 1 ]; then
63
+ printf '%s|AMBIGUOUS:%s' "$root/$n" "$(printf '%s' "$hits" | sed 's/^ //; s/ /,/g')"; return 0
64
+ fi
65
+ [ -n "$first" ] || { printf '%s|UNRESOLVED' "$root/$n"; return 0; }
66
+ [ "$first" = "$n" ] && { printf '%s|' "$root/$n"; return 0; }
67
+ printf '%s|alias:%s' "$root/$first" "$first"
68
+ }
69
+
38
70
  # ๋ณ„์นญ ํ›„๋ณด โ€” ๋‹ซํžŒ ๋ชฉ๋ก. ์Šค์บ๋„ˆ์˜ `_resolve_root` ์™€ **๊ฐ™์€ ์ˆœ์„œ**์—ฌ์•ผ ํ•œ๋‹ค.
39
71
  fh_peer_alias_candidates() { # $1=name โ†’ ํ›„๋ณด๋ฅผ ์ค„ ๋‹จ์œ„๋กœ
40
72
  printf '%s\n' "$1" "$1-dev" "$(printf '%s' "$1" | tr '_' '-')"
@@ -74,13 +106,33 @@ EOF
74
106
  printf 'peer-resolve: ํ”„๋กœ์ ํŠธ ํ™ˆ์ด ์—†๋‹ค: %s (์ „์ œ ํŒŒ์†์ด์ง€ ยซpeer ์—†์Œยป ์ด ์•„๋‹ˆ๋‹ค)\n' "$home" >&2
75
107
  return 3
76
108
  fi
77
- while IFS= read -r c; do
78
- [ -d "$home/$c" ] || continue
79
- case " $hits " in *" $home/$c "*) continue ;; esac
80
- hits="$hits $home/$c"; [ -n "$first" ] || first="$home/$c"
81
- done <<EOF
82
- $(fh_peer_alias_candidates "$n")
109
+ # ๐ŸŸฅ ์—ฌ๊ธฐ๊ฐ€ ๋‹จ์ผ ์†Œ์Šค๋กœ ๋‹ซํžŒ ์ž๋ฆฌ๋‹ค. ์˜› ์ธ๋ผ์ธ ๋ฃจํ”„(๋ณ„์นญ 3์ข… + ๊ณต๋ฐฑ ๊ตฌ๋ถ„์ž dedup)๋ฅผ
110
+ # `fh_resolve_track_root` ๋กœ ๋Œ€์ฒดํ•œ๋‹ค. ์ถœ๋ ฅ ๊ณ„์•ฝ์€ **์•„๋ž˜์—์„œ ์˜› ํ‘œ๊ธฐ๋กœ ๋ณต์›**ํ•œ๋‹ค โ€”
111
+ # ์†Œ๋น„์ž 4์ข…๊ณผ ๋ ˆ์ธ์€ ์—ฌ์ „ํžˆ rc(0/1/2/3) + ์˜› stderr ๋ฌธ๊ตฌ๋ฅผ ์ฝ๋Š”๋‹ค.
112
+ local rr note
113
+ rr="$(fh_resolve_track_root "$n" "$home" dir)"
114
+ note="${rr##*|}"
115
+ case "$note" in
116
+ AMBIGUOUS:*)
117
+ # ์˜› ํ‘œ๊ธฐ ๋ณต์›: ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” **ํ›„๋ณด ์ด๋ฆ„**์„ ์ฝค๋งˆ๋กœ ์ฃผ๊ณ , ์˜› ๋ฉ”์‹œ์ง€๋Š” **์ ˆ๋Œ€๊ฒฝ๋กœ**๋ฅผ
118
+ # ยซ, ยป ๋กœ ์ด์–ด ๋ถ™์˜€๋‹ค. ๊ณต๋ฐฑ ์—†๋Š” ์ด๋ฆ„์—์„œ๋Š” ๋ฐ”์ดํŠธ ๋™์ผํ•˜๋‹ค.
119
+ local _l="${note#AMBIGUOUS:}" _c _paths=""
120
+ while IFS= read -r _c; do
121
+ [ -n "$_c" ] || continue
122
+ if [ -n "$_paths" ]; then _paths="$_paths, $home/$_c"; else _paths="$home/$_c"; fi
123
+ done <<EOF
124
+ $(printf '%s' "$_l" | tr ',' '\n')
83
125
  EOF
126
+ printf 'peer-resolve: ํ•œ ์ด๋ฆ„์— ๋ ˆํฌ ์—ฌ๋Ÿฟ โ€” ๊ณ ๋ฅด์ง€ ์•Š๋Š”๋‹ค: %s\n' "$_paths" >&2
127
+ return 2
128
+ ;;
129
+ UNRESOLVED)
130
+ first="" # ์•„๋ž˜ ๊ณตํ†ต ยซ๋ถ€์žฌยป ๋ถ„๊ธฐ๋กœ ๋–จ์–ด์ง„๋‹ค (rc=1 ยท ์˜› ๋ฌธ๊ตฌ ๊ทธ๋Œ€๋กœ)
131
+ ;;
132
+ *)
133
+ first="${rr%|*}"
134
+ ;;
135
+ esac
84
136
  fi
85
137
 
86
138
  nh=$(printf '%s' "$hits" | wc -w | tr -d ' ')
@@ -76,6 +76,31 @@ CAP_SUBDIR=".claude/capabilities"
76
76
  # ํ…Œ์ŠคํŠธยท๋‹ค๋ฅธ ๋ฐฐ์น˜๋ฅผ ์œ„ํ•ด `FH_CLUSTER_ROOTS`(์ฝœ๋ก  ๊ตฌ๋ถ„ ์ ˆ๋Œ€๊ฒฝ๋กœ ๋ชฉ๋ก)๋กœ ํ†ต์งธ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ๋‹ค.
77
77
  PROJECTS_HOME="${FH_PROJECTS_HOME:-$HOME/projects}"
78
78
 
79
+ # track ์ด๋ฆ„ โ†’ ๋ ˆํฌ ๋ฃจํŠธ ํ•ด์„์€ scripts/fh_track_resolve.sh ๊ฐ€ ๋‹จ์ผ ์†Œ์Šค๋‹ค (2026-08-21 F-1).
80
+ # ๐ŸŸฅ **์ด ํŒŒ์ผ์ด ๊ทธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์›๋ณธ์ด๋‹ค** โ€” ๋‹ซํžŒ ๋ณ„์นญ 3์ข… + ๋ณ„์นญ ํ‘œ๋ฉดํ™” + ๋ชจํ˜ธ๊ฑฐ๋ถ€๋Š” ์—ฌ๊ธฐ์„œ
81
+ # ๋ฝ‘์•„๋‚ธ ๊ฒƒ์ด๊ณ , ๊ฐˆ๋ผ์ ธ ์žˆ๋˜ ๋‚˜๋จธ์ง€ ๋‘ ์†Œ๋น„์ž(fh_session_load.sh ยท field_canon_preload.sh)๊ฐ€
82
+ # ๊ฐ™์€ ๋ฒŒ์„ ์“ฐ๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด ์ด ๋ฐฐ์„ ์˜ ์ „๋ถ€๋‹ค. ์ด ํŒŒ์ผ์˜ ๋™์ž‘์€ ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค.
83
+ # ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์—†์œผ๋ฉด **์ˆ˜๋ฆฌ ์ด์ „ ๋™์ž‘**(= ์•„๋ž˜ ์ธ๋ผ์ธ ๊ตฌํ˜„)์œผ๋กœ ๊ฐ•๋“ฑํ•œ๋‹ค โ€” ๋‹ค๋ฅธ ๋‘ ์†Œ๋น„์ž์™€
84
+ # ๊ฐ™์€ ํ˜•ํƒœ๋‹ค. ์ƒˆ ์‹คํŒจ ๋ชจ๋“œ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค.
85
+ _FH_TRLIB="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)/fh_track_resolve.sh"
86
+ # shellcheck source=scripts/fh_track_resolve.sh
87
+ [ -f "$_FH_TRLIB" ] && . "$_FH_TRLIB"
88
+ type fh_resolve_track_root >/dev/null 2>&1 || fh_resolve_track_root() {
89
+ local n="$1" root="$2" c hits="" first=""
90
+ for c in "$n" "$n-dev" "$(printf '%s' "$n" | tr '_' '-')"; do
91
+ [ -d "$root/$c" ] || continue
92
+ case " $hits " in *" $c "*) continue ;; esac
93
+ hits="$hits $c"; [ -n "$first" ] || first="$c"
94
+ done
95
+ local nh; nh=$(printf '%s' "$hits" | wc -w | tr -d ' ')
96
+ if [ "${nh:-0}" -gt 1 ]; then
97
+ printf '%s|AMBIGUOUS:%s' "$root/$n" "$(printf '%s' "$hits" | sed 's/^ //;s/ /,/g')"; return 0
98
+ fi
99
+ [ -n "$first" ] || { printf '%s|UNRESOLVED' "$root/$n"; return 0; }
100
+ [ "$first" = "$n" ] && { printf '%s|' "$root/$n"; return 0; }
101
+ printf '%s|alias:%s' "$root/$first" "$first"
102
+ }
103
+
79
104
  _die() { printf 'โŒ %s\n' "$1" >&2; exit "$RC_HARNESS"; }
80
105
 
81
106
  # โ”€โ”€ `.cap` ํŒŒ์ผ ์—ด๊ฑฐ โ€” **๋‹จ ํ•˜๋‚˜์˜ ์†Œ์Šค** โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
@@ -150,20 +175,24 @@ _enumerate_harnesses() {
150
175
  # ๐ŸŸฅ ๊ทธ๋ž˜๋„ ยซ์กฐ์šฉํžˆ ์ถ”์ธกยป ํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค. ๋ณ„์นญ์€ **๋‹ซํžŒ ๋ชฉ๋ก**์ด๊ณ , ๋งž์€ ๋ณ„์นญ์€
151
176
  # `_ALIAS_USED` ๋กœ ํ‘œ๋ฉดํ™”๋œ๋‹ค. ์—ฌ๋Ÿฌ ๊ฐœ๊ฐ€ ๋™์‹œ์— ๋งž์œผ๋ฉด **๊ณ ๋ฅด์ง€ ์•Š๊ณ  ๋ชจํ˜ธ๋กœ ๋‚ธ๋‹ค** โ€”
152
177
  # ๋‘˜ ์ค‘ ํ•˜๋‚˜๋ฅผ ์กฐ์šฉํžˆ ๊ณ ๋ฅด๋Š” ๊ฒƒ์ด ์ด ํŒŒ์ผ์ด ๋ฐ˜๋Œ€ํ•˜๋Š” ๊ทธ ์ ‘ํž˜์ด๋‹ค.
178
+ # ๐ŸŸฅ ์ด ํ•จ์ˆ˜๋Š” ์ด์ œ **์–ด๋Œ‘ํ„ฐ**๋‹ค โ€” ํ•ด์„์€ `fh_resolve_track_root`(๋‹จ์ผ ์†Œ์Šค)๊ฐ€ ํ•˜๊ณ , ์—ฌ๊ธฐ์„œ๋Š”
179
+ # ์ด ํŒŒ์ผ์˜ **ํ‘œ์‹œ ๊ณ„์•ฝ**์œผ๋กœ ์˜ฎ๊ธด๋‹ค. ๋‘ ๊ณ„์•ฝ์ด ๋ฏธ๋ฌ˜ํ•˜๊ฒŒ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ์กฐ์šฉํžˆ ํ†ต๊ณผ์‹œํ‚ค์ง€
180
+ # ์•Š๋Š”๋‹ค:
181
+ # ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ `alias:<c>` โ†” ์—ฌ๊ธฐ `๊ฒฝ๋กœ ๋ณ„์นญ โ€” alias:<c>` (OK ํ–‰์˜ DETAIL ๋กœ ์ง์ ‘ ์ถœ๋ ฅ๋จ)
182
+ # ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ `UNRESOLVED` โ†” ์—ฌ๊ธฐ ๋นˆ ๋ฌธ์ž์—ด (๋ถ€์žฌ๋Š” `_harness_status` ๊ฐ€
183
+ # UNREACHABLE ๋กœ ํŒ์ •ํ•œ๋‹ค)
184
+ # `AMBIGUOUS:<...>` ๋Š” ์–‘์ชฝ์ด ๊ฐ™์€ ํ‘œ๊ธฐ์ด๊ณ  `_harness_status` ์˜ `AMBIGUOUS:*` ๋ถ„๊ธฐ๊ฐ€
185
+ # ๊ทธ๋Œ€๋กœ ๋ฐ›๋Š”๋‹ค. ์ˆ ์–ด๋Š” `dir` โ€” ์ด ์Šค์บ๋„ˆ๋Š” ยซ๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ์žˆ๋‚˜ยป ๋ฅผ ๋ฌป๋Š”๋‹ค(git ๋ ˆํฌ ์—ฌ๋ถ€๊ฐ€
186
+ # ์•„๋‹ˆ๋‹ค). ๊ทธ ์ฐจ์ด๋Š” ์˜๋„์ด์ง€ ๊ฐˆ๋ผ์ง์ด ์•„๋‹ˆ๋‹ค(๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ—ค๋” ์ฐธ์กฐ).
153
187
  _resolve_root() { # $1=track ์ด๋ฆ„ โ†’ "<root>|<alias-note>"
154
- local n="$1" c hits="" first="" note=""
155
- for c in "$n" "$n-dev" "$(printf '%s' "$n" | tr '_' '-')"; do
156
- [ -d "$PROJECTS_HOME/$c" ] || continue
157
- case " $hits " in *" $c "*) continue ;; esac
158
- hits="$hits $c"; [ -n "$first" ] || first="$c"
159
- done
160
- local nh; nh=$(printf '%s' "$hits" | wc -w | tr -d ' ')
161
- if [ "${nh:-0}" -gt 1 ]; then
162
- printf '%s|AMBIGUOUS:%s' "$PROJECTS_HOME/$n" "$(printf '%s' "$hits" | sed 's/^ //;s/ /,/g')"
163
- return
164
- fi
165
- [ -n "$first" ] && [ "$first" != "$n" ] && note="๊ฒฝ๋กœ ๋ณ„์นญ โ€” alias:$first"
166
- printf '%s|%s' "$PROJECTS_HOME/${first:-$n}" "$note"
188
+ local rr note
189
+ rr="$(fh_resolve_track_root "$1" "$PROJECTS_HOME" dir)"
190
+ note="${rr#*|}"
191
+ case "$note" in
192
+ alias:*) printf '%s|๊ฒฝ๋กœ ๋ณ„์นญ โ€” %s' "${rr%%|*}" "$note" ;;
193
+ UNRESOLVED) printf '%s|' "${rr%%|*}" ;;
194
+ *) printf '%s' "$rr" ;;
195
+ esac
167
196
  }
168
197
 
169
198
  # ํ•œ ํ•˜๋„ค์Šค์˜ ์ƒํƒœ๋ฅผ ํŒ์ •ํ•œ๋‹ค. ์ถœ๋ ฅ ํ•œ ์ค„: "<name>\t<STATUS>\t<n>\t<detail>"
@@ -590,12 +619,25 @@ EOF
590
619
  # ([[feedback_ci_measures_state_not_transition]] ์˜ ๋ฐ์ดํ„ฐ ํŒ๋ณธ).
591
620
  # ์•ต์ปค๋ฅผ **์ถ”์ ๋˜๋Š” ํŒŒ์ผ**๋กœ ์˜ฎ๊ธด๋‹ค โ€” `.claude/capabilities/*.cap` ์€ ์ปค๋ฐ‹๋ผ ์žˆ์–ด
592
621
  # ์–ด๋А ์ฒดํฌ์•„์›ƒ์—์„œ๋„ ์กด์žฌํ•œ๋‹ค. ์ˆ˜์ง‘ ๊ฒฝ๋กœ๊ฐ€ ์ฃฝ์œผ๋ฉด ์ด ์นด์šดํŠธ๊ฐ€ 0 ์ด ๋˜์–ด ๋นจ๊ฐœ์ง„๋‹ค.
622
+ # ๐ŸŸฅ 2026-08-20 โ€” ์ด ๋ ˆ์ธ์€ **๋‘ ๊ตฐ๋ฐ๊ฐ€ ์ €์ž์˜ ์ฒดํฌ์•„์›ƒ์— ๊ฒฐ๋ฐ•**๋ผ ์žˆ์—ˆ๊ณ , ์†Œ๋น„์ž ์„ค์น˜์—์„œ
623
+ # ์ „๊ฑด ์ ์ƒ‰์ด์—ˆ๋‹ค(์‹ค์ธก: ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ์‹ค๋ฌผ 2.5.1 ์—์„œ `discover` rc=10 โ†’ selfcheck FAIL).
624
+ # โ“ ์ „์ œ: `discover` ๋Š” `tracks/` ๋งคํ•‘ ๋ชฉ๋ก์„ ์ „์ œํ•˜๋Š”๋ฐ `tracks/` ๋Š” gitignored ๋ผ
625
+ # **๋ฐฐํฌ๋ฌผ์— ๊ตฌ์กฐ์ ์œผ๋กœ ์กด์žฌํ•  ์ˆ˜ ์—†๋‹ค.** ๋ถ€์žฌ๋Š” ยซ๋Šฅ๋ ฅ 0ยป ์ด ์•„๋‹ˆ๋ผ **๋ฏธ์ธก์ •**์ด๋ฏ€๋กœ
626
+ # FAIL ์ด ์•„๋‹ˆ๋ผ ์ด๋ฆ„ ์žˆ๋Š” SKIP ์œผ๋กœ ๋‚ธ๋‹ค([[feedback_not_found_is_not_zero_family]]).
627
+ # โ“‘ ๐ŸŸฅ **์ดˆํŒ์ด ์—ฌ๊ธฐ์„œ ํ‹€๋ ธ๊ณ , cross-family(codex/gpt-5.5)๊ฐ€ ์žก์•˜๋‹ค โ€” ์ž๋ ฅ ์ ๋ฐœ 0.**
628
+ # ๊ธฐ๋Œ€ ๋ฌธ์ž์—ด `^forge-harness\(hub\)` ๋ฅผ ยซํด๋”๋ช… ๊ฒฐ๋ฐ•ยป ์œผ๋กœ ์ฝ๊ณ  `basename` ์œ ๋„๋กœ
629
+ # ๋ฐ”๊ฟจ๋Š”๋ฐ, **์ƒ์‚ฐ์ž(:128)๋Š” ๋ฆฌํ„ฐ๋Ÿด `forge-harness(hub)` ๋ฅผ ๋‚ธ๋‹ค.** ์ฆ‰ ์œ ๋„๋กœ ๋ฐ”๊พธ๋ฉด
630
+ # ์ด๋ฆ„์ด ๋‹ค๋ฅธ ํŠธ๋ฆฌ์—์„œ ๊ธฐ๋Œ€์™€ ์‚ฐ์ถœ์ด ๊ฐˆ๋ ค **๊ฑฐ์ง“ FAIL** ์ด ๋œ๋‹ค โ€” ์ด ํŒŒ์ผ์ด ๋ฐ”๋กœ ์œ„์—์„œ
631
+ # ๊ฒฝ๊ณ ํ•˜๋Š” divergent-normalizer ๋ฅผ ์ˆ˜๋ฆฌ๊ฐ€ ์ƒˆ๋กœ ๋งŒ๋“  ๊ผด์ด๋‹ค. ๋˜๋Œ๋ ธ๋‹ค.
632
+ # ์ง„๋‹จ ์ •์ •: ์ด ๋ผ๋ฒจ์€ ยซ๋””๋ ‰ํ„ฐ๋ฆฌ ์ด๋ฆ„ยป์ด ์•„๋‹ˆ๋ผ **ํ—ˆ๋ธŒ์˜ ์ƒ์ˆ˜ ์‹๋ณ„์ž**๋‹ค. ์–‘์ชฝ์ด ๊ฐ™์€
633
+ # ์ƒ์ˆ˜๋ฅผ ์“ฐ๋Š” ํ•œ ์–ด๋А ์ฒดํฌ์•„์›ƒ์—์„œ๋„ ์ผ์น˜ํ•˜๋ฏ€๋กœ ๊ฒฐ๋ฐ•์ด ์•„๋‹ˆ๋‹ค. ๊ฒฐ๋ฐ•์€ โ“ ํ•˜๋‚˜์˜€๋‹ค.
634
+ local _hubroot
635
+ _hubroot="$(cd -P "$(dirname "$SELF")/.." && pwd)"
636
+ tracked_n="$(_cap_files "$_hubroot/.claude/capabilities" | wc -l | tr -d ' ')"
593
637
  out="$(bash "$SELF" discover 2>&1)"; rc=$?
594
- # โ˜…์„ธ๋Š” ๊ทœ์น™์€ ๋„๊ตฌ์™€ **๊ฐ™์•„์•ผ ํ•œ๋‹ค**. ์ดˆํŒ์€ `ls .../*.cap`(๋น„์žฌ๊ท€)์˜€๋Š”๋ฐ ๋„๊ตฌ๊ฐ€ ์žฌ๊ท€๋กœ
595
- # ๋ฐ”๋€Œ์ž ๋ ˆ์ธ๋งŒ 2๋ฅผ ์„ธ๊ณ  ๋„๊ตฌ๋Š” 5๋ฅผ ์„ธ์–ด ๊ฑฐ์ง“ ์ ์ƒ‰์ด ๋‚ฌ๋‹ค โ€” ๋ ˆ์ธ ์ž์‹ ์ด
596
- # divergent-normalizer ๊ฐ€ ๋œ ๊ฒฝ์šฐ๋‹ค. `_cap_files` ํ•œ ๋ฒŒ๋กœ ํ†ต์ผํ•œ๋‹ค.
597
- tracked_n="$(_cap_files "$(cd -P "$(dirname "$SELF")/.." && pwd)/.claude/capabilities" | wc -l | tr -d ' ')"
598
- if [ "${tracked_n:-0}" -lt 1 ]; then
638
+ if [ ! -d "$_hubroot/tracks" ]; then
639
+ echo " โญ L13c SKIP โ€” tracks/ ๋ถ€์žฌ๋ผ discover ์˜ ์ „์ œ๊ฐ€ ์—†๋‹ค(gitignored, ๋ฐฐํฌ๋ฌผ์— ๊ตฌ์กฐ์  ๋ถ€์žฌ). ๋ฏธ์ธก์ •์ด์ง€ ยซ0๊ฑดยป ์•„๋‹˜"
640
+ elif [ "${tracked_n:-0}" -lt 1 ]; then
599
641
  f=$((f+1)); echo " โŒ L13c ์ „์ œ ํŒŒ์† โ€” ์ถ”์ ๋˜๋Š” .claude/capabilities/*.cap ์ด 0๊ฑด์ด๋‹ค(์•ต์ปค ๋Œ€์ƒ ๋ถ€์žฌ)"
600
642
  elif printf '%s' "$out" | grep -qE '^forge-harness\(hub\)[[:space:]]+OK[[:space:]]+'"$tracked_n"'[[:space:]]'; then
601
643
  p=$((p+1)); echo " โœ… L13c ์‹ค๋ฌผ ์ˆ˜์ง‘์ด ์‚ด์•„ ์žˆ๋‹ค โ€” ํ—ˆ๋ธŒ ์ž์‹ ์˜ ์ถ”์  ์„ ์–ธ ${tracked_n}๊ฑด์„ ์‹ค์ œ๋กœ ์ง‘์—ˆ๋‹ค"