triflux 10.57.1 → 10.57.2

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.
@@ -0,0 +1,178 @@
1
+ # 자연어 → 스킬 라우팅
2
+
3
+ 사용자가 스킬명을 모르더라도 자연어로 요청하면 아래 규칙에 따라 적절한 스킬을 호출한다.
4
+
5
+ 이 문서는 **무엇을 어떤 순서로 밟는가**(워크플로 단계 + 자연어 트리거)를 정한다.
6
+ **어느 시스템이 그 기능을 소유하는가**(gstack / superpowers / triflux)는
7
+ `tfx-stack-coexistence.md` 의 책임 매트릭스가 SSOT다. 두 문서가 어긋나 보이면 층위를
8
+ 먼저 구분한다. 실행 계획은 superpowers `writing-plans`, TFX 다중모델 계획·실행은
9
+ `tfx-auto --mode deep`이 맡는다. 같은 층위에서 실제로 갈릴 때만 coexistence를 따른다.
10
+
11
+ ## 기본 워크플로우 ladder
12
+
13
+ 넓은 운영 순서와 스택 간 책임 경계는 이 파일에 둔다. 개별 `SKILL.md`
14
+ description에는 해당 스킬을 고르는 데 필요한 좁은 activation phrase만 넣는다.
15
+
16
+ | 상황 | 기본 순서 | 비고 |
17
+ |------|-----------|------|
18
+ | 메타 라우팅/스킬 선택 | `/tfx-harness` | 어떤 스킬/순서가 맞는지 먼저 판정해야 할 때 쓰는 단일 진입점. |
19
+ | 신규 제품/아이디어 | `/gstack-office-hours` → `/gstack-autoplan` → `/superpowers:writing-plans` → `/tfx-auto` | 먼저 문제/범위를 잡고, 리뷰된 계획을 실행으로 넘긴다. |
20
+ | 기존 작업 계속 | `/gstack-context-restore` → `/tfx-auto` | 저장된 맥락을 복원한 뒤 실행한다. |
21
+ | 직접 구현/수정 | `/tfx-auto` (Codex 우선) | "이거 고쳐줘", "구현해줘". 기존 작업 연장이면 `/gstack-context-restore` 먼저. |
22
+ | 디버그 | Claude 기본 Explore 에이전트 → `/superpowers:systematic-debugging` → `/tfx-auto` | 코드 위치/사용처 확인과 원인 분석을 분리한다. |
23
+ | 리서치 | `/tfx-research` 또는 `/multilingual-parallel-research` | 외부/최신/도구 가능 여부 의문문은 `/tfx-research`; 언어권 병렬 조사는 multilingual 경로. |
24
+ | 검증 | `/superpowers:requesting-code-review` + `/gstack-qa` + `/superpowers:verification-before-completion` | 코드 판정은 review, 브라우저/워크플로우 게이트는 qa, 완료 주장 전 evidence 수집은 verification. |
25
+ | 디자인 검토 | `/gstack-design-review` | 시각/UX 판정. 코드 판정과 분리. |
26
+ | TDD 시작 | `/superpowers:test-driven-development` | 테스트부터 짜는 흐름. 구현 전에 invoke. |
27
+ | 코드 변경 병렬 실행 | 작업별 worktree와 세션을 분리하고 각 세션에서 `/tfx-auto` 실행 | Claude Agent는 `isolation: worktree` 사용 가능. |
28
+ | 병렬 작업 (read-only) | `/tfx-auto --parallel N --mode deep` 또는 `/superpowers:dispatching-parallel-agents` | cwd 공유가 가능한 read-only 병렬. 코드 변경은 작업별 worktree를 분리. |
29
+ | plan 실행 (체크포인트 별도) | `/superpowers:executing-plans` | 다른 세션에서 plan 을 단계별 실행. |
30
+ | 머지 직전 정리 | `/superpowers:finishing-a-development-branch` → `/tfx-ship` 또는 `/gstack-ship` | merge/PR/cleanup 결정 후 ship. |
31
+ | 리뷰 응답 | `/superpowers:receiving-code-review` | 검토 의견에 기술적 rigor 로 응답. blind apply 금지. |
32
+ | triflux 릴리즈 | `/tfx-ship` | triflux 본체는 AI trailer 금지 정책이 있는 전용 ship 경로를 쓴다. |
33
+ | 일반 PR/배포 | `/gstack-ship` | 로컬 `/gstack-ship`도 AI attribution footer/trailer 없이 실행되어야 한다. |
34
+ | 저장/복원 | `/gstack-context-save` / `/gstack-context-restore` | 진행 상태는 gstack checkpoint 계열이 소유한다. |
35
+ | 회고/슬롭 정리 | `/gstack-retro` 또는 명시 `tfx-auto --mode consensus` | 세션 회고는 gstack, 무수식 AI slop/deslop은 host ai-slop-cleaner, TFX 3자 cleanup만 `tfx-auto --mode consensus`. |
36
+
37
+ ## 스킬 선택 결정트리 (SSOT)
38
+
39
+ 우선순위는 **명시 스킬/플래그 > D0-D11 결정트리 > 위 quick reference > 개별 description**이다. 위 표는 파생 참조이며 충돌하면 이 결정트리가 이긴다.
40
+
41
+ - D0: 명시 `/skill`, `$skill`, `tfx-*`, `--mode/--cli`는 안전 위반이 없으면 유지한다.
42
+ - D1: “어떤 스킬/경로?”만 묻는 요청은 `tfx-harness`가 recommendation-only로 owner 하나를 반환한다.
43
+ - D2: 목표·non-goal·acceptance criteria가 불명확하면 host `deep-interview`; 자연어 목표의 `/goal` 변환은 Claude Code 기본 `/goal`을 쓴다.
44
+ - D3: 제품 수요·wedge 단계는 `office-hours`; D4 해법/UX 발산은 `brainstorming`.
45
+ - D5: 승인된 spec의 실행 계획은 superpowers `writing-plans`; 명시 TFX 다중모델 계획·실행은 `tfx-auto --mode deep`.
46
+ - D6: 실패/버그/test failure는 `systematic-debugging` 후 `tfx-auto`; 정상 구조 설명은 `tfx-auto --mode consensus --shape panel`.
47
+ - D7: 로컬 파일·심볼은 Claude 기본 Explore 에이전트(스킬 없음); 외부·최신·공식 문서는 `tfx-research`.
48
+ - D8: 무수식 AI slop/deslop/refactor는 host `ai-slop-cleaner`; 명시 TFX 3자 cleanup만 `tfx-auto --mode consensus`; 회고는 `retro`. “정리” 한 단어로 cleanup을 강제하지 않는다.
49
+ - D9: web/app flow는 `qa`; code verdict는 review backend; 완료 주장은 `verification-before-completion`이 필수다.
50
+ - D10: 명확한 직접 구현은 TDD 필요 여부 뒤 `tfx-auto`; 2+ code-changing lane은 worktree와 세션을 각각 분리.
51
+ - D11: fresh verification 없이 ship 금지. triflux release는 `tfx-ship`, 일반 release는 `ship`, 중단/재개는 context save/restore.
52
+
53
+ owner availability를 실제로 검출한 경우에만 `owner unavailable → tfx-X fallback`을 명시해 fallback할 수 있다. availability가 unknown이면 추측 fallback하지 않는다.
54
+
55
+ ## 행동 유형 → 스킬 매핑
56
+
57
+ | 의도 | 자연어 신호 | 스킬 |
58
+ |------|-----------|------|
59
+ | 리드 운영 | 리드 맡아, 세션들 지휘, 워커 띄워서 조율, 승계·머지·릴리스 조율 | tfx-lead |
60
+ | 구현/수정 | 만들어, 고쳐, 구현해, 짜줘, 수정해, 바꿔 | tfx-auto |
61
+ | 리뷰 | 봐줘, 리뷰해, 검토해, 괜찮아? | `superpowers:requesting-code-review` (owner). 교차모델 판정이 필요할 때만 tfx-review |
62
+ | 분석 | 분석해, 어떻게 돌아가?, 구조가 뭐야 | tfx-auto (`--mode consensus --shape panel`) |
63
+ | 계획 | 계획, 어떻게 하지, 설계해 | superpowers `writing-plans`; TFX 다중모델 계획·실행은 `tfx-auto --mode deep` |
64
+ | 요구사항 명확화 | 요구사항, 인터뷰, 목표 구체화 | host `deep-interview`; 목표 변환은 Claude Code 기본 `/goal` |
65
+ | Codex 프로필 관리 | 프로필, 모델, effort 변경 | `~/.codex/<프로필>.config.toml` 직접 관리 |
66
+ | 검색 | 찾아, 어디있어, 파일 찾아 | Claude 기본 Explore 에이전트 |
67
+ | 리서치 (빠른) | 검색해줘, 찾아봐, 공식문서, 이거 뭐야 | tfx-research |
68
+ | 리서치 (자율) | 자율 리서치, 검색하고 정리해, research and plan | tfx-research (Auto 모드) |
69
+ | 테스트 | 테스트, 검증, 돌려봐, QA | 코드 판정은 tfx-review, 테스트→수정 반복은 tfx-auto, 브라우저 흐름은 gstack `/gstack-qa` |
70
+ | 정리 | 대상 domain을 먼저 판정; 무수식 AI slop/deslop은 host ai-slop-cleaner | D8 참조 (명시 TFX 3자 cleanup만 `tfx-auto --mode consensus`) |
71
+ | 토론/비교 | 뭐가 나을까, 비교해, A vs B | tfx-auto (`--mode consensus --shape debate`) |
72
+ | 합의 | 합의로 분석해, 3자 합의, consensus | tfx-auto (`--mode consensus`) |
73
+ | 패널 | panel, 패널, 전문가 의견, expert panel | tfx-auto (`--mode consensus --shape panel`) |
74
+
75
+ 위 표는 Claude가 요청을 읽고 스킬을 고를 때 쓰는 지도다. 자동 키워드 주입은 [ADR-0023](../../docs/adr/0023-remove-command-hooks-hub-and-adopt-mods.md)에 따라 제거한다. 명시적 스킬 호출은 해당 스킬의 설치 여부와 호출 문법을 확인한다.
76
+
77
+ ## 깊이 수정자
78
+
79
+ | 수정자 | 신호 | 효과 |
80
+ |--------|------|------|
81
+ | 기본 | (없음), 빠르게, 간단히 | Light 스킬 |
82
+ | 깊이 | 제대로, 꼼꼼히, 철저히 | `tfx-auto --mode deep` |
83
+ | 합의 | 3자, 교차, 다각도 | `tfx-auto --mode consensus` |
84
+ | 반복 | 끝까지, 멈추지마, ralph | `--retry ralph` (Phase 3 true state machine, `.claude/rules/tfx-escalation-chain.md` 참조) |
85
+ | 승격 | 알아서 승격, 안 되면 더 강한 모델 | `--retry auto-escalate` (Phase 3 CLI 체인 승격) |
86
+ | 자율 | 알아서, 자동으로, autopilot | autopilot 모드 |
87
+ | 최대 effort | ultracode, 울트라코드 | Claude Code `--effort ultracode`(v2.1.203+). 멀티에이전트 Workflow 오케스트레이션, 토큰 비용 무관 철저성 우선. tfx CLI 플래그는 아니다 |
88
+
89
+ ## CLI 우선순위 정책: default = Codex
90
+
91
+ > 근거(why): [ADR-0004: 기본 구현 CLI = Codex](../../docs/adr/0004-codex-as-default-cli.md). 이 절이 SSOT(어떻게), ADR은 결정 이력(왜).
92
+
93
+ triflux 본체 개발의 실측 운영 패턴 (v10.18.0 ~ v10.20.2, 2주, 25+ PR) 이 거의 전부 Codex 단독 또는 Codex worker spawn 으로 진행되었다. 이 운영 fact 를 default policy 로 명시한다.
94
+
95
+ | 우선순위 | CLI / 모델 | default 적용 lane |
96
+ |---------|-----------|-------------------|
97
+ | 1차 (default) | **Codex** (프로필 설정 기준) | 구현, 수정, 디버그, 리뷰, 분석, 테스트 작성, 회귀 가드, 릴리즈 prepare |
98
+ | 2차 | **Antigravity** | Codex quota exhaust, 가독성 cross-check, 별도 시각 검토 |
99
+ | 한정 (Claude 만) | **Claude** (`opus` 최신 tier 별칭) | 메타 라우팅 (`/tfx-harness`), planning gate (`/gstack-office-hours`, `/gstack-autoplan`), gstack-specific surface (`/gstack-context-*`, `/gstack-qa`), 또는 Codex/Antigravity 미가용 |
100
+
101
+ `tfx-auto`, `tfx-review`, `tfx-research` 등 multi-CLI wrapper 는 이 정책을 따른다. 명시 플래그 (`--cli claude`, `--cli codex`) 가 있으면 override. `--mode consensus` (3-CLI 합의) 도 default head 는 Codex.
102
+
103
+ `--retry auto-escalate` 체인 (`.claude/rules/tfx-escalation-chain.md`) 의 1단계가 Codex 인 것도 이 정책과 정합한다 (2단계 Claude `fable` 최신 tier 별칭은 최종 수단).
104
+
105
+ ### 모델 배치
106
+
107
+ > 근거(why): [ADR-0016](../../docs/adr/0016-codex-astra-top-tier-and-fable-escalation.md), [ADR-0017](../../docs/adr/0017-gpt6-sol-luna-lanes.md), [ADR-0019](../../docs/adr/0019-gpt61-sol-and-sonnet-55.md). 기준 문서는 OpenAI Codex 모델 페이지와 Anthropic 모델 개요(2026-09-30 확인)다.
108
+
109
+ Codex 역할별 프로필의 SSOT는 `scripts/lib/agent-route-policy.mjs`이고, 모델 ID는 `~/.codex/<프로필>.config.toml`이 정한다. `designer`, `writer` 항목은 직접 Codex를 지정한 경우에만 적용하며 일반 실행의 provider는 `hub/team/agent-map.json`이 정한다.
110
+
111
+ | 프로필 | triflux lane |
112
+ |---------|--------------|
113
+ | `gpt6_astra_xhigh` | architect, planner, critic, analyst, debugger, deep-executor, security-reviewer, scientist-deep, designer(직접 지정) |
114
+ | `gpt6_astra_max` / `gpt6_astra_ultra` | `TFX_CODEX_PROFILE=max/ultra`, 재시도 1단계 |
115
+ | `gpt61_sol_high` | executor, codex, code-reviewer, quality-reviewer, verifier, test-engineer, qa-tester, scientist, document-specialist |
116
+ | `gpt61_sol_med` | cleanup, deslop |
117
+ | `gpt6_luna_high` | build-fixer, writer(직접 지정) |
118
+ | `gpt6_luna_low` | spark, explore 재매핑, ChatGPT tier 폴백 |
119
+ | `fable` | `--retry auto-escalate` 최종 단계 |
120
+ | `opus` | 메타 라우팅, planning gate, 스킬의 `model="opus"` lane |
121
+ | `sonnet` | Claude native test-engineer, qa-tester, verifier |
122
+ | `haiku` | Claude native explore, claude |
123
+
124
+ ## CLI 라우팅
125
+
126
+ `codex exec` / `agy -y -p` 직접 호출은 금지한다. tfx 스킬 경유 필수. 자동 차단 훅(headless-guard)은 제거되었으므로 호출자가 지킨다.
127
+
128
+ **Layer 1: Light** (tfx-route.sh → 단일 CLI)
129
+
130
+ | 경로 | CLI | 용도 |
131
+ |------|-----|------|
132
+ | `tfx-auto` | 자동 | 통합 진입점 |
133
+ | `tfx-auto --cli codex` | Codex | Codex 전용 lane |
134
+ | `tfx-auto --cli antigravity` | Antigravity | Antigravity 전용 lane |
135
+ | `tfx-auto --mode quick` | Codex→검증 | 단일 파일, 5분 이내 |
136
+ | `tfx-auto --retry auto-escalate` | 자동 승격 | 실패→더 강한 모델 |
137
+
138
+ **Layer 2: Deep** (headless 3-CLI 합의)
139
+
140
+ `tfx-auto --mode deep`, `tfx-auto --mode consensus --shape consensus|debate|panel`,
141
+ `tfx-auto --retry ralph`
142
+
143
+ **Layer 3: Remote/병렬**
144
+
145
+ | 스킬 | 용도 |
146
+ |------|------|
147
+ | `tfx multi` CLI | 2+개 태스크 headless 병렬 |
148
+ | tfx-remote | Claude Code 원격 세션 (SSH, user-state hosts.json setup 필수) |
149
+
150
+ **Claude 네이티브** (CLI 불필요): tfx-setup, tfx-doctor
151
+
152
+ **Headless UI default** : `tfx-auto`, `tfx multi`의 headless 워커는 default 로 `claude agents` 패널에 노출 (`--native-bridge-ui agents`). opt-out: `--no-native-bridge-ui`. interactive (tmux/wt) 경로는 default-off. 행에서 Enter 를 누르면 워커가 도는 tmux pane 이 열린다. 상세 행동표는 `CLAUDE.md` 의 `<native-bridge>` 섹션. 근거(why): [ADR-0008 : headless 워커 native-bridge 기본 노출](../../docs/adr/0008-native-bridge-ui-default-on.md), [ADR-0026 : agents 행을 워커 tmux 방의 attach client 로](../../docs/adr/0026-agents-row-tmux-attach.md).
153
+
154
+ 자원 우선순위: remote-spawn > multi > Light > 로컬 단독
155
+
156
+ **원격 오프로드 기본 정책** : 로컬 기기의 램이 빠듯해지면 무거운 작업을 `hosts.json` 에 등록한 원격 호스트로 넘긴다. 판단 시점은 병렬 dispatch 직전이다.
157
+
158
+ - 판정 명령: `sysctl -n kern.memorystatus_vm_pressure_level` (1=normal, 2=warn, 4=critical; Stats 앱·활성 상태 보기의 메모리 색과 같은 커널 신호, 노랑=2 빨강=4)
159
+ - 4(critical): 무조건 오프로드
160
+ - 2(warn): 무거운 shard(빌드·전체 테스트 스위트·대규모 리팩터)를 띄우거나 워커 3+개를 새로 만들 때만 오프로드
161
+ - 1(normal): 로컬 유지
162
+ - sysctl 실패 시 대체 신호: `vm_stat` 5초 간격 2회 측정에서 swapouts 증가
163
+ - 원격으로 넘기기 전에 `node scripts/remote-spawn.mjs --probe <host>`로 호스트 상태를 먼저 확인한다.
164
+ - 행동: 원격 코드 변경은 별도 worktree와 세션에서 실행한다. 탐색·대화형은 tfx-remote로 원격 세션을 띄운다
165
+ - 로컬 여유가 충분하면 로컬 유지. 사용자가 로컬/원격을 명시하면 그에 따른다
166
+ - 원격 호스트 부재 시(ssh 불응) 조용히 로컬로 강등하지 말고 한 줄 알린 뒤 로컬 진행
167
+
168
+ 원격 hosts 설정은 user-state 경로만 참조한다: macOS/Linux `~/.config/triflux/hosts.json`, Windows `%APPDATA%\triflux\hosts.json`. 기존 source-tree `references/hosts.json` 은 라우팅 입력으로 사용하지 않으며 첫 실행 lazy auto-migration 대상이다.
169
+
170
+ ## 충돌 해소
171
+
172
+ - ralph = persist alias
173
+ - "auto" 단독 → tfx-auto. "알아서 해" → autopilot 모드 (위 깊이 수정자)
174
+ - "코드에서 찾아" → Claude 기본 Explore 에이전트. "알아봐" → tfx-research
175
+ - 복합 의도: "구현하고 리뷰까지" → tfx-auto 실행 후 리뷰 결과 확인
176
+ - "합의해서 비교해" 류 요청은 기본적으로 `tfx-auto --mode consensus --shape debate` 로 fold 한다
177
+ - `ralph` 표기는 항상 `--retry ralph` mode 를 지칭. persist 스킬도 동일 의미. `--retry auto-escalate` 와 동시 사용 불가 (escalation-chain.md 규약).
178
+ - 코드 변경 병렬 작업은 작업별 worktree와 세션을 분리하고 각 세션에서 `tfx-auto`로 실행한다.
@@ -15,24 +15,26 @@ tree, keyword rules, or owner matrix into this file.
15
15
  ## Locate the routing SSOT
