Multi-session control discipline for delegating work to worker sessions — investigate, decide, dispatch, verify, and merge. Use when a session is coordinating separate worker sessions or worktrees and needs to know when to delegate versus decide, and how to verify before merging.
Installs into .claude/skills of the current project.
Are you the author of Control Loop?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/this-hw-control-loop-hiway-kit)
---
name: control-loop
description: Multi-session control discipline for delegating work to worker sessions — investigate, decide, dispatch, verify, and merge. Use when a session is coordinating separate worker sessions or worktrees and needs to know when to delegate versus decide, and how to verify before merging.
---
# Control Loop 스킬
축은 *기획 대 컨트롤*이 아니라 **결정 대 조사**다. 판별식: 브리프를 전제·범위/계약·
금지사항·보고형식 4블록으로 쓸 수 있으면 그 작업은 워커에게 위임 가능한 **조사**다.
4블록을 **쓰는 행위 자체**는 위임 불가능한 **결정**이다. 이 스킬은 기획용·컨트롤용으로
나뉘지 않는다 — 같은 세션 안에서 조사와 결정을 섞지 않고 3페이즈로 가른다.
## 언제 쓰는가
컨트롤 세션이 자신이 아닌 별도의 워커(다른 세션, 격리된 워크트리, 그 밖에 호스트가
제공하는 어떤 위임 수단이든)에게 작업을 맡기고, 그 결과를 검증한 뒤 통합 브랜치로
되돌릴 때. 구체적 위임 수단은 이 스킬의 관심사가 아니다 — 아래 "운송" 참고.
---
## P1 — 조사·설계 (위임 가능)
- 목적은 결정에 필요한 **사실**을 모으는 것이다. 결정은 여기서 하지 않는다.
- **"없다 / 신설한다" 같은 존재 판정은 실측 뒤에만 쓴다.** `grep`, `git log --all`,
대상 파일 직접 열람으로 확인하지 않은 존재 판정은 그 자체가 결함이다 — 이 판정을
틀린 채로 다음 페이즈에 넘기면 컨트롤이 잘못된 전제로 브리프를 쓰게 된다.
- P1을 워커에게 위임할 때도 4블록 브리프(P2 참조)로 위임한다 — 조사 자체는 위임 가능,
결정만 위임 불가라는 뜻이다.
- P1 산출물은 사실 목록이지 결정이 아니다. "이렇게 해야 한다"가 섞여 있다면 그것은
이미 P2로 넘어간 것이므로, 컨트롤이 그 부분만 따로 검증하고 결정으로 승격한다.
### 조사를 위임하지 않는 쪽으로 끌리는 힘이 있다 — 알고 저항한다
P2 의 브리프는 **실측된 전제**로 시작해야 한다(아래 "되돌림 규칙"). 그런데 그 전제를
쓰려면 **누군가 먼저 조사해야** 하고, 가장 손쉬운 답은 "컨트롤이 직접 본다"다. 그래서
**비싼 조사는 컨트롤이 하고 값싼 타이핑만 위임**하는 쪽으로 기운다.
**브리프가 정밀할수록 이 편향이 커진다** — 자기강화 고리다. 컨트롤은 병목이므로,
조사를 컨트롤이 떠안으면 전체가 컨트롤 속도로 수렴한다.
> 실측(외부 컨트롤 세션, 2026-09-08): 하루 조사 3건(판정기 추적·레지스트리 조사·기본값
> 추적)을 **전부 컨트롤이 직접 했다.** 위임 가능한 작업이었고, 규칙도 이미 있었다.
**틀린 교정: 브리프에 가설을 쓴다.** 검증 안 된 전제를 구현 브리프에 섞으면 워커가
**틀린 설계 위에서 구현을 시작한다** — 반려보다 비싸다.
**옳은 형태: 위임이 둘이다.**
| | 산출물 | 금지 |
| --- | --- | --- |
| **① 조사 위임** | 근거(파일:줄·명령 출력) 붙은 **사실 보고** | **구현 금지** |
| ② 컨트롤이 설계 | 결정 (위임 불가) | — |
| **③ 구현 위임** | 확정된 설계의 구현 | 설계 변경 금지 |
> **조사와 구현을 한 브리프에 담지 마라. 조사 결과가 설계를 바꿀 수 있으면 그것은
> 두 개의 위임이다.** 세션은 같아도 된다 — **브리프가 둘이어야 한다.**
### 모델·티어는 위험과 검증 비용으로 고른다
짧고 검증 가능한 조사는 지연을, 복잡한 구현·분석은 정확도를 우선한다. 조사도 보안·핵심
로직이면 난도가 높다. 역할만으로 능력을 단정하지 말고 위험·난도·실측에 따라 조정한다.
모델 이름과 기본값은 사용자/호스트 설정의 몫이다. 요청값과 실제 실행 설정을 구분하고, 실제
실행 설정(모델 ID·effort)은 요청값의 되울림이 아니라 워커 세션 기록에서 확인한다 — 못 찾으면 미확인으로 적는다.
## P2 — 결정·브리프 (컨트롤 전용, 위임 불가)
P1 산출물을 놓고 채택·금지를 결정하고, 그 결정을 4블록 브리프로 쓴다.
- 4블록 스키마·보고 첫 줄 고정 형식·형식 검사 정규식은 `rules/delegation-contract.md`가
SSOT다. 이 스킬은 그 계약을 **참조만** 한다 — 여기서 다시 정의하면 두 곳이 어긋날 때
아무것도 그것을 강제하지 않는 결함 클래스(동기화 안 되는 이중 선언)가 재현된다.
- **되돌림 규칙**: 4블록(전제 / 범위·계약 / 금지사항 / 보고형식) 중 하나라도 채울 수
없으면 — 전제가 실측되지 않았거나 완료 기준을 문장으로 확정할 수 없으면 — **P1으로
되돌린다.** 4블록을 못 쓰는 채로 디스패치하지 않는다.
- 브리프의 전제 서술은 P1의 실측을 **그대로 인용**한다. 컨트롤이 전제를 추측해서 쓰면
워커가 반려하게 된다(관측된 실패 지점 — 전제를 추측한 브리프가 하루 여러 건 반려됐다).
### 기준 커밋을 못박는다
디스패치 전 컨트롤은 기준 ref의 **실제 커밋 해시**를 확인하고(`git rev-parse <ref>`)
그 해시를 브리프에 적는다. **브랜치 이름(`main` 등)은 기준으로 쓰지 않는다** — 다른
체크아웃이 그 브랜치를 물고 있으면 로컬 이름이 원격보다 여러 커밋 뒤처져 있을 수 있고,
그 상태로 워커 워크트리를 생성하면 낡은 기준점에서 작업이 시작된다(실측 사고: 워크트리
5개가 로컬 `main` 기준으로 생성됐는데 그 `main` 자체가 원격보다 11커밋 뒤처져 있었다).
워커는 착수 시 **자기 워크트리 HEAD**(`git rev-parse HEAD`)가 브리프의 해시와 같은지
확인한다. 다르면 구현을 시작하지 않고 **즉시 에스컬레이션**한다.
### 자식 생성 절차 — 인자 4개
호스트가 제공하는 위임 수단으로 워커를 만들 때, 다음 4개를 함께 전달한다:
1. **부모 세션 이름/식별자** — 워커가 완료·에스컬레이션을 보고할 대상
2. **역할 + 워크트리 경로** — 무엇을 맡았고 어디서 작업하는지
3. **4블록 브리프** — 이 페이즈의 산출물 그대로
4. **금지사항 기본값** — 최소 다음을 포함한다: push 금지, 통합 브랜치(`main` 등) 직접
쓰기 금지, 파괴적 git 명령(bare `stash`/`reset --hard`/`clean -fd`) 금지, 병합 전
공유 상태 파일(진행 원장 등) 쓰기 금지
4개 중 하나라도 못 채운 채로 디스패치하려 한다면 P2가 아직 끝나지 않은 것이다 — P2로
남아 있는다. 워커 자신이 로드해 따르는 규율(전제 검증·보고 형식 등)은 별도 스킬이
정의한다 — 이 스킬은 그 규율을 여기서 다시 쓰지 않고 참조만 한다.
---
## P3 — 디스패치·수신·통합 (컨트롤 전용, 위임 불가)
### 디스패치
호스트가 제공하는 수단으로 위임한다. 구체적 명령·플래그는 이 스킬 본문의 관심사가
아니다 — 아래 "운송" 참고. 워커는 **자기 워크트리/브랜치에만** 커밋한다. **통합
브랜치는 컨트롤만 쓴다.**
### 수신
워커 보고 원문·diff·검증 출력을 확인한다. 보고는 검증의 입력이지 결론이 아니다. 테스트
통과 주장에 종료코드 근거가 없으면(파이프로 종료코드가 삼켜진 로그만 있는 경우 등)
재요청한다 — 조건의 정의는 계약(`rules/delegation-contract.md`)에 있다.
### 게이트 — 병합 전에 컨트롤이 직접 실행한다
병합하기 전에 컨트롤이 완료 게이트(정의된 검증 스크립트·관련 테스트)를 **직접
실행**한다. 워커의 "green" 보고를 게이트 실행의 대체물로 쓰지 않는다.
- fail이면 워커에게 결함을 반려한다 — 브리프를 P2로 되돌리지 않는다(브리프는 유효한
채로 구현만 틀렸을 수 있다).
- pass이면 컨트롤만 통합 브랜치로 병합한다.
#### 그리고 **통합 후 통합 브랜치에서 한 번 더** 전량을 돌린다
병합 **전** 게이트만으로는 부족하다. 격리된 곳에서 도는 검증은 두 이유로 빈틈이 있다:
1. **격리 환경에는 런타임 산출물이 없다.** 그 부재가 "안전한 기본값"으로 흡수되면
그 단언은 **공허하게 참**이 된다 — 한 번도 실제로 평가되지 않은 채 초록이다.
2. **워커는 다른 워커의 변경을 보지 못한다.** A가 만든 코드를 B가 강화한 검사가
잡는 상황은 **양쪽 워크트리 어디에서도** 드러나지 않는다.
실측 `[confirmed: n=2, 서로 다른 프로젝트, 2026-09-10]`. 한쪽은 병합 게이트 10회가
전부 초록이었는데 통합 브랜치에서 전량을 돌리자 **안전 게이트 3건이 빨갛게** 나왔다
(위 1번 — 그 단언들은 한 번도 평가된 적이 없었다). 다른 쪽은 워커 3인이 각자
워크트리에서 초록이었는데 병합 후 **게이트 2건이 red** 였다(위 2번 — 어느 워커도
혼자서는 볼 수 없는 결함이었다).
**기전은 다르지만 처방은 같다: 통합 브랜치에서 최소 1회 전량을 돌린다.**
그 실행이 red 면 병합을 되돌리는 것이 아니라 **그 자리에서 고친다** — 통합 지점이
그 결함이 처음으로 보이는 유일한 자리다.
#### 검증 호출과 병합 호출을 물리적으로 분리한다
**게이트 실행과 병합·푸시를 같은 도구 호출에 잇지 마라.** 출력을 읽는 시점엔 이미
병합된 뒤이므로, 그건 게이트가 아니라 로그다. 호출을 나눠 **사이에 출력을 읽는 턴**을
반드시 넣는다.
`&&` 로 묶는 것도 일반해가 아니다. 세 이유다.
1. **판정이 stdout 에 있는 검증**은 종료코드가 "조회 성공"만 뜻한다 — `gh pr view`·
`gh pr checks` 는 CI 가 빨개도 exit 0 이다. `&&` 로 이어도 게이트가 되지 않는다.
2. 검증이 여러 축이면 `&&` 한 줄이 그 축들을 **조건식으로 압축**한다. 각 축이 어떤
종류의 거짓 초록을 잡으려고 있었는지가 사라지고, 몇 개를 빠뜨렸는지 아무도 안 본다.
3. **출력을 보려고 파이프에 물리는 순간 종료코드가 바뀐다.** 파이프라인의 rc 는
**마지막 명령의 rc** 다 — `gate | tail -25` 의 rc 는 `tail` 것이고, `tail` 은 거의
항상 0 이다. 게이트를 읽기 좋게 만들려던 습관이 게이트를 로그로 바꾼다.
```bash
sh -c 'exit 7' ; echo $? # 7
sh -c 'exit 7' | tail -1 ; echo $? # 0 ← 삼켜졌다
bash -c 'set -o pipefail; sh -c "exit 7" | tail -1'; echo $? # 7
```
실측 `[confirmed: 2026-09-10, 두 세션]`: 한 세션이 병합 게이트를 `--dry-run 2>&1 |
tail -25` 로 돌려 **rc 0 을 통과 신호로 읽었다.**
**규칙**: 게이트의 rc 를 쓸 거면 파이프에 물리지 마라. 출력이 보고 싶으면 **파일로
받고 rc 를 따로 잡아라**(`cmd > out 2>&1; rc=$?`). `set -o pipefail` 도 답이지만,
그건 그 셸에서만 참이라 **호출자가 잊으면 조용히 되돌아간다.**
`pytest && git push` 처럼 판정이 **종료코드에** 있고 축이 하나면 `&&` 는 진짜 게이트다.
그 경우까지 금지하지는 않는다.
> 실측 n=2 (서로 독립, 같은 날 다른 두 세션): PR 4축 검증이 `FAILURE` 를 출력했는데
> 같은 블록의 `gh pr merge` 가 조건 없이 실행돼 CI 실패 상태로 main 머지 · `pytest`
> 뒤에 `commit`·`push` 를 `;` 로 체인해 린트 오류가 main 에 올라감. n=2 면 개인
> 부주의가 아니라 도구가 유도하는 형태다.
기계적 보강은 opt-in 훅 `hooks/examples/verify-mutate-split.py` 에 있다(기본 OFF).
훅은 **한 번의 도구 호출만** 본다 — "직전 턴에 CI 결과를 읽었는가"는 판정하지 않는다.
### 응답 직전 — 자식 목록을 실제로 조회한다
**컨트롤이 사용자에게 응답하기 직전, 자기 자식 목록을 호스트에 조회해 브리프로 만든
자식과 대조한다.** 안 붙은 자식이 있으면 그 사실을 응답에 적는다.
규칙만 있고 **점검 시점이 없으면 반복된다.** 계보가 끊긴 것은 조회하기 전까지
아무 증상도 내지 않는다 — 세션은 멀쩡히 돌고 보고도 온다. 끊긴 것은 *"이 자식이 누구
밑에 있는가"* 뿐이고, 그건 물어봐야만 보인다.
> 실측(외부 컨트롤 세션): 워크트리 생성과 세션 기동을 **다른 도구로 나눠** 해서 계보가
> 안 붙었다. **3회차 재발**이었고 규칙은 이미 메모리에 있었다 — 어긴 것은 규칙이 아니라
> **자기점검**이었다(응답 직전 조회를 한 번도 하지 않았다).
**계보는 자식을 만드는 그 명령에 붙는다.** 워크트리 생성과 세션 기동을 다른 도구로
나누면 조용히 끊긴다.
#### 이 킷이 집행할 수 없는 것 — 명시한다
**"생성 도구를 섞었다"를 킷은 탐지할 수 없다.** 킷의 자식 마커
(`rules/child-marker.md`)는 **자식 세션 스킬이 로드될 때만** 생기는데, 도구를 섞는다는
것이 곧 그 경로를 우회한다는 뜻이다. 마커가 없으면 훅은 **fail-open** 이라 아무 경고도
내지 않는다 — 자기 참조적으로 판정 불가다.
킷이 집행하는 것은 **마커가 있을 때 그 내용이 옳은가**까지다: `parent`·`role`·
`base_commit` 이 비어 있으면 "자식 아님"으로 판정하고, `base_commit` 이 현재 HEAD 의
조상이 아니면 경고한다(`hooks/examples/child-git-guard.py`, opt-in).
**계보 자체는 호스트·오케스트레이터의 상태이지 킷의 상태가 아니다.**
위 "응답 직전 조회"가 이 경계에서 할 수 있는 전부다. 없는 보호를 있다고 믿지 마라.
### 통합·정리
- 통합 브랜치는 컨트롤만 쓴다. 통지는 워커의 SHA가 아닌 **통합 브랜치의 최종 SHA**로 한다.
- 완료/실패 보고를 접수·검증한 뒤 소유 세션을 즉시 재사용하거나, 사용자 요청으로 보존하거나,
해제한다. 보고 수신·처리·승인은 구분하며 호스트의 수명주기 계약을 따른다.
- **세션 해제와 작업 트리 삭제는 별개다.** 삭제 전 미병합 커밋·수정 파일·미추적/ignored
보고서의 통합 또는 복구 가능한 보존을 확인한다. 커밋 백업만으로 미커밋 파일은 보존되지 않는다.
실패/미채택 산출물도 보존 근거가 있으면 정리 가능하다. 다른 작업자의 자원은 건드리지 않는다.
- 재사용 시 이전 작업 마커는 해당 호스트 계약에 따라 정리한다. 살아 있는 작업의 권한을 지우지 않는다.
- **응답 직전 실제 자원 목록을 조회**해 각 자원의 재사용·사용자 요청 보존·해제 결과를 대조한다.
미처리/미확인은 차단 사유와 다음 담당자를 보고한다. 해제 요청의 성공 코드만으로 종료를 단정하지 않는다.
- 빈 응답·타임아웃·연결 단절은 종료 증거가 아니다. 상태 불명은 보존하고 호스트의 복구 절차로
확인한다. 종료/정산 권한 없이 정지·재시도·삭제하지 않는다. hooks trust·보안 게이트를 우회하지 않는다.
이 절은 실행자의 확인 의무다. 자동 삭제나 호스트 상태의 기계적 강제를 구현하지 않는다.
---
## P0 에스컬레이션
전제가 실측과 어긋나거나, 이 스킬의 범위 밖 결정이 필요하거나, 파괴적 작업이
필요하면 멈춘다. 사용자에게 선택지를 제시하고 답을 받는다 — 호스트가 제공하는
수단으로. 특정 툴 이름을 전제하지 않는다(헤드리스·비대화형 호스트에는 그 툴이 없을
수 있다).
## 운송
이 스킬의 규범 본문은 위임에 실제로 쓰는 수단을 모른다 — **"호스트가 제공하는
수단으로 위임한다"**로 자급한다. 운송별 실행 레시피(감지 명령 + 최소 레시피)는 레포
문서를 참조하라. 그 문서가 없거나 낡아도 이 스킬은 계속 동작한다 — 감지 가능한
수단이 없으면 단일 세션 순차 실행을 검토한다. 단, 사용자가 특정 운송/계보를 요구했거나
그 수단이 권한을 소유하면 묵시적으로 대체하지 말고 차단 사유를 보고한다.
## 관련
경로는 플러그인 루트 기준이다(소비자 프로젝트 cwd 가 아니다).
| 문서 | 위치 |
| --- | --- |
| 브리프 4블록·보고 형식 계약 | `rules/delegation-contract.md` |
| 워커 세션이 로드하는 규율 | `skills/child-session/SKILL.md` |
| 워크트리 격리·병합 규율(네이티브 서브에이전트) | `rules/parallel-worktree.md` |
| 루프 종료 가드 | `rules/loop-engineering.md` |