--- name: loop-update description: 이미 있는 loop_setup/LOOP.md 를 프로젝트 진행에 맞춰 갱신한다. 세팅 때 껐던 병렬을 켤 때 슬롯 간 DB·포트 격리 미결을 닫고 STATE.md 의 절 형식을 넓혀 에이전트끼리 기록을 덮어쓰지 않게 하고, 동시 에이전트 수·슬롯·격리 방식·구동기 모드가 바뀐 것을 반영하고, local-dev 옵션이나 task_validation 분리 수단이 바뀐 것을 규칙에 반영하고, 태스크가 추가되어 달라진 DAG.md 를 스크립트로 다시 만들고 자원 충돌 표·병렬 폭을 다시 재고, 루프가 같은 자리에서 반복해 멈출 때 규칙이 현실과 어긋난 곳을 고치며, 1.10.0 이전 loop-setup 이 만든 LOOP.md(상주 코디네이터·알림이 쌓이기만 하는 구조)를 구동 스크립트·반복 판정·알림 만료 구조로 옮기고, 옛 구동 스크립트에 정지 감시(분리 실행·잠들거나 죽은 워커·자식 실패·질문 재알림과 답하기 탭)를 넣는다. 사용자가 "루프 업데이트해줘", "LOOP.md 고쳐줘", "이제 에이전트 여러 개로 병렬로 돌리자", "워크트리 격리 방식 정하자", "태스크 추가됐으니 루프 다시 봐줘", "루프가 자꾸 같은 데서 멈춰", "코디네이터가 토큰을 너무 많이 써", "알림이 계속 쌓이기만 해", "루프가 살아 있는지 안 보여", "코디네이터가 어디 있는지 모르겠어", "질문 하나 때문에 다 놀고 있어", "질문 알림을 놓쳤어", "워커가 죽은 걸 몰랐어", "같은 테스트가 계속 흔들려", "루프가 멈춘 걸 한참 뒤에 알았어", "워커가 답을 받고도 잠들어 있어", "선행이 끝났는데 배정이 안 되고 슬롯이 놀아"라고 하거나, 개발 루프를 돌다 LOOP.md 의 규칙이 실제와 맞지 않는다고 판단했을 때 반드시 이 스킬을 사용한다. 다만 게이트가 실패한 것은 이 스킬을 부를 이유가 아니다 — 그건 루프를 도는 일이고, 코드·하네스·태스크 문서 중 어디로 갈지는 LOOP.md 의 실패 분류가 이미 정해 두었다. loop_setup/LOOP.md 가 아직 없으면 이 스킬이 아니라 loop-setup 을 쓴다. --- # 개발 루프 갱신 (Layer 5) ## 이 스킬의 목적 `LOOP.md` 는 원본 사양이 **개발 프로세스의 헌법**이라고 부른 문서다. 그런데 헌법도 한 번 쓰고 끝나지 않는다 — **미결이 닫히고, 운용 방식이 바뀌고, 규칙이 현실과 어긋난다.** 원본 사양이 "필요한 경우 적극적으로 LOOP.md는 업데이트되어야 한다"고 적은 것이 이 자리다. **그런데 이 스킬에는 다른 갱신 스킬에 없는 위험이 하나 있다.** ``` LOOP.md 에 적힌 규칙의 절반은 루프를 '멈추게 하는' 것이다 재시도 3회 · 분리된 검증 세션 · 미활성은 통과가 아니다 · 범위를 넘지 않는다 그리고 그 규칙에 걸리는 쪽과 그 규칙을 고칠 수 있는 쪽이 같다 → 막혔을 때 가장 빠른 해결은 언제나 자기를 멈춘 규칙을 지우는 것이다 ``` **그래서 이 스킬의 절반은 무엇을 고쳐도 되는지가 아니라 무엇을 고치면 안 되는지에 대한 것이다.** 하네스가 조용히 죽는 방식이 "기준을 낮추는 것"이라면, 루프가 조용히 죽는 방식은 **"멈추게 하는 규칙을 하나씩 지우는 것"**이다. 둘 다 기록에는 통과율이 올라간 것으로만 남는다. ### 여기서 하는 일과 하지 않는 일 | 하지 않는 것 | 어디서 하는가 | |---|---| | 루프를 처음 세우는 것 | `loop-setup` — `loop_setup/LOOP.md` 가 없으면 그쪽이다 | | 게이트가 무엇을 검사하고 어떤 명령으로 도는지 고치는 것 | `harness-update` | | 활성 시점·E2E 흐름 목록·`local-dev` 스크립트를 고치는 것 | `harness-update` | | 태스크·phase·수용 기준·`verification` 을 고치는 것 | Layer 3 (`plan-generator` 10단계 부분 갱신) | | 태스크 코드를 고치는 것 | 루프를 **도는** 세션 | | `STATE.md` 의 **값**을 고치는 것 | 루프를 도는 세션. 여기서는 **절의 형식**을 넓힌다 (5단계) | | 만료된 승인을 정기적으로 정리하는 것 | 루프가 매 사이클 스스로 한다. 여기는 **놓쳤을 때의 안전망**이다 | | 실패한 게이트에 대응하는 것 | 루프를 도는 일이다. `LOOP.md` 의 실패 분류가 이미 정해 두었다 | **형식은 새로 정하지 않는다.** 사이클 단계, 분류 표의 열, `Retry` 절의 필드, 미결 표의 모양은 `loop-setup` 의 산출물 템플릿에 이미 있다. 여기서 다시 정하면 두 스킬이 다른 형식을 만들고, 갱신을 한 번 돌릴 때마다 문서가 조금씩 달라진다. **기존 파일의 형식을 그대로 따라 쓴다.** 구동기가 ②(오르카)면 템플릿의 ② 부분 — 4절 ② 블록(구동 스크립트·밖의 감시·상태판·답하기)·8절 ② 값·② 스모크·② 점검 행 — 은 `loop-setup` 의 `references/orca.md` 에 있다. 아래에서 "템플릿 4절 ②" 라고 하면 그 파일이다. --- ## 0단계. 언제 이 스킬을 부르는가 ### 부르는 자리 | 언제 | 무엇을 고치나 | |---|---| | **세팅 때 병렬을 껐다가 켤 때** | 격리 미결을 닫고 슬롯·배정·필자 규칙을 켠다. **`loop-setup` 이 그 경우에만 미결을 남긴다**(기본은 병렬이라 보통 이미 켜져 있다) | | 병렬을 끌 때 | 병렬 전용 규칙을 되돌리고 구동기를 ①-단일로 | | 동시 에이전트 수 상한·슬롯 규칙·격리 방식이 바뀜 | 8절의 폭 표·슬롯 표·환경변수 표 | | `local-dev` 가 슬롯 변수를 읽게 됨 (`harness-update` 완료) | 9절의 "격리 적용 대기"를 지우고 폭 1 제한을 푼다 | | **미결(OQ)이 닫힐 때** | 그 미결에 걸려 있던 잠정 규칙을 확정 규칙으로 | | 만료된 승인이 남아 있을 때 | 지운다. **다만 이건 루프가 매 사이클 스스로 하는 일이고, 여기는 놓쳤을 때의 안전망이다** | | `local-dev` 의 명령·옵션이 바뀜 | `LOOP.md` 에 실제 값으로 적힌 곳 | | `task_validation` 의 분리 수단이 바뀜 | 세션 경계 절과 호출 지시문 | | 구동기 모드가 바뀜 (스크립트 ↔ 오르카 ↔ 서브에이전트) | 세션 경계 절의 구동기 블록 — 지시문·허용 목록·판정 블록 수집 경로 | | 역할별 모델이 바뀜 (모델 은퇴·요금제 한도·품질 문제·"이어서" 재배정으로 한 급 올림) | 8절 "에이전트·모델 배치" 표 한 곳. 지시문·스크립트에 이름이 박혀 있으면 표 참조로 바꾼다 | | 태스크가 추가·삭제됨 | `dag-render` 를 다시 돌려 `loop_setup/DAG.md` 를 갱신하고, **자원 충돌 표**·**구간별 병렬 폭** 요약을 다시 만든다 | | **②가 상주 코디네이터 구조다** — 대화형 코디네이터가 phase 내내 살아 있거나, phase 마다 코디네이터를 새 터미널로 띄우는 교체 명령·시작 프롬프트가 8절에 있다(2026-09-23 이전 `loop-setup` 산출물), 또는 phase 마다 사람이 코디네이터를 연다 | ②를 **구동 스크립트 + 라운드별 판단 자식** 구조로 옮긴다 — 아래 "1.10.0 이전 `LOOP.md` 를 옮길 때" | | **트리거가 알림으로만 쌓인다** — `Next Action` 에 `알림:` 이 여러 phase 에 걸쳐 쌓여 있고, 그중 조건이 이미 충족된 것(반복 흔들림·판단 시점이 지난 미결)이 있다 | 반복 판정·`알림(<주체>, ~<만료>)` 형식·phase 를 열 때의 확인(0절 7번)을 넣고, 쌓인 알림을 분류한다 — 같은 절 | | **격리 3인데 DB·앱을 쓰는 테스트가 여러 태스크에 걸쳐 흔들린다** | 격리 부족이다. 격리를 1·2로 올린다(8절 슬롯 표·환경변수 표, `e2e 대기` 토막 제거). `local-dev` 가 슬롯 변수를 읽게 하는 것은 `harness-update` 의 일이라 함께 부른다 | | **선행이 끝난 태스크가 배정되지 않고 슬롯이 논다** — 코디네이터 지시문의 배정 단계가 `DAG.md` 폭 표를 근거로 하거나 `dag-render` 에 `--ready` 가 없다(1.10.1 이전 산출물) | 배정 근거를 `--ready`(지금의 `completed` 집합으로 다시 센 진입 차수)로 바꾼다 — 4단계 표 | | `harness-update` 가 게이트 구성을 바꿈 | 루프가 읽는 자리(정의서 이름, 활성 시점 출처)가 달라졌는지 | | **같은 자리에서 반복해 멈춘다** | 규칙이 현실과 어긋난 곳. **아래를 먼저 읽는다** | ### 부르지 않는 자리 - **게이트가 실패했을 때** — 그건 루프를 도는 일이다. `LOOP.md` 의 분류가 코드·하네스·태스크 문서 중 어디로 갈지 이미 정해 두었고, 그중 어디에도 "`LOOP.md` 를 고친다"는 없다 - **태스크가 어려울 때** — 규칙이 아니라 그 태스크의 문제다 - **재시도 3회를 다 썼을 때** — 그때 할 일은 **사용자에게 보고**하는 것이다. 여기서 이 스킬을 부르면 한도를 늘리는 갱신으로 끝나고, 그건 정확히 3회 상한이 막으려던 것이다 **"규칙이 나를 멈춰 세웠다"는 갱신의 이유가 아니다.** 그건 규칙이 제 일을 하고 있다는 뜻이다. ### 같은 자리에서 반복해 멈추는 것은 다르다 한 번 걸린 것은 규칙이 작동한 것이지만, **여러 태스크에서 같은 규칙에 같은 방식으로 걸리면** 규칙이 현실과 어긋났을 가능성이 있다. 구별하는 방법은 **무엇을 바꾸면 안 걸리는가**를 보는 것이다. | 상황 | 무엇이 문제인가 | |---|---| | 태스크마다 `files` 밖의 설정 파일을 고쳐야 통과한다 | **플랜의 문제.** 그 파일이 어느 태스크의 `files` 에도 없다 → Layer 3 | | 매번 같은 흐름이 흔들려 멈춘다 | **하네스의 문제** → `harness-update`. **단 격리 3에서 DB·앱을 쓰는 테스트라면 루프의 문제**(격리 부족) → 여기서 격리를 올리고 `harness-update` 를 함께 | | 같은 태스크가 3회 소진을 반복하는데 분류는 매번 `진짜 결함` 이다 | 플랜·하네스가 아니면 **워커 급의 문제일 수 있다.** 그 태스크만 구현 워커를 한 급 올려 재배정한다(8절 표의 "올리는 조건"). 전체를 올리지 않는다 | | 미활성 승인을 태스크마다 다시 묻고 있다 | **루프의 문제.** `Standing Decisions` 의 범위가 너무 좁다 → 여기 | | 병렬 중 `Retry` 절이 서로 덮어써진다 | **루프의 문제.** 절 형식과 필자 규칙이 단일 에이전트 전제다 → 여기 (5단계) | **앞 둘은 이 스킬이 아니다.** 여기서 고치면 다른 레이어의 결함을 루프 규칙으로 덮는 것이 되고, 덮인 결함은 다음 phase 에서 더 크게 돌아온다. --- ## 1단계. 무엇이 바뀌었는지부터 읽는다 **고칠 곳을 정하기 전에 지금 상태를 읽는다.** 루프 규칙은 진행 상황을 전제로 쓰였다. | 읽을 것 | 무엇을 얻는가 | |---|---| | `loop_setup/LOOP.md` | 지금 규칙. **미결 절에 무엇이 열려 있는가** | | `plan_setup/STATE.md` | 지금 어느 phase·태스크인가. `Retry` 와 `Standing Decisions` 의 현재 내용 | | `plan_setup/LOG.md` | **반복되는 실패와 반복되는 중단이 여기 쌓여 있다.** 갱신의 근거는 대개 여기 있다 | | `harness_setup/HARNESS.md` | 게이트별 **현재 활성 상태** — 만료시킬 승인이 있는지 | | `harness_setup/quality_gates/*.md` | 게이트 구성이 바뀌었는지 (정의서 이름, 활성 시점 절의 유무) | | `harness_setup/scripts/` | `local-dev` 의 실제 명령·옵션이 `LOOP.md` 에 적힌 것과 같은지 | | 태스크 문서 frontmatter | 태스크가 늘었으면 **DAG 와 폭을 다시 잰다** | **`loop_setup/LOOP.md` 가 없으면 이 스킬을 쓰지 않는다.** 갱신할 대상이 없다. `loop-setup` 을 제안한다. --- ## 2단계. 갱신을 세 등급으로 나눈다 | 등급 | 무엇인가 | 어떻게 하는가 | |---|---|---| | **① 상태 반영** | 이미 일어난 사실을 규칙에 맞추는 것 — 만료된 승인 정리, 바뀐 옵션 반영, 다시 잰 폭 | **그냥 한다.** 보고에 한 줄 | | **② 규칙 추가** | 규칙을 늘리거나 조이는 것 — 격리 규칙 확정, 병렬 운용 규칙, 절 형식 확장 | 한다. **매 사이클에 얼마나 붙는지**를 보고한다 | | **③ 완화** | 루프를 멈추게 하는 것을 줄이거나 늦추는 것 | **사용자 승인 없이는 하지 않는다** | ①이 갱신의 대부분이다. **①을 미루면 규칙이 실제와 어긋나고, 어긋난 규칙은 다음 세션이 무시하기 시작한다** — 한 줄이 틀린 것을 알면 나머지도 의심하게 되기 때문이다. 망설일 이유가 없다. ### 무엇이 ③ 완화인가 | 완화 | 왜 완화인가 | |---|---| | 코드가 아닌 갈래의 재실행을 1회보다 늘린다 | 아무것도 검증하지 않는 실행이 늘어난다 | | 미활성 승인의 범위를 **무기한**으로 넓힌다 | 그 게이트가 활성돼도 아무도 다시 보지 않는다 | | 태스크 간 반복 감지(`LOG.md` 흐름 ID)를 끈다 | 같은 낭비가 태스크마다 반복되는 것을 못 잡는다 | | 반복 판정을 지시문에서 빼거나, 걸렸을 때 `질문:` 대신 `알림:` 으로 넘기게 한다 | 규칙은 남아도 아무도 실행하지 않는다 — 실전에서 이렇게 여섯 phase 를 흔들림 위에서 돌았다 | | `알림:` 의 만료나 phase 를 열 때의 확인(0절 7번)을 뺀다 | 충족된 트리거가 사람 눈에 안 띄는 목록으로 쌓인다 | | 기록 시점을 시작·전이·완료에서 줄인다 | **세션이 끊겼을 때 복구가 안 된다.** 가장 눈에 안 띈다 | | 불일치 복구 규칙을 뺀다 | 위와 같다 | | 태스크 선택에서 `depends_on`·`blocked` 확인을 뺀다 | 열리지 않은 태스크를 구현하게 된다 | | 범위 확장 금지(`files`·`architecture`)를 푼다 | 검증받지 않은 변경이 커밋에 섞인다 | | 커밋을 게이트 통과 전으로 옮긴다 | 검증받지 않은 상태가 기본선이 된다 | | phase 완료 묶음 실패를 통과로 읽게 한다 | 그 흐름들은 영원히 안 돈다 | | 병렬 폭 측정을 생략한다 | 병렬 결정의 근거가 사라진다 | **③은 거절이 아니라 확인이다.** 사용자가 직접 요청한 경우에도 마찬가지다 — **결과를 제시하고 확인받은 뒤 적용한다.** "기록을 완료 시점 하나로 줄인다"와 "세션이 중간에 끊기면 그 태스크를 처음부터 다시 한다"가 같은 말인데 앞쪽으로만 들리기 때문이다. 확인 뒤에는 그대로 하고, 무엇을 왜 줄였는지를 기록에 남긴다. 대화할 수 없는 상황이면 적용하지 말고 결과와 대안을 적어 사용자에게 넘긴다. **"지금 자꾸 걸려서"는 완화의 이유가 아니다.** 정당한 경우는 **그 규칙이 더 이상 유효하지 않을 때**뿐이다 — 운용 방식이 실제로 바뀌었거나(단일 → 병렬), 그 규칙이 전제하던 것이 사라진 경우. --- ## 3단계. 고칠 수 없는 것 **완화 승인을 받아도 이 스킬이 고치지 못하는 것이 있다.** 다른 곳에서 정해져 내려온 것들이다. | 고정된 것 | 누가 정했나 | 바꾸려면 | |---|---|---| | **재수정 사이클 최대 3회** | 원본 사양이 문장으로 못 박음 | 원본 사양을 고치는 결정. 사용자에게 그렇게 보고한다 | | **한 태스크에 한 세션** | 원본 사양 | 위와 같다 | | **`task_validation` 은 분리된 세션에서** | 원본 사양 + Layer 4 | 위와 같다 | | **멀티 에이전트 시 DAG 구현** | 원본 사양 | 위와 같다 | | 게이트 넷과 그 이름·순서 | Layer 4 | `harness-update` 도 못 바꾼다. 원본 사양이다 | | 상태 어휘 넷 (`활성`·`부분 활성`·`미활성`·`대기`) | Layer 4 | 새 이름을 만들지 않는다 | | 기록처는 `STATE.md`·`LOG.md` 둘뿐 | Layer 4 | 셋째를 만들지 않는다 | **"3회를 늘려 달라"는 요청은 여기로 온다.** 상한을 지우는 대신 답할 것이 있다 — 세 번이 부족했던 이유는 대개 그중 몇 번이 **환경 문제나 흔들림에 소모된 것**이고, 그 답은 상한 제거가 아니라 분류를 제대로 하는 것이다. `LOG.md` 를 보면 실제로 그런지 알 수 있고, **그렇지 않다면 그건 원본 사양을 다시 볼 문제**이지 루프 문서에서 조용히 바꿀 일이 아니다. --- ## 4단계. 무엇이 바뀌면 무엇을 고치는가 | 일어난 일 | 고치는 곳 | 등급 | |---|---|---| | 만료된 승인이 남아 있음 (루프가 놓침) | 지운다. 승인은 "활성될 때까지"였다. **`LOOP.md` 0절(세션을 열 때)이 원래 자리다** | ① | | `local-dev` 의 옵션·종료 코드가 바뀜 | `LOOP.md` 에 실제 값으로 적힌 곳 전부 | ① | | 분리 수단이 바뀜 (예: 헤드리스 자식 세션 → 서브에이전트) | 세션 경계 절과 검증 세션 호출 지시문 | ① | | 구동기 모드가 바뀜 (예: 스크립트 → 오르카) | 구동기 블록의 명령·허용 목록·판정 수집 경로와 `harness_setup/scripts/loop-drive.*`. 오르카로 가면 워커의 질문이 `ask` → 질문 답변 자식 → `reply` 로 바뀐다(문서로 답할 수 없으면 여전히 `질문:`). 바꾼 뒤 드라이런·스모크 테스트를 다시 돌린다 | ① | | **② 워커를 `worker-start --spec` 한 번으로 띄운다** — 오르카 워커 기록(`worktree ps` 의 에이전트 `displayName`)에 이름이 없다 (1.10.0 산출물) | 4절 ② 블록과 `loop-drive` 의 워커 기동을 `task-create --display-name "worker_<태스크 ID>"`(검증은 끝에 `_검증`) → `worker-start --task ` 두 단계로 바꾼다. 다음 라운드부터 기록에 이름이 남는다. **사이드바 카드의 워커 줄이 `worker-task_<내부 ID>` 인 것은 이 행의 증상이 아니다** — 오르카 1.4.222 는 이름을 붙여도 그렇게 보인다(2026-10-12). 누가 무엇을 하는지는 상태판(위 행)으로 본다 | ① | | **배정이 `DAG.md` 폭 표를 근거로 한다** — 코디네이터 지시문의 배정 단계가 `DAG.md(그래프·폭 표)` 로 고르거나 `dag-render` 에 `--ready` 가 없다 (1.10.1 이전 산출물) | `dag-render.*` 에 `--ready`(`DAG.md` 를 쓰지 않고 `pending`·선행 전부 `completed`·`blocked` 아님인 후보를 phase·`files` 와 출력)를 더하고 돌려 본다. 모르는 옵션은 아무것도 쓰지 않고 오류로 끝나게 한다. 코디네이터 지시문의 배정 단계·8절 "배정" 문단·폭 표 머리·코디네이터 허용 목록(`dag-render --ready`)을 `loop-setup` 템플릿대로 — 배정은 `--ready` 후보에 충돌 표·점선을 대어서, 폭 표는 세팅 때의 추정. 이 프로젝트가 고쳐 둔 배정 조건(예: 미결에 걸린 슬롯 제한)은 살린다 | ① (멈추는 규칙은 그대로다. 배정 근거를 실제와 맞춘다) | | **1.10.1 구동 스크립트의 빈틈** (1.10.1 이전 산출물 — `loop-setup` 템플릿 4절과 대조한다) | `loop-drive.*` 와 4절 ② 블록을 템플릿대로 고친다: ①행 띄우기가 dispatch 파일뿐 아니라 결과 파일이 남은 행도 건너뛴다(①-병렬도) ②격리 3 이면 검증 워커를 띄우기 직전 앱 전환·한 번에 하나, 도는 동안 코디네이터 자식·e2e·앱 전환을 미루되 `worker_done` 처리·ack 는 그대로, 코디네이터 지시문에 같은 금지. ⑥질문 답변 자식 출력은 코드 울타리·빈 줄을 건너뛰고 앞 공백·강조를 벗긴 첫 줄로 `답:`/`질문:` 을 가른다. ⑤대기 — 도는 dispatch 0 이면 기다리지 않고 첫 줄로(그 한 바퀴에 새 dispatch·행 변화가 없으면 멈춤), `check --wait` 한 번 60초(8절 대기 길이), 생존 확인은 템플릿의 [감시 11](1.15 — 도는 워커가 있는 동안 5분마다), exited·`worker_done` 없음이면 결과 파일이 없을 때만 END 없는 줄을 적고 dispatch 파일을 지운다. ④② 워커 덧붙임에 `ask` 포그라운드(Bash timeout 600000, `--timeout-ms 540000`)와 시간 초과 때 `ask --resume <같은 ID>`. ③스크립트가 `STATE.md` 에 쓰는 것은 자식 사이의 `Assignments` 토막 값뿐(e2e 출력은 `e2e-.txt`, `Retry` 에 쓰지 않는다) — 옛 문서의 "스크립트는 STATE.md 를 쓰지 않는다" 와 "e2e 출력을 Retry History 에" 를 이 예외로 맞춘다. 고친 뒤 드라이런을 다시 돌린다 | ① (멈추는 규칙은 그대로다. 스크립트를 템플릿에 맞춘다) | | **1.11 구동 스크립트 수정** (1.11 이전 산출물 — `loop-setup` 템플릿 4절 ②·8절·12단계와 대조한다) | `loop-drive.*` 를 고친다: 오르카 출력 전체(들여쓴 여러 줄 JSON)를 읽고 결과 모양을 템플릿(`references/orca.md` 4절)대로(메시지 `payload` 는 JSON 문자열), reply 결과 — 성공·`answer_conflict` = 답함, 그 밖의 오류 코드 = 답할 수 없음(넘기고 기록), 응답 JSON 없는 호출 실패만 그 배달 ack 보류하고 1·5·15분 간격으로 reply 만 다시(간격 없이 맡기면 재생된 배달이 답변 자식을 쉬지 않고 부른다) — , 멈출 때 처리한 배달을 `check --ack --peek` 로, 자식(`claude -p`·`orca`)의 표준 입력 끊기, 파일 지우기는 `unlinkSync`(Node 24·Windows 의 `fs.rmSync` 는 비ASCII 경로에서 조용히 지우지 않는다). 8절에 "구동 터미널은 구동 전용"(오르카 알림 문구가 그 터미널 입력으로 들어간다). 슬롯 1 워커도 `--worktree path:<코디네이터 트리>`(`current` 는 구동 터미널의 워크트리로 풀려 `main` 에 뜬다), 결과 파일을 다루는 자식(①-단일 반영·코디네이터·질문 답변)에 `--add-dir <임시>`(없으면 결과 파일을 못 지워 진전 없음으로 선다), ② 워커 덧붙임에 "ask 가 연결 끊김(`runtime closed the connection`·`runtime_unavailable`)으로 끝나도 30초 뒤 같은 ID 로 `ask --resume`, 세 번 연속이면 `failed`"(없으면 오르카 재시작 뒤 워커가 잠든다). **정지 감시**: 4절 ② 블록·"밖의 감시" 블록과 8절(분리 실행 방식·알림 수단·감시 기준값·임시 폴더의 구동 파일)을 템플릿대로 넣고, `loop-drive.*` 에 띄우기(터미널 프로세스 밖으로 분리 — `Start-Process` 자식은 터미널을 닫을 때 함께 죽는다, 자기 환경에 `ORCA_*` 없음)·시작 확인(PID 파일·`--force`)·단계·생존 파일·종료 사유(앞머리 순서)·쌓인 배달 처리와 `exited` 정리 필요·자식 실패(한도 쉼·1/5/15분 3회)·시작 실패·깨우기·답 없는 질문·이미 답한 질문 건너뛰기를 더하고 `loop-watch.*` 를 새로 쓴다. 옮길 때는 옛 스크립트가 멈춘 뒤에 한다(옛 스크립트에는 시작 확인이 없어 둘이 돈다). 옮긴 뒤 첫 시작은 단계·생존 파일이 없어 그대로 된다. 고친 뒤 스모크 테스트(분리 실행·터미널 닫기·시험 알림 포함)를 다시 돈다 | ① (멈추는 규칙은 그대로다 — 감시는 알림과 정해진 일곱 조치만 한다) | | **질문 하나가 모든 슬롯을 세운다** — ①-병렬·② 반복의 첫 줄이 "질문: 이 있고 답: 이 없으면 새로 띄우지 않는다/멈춘다" 하나뿐이다 (1.14 이전 병렬 산출물. 사용자가 "질문 하나 때문에 다 놀고 있다" 고 할 때도) | `loop-setup` 템플릿 11단계 "질문의 범위" 와 4절 ②·①-병렬 반복의 첫 줄, "태스크 질문만 남고 행이 전부 정지면 질문 대기" 멈춤, 코디네이터 지시문의 질문 머리 규칙, 0절 5번의 답 머리, 상태판의 태스크 질문 줄을 그대로 옮기고 `loop-drive.*` 를 고친다(다음 시작부터). 이미 Next Action 에 있는 질문은 머리를 보고 가른다 — 태스크 ID 로 시작하지만 병합 충돌·반복처럼 전역인 것은 머리를 고쳐 적는다 | ① (멈추는 범위를 좁힐 뿐 멈추는 조건·필자는 그대로다) | | **죽은 워커를 못 잡고, 질문이 화면에 안 뜨고, 정지 행이 슬롯을 쥔다** — ② `loop-drive.*` 의 생존 확인이 "빈 대기 15분" 에만 돌고 `unverifiable` 이면 알림뿐이며, 답 없는 `질문:` 이 토스트 한 번으로 끝나고, 정지 행이 슬롯을 놓지 않는다 (1.15 이전 병렬 산출물. 사용자가 "워커가 죽었는데 한참 몰랐어", "질문 알림을 놓쳤어", "질문 하나 때문에 슬롯이 놀아" 라고 할 때도) | `loop-setup` 템플릿대로 넷을 옮긴다(실전 2026-10-13). **먼저 예방** — 워커가 오류 기록 없이 사라졌으면 하네스 스크립트의 프로세스 정리(이름·PID 만으로 끄기)를 `harness-update` 의 "워커가 오류 기록 없이 사라진다" 행으로 고친다(그날의 원인이었다 — 다른 슬롯의 게이트가 PID 를 다시 받은 워커를 껐다). 아래는 그래도 죽었을 때의 안전망이다. **워커 생존** — 4절 ② 블록의 [감시 11](도는 워커가 있는 동안 5분마다·라운드를 넘어 이어 셈, `unverifiable`·`missing` 에 전사·터미널 20분 정체면 `worker-stop` 뒤 END 없는 결과, 워커 상한 구현 90·검증 45분 알림)과 8절 감시 기준값·알림 종류(`워커정리`·`워커상한`·`질문재알림`), `references/orca.md` 1절 "병렬 운용에서 ② 가 다른 것" 의 "대기는 Run 단위다". **답하기** — `loop-answer.*` 를 새로 쓰고(4절 "답하기"), `loop-watch.*` 에 재알림(30분·2시간·8시간)과 답하기 탭(떠 있으면 `terminal switch` 로 앞으로, 아니면 `orca terminal create … --focus` 를 `ORCA_*` 없이 — 핸들 파일·`loop-answer.pid`), 8절 답하기 명령·알림 수단·임시 파일, 상태판의 답하기 줄, `CLAUDE.md`·`AGENTS.md` 루프 절의 "답은 코디네이터 트리의 STATE.md" 줄. **슬롯 반납** — 8절 "정지 행과 park"·워커 규칙, 코디네이터 지시문 0)·1)·2)·2.6)·3), `Assignments` 의 워커 칸 `park/`, 상태판의 park 표시. 지금 슬롯을 쥔 정지 행은 다음 라운드의 2.6) 이 옮긴다. **늦은 worker_done** — 코디네이터 지시문 1)·2) 의 "dispatch 파일이 남은 행은 건너뛴다", `loop-drive.*` worker_done 의 "dispatch 파일이 없으면(재생) 결과 파일을 만들지 않는다". 구동 스크립트를 멈춘 뒤 옮기고(라운드 사이), 스모크(답하기 탭 띄우기 포함)를 다시 돈다. 프로젝트가 이미 만든 같은 장치(재알림·생존 확인 등)는 버리지 않고 빠진 항목만 더한다 | ① (멈추는 조건·필자는 그대로다. 알림·죽은 워커 정리·정지 행의 변경을 두는 자리를 바꾼다) | | **살아 있는 워커가 모델 오류로 입력창에 서 있거나, 지시문이 초안으로 남아 시작 실패로 선다** — ② `loop-drive.*` 에 [감시 11-가] 모델 오류 화면이 없어 `at capacity` 로 선 워커가 정체 규칙(`unverifiable`·20분)에도 걸리지 않거나, `worker-start` 의 `outcome_unknown` 에 입력 초안 Enter 가 없거나 `draft:` 모양만 알아봐 codex 0.162 의 `› [Pasted Content N chars]` 를 놓친다 (1.15.1 이전 ② 산출물. 사용자가 "워커가 오류 화면에 30분째 서 있어", "시작 실패로 섰는데 입력창에 지시문이 있어" 라고 할 때도) | `loop-setup` 템플릿대로 둘을 옮긴다(실전 2026-10-13). **모델 오류 화면** — 4절 ② [감시 11] 의 [감시 11-가](도는 dispatch 마다 5분 확인 때 `terminal read --screen --limit 12`, 8절 모델 오류 문구가 있고 `esc to interrupt` 가 없으면 같은 터미널에 계속 입력·알림 `워커모델오류`, 5분 뒤에도 그대로면 `worker-stop` → 멈춤 확인 → END 없는 결과 → 알림 `워커정리`), 8절 감시 기준값·모델 오류 문구 줄·알림 종류 `워커모델오류`. **시작** — 4절 ② [감시 5] 의 `outcome_unknown` 갈래(시작 판정에 전사의 에이전트 메시지, 화면에 초안이면 `terminal send --enter` 1회 뒤 30초, 그래도 안 뜨면 `worker-stop` → 멈춤 확인 → `--retry-of`, 영수증의 다음 명령 목록은 따르지 않음)와 `nudged.txt`(구동기가 입력한 터미널만 `user_takeover` release 뒤 닫는다). 프로젝트가 이미 넣은 초안 Enter·`user_takeover` 닫기는 버리지 않고 빠진 모양·갈래만 더한다. 구동 스크립트만 고치는 일이라 6단계 "구동 스크립트를 멈추는 동안" 의 첫 갈래대로 — 커밋해 코디네이터 트리에 들이고 곧바로 다시 띄운다. 다음 스모크에서 시험 워커의 시작 화면을 본다 | ① (멈추는 조건·필자는 그대로다. 살아 있지만 일하지 않는 워커를 정리하는 경우를 더한다) | | **phase 완료 묶음을 판단 자식이 돌리게 되어 있다** — 코디네이터 지시문(①-단일이면 반영 지시문)의 phase 판정이 묶음을 직접 돌리고, 코디네이터 허용 목록에 "phase 완료 묶음 명령", 자식 상한에 "(phase 완료 묶음 포함)" 이 있다 (1.15.1 이전 ①·② 산출물. 사용자가 "phase 가 끝났는데 다음으로 안 넘어가", "묶음 검사를 왜 내가 돌려야 해" 라고 할 때도. `LOG.md` 에 "완료 묶음을 돌리지 못했다" 가 라운드마다 남는다) | `loop-setup` 템플릿대로 옮긴다(실전 2026-10-14 — 허용 목록이 슬롯 환경변수를 정하는 명령을 막고 도구 한 번은 10분까지라 묶음이 한 번도 돌지 않았고, 질문이 사람에게 "직접 돌려 달라" 로 넘어갔다). 9단계 "완료 묶음을 돌리는 것은 구동기다"·5절의 묶음 단락과 `묶음 대기` 기록 행, 코디네이터 지시문 2)·2.4)·3)(①-단일이면 반영 지시문의 묶음 두 줄), 코디네이터 허용 목록에서 묶음 명령 빼기, 4절의 "phase 완료 묶음" 블록과 ①·②·①-병렬 반복의 `묶음 대기:` 줄, [감시 7 가] 의 `묶음 대기:`, "진전 없음" 의 예외, 8절 단계 이름 `phase 묶음`·상한(코디네이터 자식 60분에서 묶음을 빼고 phase 묶음 120분)·임시 파일 `bundle-phase-NN.*`, 상태판의 묶음 줄. `loop-drive.*` 에 묶음 모드(`--bundle phase-NN --commit ` — 창 없이, 띄우기·시작 확인·감시·`loop-exit.txt` 를 건너뛰고 `bundle-phase-NN.*` 만 쓴다. 명령은 돌 때마다 `e2e.md` 의 `## phase 완료 묶음 명령` 절을 읽는다)를 더하고 12단계대로 한 번 돌려 본다. 그 절이 없으면(옛 하네스) `harness-update` 로 먼저 만든다 — 지금 쓰던 묶음 명령 그대로, 활성 흐름만. 프로젝트가 이미 만든 비슷한 장치(구동 스크립트가 도는 중간 묶음 등)는 버리지 않는다 — 같은 방식(창 없는 프로세스·결과 파일)이면 그것을 늘려 쓴다. 지금 `Next Action` 에 "묶음 결과를 알려 달라" 같은 질문이 서 있으면 그 줄을 `묶음 대기: phase-NN` 으로 바꿔 적는다(사람이 이미 돌려 결과를 적었으면 그 답을 0절 5번대로 먼저 반영한다). 구동 스크립트를 멈춘 뒤 옮기고(라운드 사이 — 6단계) 다시 띄운다 | ① (멈추는 조건·필자는 그대로다. 판단 없는 실행을 판단 자식에서 구동 스크립트로 옮긴다) | | **워커 하나의 시작 실패가 루프 전체를 세우고, 멈춘 루프를 다시 알리지 않는다** — ② `loop-drive.*` 가 `draft` 를 에이전트 입력창 초안으로 보고 Enter 만 보내거나, `worker-stop` 멈춤 미확인을 "시작 실패" 정지로 처리하며, `loop-watch.*` 가 "질문 대기" 밖의 정지에 한 번 알리고 끝난다 (1.15.2 이전 ② 산출물. 사용자가 "워커 하나 때문에 다 섰다", "멈춘 걸 한참 몰랐다" 고 할 때도) | `loop-setup` 템플릿대로 옮긴다(실전 2026-10-14): 4절 ② [감시 5](초안 두 갈래·끝내 안 뜨면 그 행만)와 [워커 정리](abandon → 터미널 닫기 → 닫힘 확인 — [감시 11-가]·[감시 11] 도 이것을 부른다), [감시 9] 의 exited(그 행만 정리), "밖의 감시" 의 [정지 재알림], 8절 기준값·알림 종류(`시작실패`·`정지재알림`)·멈추는 방법(`loop-exit.txt` 에 "정지 — 사람 요청" — 1.15.5 부터는 `--stop`, 아래 행), 상태판의 "중단 — …" 줄. 프로젝트의 정지 명령(`-Stop` 등)이 같은 사유를 쓰면 그대로 둔다. 구동 스크립트·감시만 고치는 일이라 6단계 "구동 스크립트를 멈추는 동안" 의 첫 갈래대로 — 커밋해 들이고 곧바로 다시 띄운다 | ① (멈추는 범위를 그 행으로 좁히고 알림을 더할 뿐 필자는 그대로다) | | **구동기를 멈춘 뒤 기록 없는 워커가 남는다** — ② `loop-drive.*` 가 dispatch 파일을 시작 확인 뒤에 쓰거나, 시작 때 dispatch 파일에 없는 활성 워커를 보지 않는다 (1.15.3 이전 ② 산출물. 사용자가 "멈췄다 다시 띄웠더니 워커가 하나 더 떴다", "구동기가 모르는 워커가 있다" 고 할 때도) | `loop-setup` 템플릿(`references/orca.md` 2절)대로: [감시 5] 의 "dispatchId 를 받는 즉시 dispatch 파일에, 재시도면 바꿔 쓴다"(실전 2026-10-14 — 워커를 띄우는 도중 구동기가 꺼져 시작 확인 전의 워커가 기록 없이 남았다)와 [감시 9] 의 "기록 없는 워커"(`worker-list --run --terminal-state active` 중 dispatch 파일에 없는 것 → [워커 정리], 정리 실패면 태스크 이름으로 행을 찾아 dispatch 파일에 써 두고 그 행만). 구동 스크립트만 고치는 일이라 6단계 첫 갈래대로 곧바로 다시 띄운다 | ① (필자·멈추는 조건은 그대로다) | | **구동기를 멈추는 방법이 PID 끄기뿐이다** — ② `loop-drive.*` 에 정지 요청(`loop-drive.stop`)이 없거나, 판단 자식(코디네이터·답변)이 도는 중에도 끄거나, 다시 시작할 때 앞 실행의 판단 자식을 보지 않는다 (1.15.4 이전 ② 산출물. 사용자가 "멈추라 했는데 90초 뒤 강제로 꺼졌다" 고 할 때도) | `loop-setup` 템플릿(`references/orca.md`)대로: 2절 [정지 요청](반복 맨 앞·`check --wait` 직전과 돌아온 직후·판단 자식 직전·쉬는 단계 15초마다, 워커 띄우는 도중과 판단 자식 중엔 보지 않는다), 판단 자식을 띄우면 단계 파일에 child(PID·시작 시각), 시작 확인 뒤 "앞 실행의 판단 자식" 기다림, 3절 정지 요청(`--stop` — 대기 길이 + 90초 기다리고, 판단 자식 단계면 더 기다리며, `--force` 만 판단 자식까지 끈다)·임시 파일·단계 파일 모양, 상태판의 "정지 요청됨" 줄, 본문 8절 멈추는 방법. 프로젝트가 이미 만든 정지 명령(`-Stop` 등)은 버리지 않고 빠진 자리·기다리는 시간만 맞춘다. 6단계 첫 갈래대로 — 새 정지 명령은 옮긴 뒤부터 쓰고, 옮기기 위해 멈출 때는 단계가 대기이고 판단 자식이 없을 때 멈춘다 | ① (필자·멈추는 조건은 그대로다) | | **② 상태판이 없다** — `harness_setup/scripts/loop-status.*` 가 없거나 구동 시작 명령이 분리 뒤 로그만 따라 읽는다 (1.13 이전 ② 산출물. 사용자가 "루프가 살아 있는지 모르겠다·코디네이터는 어디냐" 고 물을 때도) | `loop-setup` 템플릿 4절 "상태판" 블록을 그대로 옮겨 `loop-status.*` 를 새로 쓰고(읽기만 — 파일 쓰기 0, 오르카는 `worker-list --run` 만 `ORCA_*` 없이), 이 프로젝트의 임시 폴더 파일 이름·`STATE.md` 형식·`dispatch-.txt` 내용에 맞춘다(옛 스크립트가 템플릿과 다른 이름을 쓰면 그 이름을 따른다 — 상태판이 구동 스크립트에 맞춘다). `loop-drive.*` 의 띄우기 끝을 "로그를 따라 읽는다" 에서 "상태판을 그린다" 로, 8절에 상태판 명령과 "오르카 사이드바로는 루프를 볼 수 없다" 줄, `CLAUDE.md`·`AGENTS.md` 루프 절에 `--once` 줄을 더한다. `loop-drive.*` 에 4절 ② 의 [완료 알림](코디네이터 자식 앞뒤 completed 비교 → 알림 `완료`)과 8절 알림 수단의 "완료 소식이 오래 없으면 상태판" 줄도 더한다(다음 시작부터). 프로젝트가 이미 만든 상태판이 있으면 버리지 않고 템플릿 항목 중 빠진 것만 더한다. 루프가 도는 중에도 더할 수 있다 — 읽기만 하므로 구동 스크립트를 멈출 필요가 없고(띄우기 끝 수정은 다음 시작부터), 도는 루프에 `--once` 를 돌려 실제와 맞는지 본다 | ① (보여 주기만 한다 — 멈추는 규칙·필자는 그대로다) | | **② 상주 코디네이터 → 구동 스크립트** (1.10.0 이전 산출물) | 4절 ② 블록·코디네이터 지시문(교체 단계 삭제)·질문 답변 지시문·② 워커 덧붙임, 8절(교체 명령·시작 프롬프트 → 구동 스크립트·시작 명령·대기 길이·스모크 테스트), `STATE.md` 에 `Assignments` 가 없으면 더함, `loop-drive` 작성·스모크 | ① (멈춤 규칙은 그대로다. 정지 조건 `질문:`·전부 `completed` 도 그대로) | | **반복 판정·알림 만료·미결 시점이 없다** (1.10.0 이전 산출물) | 3절 실패 줄 형식과 반복 판정, 코디네이터 지시문 2.5)(단일이면 반영 지시문), 0절 7번, `알림(<주체>, ~<만료>)` 형식, 9절 `언제` 를 확인 가능한 시점으로 | ② (멈추는 자리가 늘어난다 — 보고에 비용을 적는다) | | **`답: 반영함` 을 모른다** — 0절 5번 "반영" 목록에 `답: 반영함` 분기가 없다 (1.12 이전 산출물) | 0절 5번에 `loop-setup` 템플릿의 `답: 반영함` 분기를 그대로 더한다. 1.12 의 `harness-update`·`loop-update`·`plan-generator` 10단계가 루프 안에서 이 답을 요구하는 질문을 남기므로, 없으면 답이 "어느 종류에도 안 맞음"으로 빠져 루프가 다시 멈춘다 | ① | | 역할별 모델·추론 강도가 바뀜 | 8절 "에이전트·모델 배치" 표와 `CLAUDE.md`·`AGENTS.md` 의 `구동:` 줄. 한 급 **내리는** 것은 사용자 결정이고 `LOG.md` 에 이유를 남긴다 — 코디네이터를 보통 급으로 내리면 병합·분류 판단이 흔들린다 | ①(올림) / ②(내림) | | 태스크가 추가·삭제됨 | `dag-render` 재실행으로 `DAG.md` 갱신, 8절의 자원 충돌 표(새 겹침 쌍의 처리)·**구간별 폭·권장 에이전트 수** | ① | | 기본 브랜치 이름이 바뀜 (드묾 — `master` → `main` 이전 등) | 8절의 기본 브랜치 값. 코디네이터 트리는 새 이름에서 다시 분기하거나 `git branch -u` 로 상류만 바꾼다 | ① | | 슬롯 경로·격리 방식이 바뀜 (예: 격리 3 → 1, 동기화 폴더 문제) | 8절 슬롯 표·환경변수 표. 격리 3 을 벗어나면 `e2e 대기` 토막을 지운다 | ① | | 차단(OQ·CF)이 풀려 태스크가 열림 (태스크 문서는 루프의 차단 해소 절차가 이미 고쳤다) | 막다른 곳 서술과 폭 표. `대기` 였던 것이 실행 대상이 된다 | ① | | **세팅 때 껐던 병렬을 켠다** | 격리 미결을 닫고 확정 규칙으로. 슬롯 규칙을 켠다. **아래 5단계의 두 규칙을 함께 넣는다.** 그리고 둘 더 — 구동기를 ②(오르카 있음) 또는 ①-병렬(없음)로 바꾸고 코디네이터 지시문·`Assignments` 절을 넣는다, 배정 규칙을 켠다(코디네이터가 진입 차수 0·충돌 표를 판정해 구현 지시문에 `태스크: ` 를 넣고 워커는 선택 규칙을 건너뛴다) | ② | | 실측으로 새 위험이 드러남 (성능 측정 태스크가 병렬 부하에 오염 등) | 그 태스크를 단독 실행 대상으로 지정하는 규칙 | ② | | 미결이 닫힘 | 미결 표에서 지우고 **결정을 본문 규칙으로 옮긴다.** 표에만 남기면 아무도 안 지킨다 | ② | | 미활성 승인을 태스크마다 다시 묻고 있음 | `Standing Decisions` 의 범위를 게이트 단위로 넓힌다. **기한은 유지한다** | ②~③ | | 운용이 병렬 → 단일로 돌아감 | 병렬 전용 규칙을 되돌린다. **격리 미결은 다시 열어 둔다** | ① | | 규칙이 전제하던 것이 사라짐 | 그 규칙을 뺀다. **무엇이 사라졌는지 앞 문서에서 확인한 뒤** | ③ | **미결을 닫을 때가 가장 실수하기 쉽다.** 미결 표의 행을 지우고 끝내면 결정이 어디에도 안 남는다. **결정은 본문의 규칙이 되어야 하고, 미결 표에서는 사라져야 한다.** 둘 다 해야 한 번 닫힌 것이다. --- ## 5단계. 병렬로 켤 때 `STATE.md` 를 꼬이지 않게 하는 두 규칙 **멀티 에이전트를 켜는 갱신에서 이 둘을 빼면 기록이 반드시 깨진다.** 서로 다른 문제이고 해법도 다르다. ### (가) 칸이 모자란다 — 형식을 넓힌다 `Current Task` 는 값 하나, `Last Verification` 은 "지금 상태 한 줄", `Retry` 는 태스크 하나를 담는다. 넷이 동시에 돌면 **넣을 자리가 없다.** 태스크별 블록이나 목록으로 넓힌다. **넓힐 때 기존 값을 새 형식으로 옮긴다.** 지우고 새로 만들면 **진행 중인 태스크의 재시도 이력이 사라지고**, 그 태스크는 다음 세션에서 앞의 시도를 처음부터 반복한다. 넓히는 것이 목적이지 초기화가 아니다. **`Current Task`·`Last Verification` 은 Layer 3이 만든 절이다.** 병렬에는 정말 필요하므로 넓히되, **Layer 3이 초기화한 형식을 바꾸는 일이므로 ②로 분류해 보고에 올린다.** 조용히 바꾸면 `plan-generator` 의 형식 규칙과 실제 파일이 갈라지고, 다음에 누가 맞는지 아무도 모른다. ### (나) 동시에 쓰다가 서로 덮어쓴다 — 필자를 하나로 줄인다 칸을 나눠 줘도 **파일 쓰기는 통째로** 일어나므로 "각자 자기 줄만 고친다"로는 여전히 잃는다. **잠금으로 풀지 않는다** — 잠근 채로 죽은 에이전트가 전체를 세우고, 그건 병렬의 이득을 통째로 지운다. | 무엇 | 병렬에서 누가 쓰나 | |---|---| | 태스크 문서의 `status` | **코디네이터만 자기 트리에서.** 슬롯 브랜치가 문서를 고치면 `failed`·"이어서" 경로에서 병합 충돌이 난다 — 슬롯은 코드만 바꾼다 | | 하네스 문서 (`harness-update`) | **코디네이터만 자기 트리에서.** 워커는 `하네스 부족` 으로 돌려보낸다 | | `STATE.md` | **orchestration 만** (예외: ②의 질문 답변 자식은 `Next Action` 의 `질문:` 한 줄, 답하기 탭은 사람의 `답:` 한 줄, 구동 스크립트는 자식 사이의 `Assignments` 토막 값 — `loop-setup` 7단계) | | `LOG.md` | **orchestration 만.** 에이전트는 판정 블록을 넘기고 병합 순서대로 append 된다 | **이 분업이 맞는 이유는 루프가 이미 갖고 있다** — "어긋나면 태스크 문서가 이긴다". 태스크 문서가 원본이고 `STATE.md` 는 요약이므로, **원본만 각자 쓰고 요약은 한 명이 만들면 충돌이 구조적으로 사라진다.** 에이전트가 중간에 죽어도 `status` 가 남아 있어 orchestration 이 다시 만든다 — 세션이 끊겨도 이어지게 만든 장치가 여기서 그대로 쓰인다. `STATE.md` 가 실시간이 아니게 되는 것은 감수한다. **실시간인데 틀린 요약보다 조금 늦고 맞는 요약이 낫고**, 지금 무엇이 도는지가 급하면 태스크 문서를 보면 된다. ### 병렬을 끌 때 형식은 되돌리지 않아도 되지만 **필자 규칙은 되돌린다** — 단일 에이전트인데 orchestration 을 기다리면 아무도 `STATE.md` 를 쓰지 않게 된다. 그리고 **격리 미결을 다시 열어 둔다.** ### 다른 레이어 반영 — 넘기고 끝내지 않는다 루프 규칙이 아니라 플랜·하네스·앞 문서가 원인이면, "그쪽으로 넘긴다"고 보고만 하고 끝내면 사람이 그 스킬을 다시 불러야 흐름이 이어진다. **누가 불렀는가**로 갈린다. - **사람이 대화형 세션에서 불렀다**(구동기가 멈춘 뒤, 또는 루프 시작 전) — 원인 쪽 스킬을 이어서 부른다(대화할 수 없는 세션이면 부르지 않고 "어느 스킬의 몫인가"만 보고한다): `files`·`depends_on`·수용 기준은 `plan-generator` 10단계, 게이트·흐름·local-dev 는 `harness-update`. 그것이 요구사항·기술·구조를 바꿔야 하면 사용자에게 묻고(답을 받기 전에는 고치지 않는다) **가장 앞 문서부터** (`prd-generator` 8단계·`trd-generator` 9단계·`architecture-generator` 9단계 — 버전과 `변경 이력` 한 줄, 사이 문서의 바뀐 ID). 그다음 이 스킬로 돌아와 LOOP.md·DAG 를 맞춘다. 단 루프가 도는 중(`Phase Overview` 에 `in_progress`)이면 앞 문서는 고쳐도 `plan-generator` 10단계는 부르지 않고 무엇이 낡았는지 보고한다 — PRD 8단계·TRD 9단계와 같은 판정이다(플랜의 필자는 루프다). Layer 6(`feature-add`·`refactor-plan`)이 이 스킬을 부른 중이면 앞 문서는 본문이 아니라 **그 요청의 후미 절**(`## 추가 N`·`## 리팩토링 N`)에 적는다 — 원본 규칙이다. `refactor-plan` 이 부른 중에 PRD 를 바꿔야 하면 그것은 요구 변경이라 반영하지 않고 "`feature-add` 로 다룰 일"로 보고한다(리팩토링은 PRD 를 쓰지 않는다). 프로젝트가 완료됐으면(`Phase Overview` 전부 `completed`) 앞 문서는 고치지 않고 어긋난 곳을 보고만 한다 — 완료 뒤 문서 변경은 `feature-add`·`refactor-plan` 의 후미 절이다 - **루프 세션(코디네이터·무인 자식)이 불렀다** — 앞 문서를 고치지 않는다. `Next Action` 에 `질문: <원인> — <스킬> 로 반영한 뒤 "답: 반영함" 으로 알려 달라` 를 남긴다 ## 6단계. 고친다 **병렬 운용 중이면 라운드 사이에 고친다.** 구동기가 자식들을 띄운 채로 `LOOP.md`·`STATE.md` 형식을 바꾸면 도는 워커는 옛 규칙으로 끝나고 코디네이터는 새 규칙으로 읽어 기록이 어긋난다. `Assignments` 의 행이 전부 끝나 코디네이터가 종료한 뒤(또는 사람이 구동기를 멈춘 뒤) 고치고, 고친 뒤에 구동기를 다시 돌린다. 하네스 갱신(`harness-update` ②③)도 같은 타이밍이다. **구동 스크립트를 멈추는 동안 도는 워커를 지켜보는 쪽이 없다**(②). 워커 생존([감시 11])·모델 오류 화면·깨우기는 구동 스크립트 안에 있어서, 워커가 도는 채로 스크립트를 멈추면 그동안 멈춘 워커를 아무도 잡지 않는다 (실전 2026-10-13: 슬롯을 늘리려고 멈춘 사이 모델 오류로 선 워커가 그대로 슬롯을 쥐었다). 그래서 멈추는 시간을 짧게 한다. - **구동 스크립트만 고칠 때**(`loop-drive`·`loop-watch`·`loop-status`·`loop-answer`, 그리고 그 스크립트만 읽는 4절 ② 블록·8절 값) — 워커가 끝나기를 기다리지 않는다. 먼저 고쳐 **커밋해 코디네이터 트리에 들인 뒤**(단계 파일이 "코디네이터 자식" 이 아닐 때 — 자식도 그 트리에 커밋한다) (구동 시작 명령은 코디네이터 트리의 파일을 실행한다 — `main` 에만 두면 옛 파일이 뜨고, 코디네이터 트리에 커밋 없이 두면 슬롯 1 park 의 `git add -A` 가 쓸어 간다) 멈추고 곧바로 다시 띄운다. 도는 스크립트는 시작 때 읽은 코드로 돌므로 파일을 고치는 동안에는 영향이 없고, 다시 뜬 스크립트는 남은 dispatch 를 [감시 9] 로 이어받아 계속 지켜본다 - **`LOOP.md` 의 지시문·`STATE.md` 형식을 바꿀 때** — 위 문단대로 라운드 사이다(코디네이터 자식은 라운드마다 `LOOP.md` 를 새로 읽는다). 워커가 끝나기를 기다리며 스크립트를 멈춰 둔 동안은 상태판(`loop-status --once`)과 `orca orchestration worker-list --json` 으로 사람이 5분쯤마다 직접 보라고 보고에 적는다(화면 끝의 오류 문구는 오르카에서 그 워커 터미널을 본다) **②의 상주 코디네이터를 옮길 때는** 돌던 워커가 끝나기를 기다려 그 코디네이터 세션을 종료한 뒤 고친다 — 옛 코디네이터가 살아 있는 채로 구동 스크립트를 띄우면 코디네이터가 둘이 된다. **기존 파일을 부분 수정한다. 통째로 다시 쓰지 않는다.** `LOOP.md` 에는 그동안 쌓인 것이 들어 있다 — 이 프로젝트의 실제 값, 원본 예시에서 조정한 항목과 그 이유, 닫힌 미결의 흔적, 실측한 DAG 결과. 다시 쓰면 그게 조용히 사라지고, 사라진 것은 아무도 눈치채지 못한다. 지킬 것 넷. - **파일은 하나 그대로다.** 규칙이 늘었다고 쪼개지 않는다 — 매 세션이 시작할 때 읽는 문서라, 흩어지면 일부만 읽고 시작한다. 정말 길어지면 **조건부로만 필요한 절**(멀티 에이전트 등)을 빼는 것이 먼저다 - **새 기록처를 만들지 않는다.** 갱신 이력은 `LOG.md` 한 줄이고, `LOOP.md` 는 **현재 규칙**만 보여준다 - **`STATE.md` 는 절의 형식과 만료된 승인만 건드린다.** 값은 루프를 도는 세션의 것이다 (형식 규칙은 5단계) - **하네스에서 읽어 오는 것을 여기 베껴 적지 않는다.** 게이트 명령은 여전히 정의서에서 꺼낸다. `local-dev` 경로·옵션처럼 원래 실제 값으로 적기로 한 것만 실제 값을 유지한다 ### 갱신을 기록에 남긴다 `LOG.md` 에 한 줄 남긴다. 형식은 그 파일의 기존 형식을 따른다. **멀티 에이전트로 운용 중이면 직접 쓰지 않고 그 한 줄을 orchestration 에 넘긴다** — 병렬에서 `LOG.md` 의 필자는 하나다(5단계). **완화(③)와 미결을 닫은 것은 이유를 함께 남긴다.** "무엇을 왜 바꿨고 사용자가 무엇에 동의했는지", 미결이면 "무엇을 근거로 그 선택지를 골랐는지". **루프가 헐거워진 이력은 루프 자체보다 중요하다** — 지금 아무도 안 멈추는 자리가 원래 무엇을 막던 것인지가 거기 있다. ### `CLAUDE.md` · `AGENTS.md` 갱신 `LOOP.md` 를 고친 뒤 프로젝트 루트의 `CLAUDE.md` 와 `AGENTS.md` 를 **같은 내용으로** 갱신한다. 사용자가 직접 연 세션과 루프의 자식 세션은 이 스킬을 읽지 않고 시작하는데, 그때 자동으로 읽히는 파일은 Claude Code 는 `CLAUDE.md`, Codex 는 `AGENTS.md` 뿐이다. 그래서 **지금 어느 호스트에서 돌든 둘 다 쓴다** — 한쪽만 쓰면 다른 호스트의 세션이 규칙을 모른 채 시작한다. 내용은 **어느 세션에서든 반드시 지킬 것**만 두고 세부 규칙은 문서를 가리킨다 — 규칙을 옮겨 적으면 두 곳이 곧 어긋나고, 어긋나면 세션은 짧은 쪽을 믿는다. - 둘 다 없으면 아래 뼈대로 만든다. 있으면 **`## 루프 (loop_setup/LOOP.md)` 절의 바뀐 값만** 고친다. `현재 단계` 줄은 건드리지 않는다 — 루프 중간의 갱신이지 단계가 바뀐 것이 아니다. 다른 절과 사용자가 손으로 적은 내용은 건드리지 않는다 - 두 파일이 서로 다르면 지금 호스트의 파일을 기준으로 다른 쪽을 맞춘다. 손으로 적은 줄은 양쪽에 남긴다 - 파일은 있는데 `## 문서 지도`·`## 공통 규칙` 이 없으면(예: `/init` 이 만든 파일) 기존 내용 **아래에** 뼈대 두 절을 더한다. 기존 내용은 손대지 않는다 - 절이 없으면 `공통 규칙` 뒤에 제품·기술·구조·플랜·하네스·루프 순서의 자리에 더한다 - 전체 **100줄 안팎, 200줄을 넘기지 않는다.** 넘치면 규칙을 문서 쪽으로 돌려보내고 가리키는 줄만 남긴다 ```markdown # <프로젝트 이름> > harness-loop-fullstack 스킬이 만들고 갱신하는 파일이다. 각 절은 그 레이어의 스킬만 고친다. > `CLAUDE.md` 와 `AGENTS.md` 는 같은 내용이다. 한쪽을 손으로 고치면 다른 쪽도 같이 고친다. > 세부 규칙은 각 문서에 있다 — 여기에는 어느 세션에서든 반드시 지킬 것만 둔다. 200줄을 넘기지 않는다. ## 문서 지도 - `docs/IDEA.md` → `docs/PRD.md` → `docs/TRD.md` → `docs/ARCHITECTURE/` → `plan_setup/` → `harness_setup/` → `loop_setup/LOOP.md` - 뒤 문서는 앞 문서를 근거로 한다. 어긋나면 앞 문서가 이긴다 — 바꿔야 하면 사용자 확인 뒤 **가장 앞 문서부터** 고치고(버전·`변경 이력` 한 줄. 프로젝트 완료 뒤에는 `feature-add`·`refactor-plan` 의 후미 절로) 사이 문서를 맞춘 다음 이어 간다(변경 전파). 손으로 고칠 때도 같다. 루프 세션은 앞 문서를 고치지 않고 LOOP.md 대로 멈춘다 - 현재 단계: <마지막으로 끝낸 스킬> → 다음: <다음 스킬> ## 공통 규칙 - 앞 레이어의 **파일**만 입력이다. 대화 기억이나 추측으로 요구사항·기술 결정을 만들지 않는다 - ID(`FR-`·`NFR-`·`US-`·`OQ-`·`TD-`·`phase-NN-task-MM`)는 참조만 한다. 새 ID 는 마지막 번호 다음으로만 만든다 — OQ 는 프로젝트 전체(`HARNESS.md`·`LOOP.md` 의 미결 표까지)에서, 나머지는 그 문서에서 센다(변경 전파로 앞 문서에 더할 때도) - 사용자가 말하지 않은 값은 `(가정)` 으로 표시한다. 모호하면 추측하지 말고 묻는다 - 요청된 범위를 조용히 넓히거나 줄이지 않는다. 범위 판단은 보고한다 ``` `## 루프 (loop_setup/LOOP.md)` 절에서 **바뀐 값만** 고친다 — `구동` 줄(모드·동시 수·격리 방식)과, 병렬을 켜면서 STATE.md 절 형식이 넓어졌다면 그 사실 한 줄. 문장 규칙(3회·분리 세션·슬롯 밖 수정 금지)은 바뀌지 않으므로 손대지 않는다. 절 형식은 `loop-setup` 이 만든 것 그대로다. ```markdown ## 루프 (loop_setup/LOOP.md) - 세션을 열면 `loop_setup/LOOP.md` 0절부터 따른다. 이 절은 요약이 아니다 — LOOP.md 가 규칙이다 - 완료는 네 게이트를 통과한 뒤다. 코드가 끝난 것은 완료가 아니다 - 실패→수정→재검증은 3회까지. 소진하면 STATE.md `Next Action` 에 `질문:` 을 적고 멈춘다 - 커밋은 네 게이트 통과 뒤. 시점과 횟수는 LOOP.md 5절. 검증 세션은 STATE.md·LOG.md 를 쓰지 않는다 - 병렬이면 자기 슬롯 워크트리 밖을 고치지 않고, 워커는 STATE.md·LOG.md·태스크 문서·하네스 문서를 쓰지 않는다(출력 블록으로 넘긴다). merge 는 코디네이터 몫 - 구동: <스크립트 / 오르카 / 서브에이전트> · 동시 · 격리 <방식> · 모델 코디 <급> / 구현 <급> / 검증 <급> ← loop-update 가 갱신 ``` --- ## 7단계. 자체 점검 | 확인할 것 | 통과 조건 | 걸렸을 때 | |---|---|---| | 파일 | `loop_setup/LOOP.md` 하나 그대로. 쪼개지 않음 | 되돌린다 | | 고정 규칙 | 재시도 3회·한 태스크 한 세션·분리 세션·DAG 가 그대로 | 되돌린다. 원본 사양이다 | | 게이트 | 게이트 넷·이름·순서와 상태 어휘 넷이 그대로 | 되돌린다 | | 멈춤 규칙 | 루프를 멈추게 하는 규칙 중 **줄어든 것이 0건**(승인받은 것 제외) | 되돌리고 묻는다 | | 기록 시점 | 시작·전이·완료 세 시점과 불일치 복구 규칙이 그대로 | 되돌린다. 없으면 끊긴 세션이 복구되지 않는다 | | 승인 만료 | 활성된 게이트에 걸린 `Standing Decisions` 항목이 남아 있지 않음 | 지운다. 반복되면 루프가 `LOOP.md` 0절을 안 지키는 것이므로 그것을 보고한다 | | 승인 기한 | 남은 승인에 **범위나 기한**이 있음 (무기한 0건) | 기한을 붙인다 | | 미결 | 닫은 미결이 표에서 사라졌고 **결정이 본문 규칙이 됨** | 옮긴다. 둘 다 해야 닫힌 것이다 | | 격리 | 멀티 에이전트를 켰다면 격리 방식이 **확정**되어 있음 | 정한다. 미결인 채로 켜면 DB가 조용히 오염된다 | | DAG | 태스크가 바뀌었으면 순환·미존재 참조·`files` 겹침을 **다시** 확인 | 다시 잰다 | | 폭 | 병렬을 운용한다면 구간별 폭과 권장 에이전트 수가 현재 태스크 기준 | 다시 잰다 | | STATE 형식 | 절 형식을 넓혔다면 **기존 값이 새 형식으로 옮겨져 있음** | 옮긴다 | | STATE 필자 | 병렬을 켰다면 `STATE.md`·`LOG.md` 의 필자가 **orchestration 하나**로 정해져 있음(질문 답변 자식의 `질문:`·구동 스크립트의 토막 값만 예외) | 정한다. 칸만 나누면 통째 쓰기에서 서로 덮어쓴다 | | 배정 주체 | 병렬을 켰다면 태스크 배정이 **코디네이터**이고 구현 지시문에 `태스크: ` 가 들어가며 구동기가 ②나 ①-병렬로 되어 있음 | 정한다. 워커가 스스로 고르면 같은 태스크를 둘이 집는다 | | 슬롯 | `main` 에서 작업하는 자리가 0건이고 코디네이터 트리(`coordinator`, 코디네이터 + `worker-1`)와 둘째부터의 `worker-N` 트리가 있으며, 검증·비코드 태스크는 워크트리를 받지 않음 | 고친다. 태스크마다 만들면 환경 N벌이 낭비된다 | | DAG 그림 | 태스크가 바뀌었으면 `DAG.md` 를 스크립트로 다시 만들었고 노드 수 = 태스크 문서 수, 8절 충돌 표가 새 겹침 쌍을 다 다룸. `LOOP.md` 안에 머메이드가 없음 | 스크립트를 돌린다. 손으로 그린 것은 지운다 | | 잠금 | 파일 잠금·대기열로 푼 곳이 0건 | 필자 규칙으로 바꾼다. 잠근 채 죽으면 전체가 선다 | | Layer 3 절 | `Current Task`·`Last Verification` 을 넓혔다면 **②로 분류해 보고**했음 | 보고에 넣는다. 조용히 바꾸면 Layer 3 형식과 갈라진다 | | 실제 값 | `local-dev` 경로·옵션, 태스크 ID가 현재 하네스·플랜과 일치 | 맞춘다 | | 베낀 명령 | 게이트 명령을 `LOOP.md` 에 베껴 적은 곳이 0건 | 지운다 | | 침범 | 게이트 정의서·태스크 문서·`STATE.md` 의 **값**·코드를 이 스킬이 직접 고친 곳이 0건 ("다른 레이어 반영"으로 `plan-generator` 10단계·`harness-update` 를 불러 — 부를 수 없으면 그 규칙대로 — 고친 것은 침범이 아니다) | 되돌린다 | | 형식 | 고친 문서가 기존 절 구성과 표 열을 그대로 유지 | 맞춘다 | | 기록 | 갱신이 `LOG.md` 에 남았고, 완화와 미결 종료는 이유가 함께 있음 | 채운다 | | 배정 근거 | 병렬이면 코디네이터 지시문의 배정 단계가 `dag-render --ready` 후보에서 고르고 폭 표를 근거로 쓰지 않으며, `dag-render` 에 `--ready` 가 있고 돌려 봤고, 코디네이터 허용 목록에 그 명령이 있음 | 고친다. 폭 표로 배정하면 선행이 끝난 태스크가 "다음 단계"로 읽혀 슬롯이 논다 | | 1.15.2 빈틈 | ①·②면 phase 완료 묶음을 판단 자식이 돌리는 곳이 0건이고(코디네이터 허용 목록·자식 상한·지시문), `묶음 대기:` 신호와 `bundle-phase-NN.txt` 결과 파일, `loop-drive` 의 묶음 모드가 있으며 묶음 명령을 `e2e.md` 의 그 절에서 읽음(베낀 명령 0건) | 템플릿대로 고친다. 판단 자식은 묶음을 못 돌려 phase 마다 사람에게 넘어간다 | | 1.15.3 빈틈 | ②면 `loop-drive`·`loop-watch` 가 4절 [감시 5]·[워커 정리]·[감시 9]·[정지 재알림] 과 같음(오르카 쪽 `draft` 를 Enter 로 처리하는 곳, 시작·정리 실패로 루프 전체가 서는 곳 0건 — 거부된 `worker-start` 만 예외) | 템플릿대로 고친다. 워커 하나의 시작 실패가 루프 전체를 세우고 아무도 다시 알리지 않는다 | | 1.15.4 빈틈 | ②면 `loop-drive` 가 dispatch 파일을 `worker-start` 응답을 받는 즉시 쓰고, 시작 때 dispatch 파일에 없는 활성 워커를 정리함 | 템플릿대로 고친다. 워커를 띄우는 도중 구동기가 꺼지면 기록 없는 워커가 남아, 다시 띄울 때 한 트리에 워커가 둘이 된다 | | 1.15.5 빈틈 | ②면 `loop-drive` 에 [정지 요청] 다섯 자리와 `--stop`(대기+90초, 판단 자식 중엔 끄지 않음)이 있고, 단계 파일에 판단 자식의 child 를 쓰며, 시작 때 앞 실행의 판단 자식을 기다림 | 템플릿대로 고친다. 판단 자식이 `STATE.md`·커밋 도중에 끊기거나, 다시 시작한 구동기와 앞 실행의 자식이 함께 `STATE.md` 를 쓴다 | | 1.15 빈틈 | 병렬(①-병렬·②)이면 코디네이터 지시문 첫 문단이 "슬롯은 2.6) 대로 park" 이고 2.6)·2.7) 이 있으며, ②면 생존 확인이 도는 워커가 있는 동안 5분마다이고 `unverifiable` 에 활동 20분 정체면 정리하며, 밖의 감시가 답 없는 `질문:` 을 다시 알리고 답하기 탭을 띄우고, `loop-answer.*` 가 있으며, 정지 행이 `park/` 로 슬롯을 놓고, 코디네이터가 dispatch 파일이 남은 행의 결과 파일을 건너뜀 | 템플릿대로 고친다 | | 1.10.1 빈틈 | `loop-drive` 가 답변 자식 출력의 첫 줄을 울타리·빈 줄·강조를 걷고 읽으며, 도는 dispatch 0 에서 기다리지 않고(진전 없는 바퀴면 멈춤) 생존을 보며(1.15 부터 5분마다) exited 행에 END 없는 줄을 남기고, 결과 파일이 남은 행을 다시 띄우지 않고, 격리 3 의 ②면 검증 워커가 앱 전환 뒤 한 번에 하나이며 도는 동안 코디네이터 자식·앱 전환을 미룸 | 템플릿대로 고친다 | | 구동기 | ②면 상주 코디네이터·phase 마다의 교체 명령이 0건이고, `loop-drive` 가 있으며 스모크 테스트(①은 드라이런)를 다시 돌렸음 | 옮기고 돌린다 | | 트리거 실행 | 실패 줄 형식·반복 판정 단계(지시문 안)·`알림(<주체>, ~<만료>)`·0절 7번이 있고, 쌓여 있던 알림이 전부 분류됨(①은 처리, 나머지는 새 형식, 만료된 것은 `질문:`) | 채운다. 옛 알림을 새 형식으로만 바꾸고 분류하지 않으면 같은 것이 다시 쌓인다 | | `CLAUDE.md` · `AGENTS.md` | 두 파일이 같고 `## 루프` 절이 갱신됐고 각 200줄 이하 | 자기 절만 고친다. 넘치면 규칙을 문서로 돌려보내고 가리키는 줄만 남긴다 | **보존이 점검의 절반인 이유가 있다.** 갱신은 늘 "지금 걸리는 것"을 보고 하는데, `LOOP.md` 에 쌓인 것은 **과거에 무언가를 막았던 규칙**이라 지금 눈에는 군더더기로 보인다. 막고 있는 동안에는 아무 일도 안 일어나는 것이 규칙이 일하고 있다는 증거이고, 그래서 **잘 작동하는 규칙일수록 지워도 될 것처럼 보인다.** --- ## 8단계. 보고 1. **③ 완화가 있었으면 맨 앞에.** 무엇이 더 이상 루프를 멈추지 않는지 2. **무엇을 왜 고쳤는가** — 등급(①②③)과 함께. ①은 묶어서 한 줄씩 3. **닫은 미결** — 무엇을 근거로 그 선택지를 골랐고, 되돌리기 비용은 얼마인지 4. **멀티 에이전트를 켰다면** — 격리 방식, 구간별 폭과 권장 에이전트 수, **넷을 띄워도 노는 구간이 어디인지.** 병렬의 이득을 부풀리지 않는다 5. **② 규칙 추가의 비용** — 매 사이클에 얼마나 붙는가 6. **다른 레이어에 반영한 것** — 루프가 반복해 멈추는 원인이 플랜(`files` 에 없는 파일)이나 하네스(흔들리는 흐름)·앞 문서에 있으면 여기서 덮지 않고 그쪽을 고친다(6단계 앞의 "다른 레이어 반영"). 고친 문서·새 버전, 또는 루프 중이라 `질문:` 으로 남긴 것 7. **아직 열려 있는 미결** — 무엇이 언제 닫히는지 8. **옛 구조를 옮겼다면** — 구동기를 어떻게 바꿨고 스모크 테스트가 어떻게 돌았는지, 쌓여 있던 알림을 몇 개 처리·전환·질문으로 올렸는지 **루프가 헐거워졌으면 그 사실이 보고의 맨 앞이다.** 진행이 빨라진 것을 성과로 적지 않는다 — 멈춤 규칙을 지워서 빨라진 것은 아무것도 뜻하지 않고, 그걸 성과로 읽으면 다음에도 같은 방식으로 빨라진다. --- ## 자주 만나는 갱신 네 가지 ### 격리 미결을 닫을 때 (세팅 때 껐던 병렬을 켠다) 기본이 병렬이라 보통은 `loop-setup` 이 세팅 때 격리를 이미 정했다. 이 절은 **사용자가 세팅 때 병렬을 꺼서** 미결이 남은 경우다. 닫을 때 필요한 것은 셋이다. 1. **실제 동시 에이전트 수** — 구간별 폭을 다시 재서 넷이 실제로 차는 구간이 있는지 본다. 폭이 2인데 넷을 띄우면 격리 비용만 치르고 이득이 없다 2. **공유해야 하는 것이 실재하는지** — DB를 공유해야만 하는 태스크가 실제로 있는가 3. **선택지 중 하나를 고르고 되돌리기 비용을 적는다** — 환경 통째 분리 / DB 하나에 워크트리별 계정·권한 / 공유 자원을 쓰는 게이트만 직렬화 **첫 번째 선택지는 `local-dev` 수정이 필요할 수 있고, 그건 `harness-update` 의 일이다.** 루프가 스크립트를 고치지 않는다. 그리고 **미결을 닫으면 표에서 지우고 결정을 본문 규칙으로 옮긴다.** 표에 "이렇게 하기로 했다"고만 남기면 사이클을 도는 세션이 그것을 읽지 않는다. ### 게이트가 활성돼 승인이 만료될 때 `Standing Decisions` 의 미활성 승인은 **"그 게이트가 활성될 때까지"** 로 발급됐다. 게이트가 활성되면 그 항목은 **지워야 한다.** **빠뜨리기 쉽다.** 활성은 하네스 문서에서 일어나는데 승인은 `STATE.md` 에 있어서, 둘을 잇는 것이 사람의 기억밖에 없기 때문이다. `HARNESS.md` 의 게이트 표를 보고 `미활성` 이 아니게 된 게이트를 `Standing Decisions` 에서 검색하는 것이 확실하다. 남기면 무슨 일이 생기냐면 — **이제 실제로 도는 게이트를 루프가 "승인받았으니 건너뛴다"로 읽는다.** 가장 조용한 종류의 구멍이고, 통과율은 오히려 좋아 보인다. ### 루프가 같은 자리에서 반복해 멈출 때 먼저 0단계로 **어느 레이어의 문제인지 가른다.** `LOG.md` 에 쌓인 중단 기록이 근거다. 그리고 **루프의 문제라는 결론이 나와도, 멈춤을 줄이는 방향인지 확인한다.** 같은 중단을 없애는 방법이 보통 둘 있다 — **원인을 없애는 것**과 **멈추지 않게 하는 것**. 앞쪽이 먼저다. 승인 범위가 좁아 매번 묻는 것이면 범위를 게이트 단위로 넓히면 되고(기한은 유지), `Retry` 절이 병렬에서 덮어써지는 것이면 형식을 넓히면 된다. **뒤쪽(멈추지 않게 하기)이 답인 경우는 드물고, 그때는 ③이라 승인이 필요하다.** ### 1.10.0 이전 `LOOP.md` 를 옮길 때 (상주 코디네이터·쌓이는 알림) 2026-09-23 이전 `loop-setup` 이 만든 `LOOP.md` 에는 두 구조가 있다. ②에서 **대화형 코디네이터가 phase 내내 상주**하고(또는 phase 마다 새 터미널로 교체되고), "두 태스크에 걸친 같은 실패"·미결의 판단 시점 같은 트리거가 **설명으로만** 있어 코디네이터가 `알림:` 한 줄로 쌓고 지나간다. 실전(ERP)에서 앞쪽은 전체 토큰의 30%를, 뒤쪽은 여섯 phase 동안 같은 흔들림을 만들었다. 옮기는 순서는 이렇다. 1. **라운드 사이를 잡는다** — 돌던 워커가 끝나고 상주 코디네이터가 종료된 뒤. 살아 있는 코디네이터 옆에 구동 스크립트를 띄우지 않는다 2. **4절 ② 블록을 `loop-setup` 템플릿의 "② 오르카 스크립트 구동"으로 바꾼다** — 코디네이터 지시문에서 교체 단계(다음 코디네이터를 띄우는 줄)를 지우고, 반복 판정 2.5)·질문 답변 지시문·② 워커 덧붙임을 넣는다. 배정 3)은 `--ready` 판으로 바꾸고 `dag-render` 에 `--ready` 가 없으면 더한다(4단계 표). 이 프로젝트가 고쳐 둔 지시문 문장은 살린다 — 통째로 붙여 넣지 않는다 3. **8절** — 코디네이터 수명·교체 명령·시작 프롬프트를 지우고 구동 스크립트·구동 시작 명령·대기 길이·스모크 테스트 칸을 넣는다. 배정표가 오르카 Run·Task 였다면 `STATE.md` 에 `Assignments` 절을 더한다 4. **`harness_setup/scripts/loop-drive.*` 를 쓰고 스모크 테스트를 돌린다**(`loop-setup` 12단계와 같다). 오르카 터미널 안에서, 가벼운 모델의 시험 워커 하나로 5. **트리거를 넣는다** — 3절 실패 줄 형식과 반복 판정, 0절 7번, 9절 `언제` 를 `phase-NN 진입 전` 같은 확인 가능한 시점으로 6. **쌓인 알림을 분류한다.** 새 형식으로 바꾸기만 하면 같은 목록이 다시 자란다 - 루프가 ①로 할 수 있는 것(`harness-update` ① 후보 등) → 이번에 처리하고 지운다 - 조건이 이미 충족된 트리거(두 태스크 이상 반복된 흔들림, 판단 시점이 지난 미결) → `질문:` 으로 모아 올린다. 격리 3에서 DB 테스트가 흔들렸다면 선택지에 격리 1·2를 넣는다 - 다른 레이어의 일(문서 표기 보정, `files` 보완) → `알림(<주체>, ~다음 phase 전)` 형식으로 바꾼다 - LOG.md 의 과거 실패 줄은 형식이 달라도 고치지 않는다 — append-only 다. 반복 판정은 옮긴 뒤의 새 형식 줄부터 센다(처음 판정 때 옛 기록에서 확인한 반복은 위 `질문:` 으로 이미 올렸다) 7. `CLAUDE.md`·`AGENTS.md` 의 `구동:` 줄을 고치고 `LOG.md` 에 한 줄 — 무엇을 옮겼고 알림을 몇 개 처리·전환·질문했는지 **이 갱신은 멈춤을 줄이지 않는다** — 정지 조건(`질문:`·전부 `completed`)과 멈춤 규칙은 그대로이고, 트리거가 실행되면서 멈추는 자리는 오히려 는다(②). 대신 사람이 불리는 곳이 phase 경계로 모인다 — 알림은 phase 안에서 묶였다가 경계에서 한 번 묻는다. --- ## 에이전트별 참고 - Claude Code에서 실행 중이면: `references/claude-code.md` - Codex에서 실행 중이면: `references/codex.md` **형식과 원칙은 `loop-setup` 의 SKILL.md 에 있다.** 사이클 단계, 실패 분류 표, `Retry` 절의 필드, 검증 세션에 주지 않는 것 목록, DAG 확인 항목이 필요하면 그쪽을 본다. 여기서 다시 정하지 않는다.