@tienne/gestalt 0.105.0 → 0.106.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.
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tienne/gestalt",
3
- "version": "0.105.0",
3
+ "version": "0.106.0",
4
4
  "description": "TypeScript AI Development Harness - Gestalt psychology-driven requirement clarification",
5
5
  "repository": {
6
6
  "type": "git",
@@ -82,7 +82,7 @@
82
82
  | "orca로 실행", "orca로 병렬", "워커로 뿌려", "터미널로 뿌려", "병렬 디스패치", "다른 에이전트로 실행", "codex로 실행", "워커 띄워서 실행" | `dispatch` 스킬 사용 | 런타임 감지 → 같은 워크트리에 터미널 → worker_done 대기 → ready 재계산. 런타임 없으면 execute의 기본 병렬 경로 |
83
83
  | "doubt", "의심해줘", "반박해줘", "반박부터 받아", "반론부터 들어보자", "이 결정 의심", "되돌리기 어려운 작업 전에" | `doubt` 스킬 사용 | 자동으로는 되돌리기 어려운 작업(배포, 데이터 마이그레이션, 공개 API 변경), 보안 성질 주장, 낯선 코드에서 분기 로직을 바꿀 때만 띄운다. 결정마다 띄우지 않는다. 서브에이전트 안에서는 쓰지 않고 메인 세션에 올린다. 다 만든 뒤 판정은 `continuity-judge`, PR 단위 검토는 `review` |
84
84
  | "설명해줘", "쉽게 풀어줘", "풀어서 설명", "이해하기 쉽게", "쉽게 말하면", "이거 뭐야", "이 에러 뭐야", "무슨 뜻이야", "ELI5", "한테 설명해줘", "에게 설명해줘", "기획팀한테", "비개발자한테", "경영진 보고용으로 설명" | `explain` 스킬 사용 | 소스 확보 → 대상 확정 → explainer 위임 → explain-check → 재시도. 설명 문장만 빠르게 필요하면 `explainer` 에이전트 |
85
- | "handoff", "핸드오프", "핸드오버", "어디에 맡길지", "서브에이전트로 맡겨", "워크트리로 넘겨", "하위 워크트리로", "독립 워크트리로" | `handoff` 스킬 사용 | 맡길 곳 고르기(메인, 서브에이전트, 하위 워크트리, 독립 워크트리) → model과 effort 쌍 → 지시서 → 오르카 또는 Agent 도구로 위임. 같은 브랜치 병렬 쪼개기는 `dispatch` |
85
+ | "handoff", "핸드오프", "핸드오버", "어디에 맡길지", "서브에이전트로 맡겨", "워크트리로 넘겨", "하위 워크트리로", "독립 워크트리로", "원격 머신으로", "원격 서버로 넘겨" | `handoff` 스킬 사용 | 맡길 곳 고르기(메인, 서브에이전트, 하위 워크트리, 독립 워크트리, 원격 Orca 서버) → model과 effort 쌍 → 지시서 → 오르카 또는 Agent 도구로 위임. 같은 브랜치 병렬 쪼개기는 `dispatch` |
86
86
  | "지라 티켓", "지라 이슈", "티켓 만들어", "티켓 생성", "이슈 만들어", "이슈 생성", "지라에 올려", "지라에 등록", "백로그에 추가", "jira 티켓", "jira 이슈", "create jira" | `jira-create` 스킬 사용 | 내부적으로 jira-writer 구조화 → 프로젝트·필드 확정 → 승인 단계 → createJiraIssue |
87
87
  | "시장에 어떤 서비스가 있는지 훑어줘", "레퍼런스 후보 앱 추려줘", "이 기능 하는 앱 후보 추려줘", "서비스 지형 훑어줘", "시장 레퍼런스 분석" | `market-scan` 스킬 사용 | 제품 후보와 유형 묶기까지. 화면을 캡처하면 `screen-ref`, 사용자 관점 분석이면 `ux-research`, 전체를 한 번에 돌리면 `research` |
88
88
  | "PR 작성", "PR 만들어", "PR 써줘", "PR 올려", "풀리퀘", "풀 리퀘스트", "pull request", "create PR" | `pr` 스킬 사용 | 다른 레포의 PR 스킬과 이름이 겹치면 `gestalt:pr`로 부른다 |
@@ -107,6 +107,18 @@ Orca 런타임이 붙지 않아 워커 디스패치는 못 합니다.
107
107
 
108
108
  워크트리를 새로 만드는 것은 **ready 집합의 태스크들이 같은 파일을 건드릴 때만**이다. 그 경우 먼저 사용자에게 충돌을 짚고 워크트리 분리가 필요하다고 말한 뒤 진행한다. 워크트리를 나눴다면 그 워커는 코드 그래프와 memory가 비어 있다는 사실도 함께 알린다.
109
109
 
110
+ ### 원격 Orca 서버로는 뿌리지 않는다
111
+
112
+ 페어링된 원격 서버(`orca host list`에서 `--environment <이름>`으로 나오는 대상)는 이 스킬의 워커 자리가 아니다. 이 스킬이 기대는 전제가 원격에서는 깨진다.
113
+
114
+ - 같은 워크트리를 못 쓴다. 원격 서버는 디스크가 따로라 `--worktree active`나 `current`가 저쪽 워크트리를 가리키지 못하고 오르카도 원격의 `current`와 `new-child`를 거절한다. 원격 워커는 늘 저쪽의 독립 워크트리에서 돈다. 그러면 `.gestalt/`를 공유한다는 이유가 사라진다.
115
+ - 결과를 같은 실행 세션에 못 낸다. 게슈탈트 이벤트 DB가 홈 디렉토리 아래라서 원격 워커의 MCP 서버는 저쪽 DB를 연다. 워커가 `ges_execute action=execute_task`로 낸 결과가 이 머신의 세션에 안 들어간다. 이건 구조를 보고 판단한 것이고 원격에서 돌려보지는 않았다.
116
+ - 3단계 명령이 그대로 안 통한다. `terminal create --environment <이름>`이 낸 핸들은 원격 서버 것이라 이 머신의 `orchestration dispatch --to <핸들>`로 넣을 수 없다. `dispatch`에도 `--environment`를 붙이면 태스크가 원격 서버의 Run에 생겨 코디네이터의 Run과 갈라진다. 오르카 가이드는 원격 워커를 `worker-start --on` 하나로만 띄우고 Run과 Task는 지금 서버에 둔다(`orca skills get orchestration --reference placement-and-remote`).
117
+
118
+ 그래서 태스크를 원격 머신에 맡겨야 하면 [`handoff`](../handoff/SKILL.md)의 원격 경로로 보낸다. 그 태스크는 브랜치 단위의 독립 작업으로 넘기고 워커의 `worker_done` 보고를 받아 코디네이터가 이 머신에서 `execute_task`로 대신 제출한다. ready 집합의 나머지 태스크는 이 스킬대로 같은 워크트리에 뿌린다.
119
+
120
+ 원격 위임은 메인 세션이 맡는다. 디스패치된 워커가 `worker-start`를 부르면 기본 설정에서 `nested_worker_depth_exceeded`로 거절된다. handoff 쪽 시험에서 확인했다. 이 스킬의 `terminal create`와 `dispatch --inject` 경로도 같은 제한에 걸리는지는 확인하지 않았다.
121
+
110
122
  ## 3단계: 태스크를 디스패치한다
111
123
 
112
124
  태스크마다 Orca 오케스트레이션 태스크를 만들고 워커에 넣는다. `--inject`가 워커에게 생애주기 프리앰블을 붙여 `worker_done`을 보내게 한다.
@@ -179,6 +191,7 @@ Orca 쪽에서 사람이 답해 해소되면(`gate-resolve`) 그 답을 게슈
179
191
  - **병렬 실행을 이유로 워크트리를 만들지 않는다.** 파일 충돌만이 사유다.
180
192
  - **워커에게 ready 집합을 굴리게 하지 않는다.** 코디네이터만 한다.
181
193
  - **`worker_done` 없이 완료로 보고하지 않는다.** 터미널이 조용한 것은 완료가 아니다.
194
+ - **원격 Orca 서버에 터미널을 만들어 태스크를 뿌리지 않는다.** 결과가 이 머신의 실행 세션에 안 들어간다.
182
195
  - **워커를 무제한으로 띄우지 않는다.** 워커마다 에이전트와 MCP 서버 프로세스가 하나씩 붙는다.
183
196
 
184
197
  ## 에러 처리
@@ -191,6 +204,7 @@ Orca 쪽에서 사람이 답해 해소되면(`gate-resolve`) 그 답을 게슈
191
204
  | 터미널 핸들이 `terminal_handle_stale` | `terminal list`로 다시 조회해 교체 핸들만 쓴다. 낡은 핸들과 새 핸들에 이중 전송하지 않는다 |
192
205
  | 워커가 같은 태스크를 3회 연속 실패 | 그 태스크 디스패치를 멈추고 사용자에게 보고. evolve 파이프라인으로 넘길지 확인 |
193
206
  | `check --wait` 타임아웃 | 실패가 아니다. `task-list`나 `terminal read`로 생존을 확인하고 대기를 다시 건다 |
207
+ | 태스크를 원격 머신에 맡겨 달라는 요청 | 이 스킬로 뿌리지 않는다. 그 태스크만 handoff의 원격 경로로 보내고 결과는 코디네이터가 `execute_task`로 제출한다 |
194
208
  | DB 잠금 오류(`SQLITE_BUSY`) | 워커 수를 줄이고 재시도. 게슈탈트 이벤트 DB는 홈 글로벌이라 프로세스가 겹친다 |
195
209
 
196
210
  ## 완료 보고
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: handoff
3
3
  version: "1.0.0"
4
- description: "일을 받으면 어디에 맡길지(메인 직접, 서브에이전트, 같은 워크트리 터미널 워커, 하위 워크트리, 독립 워크트리)를 기준으로 고르고, 고른 경로로 model과 effort를 쌍으로 실어 실제로 넘긴다. 오르카가 있으면 워크트리 위임은 오르카를 먼저 쓴다. 같은 브랜치 안의 병렬 쪼개기는 dispatch, 티켓과 브랜치와 PR 단위로 끝나는 위임은 handoff가 맡는다."
4
+ description: "일을 받으면 어디에 맡길지(메인 직접, 서브에이전트, 같은 워크트리 터미널 워커, 하위 워크트리, 독립 워크트리, 원격 머신)를 기준으로 고르고 고른 경로로 model과 effort를 쌍으로 실어 실제로 넘긴다. 오르카가 있으면 워크트리 위임은 오르카를 먼저 쓴다. 같은 브랜치 안의 병렬 쪼개기는 dispatch, 티켓과 브랜치와 PR 단위로 끝나는 위임은 handoff가 맡는다."
5
5
  triggers:
6
6
  - "handoff"
7
7
  - "핸드오프"
@@ -11,6 +11,8 @@ triggers:
11
11
  - "워크트리로 넘겨"
12
12
  - "하위 워크트리로"
13
13
  - "독립 워크트리로"
14
+ - "원격 머신으로"
15
+ - "원격 서버로 넘겨"
14
16
  inputs:
15
17
  task:
16
18
  type: string
@@ -19,7 +21,7 @@ inputs:
19
21
  target:
20
22
  type: string
21
23
  required: false
22
- description: "맡길 곳을 사용자가 지정한 경우. subagent, child-worktree, independent-worktree 중 하나. 비우면 이 스킬의 기준으로 고른다"
24
+ description: "맡길 곳을 사용자가 지정한 경우. subagent, child-worktree, independent-worktree, remote 중 하나. 비우면 이 스킬의 기준으로 고른다"
23
25
  mode:
24
26
  type: string
25
27
  required: false
@@ -31,7 +33,7 @@ outputs:
31
33
  - briefPath
32
34
  - launchReceipt
33
35
  routing:
34
- note: "맡길 곳 고르기(메인, 서브에이전트, 하위 워크트리, 독립 워크트리) → model과 effort 쌍 → 지시서 → 오르카 또는 Agent 도구로 위임. 같은 브랜치 병렬 쪼개기는 `dispatch`"
36
+ note: "맡길 곳 고르기(메인, 서브에이전트, 하위 워크트리, 독립 워크트리, 원격 Orca 서버) → model과 effort 쌍 → 지시서 → 오르카 또는 Agent 도구로 위임. 같은 브랜치 병렬 쪼개기는 `dispatch`"
35
37
  ---
36
38
 
37
39
  # Handoff Skill
@@ -58,8 +60,9 @@ routing:
58
60
  | 같은 워크트리 터미널 워커 | 같은 브랜치 안에서 쪼갠 병렬이고 파일군이 안 겹칠 때. `dispatch`로 보낸다 |
59
61
  | 하위 워크트리 | 파일을 쓰고 끝이 브랜치나 PR로 남는 일(티켓 단위 구현). 15분을 넘기거나 사람이 중간에 들여다보고 끼어들어야 하는 일. `ship`이나 `review-loop` 같은 스킬 체인을 처음부터 끝까지 돌리는 일. 다른 레포 작업. 메인과 파일이 안 겹치는 독립 작업 |
60
62
  | 독립 워크트리 | 현재 브랜치와 무관한 독립 작업. 현재 브랜치 맥락은 프롬프트에 적는다 |
63
+ | 원격 머신(페어링된 Orca 서버) | 오래 걸리거나 이 머신의 CPU와 메모리를 많이 쓰는 일. 저쪽 머신에만 있는 레포나 환경이 필요한 일. 끝이 브랜치나 PR로 남는 일이어야 한다. 메인 세션만 띄울 수 있다 |
61
64
 
62
- 하위 워크트리는 오르카의 `new-child`(`worktree create`로는 `--parent-worktree`), 독립 워크트리는 `new-top-level`(`worktree create`로는 `--no-parent`)에 해당한다.
65
+ 하위 워크트리는 오르카의 `new-child`(`worktree create`로는 `--parent-worktree`), 독립 워크트리는 `new-top-level`(`worktree create`로는 `--no-parent`)에 해당한다. 원격 머신에서는 하위 워크트리를 못 만든다. 워크트리 부모와 자식은 같은 머신에 있어야 하고 오르카가 원격의 `new-child`와 `current`를 거절한다. 원격으로 보내는 일은 늘 저쪽의 독립 워크트리(`new-top-level`)가 된다. 절차는 4단계의 원격 소절에 있다.
63
66
 
64
67
  충돌과 파일 규칙은 어느 경로에서나 같다.
65
68
 
@@ -138,6 +141,29 @@ dispatch는 `worker-start` 대신 `terminal create`와 `orchestration task-creat
138
141
 
139
142
  이 4단계는 sonnet과 low로 끝까지 돌려 확인했다. 2단계 직후 터미널 머리글에 `Sonnet 5.5 with low effort`가 떴고 4단계 접수증에는 `turn_started`까지 찍혔다. 값이 먹혔는지는 모델의 답이 아니라 이 머리글로 본다. 모델은 자기 effort를 모른다고 답한다. 이 경로의 워커는 `worker-start`와 달리 `auto mode on`으로 뜬다. 오르카 문서가 경고하는 빈 셸 탭은 이번에는 생기지 않았다.
140
143
 
144
+ ### 원격 머신(페어링된 Orca 서버)
145
+
146
+ `orca host list`가 맡길 수 있는 머신을 한 번에 보여 준다. 이 머신은 `--host local`, SSH 대상은 `--host ssh:<id>`, 페어링된 Orca 서버는 `--environment <이름>`이다. SSH 대상과 페어링된 서버를 서로 바꿔 넣는 게 가장 흔한 실수다. SSH 대상 중 `github.com`처럼 git 리모트용으로 등록된 이름은 위임 대상이 아니다. 이 소절은 페어링된 Orca 서버만 다룬다.
147
+
148
+ 원격 위임은 메인 세션이 직접 띄운다. 디스패치된 워커가 `worker-start`를 부르면 기본 설정에서 `nested_worker_depth_exceeded`(depth 2, max 1)로 거절된다. 직접 돌려 확인했다. 워커가 원격에 맡겨야 한다고 판단하면 코디네이터에게 `ask`로 올린다. 코디네이터는 자기가 묶인 Run에만 워커를 띄울 수 있고 다른 Run id를 주면 `consumer_fenced`로 거절된다. 이것도 확인했다.
149
+
150
+ 감독형 순서는 이렇다.
151
+
152
+ 1. 대상을 확인한다. `orca status --environment <이름> --json`에서 `runtime.reachable`이 `true`이고 `connectionState`가 `connected`인지 본다. 원격과 이 머신의 오르카는 따로 업데이트된다. model과 effort는 원격이 그 기능을 알릴 때만 전달되고 원격 기능이나 권한이 확인 안 되면 로컬 실행으로 내려가지 않는다(`orca skills get orchestration --reference placement-and-remote`). 시험한 서버는 이 머신보다 버전이 낮았고(1.4.237 대 1.4.240) capabilities에 `orchestration.worker-launch-preferences.v1`이 있었다.
153
+ 2. 원격 레포 선택자를 읽는다. `orca repo list --environment <이름> --json`에서 레포 id를 찾아 `--repo id:<원격 레포 id>`로 넘긴다. 저쪽의 id와 경로는 이 머신과 다르다. 이 머신의 레포 id를 넣지 않는다.
154
+ 3. 원격 클론이 필요한 커밋을 갖고 있는지 먼저 본다. 저쪽 클론은 이 머신과 따로라서 push하지 않은 커밋은 안 보인다. 시험에서는 저쪽을 pull하지 않았는데도 새 워크트리가 origin main의 최신 커밋에서 만들어졌다. 한 번만 본 거라 늘 그런지는 모른다. 그래서 지시서 첫 단계에 `git log --oneline -1`을 보고하게 하고 기대한 커밋과 맞춰 본다.
155
+ 4. `worker-start --on`으로 띄운다. Run과 Task는 이 머신에 남고 `--on`은 워커가 도는 서버만 고른다. 시험에 쓴 꼴은 아래와 같다.
156
+
157
+ ```bash
158
+ orca orchestration worker-start --run <내 Run id> --on "<서버 이름>" --worktree new-top-level --repo id:<원격 레포 id> --name <이름> --agent claude --model <별칭|전체ID> --spec "<지시서 경로를 읽고 시작>" --json
159
+ ```
160
+
161
+ 5. 접수증의 `launch.requested`와 `effective`를 비교한다. haiku로 돌렸을 때 둘이 같았고(`claude`, `haiku`, effort `null`) 원격에서도 model이 먹혔다. effort는 원격에서 돌려보지 않았다. 원격 워커는 `structured`를 골라도 `remote_execution_host` 사유로 터미널 에이전트 워커로 뜬다. 원격 워크트리 경로는 접수증 `effects`의 `created_top_level` 항목에 있으니 정리할 때 쓰도록 적어 둔다.
162
+ 6. 띄운 뒤에는 Dispatch ID로만 따라간다. `worker-show --dispatch <id>`로 상태를, `worker-read --dispatch <id>`로 원격 트랜스크립트를 읽었다. `worker-list --run <id> --include-remote`에서는 원격 워커가 `host.kind: remote`, liveness `live`(source `execution_host`)로 보였다. `--include-remote`를 빼면 원격 워커가 전부 `unverifiable`로 읽힌다. `--on`을 다시 붙이거나 원격 터미널 핸들로 바꿔 부르지 않는다. 워커의 `worker_done`은 이 머신의 Run 메일함으로 온다.
163
+ 7. 끝나면 정리한다. `worker-release --dispatch <id>`가 원격 터미널을 닫는다. 기록은 남아서 릴리스 뒤에도 `worker-read`가 된다. 워크트리는 `worker-release`가 안 지우니 `orca worktree rm --environment "<서버 이름>" --worktree id:<원격 레포 id>::<원격 경로> --force`로 지우고 `orca worktree list --environment "<서버 이름>"`에서 사라졌는지 본다. 저쪽 클론에 워크트리 브랜치가 남는지는 확인하지 않았다.
164
+
165
+ full handoff로 원격에 넘길 때는 위 우회 경로(`worktree create` → `terminal create` → `terminal wait` → `terminal send`)의 모든 명령에 같은 `--environment <서버 이름>`을 붙인다. 터미널 핸들은 그 핸들을 낸 서버 것이라 뒤따르는 `terminal wait`와 `terminal send`에도 빠지면 안 된다. `--parent-worktree active`나 `--worktree active` 대신 `--no-parent --repo id:<원격 레포 id>`와 그 서버가 돌려준 정확한 워크트리 선택자를 쓴다. 이 경로는 원격에서 돌려보지 않았고 오르카 가이드(`orca skills get orca-cli`)를 따른 것이다.
166
+
141
167
  ### 재전송하지 않는다
142
168
 
143
169
  `terminal send`의 `accepted: true`는 입력이 받아졌다는 뜻이지 시작했다는 증명이 아니다. 조용하다고 다시 보내면 지시가 중복된다. `--wait-submit`으로 확인하고 다시 보내지 않는다.
@@ -157,15 +183,17 @@ Agent { subagent_type: "general-purpose", model: "<표에서 고른 tier의 모
157
183
  - 워크트리 상태판은 `worktree set --worktree <선택자> --comment "<짧은 현황>"`으로 갱신한다. 짧고 최신으로 유지한다.
158
184
  - 끝난 child는 결과를 확인한 뒤 `worktree rm --worktree id:<id> --force`로 정리한다. 안 지우면 쌓인다.
159
185
  - 감독형을 돌렸다면 임시 Run이 오르카에 남는다. 지우지 않고 id를 보고에 적어 사용자가 판단하게 한다.
186
+ - 원격 워크트리는 `worker-release`로 안 지워진다. `--environment`를 붙인 `worktree rm`으로 따로 지운다.
160
187
 
161
188
  ## 완료 보고
162
189
 
163
- - 어느 경로로 돌았는지: 메인, 서브에이전트, 하위 워크트리, 독립 워크트리, 오르카 여부
190
+ - 어느 경로로 돌았는지: 메인, 서브에이전트, 하위 워크트리, 독립 워크트리, 원격 머신, 오르카 여부
164
191
  - 쓴 model과 effort
165
192
  - 오르카를 썼다면 접수증의 `launch.requested`와 `effective` 비교 결과
166
193
  - 지시서 경로
167
194
  - 정리한 워크트리와 남은 Run
168
195
  - 값이 먹혔다는 근거: 감독형은 접수증, full handoff 우회는 터미널 머리글
196
+ - 원격으로 보냈다면 서버 이름, 원격 레포 선택자, 원격 워크트리 경로와 정리 여부, 워커가 보고한 기준 커밋
169
197
 
170
198
  ## Do-NOT
171
199
 
@@ -176,6 +204,7 @@ Agent { subagent_type: "general-purpose", model: "<표에서 고른 tier의 모
176
204
  - Agent 호출에서 `model`을 빼지 않는다.
177
205
  - 범위를 안 적은 지시서로 워크트리 워커를 띄우지 않는다.
178
206
  - 사용자와 주고받아야 하는 일을 넘기지 않는다.
207
+ - 원격에 맡긴 워커를 `--on`이나 원격 터미널 핸들로 다시 부르지 않는다. Dispatch ID로만 부른다.
179
208
 
180
209
  ## 에러 처리
181
210
 
@@ -186,3 +215,8 @@ Agent { subagent_type: "general-purpose", model: "<표에서 고른 tier의 모
186
215
  | `terminal wait`의 `wait.satisfied`가 `false` | 더 큰 `--timeout-ms`로 한 번 다시 기다린다. 그래도 아니면 시작 안 한 것으로 보고하고 보내지 않는다 |
187
216
  | 오르카 감지 실패 | 사용자에게 알리고 Agent 도구의 `isolation: "worktree"`로 내려간다 |
188
217
  | 빈 셸 탭이 먼저 생김 | 지시를 보내기 전에 올바른 터미널 핸들인지 확인한다 |
218
+ | `nested_worker_depth_exceeded` | 디스패치된 워커가 하위 워커를 띄우려 한 것이다. 아무것도 안 만들어졌다. 메인 세션이 띄우거나 코디네이터에게 `ask`로 올린다 |
219
+ | `consumer_fenced` | 자기가 묶인 Run이 아닌 Run id를 넣었다. 에러 문구에 나온 자기 Run id로 바꿔 다시 한다 |
220
+ | 원격 서버가 `reachable: false`거나 연결이 끊김 | 원격으로 못 보냈다고 보고한다. 같은 일을 이 머신에서 대신 돌리지 않는다. 사용자가 고르게 한다 |
221
+ | 원격 워커가 `unverifiable` | `worker-list`에 `--include-remote`를 줬는지 먼저 본다. 줬는데도 그렇다면 연결이 끊긴 것이지 끝난 게 아니다. 끝났다고 보고하지 않고 `worker_done`을 기다린다 |
222
+ | 원격 워크트리가 기대한 커밋보다 오래됨 | 필요한 커밋을 push했는지 확인한다. 저쪽 클론에서 pull이나 브랜치 변경을 하기 전에 사용자에게 묻는다 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tienne/gestalt",
3
- "version": "0.105.0",
3
+ "version": "0.106.0",
4
4
  "description": "TypeScript AI Development Harness - Gestalt psychology-driven requirement clarification",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gestalt",
3
- "version": "0.105.0",
3
+ "version": "0.106.0",
4
4
  "description": "Gestalt psychology-driven AI development harness. Transforms scattered requirements into structured, validated specifications through interactive interviews.",
5
5
  "author": {
6
6
  "name": "tienne"
package/plugin/.mcp.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "mcpServers": {
3
3
  "gestalt": {
4
4
  "command": "npx",
5
- "args": ["-y", "@tienne/gestalt@0.105.0", "serve"],
5
+ "args": ["-y", "@tienne/gestalt@0.106.0", "serve"],
6
6
  "startup_timeout_sec": 180,
7
7
  "tool_timeout_sec": 900
8
8
  }
package/plugin/mcp.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "mcpServers": {
3
3
  "gestalt": {
4
4
  "command": "npx",
5
- "args": ["-y", "@tienne/gestalt@0.105.0", "serve"],
5
+ "args": ["-y", "@tienne/gestalt@0.106.0", "serve"],
6
6
  "startup_timeout_sec": 180,
7
7
  "tool_timeout_sec": 900
8
8
  }
@@ -82,7 +82,7 @@
82
82
  | "orca로 실행", "orca로 병렬", "워커로 뿌려", "터미널로 뿌려", "병렬 디스패치", "다른 에이전트로 실행", "codex로 실행", "워커 띄워서 실행" | `dispatch` 스킬 사용 | 런타임 감지 → 같은 워크트리에 터미널 → worker_done 대기 → ready 재계산. 런타임 없으면 execute의 기본 병렬 경로 |
83
83
  | "doubt", "의심해줘", "반박해줘", "반박부터 받아", "반론부터 들어보자", "이 결정 의심", "되돌리기 어려운 작업 전에" | `doubt` 스킬 사용 | 자동으로는 되돌리기 어려운 작업(배포, 데이터 마이그레이션, 공개 API 변경), 보안 성질 주장, 낯선 코드에서 분기 로직을 바꿀 때만 띄운다. 결정마다 띄우지 않는다. 서브에이전트 안에서는 쓰지 않고 메인 세션에 올린다. 다 만든 뒤 판정은 `continuity-judge`, PR 단위 검토는 `review` |
84
84
  | "설명해줘", "쉽게 풀어줘", "풀어서 설명", "이해하기 쉽게", "쉽게 말하면", "이거 뭐야", "이 에러 뭐야", "무슨 뜻이야", "ELI5", "한테 설명해줘", "에게 설명해줘", "기획팀한테", "비개발자한테", "경영진 보고용으로 설명" | `explain` 스킬 사용 | 소스 확보 → 대상 확정 → explainer 위임 → explain-check → 재시도. 설명 문장만 빠르게 필요하면 `explainer` 에이전트 |
85
- | "handoff", "핸드오프", "핸드오버", "어디에 맡길지", "서브에이전트로 맡겨", "워크트리로 넘겨", "하위 워크트리로", "독립 워크트리로" | `handoff` 스킬 사용 | 맡길 곳 고르기(메인, 서브에이전트, 하위 워크트리, 독립 워크트리) → model과 effort 쌍 → 지시서 → 오르카 또는 Agent 도구로 위임. 같은 브랜치 병렬 쪼개기는 `dispatch` |
85
+ | "handoff", "핸드오프", "핸드오버", "어디에 맡길지", "서브에이전트로 맡겨", "워크트리로 넘겨", "하위 워크트리로", "독립 워크트리로", "원격 머신으로", "원격 서버로 넘겨" | `handoff` 스킬 사용 | 맡길 곳 고르기(메인, 서브에이전트, 하위 워크트리, 독립 워크트리, 원격 Orca 서버) → model과 effort 쌍 → 지시서 → 오르카 또는 Agent 도구로 위임. 같은 브랜치 병렬 쪼개기는 `dispatch` |
86
86
  | "지라 티켓", "지라 이슈", "티켓 만들어", "티켓 생성", "이슈 만들어", "이슈 생성", "지라에 올려", "지라에 등록", "백로그에 추가", "jira 티켓", "jira 이슈", "create jira" | `jira-create` 스킬 사용 | 내부적으로 jira-writer 구조화 → 프로젝트·필드 확정 → 승인 단계 → createJiraIssue |
87
87
  | "시장에 어떤 서비스가 있는지 훑어줘", "레퍼런스 후보 앱 추려줘", "이 기능 하는 앱 후보 추려줘", "서비스 지형 훑어줘", "시장 레퍼런스 분석" | `market-scan` 스킬 사용 | 제품 후보와 유형 묶기까지. 화면을 캡처하면 `screen-ref`, 사용자 관점 분석이면 `ux-research`, 전체를 한 번에 돌리면 `research` |
88
88
  | "PR 작성", "PR 만들어", "PR 써줘", "PR 올려", "풀리퀘", "풀 리퀘스트", "pull request", "create PR" | `pr` 스킬 사용 | 다른 레포의 PR 스킬과 이름이 겹치면 `gestalt:pr`로 부른다 |
@@ -107,6 +107,18 @@ Orca 런타임이 붙지 않아 워커 디스패치는 못 합니다.
107
107
 
108
108
  워크트리를 새로 만드는 것은 **ready 집합의 태스크들이 같은 파일을 건드릴 때만**이다. 그 경우 먼저 사용자에게 충돌을 짚고 워크트리 분리가 필요하다고 말한 뒤 진행한다. 워크트리를 나눴다면 그 워커는 코드 그래프와 memory가 비어 있다는 사실도 함께 알린다.
109
109
 
110
+ ### 원격 Orca 서버로는 뿌리지 않는다
111
+
112
+ 페어링된 원격 서버(`orca host list`에서 `--environment <이름>`으로 나오는 대상)는 이 스킬의 워커 자리가 아니다. 이 스킬이 기대는 전제가 원격에서는 깨진다.
113
+
114
+ - 같은 워크트리를 못 쓴다. 원격 서버는 디스크가 따로라 `--worktree active`나 `current`가 저쪽 워크트리를 가리키지 못하고 오르카도 원격의 `current`와 `new-child`를 거절한다. 원격 워커는 늘 저쪽의 독립 워크트리에서 돈다. 그러면 `.gestalt/`를 공유한다는 이유가 사라진다.
115
+ - 결과를 같은 실행 세션에 못 낸다. 게슈탈트 이벤트 DB가 홈 디렉토리 아래라서 원격 워커의 MCP 서버는 저쪽 DB를 연다. 워커가 `ges_execute action=execute_task`로 낸 결과가 이 머신의 세션에 안 들어간다. 이건 구조를 보고 판단한 것이고 원격에서 돌려보지는 않았다.
116
+ - 3단계 명령이 그대로 안 통한다. `terminal create --environment <이름>`이 낸 핸들은 원격 서버 것이라 이 머신의 `orchestration dispatch --to <핸들>`로 넣을 수 없다. `dispatch`에도 `--environment`를 붙이면 태스크가 원격 서버의 Run에 생겨 코디네이터의 Run과 갈라진다. 오르카 가이드는 원격 워커를 `worker-start --on` 하나로만 띄우고 Run과 Task는 지금 서버에 둔다(`orca skills get orchestration --reference placement-and-remote`).
117
+
118
+ 그래서 태스크를 원격 머신에 맡겨야 하면 [`handoff`](../handoff/SKILL.md)의 원격 경로로 보낸다. 그 태스크는 브랜치 단위의 독립 작업으로 넘기고 워커의 `worker_done` 보고를 받아 코디네이터가 이 머신에서 `execute_task`로 대신 제출한다. ready 집합의 나머지 태스크는 이 스킬대로 같은 워크트리에 뿌린다.
119
+
120
+ 원격 위임은 메인 세션이 맡는다. 디스패치된 워커가 `worker-start`를 부르면 기본 설정에서 `nested_worker_depth_exceeded`로 거절된다. handoff 쪽 시험에서 확인했다. 이 스킬의 `terminal create`와 `dispatch --inject` 경로도 같은 제한에 걸리는지는 확인하지 않았다.
121
+
110
122
  ## 3단계: 태스크를 디스패치한다
111
123
 
112
124
  태스크마다 Orca 오케스트레이션 태스크를 만들고 워커에 넣는다. `--inject`가 워커에게 생애주기 프리앰블을 붙여 `worker_done`을 보내게 한다.
@@ -179,6 +191,7 @@ Orca 쪽에서 사람이 답해 해소되면(`gate-resolve`) 그 답을 게슈
179
191
  - **병렬 실행을 이유로 워크트리를 만들지 않는다.** 파일 충돌만이 사유다.
180
192
  - **워커에게 ready 집합을 굴리게 하지 않는다.** 코디네이터만 한다.
181
193
  - **`worker_done` 없이 완료로 보고하지 않는다.** 터미널이 조용한 것은 완료가 아니다.
194
+ - **원격 Orca 서버에 터미널을 만들어 태스크를 뿌리지 않는다.** 결과가 이 머신의 실행 세션에 안 들어간다.
182
195
  - **워커를 무제한으로 띄우지 않는다.** 워커마다 에이전트와 MCP 서버 프로세스가 하나씩 붙는다.
183
196
 
184
197
  ## 에러 처리
@@ -191,6 +204,7 @@ Orca 쪽에서 사람이 답해 해소되면(`gate-resolve`) 그 답을 게슈
191
204
  | 터미널 핸들이 `terminal_handle_stale` | `terminal list`로 다시 조회해 교체 핸들만 쓴다. 낡은 핸들과 새 핸들에 이중 전송하지 않는다 |
192
205
  | 워커가 같은 태스크를 3회 연속 실패 | 그 태스크 디스패치를 멈추고 사용자에게 보고. evolve 파이프라인으로 넘길지 확인 |
193
206
  | `check --wait` 타임아웃 | 실패가 아니다. `task-list`나 `terminal read`로 생존을 확인하고 대기를 다시 건다 |
207
+ | 태스크를 원격 머신에 맡겨 달라는 요청 | 이 스킬로 뿌리지 않는다. 그 태스크만 handoff의 원격 경로로 보내고 결과는 코디네이터가 `execute_task`로 제출한다 |
194
208
  | DB 잠금 오류(`SQLITE_BUSY`) | 워커 수를 줄이고 재시도. 게슈탈트 이벤트 DB는 홈 글로벌이라 프로세스가 겹친다 |
195
209
 
196
210
  ## 완료 보고
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: handoff
3
3
  version: "1.0.0"
4
- description: "일을 받으면 어디에 맡길지(메인 직접, 서브에이전트, 같은 워크트리 터미널 워커, 하위 워크트리, 독립 워크트리)를 기준으로 고르고, 고른 경로로 model과 effort를 쌍으로 실어 실제로 넘긴다. 오르카가 있으면 워크트리 위임은 오르카를 먼저 쓴다. 같은 브랜치 안의 병렬 쪼개기는 dispatch, 티켓과 브랜치와 PR 단위로 끝나는 위임은 handoff가 맡는다."
4
+ description: "일을 받으면 어디에 맡길지(메인 직접, 서브에이전트, 같은 워크트리 터미널 워커, 하위 워크트리, 독립 워크트리, 원격 머신)를 기준으로 고르고 고른 경로로 model과 effort를 쌍으로 실어 실제로 넘긴다. 오르카가 있으면 워크트리 위임은 오르카를 먼저 쓴다. 같은 브랜치 안의 병렬 쪼개기는 dispatch, 티켓과 브랜치와 PR 단위로 끝나는 위임은 handoff가 맡는다."
5
5
  triggers:
6
6
  - "handoff"
7
7
  - "핸드오프"
@@ -11,6 +11,8 @@ triggers:
11
11
  - "워크트리로 넘겨"
12
12
  - "하위 워크트리로"
13
13
  - "독립 워크트리로"
14
+ - "원격 머신으로"
15
+ - "원격 서버로 넘겨"
14
16
  inputs:
15
17
  task:
16
18
  type: string
@@ -19,7 +21,7 @@ inputs:
19
21
  target:
20
22
  type: string
21
23
  required: false
22
- description: "맡길 곳을 사용자가 지정한 경우. subagent, child-worktree, independent-worktree 중 하나. 비우면 이 스킬의 기준으로 고른다"
24
+ description: "맡길 곳을 사용자가 지정한 경우. subagent, child-worktree, independent-worktree, remote 중 하나. 비우면 이 스킬의 기준으로 고른다"
23
25
  mode:
24
26
  type: string
25
27
  required: false
@@ -31,7 +33,7 @@ outputs:
31
33
  - briefPath
32
34
  - launchReceipt
33
35
  routing:
34
- note: "맡길 곳 고르기(메인, 서브에이전트, 하위 워크트리, 독립 워크트리) → model과 effort 쌍 → 지시서 → 오르카 또는 Agent 도구로 위임. 같은 브랜치 병렬 쪼개기는 `dispatch`"
36
+ note: "맡길 곳 고르기(메인, 서브에이전트, 하위 워크트리, 독립 워크트리, 원격 Orca 서버) → model과 effort 쌍 → 지시서 → 오르카 또는 Agent 도구로 위임. 같은 브랜치 병렬 쪼개기는 `dispatch`"
35
37
  ---
36
38
 
37
39
  # Handoff Skill
@@ -58,8 +60,9 @@ routing:
58
60
  | 같은 워크트리 터미널 워커 | 같은 브랜치 안에서 쪼갠 병렬이고 파일군이 안 겹칠 때. `dispatch`로 보낸다 |
59
61
  | 하위 워크트리 | 파일을 쓰고 끝이 브랜치나 PR로 남는 일(티켓 단위 구현). 15분을 넘기거나 사람이 중간에 들여다보고 끼어들어야 하는 일. `ship`이나 `review-loop` 같은 스킬 체인을 처음부터 끝까지 돌리는 일. 다른 레포 작업. 메인과 파일이 안 겹치는 독립 작업 |
60
62
  | 독립 워크트리 | 현재 브랜치와 무관한 독립 작업. 현재 브랜치 맥락은 프롬프트에 적는다 |
63
+ | 원격 머신(페어링된 Orca 서버) | 오래 걸리거나 이 머신의 CPU와 메모리를 많이 쓰는 일. 저쪽 머신에만 있는 레포나 환경이 필요한 일. 끝이 브랜치나 PR로 남는 일이어야 한다. 메인 세션만 띄울 수 있다 |
61
64
 
62
- 하위 워크트리는 오르카의 `new-child`(`worktree create`로는 `--parent-worktree`), 독립 워크트리는 `new-top-level`(`worktree create`로는 `--no-parent`)에 해당한다.
65
+ 하위 워크트리는 오르카의 `new-child`(`worktree create`로는 `--parent-worktree`), 독립 워크트리는 `new-top-level`(`worktree create`로는 `--no-parent`)에 해당한다. 원격 머신에서는 하위 워크트리를 못 만든다. 워크트리 부모와 자식은 같은 머신에 있어야 하고 오르카가 원격의 `new-child`와 `current`를 거절한다. 원격으로 보내는 일은 늘 저쪽의 독립 워크트리(`new-top-level`)가 된다. 절차는 4단계의 원격 소절에 있다.
63
66
 
64
67
  충돌과 파일 규칙은 어느 경로에서나 같다.
65
68
 
@@ -138,6 +141,29 @@ dispatch는 `worker-start` 대신 `terminal create`와 `orchestration task-creat
138
141
 
139
142
  이 4단계는 sonnet과 low로 끝까지 돌려 확인했다. 2단계 직후 터미널 머리글에 `Sonnet 5.5 with low effort`가 떴고 4단계 접수증에는 `turn_started`까지 찍혔다. 값이 먹혔는지는 모델의 답이 아니라 이 머리글로 본다. 모델은 자기 effort를 모른다고 답한다. 이 경로의 워커는 `worker-start`와 달리 `auto mode on`으로 뜬다. 오르카 문서가 경고하는 빈 셸 탭은 이번에는 생기지 않았다.
140
143
 
144
+ ### 원격 머신(페어링된 Orca 서버)
145
+
146
+ `orca host list`가 맡길 수 있는 머신을 한 번에 보여 준다. 이 머신은 `--host local`, SSH 대상은 `--host ssh:<id>`, 페어링된 Orca 서버는 `--environment <이름>`이다. SSH 대상과 페어링된 서버를 서로 바꿔 넣는 게 가장 흔한 실수다. SSH 대상 중 `github.com`처럼 git 리모트용으로 등록된 이름은 위임 대상이 아니다. 이 소절은 페어링된 Orca 서버만 다룬다.
147
+
148
+ 원격 위임은 메인 세션이 직접 띄운다. 디스패치된 워커가 `worker-start`를 부르면 기본 설정에서 `nested_worker_depth_exceeded`(depth 2, max 1)로 거절된다. 직접 돌려 확인했다. 워커가 원격에 맡겨야 한다고 판단하면 코디네이터에게 `ask`로 올린다. 코디네이터는 자기가 묶인 Run에만 워커를 띄울 수 있고 다른 Run id를 주면 `consumer_fenced`로 거절된다. 이것도 확인했다.
149
+
150
+ 감독형 순서는 이렇다.
151
+
152
+ 1. 대상을 확인한다. `orca status --environment <이름> --json`에서 `runtime.reachable`이 `true`이고 `connectionState`가 `connected`인지 본다. 원격과 이 머신의 오르카는 따로 업데이트된다. model과 effort는 원격이 그 기능을 알릴 때만 전달되고 원격 기능이나 권한이 확인 안 되면 로컬 실행으로 내려가지 않는다(`orca skills get orchestration --reference placement-and-remote`). 시험한 서버는 이 머신보다 버전이 낮았고(1.4.237 대 1.4.240) capabilities에 `orchestration.worker-launch-preferences.v1`이 있었다.
153
+ 2. 원격 레포 선택자를 읽는다. `orca repo list --environment <이름> --json`에서 레포 id를 찾아 `--repo id:<원격 레포 id>`로 넘긴다. 저쪽의 id와 경로는 이 머신과 다르다. 이 머신의 레포 id를 넣지 않는다.
154
+ 3. 원격 클론이 필요한 커밋을 갖고 있는지 먼저 본다. 저쪽 클론은 이 머신과 따로라서 push하지 않은 커밋은 안 보인다. 시험에서는 저쪽을 pull하지 않았는데도 새 워크트리가 origin main의 최신 커밋에서 만들어졌다. 한 번만 본 거라 늘 그런지는 모른다. 그래서 지시서 첫 단계에 `git log --oneline -1`을 보고하게 하고 기대한 커밋과 맞춰 본다.
155
+ 4. `worker-start --on`으로 띄운다. Run과 Task는 이 머신에 남고 `--on`은 워커가 도는 서버만 고른다. 시험에 쓴 꼴은 아래와 같다.
156
+
157
+ ```bash
158
+ orca orchestration worker-start --run <내 Run id> --on "<서버 이름>" --worktree new-top-level --repo id:<원격 레포 id> --name <이름> --agent claude --model <별칭|전체ID> --spec "<지시서 경로를 읽고 시작>" --json
159
+ ```
160
+
161
+ 5. 접수증의 `launch.requested`와 `effective`를 비교한다. haiku로 돌렸을 때 둘이 같았고(`claude`, `haiku`, effort `null`) 원격에서도 model이 먹혔다. effort는 원격에서 돌려보지 않았다. 원격 워커는 `structured`를 골라도 `remote_execution_host` 사유로 터미널 에이전트 워커로 뜬다. 원격 워크트리 경로는 접수증 `effects`의 `created_top_level` 항목에 있으니 정리할 때 쓰도록 적어 둔다.
162
+ 6. 띄운 뒤에는 Dispatch ID로만 따라간다. `worker-show --dispatch <id>`로 상태를, `worker-read --dispatch <id>`로 원격 트랜스크립트를 읽었다. `worker-list --run <id> --include-remote`에서는 원격 워커가 `host.kind: remote`, liveness `live`(source `execution_host`)로 보였다. `--include-remote`를 빼면 원격 워커가 전부 `unverifiable`로 읽힌다. `--on`을 다시 붙이거나 원격 터미널 핸들로 바꿔 부르지 않는다. 워커의 `worker_done`은 이 머신의 Run 메일함으로 온다.
163
+ 7. 끝나면 정리한다. `worker-release --dispatch <id>`가 원격 터미널을 닫는다. 기록은 남아서 릴리스 뒤에도 `worker-read`가 된다. 워크트리는 `worker-release`가 안 지우니 `orca worktree rm --environment "<서버 이름>" --worktree id:<원격 레포 id>::<원격 경로> --force`로 지우고 `orca worktree list --environment "<서버 이름>"`에서 사라졌는지 본다. 저쪽 클론에 워크트리 브랜치가 남는지는 확인하지 않았다.
164
+
165
+ full handoff로 원격에 넘길 때는 위 우회 경로(`worktree create` → `terminal create` → `terminal wait` → `terminal send`)의 모든 명령에 같은 `--environment <서버 이름>`을 붙인다. 터미널 핸들은 그 핸들을 낸 서버 것이라 뒤따르는 `terminal wait`와 `terminal send`에도 빠지면 안 된다. `--parent-worktree active`나 `--worktree active` 대신 `--no-parent --repo id:<원격 레포 id>`와 그 서버가 돌려준 정확한 워크트리 선택자를 쓴다. 이 경로는 원격에서 돌려보지 않았고 오르카 가이드(`orca skills get orca-cli`)를 따른 것이다.
166
+
141
167
  ### 재전송하지 않는다
142
168
 
143
169
  `terminal send`의 `accepted: true`는 입력이 받아졌다는 뜻이지 시작했다는 증명이 아니다. 조용하다고 다시 보내면 지시가 중복된다. `--wait-submit`으로 확인하고 다시 보내지 않는다.
@@ -157,15 +183,17 @@ Agent { subagent_type: "general-purpose", model: "<표에서 고른 tier의 모
157
183
  - 워크트리 상태판은 `worktree set --worktree <선택자> --comment "<짧은 현황>"`으로 갱신한다. 짧고 최신으로 유지한다.
158
184
  - 끝난 child는 결과를 확인한 뒤 `worktree rm --worktree id:<id> --force`로 정리한다. 안 지우면 쌓인다.
159
185
  - 감독형을 돌렸다면 임시 Run이 오르카에 남는다. 지우지 않고 id를 보고에 적어 사용자가 판단하게 한다.
186
+ - 원격 워크트리는 `worker-release`로 안 지워진다. `--environment`를 붙인 `worktree rm`으로 따로 지운다.
160
187
 
161
188
  ## 완료 보고
162
189
 
163
- - 어느 경로로 돌았는지: 메인, 서브에이전트, 하위 워크트리, 독립 워크트리, 오르카 여부
190
+ - 어느 경로로 돌았는지: 메인, 서브에이전트, 하위 워크트리, 독립 워크트리, 원격 머신, 오르카 여부
164
191
  - 쓴 model과 effort
165
192
  - 오르카를 썼다면 접수증의 `launch.requested`와 `effective` 비교 결과
166
193
  - 지시서 경로
167
194
  - 정리한 워크트리와 남은 Run
168
195
  - 값이 먹혔다는 근거: 감독형은 접수증, full handoff 우회는 터미널 머리글
196
+ - 원격으로 보냈다면 서버 이름, 원격 레포 선택자, 원격 워크트리 경로와 정리 여부, 워커가 보고한 기준 커밋
169
197
 
170
198
  ## Do-NOT
171
199
 
@@ -176,6 +204,7 @@ Agent { subagent_type: "general-purpose", model: "<표에서 고른 tier의 모
176
204
  - Agent 호출에서 `model`을 빼지 않는다.
177
205
  - 범위를 안 적은 지시서로 워크트리 워커를 띄우지 않는다.
178
206
  - 사용자와 주고받아야 하는 일을 넘기지 않는다.
207
+ - 원격에 맡긴 워커를 `--on`이나 원격 터미널 핸들로 다시 부르지 않는다. Dispatch ID로만 부른다.
179
208
 
180
209
  ## 에러 처리
181
210
 
@@ -186,3 +215,8 @@ Agent { subagent_type: "general-purpose", model: "<표에서 고른 tier의 모
186
215
  | `terminal wait`의 `wait.satisfied`가 `false` | 더 큰 `--timeout-ms`로 한 번 다시 기다린다. 그래도 아니면 시작 안 한 것으로 보고하고 보내지 않는다 |
187
216
  | 오르카 감지 실패 | 사용자에게 알리고 Agent 도구의 `isolation: "worktree"`로 내려간다 |
188
217
  | 빈 셸 탭이 먼저 생김 | 지시를 보내기 전에 올바른 터미널 핸들인지 확인한다 |
218
+ | `nested_worker_depth_exceeded` | 디스패치된 워커가 하위 워커를 띄우려 한 것이다. 아무것도 안 만들어졌다. 메인 세션이 띄우거나 코디네이터에게 `ask`로 올린다 |
219
+ | `consumer_fenced` | 자기가 묶인 Run이 아닌 Run id를 넣었다. 에러 문구에 나온 자기 Run id로 바꿔 다시 한다 |
220
+ | 원격 서버가 `reachable: false`거나 연결이 끊김 | 원격으로 못 보냈다고 보고한다. 같은 일을 이 머신에서 대신 돌리지 않는다. 사용자가 고르게 한다 |
221
+ | 원격 워커가 `unverifiable` | `worker-list`에 `--include-remote`를 줬는지 먼저 본다. 줬는데도 그렇다면 연결이 끊긴 것이지 끝난 게 아니다. 끝났다고 보고하지 않고 `worker_done`을 기다린다 |
222
+ | 원격 워크트리가 기대한 커밋보다 오래됨 | 필요한 커밋을 push했는지 확인한다. 저쪽 클론에서 pull이나 브랜치 변경을 하기 전에 사용자에게 묻는다 |