16
16
 
17
17
  This skill is installed globally but the SSOT belongs to the Triflux project.
18
- Try these locations in order and return blocked only when all three fail:
18
+ Try these locations in order and return blocked only when all four fail:
19
19
 
20
20
  1. `$TFX_ROUTING_SSOT`, when set.
21
21
  2. `.claude/rules/tfx-routing.md` below the main Git worktree, derived from
22
22
  the parent of `git rev-parse --git-common-dir`.
23
23
  3. Walk upward from the current working directory looking for
24
24
  `.claude/rules/tfx-routing.md`.
25
+ 4. `references/tfx-routing.md` inside this skill directory, copied from the
26
+ package by `tfx setup`. It serves machines without a clone and may lag the
27
+ clone, so it comes last.
25
28
 
26
29
  Do not use `git rev-parse --show-toplevel` for step 2. In a linked worktree it
27
30
  returns that worktree root, whose SSOT may be a stale branch snapshot.
28
31
  `--git-common-dir` still points to the main repository's `.git` directory.
29
32
 
30
- When blocked, include this one-line remedy:
31
-
32
- `export TFX_ROUTING_SSOT=<triflux clone>/.claude/rules/tfx-routing.md`
33
-
34
- Prefer `~/.zshenv` for this variable because hooks and headless workers may not
35
- load interactive `~/.zshrc`. Do not create a global copy or symlink under
33
+ When blocked, include this one-line remedy: rerun `tfx setup` to install the
34
+ step 4 copy. To follow a clone's latest policy instead, set
35
+ `export TFX_ROUTING_SSOT=<triflux clone>/.claude/rules/tfx-routing.md` in
36
+ `~/.zshenv`, because hooks and headless workers may not load interactive
37
+ `~/.zshrc`. Do not create a global copy or symlink under
36
38
  `~/.claude/rules/`; Claude auto-loads that directory into unrelated sessions.
37
39
 
38
40
  ## Return contract
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "triflux",
3
- "version": "10.57.1",
3
+ "version": "10.57.2",
4
4
  "description": "CLI-first multi-model orchestrator for Claude Code — route tasks to Codex, Antigravity, and Claude",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,6 +23,7 @@
23
23
  "adapters",
24
24
  "bin",
25
25
  "config",
26
+ ".claude/rules/tfx-routing.md",
26
27
  "skills",
27
28
  "!skills/tfx-workspace",
28
29
  "hooks",
@@ -33,6 +33,8 @@ const MIRROR_TOPS = [
33
33
  "scripts",
34
34
  "skills",
35
35
  ];
36
+ // 디렉터리 전체가 아니라 한 파일만 게시하는 경로. tfx-harness 가 clone 없는 기기에서 읽는다.
37
+ const MIRROR_FILES = [".claude/rules/tfx-routing.md"];
36
38
  // core 미러 대상은 pack.mjs 의 CORE_ENTRIES 하나로 정한다. 검사기가 따로 손 목록을 두면
37
39
  // 하위 디렉터리나 새 파일이 빠져도 OK 가 나왔다(#674).
38
40
  const CORE_NON_MIRROR = new Set([
@@ -189,48 +191,55 @@ function compareMirror({
189
191
  const issues = [];
190
192
  const fixed = [];
191
193
 
194
+ const pairs = [];
192
195
  for (const top of MIRROR_TOPS) {
193
- const srcDir = join(repoRoot, top);
194
- const dstDir = join(mirrorRoot, top);
195
196
  const skipRels = SKIP_RELS.get(top) ?? new Set();
196
- const srcFiles = new Set(walkRelFiles(srcDir, skipRels));
197
- const dstFiles = new Set(walkRelFiles(dstDir, skipRels));
198
- const allFiles = new Set([...srcFiles, ...dstFiles]);
197
+ const srcFiles = new Set(walkRelFiles(join(repoRoot, top), skipRels));
198
+ const dstFiles = new Set(walkRelFiles(join(mirrorRoot, top), skipRels));
199
+ for (const rel of new Set([...srcFiles, ...dstFiles]))
200
+ pairs.push({
201
+ rel: `${top}/${rel}`,
202
+ inSrc: srcFiles.has(rel),
203
+ inDst: dstFiles.has(rel),
204
+ });
205
+ }
206
+ for (const rel of MIRROR_FILES) {
207
+ const inSrc = existsSync(join(repoRoot, rel));
208
+ const inDst = existsSync(join(mirrorRoot, rel));
209
+ if (inSrc || inDst) pairs.push({ rel, inSrc, inDst });
210
+ }
199
211
 
200
- for (const rel of allFiles) {
201
- const srcPath = join(srcDir, rel);
202
- const dstPath = join(dstDir, rel);
203
- const inSrc = srcFiles.has(rel);
204
- const inDst = dstFiles.has(rel);
205
- const displayPath = `packages/triflux/${top}/${rel}`;
212
+ for (const { rel, inSrc, inDst } of pairs) {
213
+ const srcPath = join(repoRoot, rel);
214
+ const dstPath = join(mirrorRoot, rel);
215
+ const displayPath = `packages/triflux/${rel}`;
206
216
 
207
- if (inSrc && !inDst) {
208
- if (fix) {
209
- mkdirSync(dirname(dstPath), { recursive: true });
210
- copyFileSync(srcPath, dstPath);
211
- fixed.push({ path: displayPath, kind: "added" });
212
- } else {
213
- issues.push({ path: displayPath, kind: "missing-in-mirror" });
214
- }
215
- continue;
217
+ if (inSrc && !inDst) {
218
+ if (fix) {
219
+ mkdirSync(dirname(dstPath), { recursive: true });
220
+ copyFileSync(srcPath, dstPath);
221
+ fixed.push({ path: displayPath, kind: "added" });
222
+ } else {
223
+ issues.push({ path: displayPath, kind: "missing-in-mirror" });
216
224
  }
225
+ continue;
226
+ }
217
227
 
218
- if (!inSrc && inDst) {
219
- // Orphan in mirror — source of truth is root, mirror must not have
220
- // extra files. Do not auto-delete; require manual decision.
221
- issues.push({ path: displayPath, kind: "orphan-in-mirror" });
222
- continue;
223
- }
228
+ if (!inSrc && inDst) {
229
+ // Orphan in mirror — source of truth is root, mirror must not have
230
+ // extra files. Do not auto-delete; require manual decision.
231
+ issues.push({ path: displayPath, kind: "orphan-in-mirror" });
232
+ continue;
233
+ }
224
234
 
225
- const a = readFileSync(srcPath);
226
- const b = readFileSync(dstPath);
227
- if (!a.equals(b)) {
228
- if (fix) {
229
- copyFileSync(srcPath, dstPath);
230
- fixed.push({ path: displayPath, kind: "updated" });
231
- } else {
232
- issues.push({ path: displayPath, kind: "content-diff" });
233
- }
235
+ const a = readFileSync(srcPath);
236
+ const b = readFileSync(dstPath);
237
+ if (!a.equals(b)) {
238
+ if (fix) {
239
+ copyFileSync(srcPath, dstPath);
240
+ fixed.push({ path: displayPath, kind: "updated" });
241
+ } else {
242
+ issues.push({ path: displayPath, kind: "content-diff" });
234
243
  }
235
244
  }
236
245
  }
package/scripts/setup.mjs CHANGED
@@ -1241,6 +1241,19 @@ function syncCodexManagedSkills({
1241
1241
  }));
1242
1242
  }
1243
1243
 
1244
+ // tfx-harness 는 triflux clone 밖에서도 호출된다. clone 이 없는 기기에서도 라우팅 문서를
1245
+ // 읽도록 패키지에 든 사본을 설치된 스킬 옆에 둔다. ~/.claude/rules 는 모든 세션이 자동
1246
+ // 로드하므로 쓰지 않는다.
1247
+ function installRoutingSsot(pluginRoot, skillDir) {
1248
+ const src = join(pluginRoot, ".claude", "rules", "tfx-routing.md");
1249
+ const dst = join(skillDir, "references", "tfx-routing.md");
1250
+ if (!existsSync(src) || !existsSync(join(skillDir, "SKILL.md"))) return false;
1251
+ if (!shouldSyncTextFile(src, dst)) return false;
1252
+ mkdirSync(dirname(dst), { recursive: true });
1253
+ copyFileSync(src, dst);
1254
+ return true;
1255
+ }
1256
+
1244
1257
  export function syncSkills({
1245
1258
  pluginRoot = PLUGIN_ROOT,
1246
1259
  claudeDir = CLAUDE_DIR,
@@ -1293,6 +1306,12 @@ export function syncSkills({
1293
1306
  } else if (skill.action === "skipped")
1294
1307
  result.warnings.push(`Codex ${skill.name}: 사용자 스킬 보존`);
1295
1308
  }
1309
+ const harnessDirs = [join(destination, "tfx-harness")];
1310
+ const codexHarness = join(codexDir, "skills", "tfx-harness");
1311
+ if (existsSync(join(codexHarness, MANAGED_CODEX_SKILL_MARKER)))
1312
+ harnessDirs.push(codexHarness);
1313
+ for (const dir of harnessDirs)
1314
+ if (installRoutingSsot(pluginRoot, dir)) result.changed++;
1296
1315
  return result;
1297
1316
  }
1298
1317
 
@@ -21,19 +21,22 @@ host capability evidence를 판정한다. 정책 표, keyword 표, owner 매트
21
21
  2. git 메인 워크트리 — `$(git rev-parse --git-common-dir)`의 부모 아래
22
22
  `.claude/rules/tfx-routing.md`
23
23
  3. cwd에서 위로 올라가며 `.claude/rules/tfx-routing.md`
24
+ 4. 이 스킬 디렉터리의 `references/tfx-routing.md` (`tfx setup`이 패키지에서 복사한 사본)
25
+
26
+ 4순위는 clone이 없는 기기를 위한 것이다. 설치한 버전의 정책이라 clone보다 늦을 수 있으므로
27
+ 1~3순위 뒤에 둔다.
24
28
 
25
29
  2순위에 `--show-toplevel`을 쓰지 않는다. linked worktree 안에서는 worktree 루트를
26
30
  반환하는데 그곳의 SSOT는 브랜치 분기 시점 사본이라, blocked 없이 stale 정책으로
27
31
  조용히 오판정한다. `--git-common-dir`은 worktree 안에서도 메인 repo의 `.git`을 가리킨다.
28
32
 
29
- blocked를 반환할 때는 해소 방법을 한 줄 덧붙인다:
30
- `export TFX_ROUTING_SSOT=<triflux clone>/.claude/rules/tfx-routing.md`
31
- (`~/.zshrc`가 아니라 `~/.zshenv`에 둔다. zshrc는 interactive 전용이라 훅·headless
32
- 워커·툴 셸에서는 1순위가 통째로 죽는다.)
33
+ blocked를 반환할 때는 해소 방법을 한 줄 덧붙인다: `tfx setup`을 다시 실행해 4순위 사본을 설치한다.
34
+ clone의 최신 정책을 쓰려면 `export TFX_ROUTING_SSOT=<triflux clone>/.claude/rules/tfx-routing.md`
35
+ 를 `~/.zshenv`에 둔다. `~/.zshrc`는 interactive 전용이라 훅·headless 워커·툴 셸에서는 읽히지 않는다.
33
36
 
34
37
  `~/.claude/rules/`에 사본이나 symlink를 두지 않는다. 그 디렉터리는 Claude가 모든 세션에
35
38
  자동 로드하므로 tfx와 무관한 세션까지 SSOT 전문을 매번 컨텍스트에 싣게 된다.
36
- npm 패키지에도 `.claude/`는 없다(`tfx-mirror-policy.md` mirror 제외 대상).
39
+ npm 패키지에는 `.claude/` 중 `rules/tfx-routing.md` 한 파일만 들어 있다.
37
40
 
38
41
  반환 형식:
39
42