LogoSkills

Orchestration Graph — 흐름 선언 규약 (substrate-neutral)

흐름을 프로즈로 네 번 적는 대신, ` ```flow ` 블록 하나를 규범으로 두고 나머지는 그것의 파생 뷰로

하나의 흐름(flow)에는 규범 선언이 정확히 하나만 존재한다. 그 선언은 ```flow 블록이며, 같은 흐름을 설명하는 단계 표·ASCII 박스·TodoWrite 시드·다른 문서의 요약은 모두 그 블록의 파생 뷰(non-normative) 다. 이 문서는 표기법·루프 계약·게이트 계약·기질(substrate) 선택만 정한다. 개별 루프의 값(반복 횟수, 타임아웃 초, 무엇을 무효화하는지)은 여기 적지 않는다 — 각 루프의 자기 자리에 적고 이 문서를 참조한다. 값을 여기로 옮기면 40개 루프의 두 번째 규범 사본이 생기고, 그게 이 저장소가 가장 자주 지불한 실패다.

한마디로#

흐름을 프로즈로 네 번 적는 대신, ```flow 블록 하나를 규범으로 두고 나머지는 그것의 파생 뷰로 표시하는 규약입니다. 노드 7종과 화살표 4종뿐이라 30줄이면 2,600줄이 감추던 것 — 무엇도 무효화하지 않는 되돌림 화살표, 무제한 순환, 실패 경로가 없는 게이트 — 이 눈에 보입니다.

0. 적용 범위 (consumers)#

```flow 블록은 피드백 엣지·팬아웃·실패 경로 없는 게이트 중 하나라도 실재하는 흐름에만 쓴다. 선형이거나 순수 레퍼런스인 문서에 표기법을 넣는 것은 유지보수 표면만 늘린다.

흐름규범 블록이 놓이는 곳
13단계 개발 사이클plugins/cc-dev/commands/run.md
계층 배치(재귀 + Orca 디스패치)plugins/cc-dev/commands/batch.md
원스톱 파이프라인plugins/cc-dev/commands/go.md
제품 파이프라인 게이트plugins/cc-product/skills/pipeline/SKILL.md
E2E 시나리오 래더plugins/cc-e2e/ 커맨드 계열

각 문서는 자기 블록을 갖고 이 문서를 참조한다. 이 문서가 남의 흐름을 다시 선언하지 않는다.


1. Notation — ```flow 블록 하나#

구성물은 두 줄 종류뿐이다. 다른 글리프·다른 블록 종류를 만들지 않는다. 팬아웃/조인 의미는 노드 KIND 와 속성이 지므로 화살표는 4개로 고정된다.

# NODE   < id >    < KIND >    < label >     [key:value ...]
# EDGE   < src >   < arrow >   < dst >       [key:value ...]
#   KINDS: ACT GATE FORK JOIN LOOP WAIT ASK
#   ARROWS: -- >    hard dependency   .. >    advisory order
#           == >    feedback (needs bound: + invalidates:, must land on a LOOP)
#           ~~ >    skip/degrade (needs on: + record: < durable location > )
#   RESERVED: HALT · SKIP — 종단 sink 이며 노드 id 가 아니다(선언 불필요).
#   extern:true — 이 블록 밖에서 선언된 경계 노드(발췌를 쓸 때 dangling id 를 막는다).

1.1 실제 흐름 재표현 — commands/run.md Steps 7–12#

# <- 는 표기법이 드러낸 결함이다. 파일에서 확인한 것만 붙였다.

S5     ACT   브랜치 + In Progress(부모 cascade)   extern:true
S6     ACT   BDD 시나리오 작성                    extern:true
S7     ACT   구현 (implementation-agent)          writes:kobic/lib/**,kobic_server/lib/**  tier:standard
S7.1   ACT   design reference + DDR               writes:.claude/docs/{scope}/design-decisions.md
S7.2   GATE  화면 PR diff 에 CoUI 소스 없음         verdict:git diff --name-only|grep coui  undet:fail  fail:S7
S7.3   LOOP  design verification (Figma↔runtime)  contract:L-7.3
S7.5   ACT   backend codegen                      writes:**/*.g.dart  own:lead
S7.7   GATE  local full-stack integration         verdict:dart test -t integration exit  undet:fail  fail:S7
S8     GATE  pr:preflight (unit/widget/integ)     verdict:/cc-dev:pr:preflight exit  undet:fail  fail:S7
S8.3   GATE  BDD coverage 100% + assertions       verdict:stepCoverage==1  & &   !assertionless  undet:fail  fail:S6
S8.5   LOOP  runPrePushGate                       contract:L-8.5
S8.7   LOOP  code review gate                     contract:L-8.7
S9.0   GATE  중복 착수 재확인 (--state all)         verdict:gh pr list  undet:미선언  fail:HALT  #  < - §3 기본값 fail 로 읽는다
S9     GATE  pre-PR 7종 (children/branch/base/commit/CoUI/designVerification/8.5)
                                                  verdict:mayClose(openChildrenStatus)  undet:fail  fail:HALT
S9x    ACT   gh pr create                         writes:github:pr
S10    ACT   moveIssueToPipeline(Review/QA)       writes:zenhub:pipeline
S10.5  LOOP  CI 대기 ∥ 작업내역 발행                contract:L-10.5
S11    LOOP  리뷰 피드백 반영                      contract:L-11
S11.9  GATE  openChildrenStatus === none          verdict:mayClose(...)  undet:fail  fail:HALT
S12    ASK   머지 승인                             options:승인|수정필요|취소  pre:--merge=pre-authorized
S12.5  ACT   close 검증 + 자식 불변식 복구          writes:github:issue,zenhub:state,git:base

S7 -- >   S7.2 -- >   S7.3 -- >   S7.5 -- >   S7.7 -- >   S8 -- >   S8.3 -- >   S8.5 -- >   S8.7 -- >   S9.0 -- >   S9
S9 -- >   S9x -- >   S10 -- >   S10.5 -- >   S11 -- >   S11.9 -- >   S12 -- >   S12.5
S5   -- >   S7                          # hard prereq 는 S5 (Step-by-Step Prerequisites 표 그대로)
S7   .. >   S7.1                        # 비차단 참고 — 절대 게이트하지 않는다
S6   .. >   S7                          # advisory: S6 도 S5 를 전제로 하는 형제다
S7.3 ~~ >   S7.5  on:!screenFeature||!figmaSource   record:log( " not-applicable " )
S7.3 ~~ >   S7.5  on:toolingMissing                 record:ASK+prBodyExtras.designVerification
S8   ~~ >   S8.3  on:--skip-tests                   record:ASK                #  < - ASK 만 하고 PR body 에
                                     #    안 남기며,  " 테스트 실행 "   답을 분기하지도 않는다
S8.3 ~~ >   S8.5  on:--skip-bdd                     record:warn               #  < - 비내구. ASK+PR body 로 승격
S7.3 == >   S7    on:verify.fail_after_repair  bound:1  invalidates:S7.2,S7.3
                                     #  < - bound·invalidates 는 선언됐지만 착지점 S7 이 ACT 다.
                                     #    되돌림의 예산을 들고 있을 LOOP head 가 없다 (§7 규칙 4)
S8   == >   S7    on:test.fail                 bound:?  invalidates:?         #  < - 미선언
S8.3 == >   S6    on:coverage < 1                bound:?  invalidates:?         #  < - 미선언
S10.5== >   S8.5  on:ci.fail                   bound:?  invalidates:S8.5      #  < - 무제한 순환
S11  == >   S8.5  on:feedback.commit           bound:?  invalidates:S8.5,S10.5  #  < - 미선언
S12  == >   S7    on:approval== " 수정 필요 "         bound:NONE invalidates:NONE
                                     #  < - ill-formed: LOOP head 없음. 10개 노드를 건너뛰는데
                                     #    무엇도 무효화하지 않아 어떤 게이트도 재실행되지 않는다

프로즈 2,611줄이 감추던 것이 30줄에서 읽힌다. S12 ==> S7 은 열 개 노드를 거슬러 오르면서 그중 무엇도 무효화하지 않는다 — validateStepPrerequisites()completed/skipped 만 통과시키므로, 이미 completed 인 게이트들은 아무것도 재실행되지 않는다. S10.5 ==> S8.5S11 ==> S8.5 는 서로를 먹이는 진짜 무제한 순환이다(runStep10_5() → CI 실패 → 수정 → runPrePushGate() → re-push). 그리고 세 개의 형제 skip 엣지 중 S8.3 ~~> S8.5 만 콘솔에 기록하고 나머지는 PR body 에 남긴다.

이 블록은 §7 체크리스트를 아직 통과하지 못한다 — 의도적이다. S7.2·S7.7fail: 만 있고 대응 ==> 줄이 없어 규칙 1 미충족, S9.0 은 규칙 2 미충족이다. 승격 시 이 세 자리를 먼저 채운다(§8).


2. Loop Contract — 필수 7 필드#

LOOP 노드는 contract:<id> 를 갖고, 그 id 아래 7 필드 전부를 자기 자리에 적는다.

필드규칙
inv:매 iteration 의 진입·종료 양쪽에서 성립해야 하는 것. 검증 가능한 문장으로.
prog: 이름 붙은 단조량 + 비교. no-prog: 행동을 반드시 함께 적는다.
term: 정지 술어. prog:같은 단위로. "재검토 필요 시 반복" 은 term: 이 아니다.
budget: 단위 붙은 숫자. 재진입되는 루프는 inner/outer 를 따로. "소폭"·"a small bounded number" 는 실패.
exhaust: 명시적 행동. 침묵은 실패. "경고 후 계속" 은 §3 의 degradable 선언이 있을 때만.
resume:재진입이 자기 위치를 찾기 위해 읽는 것 + 어떤 부작용이 멱등인지.
log: iteration 당 한 줄(= prog: 값 포함) + 잔여 상태와 탈락시킨 것의 내구 기록.
  • budget:숫자를 어떻게 고르는지plugins/cc-dev/skills/job-timeout-budget/SKILL.md 가 SoT다(실측 최대 × 2, 표본 없으면 형제 잡에 정합). 여기서 재정의하지 않는다.
  • exhaust:기본 사다리plugins/cc-dev/agents/sequential-workflow.md 의 3단 stall ladder다 (Rung 1 같은 전략 재시도 → Rung 2 /cc-dev:unstuck종류가 다른 리프레임 → Rung 3 BLOCKED('unstuck_exhausted') + 보드 반영 + 형제 계속). 루프마다 새 사다리를 발명하지 않는다.
  • prog: 없는 재시도가 이 저장소의 최빈 결함이다. no-prog: 기본값은 남은 예산을 같은 작업에 쓰지 않고 Rung 2 로 올린다.
  • 위임된 루프도 루프다. auto_fix: true, Skill 호출, 서브에이전트로 넘긴 반복은 budget: · exhaust: · 무엇을 되돌려 기록하는지를 호출 지점(call site) 에 공표해야 한다. 위임했다고 루프가 아니게 되지 않는다.
  • 디스패치된 워커 안에서 도달 가능한 루프의 exhaust:대화형 AskUserQuestion 은 무효다 (물어볼 사람이 그 세션에 없다).

필드 모양 예시 — L-8.5 runPrePushGate (비규범 예시. 확정 값은 commands/run.md Step 8.5 에 있다)

inv:      iteration 종료 시 작업 트리 commit-clean · auto-fix 커밋은 원자적
          `// ignore:` 삽입이나 `LEFTHOOK=0` 로 0건을 달성하는 것 금지
prog:     residual = dartAnalyzeIssues + dcmErrorIssues, 매 iteration 강한 감소
          no-prog: 동일 잔여에 같은 수정 반복 금지 → Rung 2      #  < - 현행은 루프 진입 전
          스냅샷(analyzeLines)을 다시 순회한다: 감소 보장이 없다
term:     dartAnalyzeIssues == 0  & &   dcmErrorIssues == 0
budget:   inner 2 iterations (Phase 1) / outer = push 당 1회 재진입   #  < - outer 미선언
exhaust:  throwPR 생성 차단 (경고 후 계속 금지)
resume:   `melos run analyze` · `melos run dcm:analyze` 재실측으로 위치 판정
          (대화 카운터는 /clear 를 못 넘으므로 예산이 아니다)
log:       " #2: dart=5→2 dcm=1→0 "   · 0**주장된 0** 으로 출력 (없음 ≠ 확인함)

3. Gate Contract — verdict 는 tri-state 다#

  1. 모든 GATE 의 verdict 는 pass / fail / undetermined 세 값이다.
  2. undetermined 의 기본값은 fail 이다. 판정 불가 ≠ 통과.
  3. 정본 구현은 plugins/cc-dev/rules/zenhub-conventions.md → "Child Enumeration Contract" 의 openChildrenStatus() open | none | unknown 이다. 새 게이트는 그 삼분 판정을 복제하지 말고 그대로 호출한다. 통과는 none 하나뿐이고 unknown 은 차단이다.

3.1 warn 으로 완화할 수 있는 세 경우 (사전 선언 필수)#

undet:warn 은 아래 셋 중 하나에 해당하고, 그 이유를 노드에 적고, 내구 기록을 남길 때만 쓴다.

경우
막을 대상이 이미 없다run.md Step 12.5 종료 후 자식 확인 — 머지가 끝나 되돌릴 게 없다
구조적으로 해당 없음Sub-task(구조적 leaf)의 자식 조회 실패
안전이 아니라 도구 부재 melos/serverpod 미설치 → 폴백 + 경고 (Step 0 Degradation Contract, GD-01)

3.2 이름 붙은 fail-open 5형 — 새 게이트를 쓸 때 이 다섯을 먼저 대조한다#

증상명명 사고
empty-set pass 조회 실패가 [] 가 되고 open.length > 0[] 에서 통과 #3451
pipe-masked exit code cmd | grep -cgrep 의 exit code 만 남겨 실패를 0으로 가림 FO-10 (run.md Step 8)
null-as-pass testPass === null("미검증")을 "실패 아님"으로 읽음 FO-04 (run.md Step 8)
nothing-to-check pass 검사 대상 0건을 커버리지 100% 로 계산 FO-03 (run.md Step 8.3)
silent skip 도구 부재를 무음 degrade 해 게이트가 사라진 걸 아무도 모름 GD-01 (run.md Step 0)

4. Substrate Selection — 6행 매트릭스#

substrate 워커가 쓰나? 파일시스템 격리 자기 브랜치/커밋/PR? 제어 흐름 fan-out N 스폰 전 사람 승인 크래시 후 재개 상대 비용
inline / sequential (기본값, 정당한 답) 해당 없음 — 트리 하나 해당 없음 LLM 1 아니오 아니오 최저
Task 서브에이전트 읽기 위주 같은 트리 아니오 LLM 위임된 판단 1건 아니오 아니오 낮음
Agent Teams 예, 단 프로즈 파일 소유권 분할 필수 같은 워크트리 아니오 — teammate 는 다른 브랜치로 git checkout/switch/commit 금지 LLM 고정 3~5, 역할 단위 필수(계획 승인) 아니오 중간
Orca 워크트리 디스패치 형제마다 별도 워크트리 + 터미널 예 — 그게 목적이다 LLM ZenHub 형제 1개당 1, width cap 필요(3) 항상, --merge=pre-authorized 도 면제 못 함 /cc-dev:batch {n} 재실행(멱등) + 미회수 워크트리 스윕 최고, 머지는 항상 직렬 · 머지 = 워크트리 반납
Workflow 도구 (6번째 기질, skills/discovery-audit/SKILL.md 에서 이미 운영 중) isolation:'worktree' per agent 결정적 JS ( pipeline() / parallel() / agent() / phase() / budget ) 데이터 주도, 수십~수백 없음 — 그래서 PR 생성에서 멈춰야 하고 절대 머지하지 않는다 resumeFromRunId 에이전트당 중간

⚠️ pipeline() 이 기본값이고 parallel() 은 barrier 전용이다. pipeline(items, stage1, stage2…) 는 항목마다 독립적으로 전 단계를 통과시킨다(단계 사이에 barrier 없음) — 소요 시간이 가장 느린 한 항목의 사슬이지 단계별 최댓값의 합이 아니다. parallel()barrier 라 전원이 끝나야 반환한다. 저장소 안 유일한 사례(discovery-audit)가 parallel() 만 쓰는 것은 그 흐름이 실제로 barrier 를 필요로 해서가 아니라 pipeline() 이 없던 시절의 잔재이므로, in-repo 용례를 근거로 parallel() 을 기본값으로 삼지 말 것. barrier 정당화는 §4.2 목록에 있는 사유(교차 항목 dedup/병합 · 집계 판정 · 0건 조기 종료 · 직렬 공유 자원)뿐이다.

Agent Teams 의 브랜치 금지는 기능 부족이 아니라 표현 불가다 — git checkout 은 워크트리 전역 이므로 한 워크트리에서 N teammate 가 N 브랜치에 앉는 상태 자체가 없다. 진짜 브랜치 격리가 필요하면 Orca 디스패치(머지 직렬화 + 사람 승인 포함)나 Workflow isolation:'worktree' 를 쓴다.

4.1 결정 절차 (5문, 첫 일치 우선)#

  1. 경계가 분명한 단일 작업인가? → inline, 위임된 판단 하나면 Task 서브에이전트.
  2. 워커가 읽기 전용인가? → N≤5 이고 역할 단위면 Agent Teams, 아니면 Workflow.
  3. 워커가 자기 브랜치/커밋/PR 을 가져야 하나? → 워크트리 격리 필수, Agent Teams 탈락. 작업 단위가 보드 상태를 가진 ZenHub 이슈면 Orca, 코드 수준 발견 항목이면 Workflow.
  4. N 이 데이터 주도이고 5 초과인가? → Workflow, 그리고 pipeline() 을 기본으로 쓴다. parallel()(barrier)은 §4.2 의 정당화 사유가 있을 때만.
  5. 머지 전에 사람이 승인해야 하나? → Orca(또는 inline). Workflow 스크립트는 PR 생성에서 멈춘다.

"순차 / 변경 없음" 은 정당하고 자주 옳은 답이다. 이 저장소의 사고 이력에서 루프가 너무 많이 돈 원인은 0건, 동시성을 추가해서 생긴 것은 5건이며, 병렬을 추가할 때마다 보상 게이트를 뒤에 덧붙여야 했다. 기질을 올리기 전에 3의 답이 "예" 인지 다시 확인한다.

4.2 barrier(= mode:barrier)가 정당한 자리 — 확인된 목록#

교차 항목 근거가 있는 곳만이다. 나머지는 전부 스트림/파이프라인이다. run.md Step 10.5 CI join · run.md Step 8.7 Critical 통합 · commands/unstuck.md 의 5-페르소나 Disagreements 교차 비교 · cc-quality/commands/feature-qa.md 의 5축 가중 등급 · cc-spec/commands/evaluate.md Stage 3 합의 정족수 · batch.md Phase 2-d 완료성 · discovery-audit 의 Verify→Fix 파일 단위 묶기 · 코드젠 배리어(build_runner / pod:generate) 앞.

4.3 런타임 격리는 6행 전부와 직교한다#

포트·DB·시뮬레이터·브라우저 프로필은 어떤 기질을 골랐는지와 무관하게 plugins/cc-serverpod/skills/serverpod-worktree-parallel/SKILL.mdplugins/cc-dev/skills/parallel-test-env/SKILL.md 가 지배한다. 두 문서의 슬롯 할당은 워크트리 경로/브랜치에서 결정적으로 뽑히므로, 워크트리 하나를 공유하는 Agent Teams teammate 들에게는 쓸 수 없다 — 전원이 같은 슬롯을 계산한다.

할당에는 반납이 짝으로 붙는다. 슬롯이 워크트리 경로에서 뽑힌다는 성질은 반대 방향으로도 성립한다 — 워크트리를 지우면 그 경로로 만들어진 자원(워크트리별 DB·컨테이너·포트 블록·디바이스 슬롯)의 주인을 되짚을 근거가 사라진다. 그래서 격리 자원의 반납은 체크아웃 제거보다 먼저 와야 하고, 제거 시점·안전 판정·소유권(만든 쪽이 회수, 워커의 자기 제거 금지)의 SoT 는 plugins/cc-dev/skills/orca-worktree-lifecycle/SKILL.md 다. isolate:worktree 를 선언한 FORK 는 그 짝이 되는 반납 노드를 흐름 안에 갖는다(commands/batch.mdB2aB2b.r) — 짝이 없으면 width: 는 한 실행 안에서만 유효한 숫자가 되고, 다음 실행은 이미 찬 슬롯 위에서 시작한다.


5. Fan-out Obligations#

  • 분기가 쓰기를 하면 FORK 는 분기별 own: 또는 isolate:worktree 를 선언한다. join 이후의 쓰기는 소유자를 이름으로 밝힌다(공용·생성 파일은 Lead 전용).
  • width: 는 예산이다. cap 때문에 미룬 항목은 deferred 로그에 남긴다. 조용한 절단 금지.
  • 에이전트/세션을 스폰하는 모든 노드에 tier: 를 적는다(skills/agent-teams/SKILL.md → "Effort Routing Convention" 의 Frugal/Standard/Frontier). unset = silent inherit 이며, degree N 팬아웃 에서의 미설정은 기본값이 아니라 N배 승수다.
  • JOIN 기본값은 mode:stream 이다. mode:barrier교차 항목 because: 가 있어야 한다. 꼬리를 줄 세워야 하면 serialize:k 를 붙인다.
  • 기질을 쓸 수 없을 때는 하드 실패하지 않고 순차로 내려간다 — skills/agent-teams/SKILL.md 의 Fallback Principle(기능 차이 없음, 소요 시간만 다름)이 모든 기질의 기본 동작이다.

5.1 작은 실제 예 — commands/batch.md Phase 2–3#

B2a    FORK  독립 형제 child 병렬 착수     isolate:worktree  approve:AskUserQuestion  width:미선언  #  < - cap·deferred 로그 없음
B2a.g  GATE  listChildren(child).known    verdict:known  undet:fail  fail:B2c
B2a.1  ACT   child 처리 (batch 재귀 | run) own:child-worktree  tier:standard
B2b    JOIN  worker_done 수집 → 머지 꼬리   mode:stream  serialize:1  order:arrival  because:myBranch 충돌 방지
B2c    LOOP  stall ladder (child 단위)     contract:L-B2c
B2d    GATE  완료성 Hard Gate (전수 재조회) mode:barrier  because:모든 child 를 한 번에 판정  verdict:openChildrenStatus=== " none "    undet:fail  fail:HALT
B3     ACT   컨테이너 최종 PR + Review/QA  writes:github:pr,zenhub:pipeline
B3.9   GATE  머지 직전 완료성 재검증        verdict:openChildrenStatus=== " none "    undet:fail  fail:HALT

B2a -- >   B2a.g -- >   B2a.1 -- >   B2b -- >   B2d -- >   B3 -- >   B3.9
B2a.g ~~ >   B2b  on:known===false   record:BLOCKED+board       # 조회 실패를 leaf 로 읽지 않는다
B2a.1 == >   B2c  on:child.fail      bound:3rung  invalidates:B2a.1
B2c   ~~ >   B2b  on:rung3           record:BLOCKED( ' unstuck_exhausted ' )+board  # 형제는 계속

읽는 법: 구현은 폭 N 으로 벌어지지만 머지는 serialize:1 로 좁혀지고(도착 순), 완료성 판정만 mode:barrier 다 — 전수 재조회가 교차 항목이기 때문이다. B2dB3.9 는 같은 술어를 두 번 평가하는 별개 게이트이며(그 사이에 CI·리뷰로 수 시간이 흐른다), 통과는 none 하나뿐이다.


6. 파생 뷰는 규범이 아니다 ⚠️#

한 흐름의 규범 선언은 ```flow 블록 하나다. 아래는 전부 그 블록에서 파생된 읽기용 뷰이며, 서로 어긋나면 블록이 이긴다. 각 뷰 근처에 그 사실을 한 줄로 적는다.

파생 뷰상태
단계 표(Step / Phase / Prerequisites 표)비규범 — 사람이 훑는 색인
ASCII 박스 다이어그램비규범 — 값(반복 횟수·타임아웃)을 여기만 적어 두지 않는다
TodoWrite 시드 목록비규범 — 상태 추적용 투영
다른 문서의 흐름 요약(skills/dev/SKILL.md 류)비규범 — 블록을 링크한다

이 규칙이 없으면 같은 흐름이 네 곳에서 각자 진화한다. 실제로 그렇게 됐고, 그게 이 표기법의 존재 이유다.


7. Well-formedness Checklist#

error 는 CI 차단 대상, warn 은 경고 — scripts/check_skill_drift.pyWARN_KINDS 분류와 같은 축이다.

#심각도규칙
1 error 모든 GATE 에 실패 경로가 있다 — fail: 그리고 대응하는 ==> 줄. 실패 경로 없는 게이트는 게이트가 아니다.
2 error 모든 GATEundet: 를 선언한다(기본 fail ). warn 은 §3.1 세 경우 + 노드에 이유 기재 시만.
3 error 모든 LOOP 이 7 필드 전부를 갖고 budget:숫자다. 프로즈 수량은 실패.
4 error 모든 ==>bound: + invalidates: 를 갖고 LOOP head 에 착지 한다. 맨몸 back-edge 금지.
5 error dangling id 없음 — 엣지· fail: · invalidates: 가 가리키는 id 는 모두 선언됨( extern:true 포함), 모든 노드는 진입점에서 도달 가능.
6 warn 모든 ~~>on: + 내구 record: 를 갖는다. 콘솔은 내구가 아니다. 스킵된 노드는 skipped 로 표시하고 pending 으로 두지 않는다.
7 warn 소유권 없는 쓰기 병렬 금지 — 쓰는 FORK 는 분기별 own: 또는 isolate:worktree.
8 warn 교차 항목 because: 없는 barrier 금지. 기본은 mode:stream.
9 warn 모든 WAITtimeout: + on-timeout: ; 에이전트/세션 스폰 노드에 tier: .
10 warn 모든 ASK (그리고 record:ASK… 를 쓰는 ~~> skip-edge)가 unattended: 를 선언한다 — 답할 사람이 없을 때의 선언된 기본값 . AskUserQuestion 은 워커 안에서 무효이므로(§2), 선언이 없으면 그 자리에서 조용히 멈춘다(§0 consumers 중 Orca 워크트리·헤드리스 디스패치가 실제로 도달하는 흐름에 적용 — commands/run.md "Unattended & Approval Contract"가 실 사례).
11error흐름당 규범 선언 정확히 하나. 나머지 뷰는 §6 대로 비규범 표시 + 블록 지시.

8. 도입 순서 — 11개 규칙이 첫날부터 강제되는 것이 아니다#

  1. 이 문서 + 흐름 블록 두 개(run.md, batch.md)만 먼저.
  2. §3.2 fail-open 중 확인된 3건 수정(no-checks 무음 통과, 미정의 finalCheck, --skip-bdd 비내구 기록).
  3. 전제 검사(validateStepPrerequisites)가 back-edge 를 실제로 되돌리게 만든다.
  4. 피드백 엣지에 bound: · invalidates: 주석 채우기.
  5. 루프 계약 7 필드를 기존 루프에 채우기(값은 각 루프 자리에).
  6. 린터에 warn kind 로 먼저 얹는다 — 기존 위반은 baseline 으로 grandfathering.
  7. 위반 수가 0 으로 수렴한 kind 만 error 로 승격.

역순으로 하면(먼저 error 로 켜면) 블록이 없는 문서 전체가 붉게 뜨고, 표기법은 신뢰를 잃는다.

9. 관련 문서#

  • plugins/cc-dev/rules/zenhub-conventions.md — Child Enumeration Contract(tri-state 정본), Pipeline State Contract(보드 반영 전이표·reflectBoardState()·blockIssue()), Work Claim Contract(점유 판정·TTL·인수/해제), Blocked Issue Contract
  • plugins/cc-dev/agents/sequential-workflow.md — 3단 stall ladder(기본 exhaust:)
  • plugins/cc-dev/skills/job-timeout-budget/SKILL.mdbudget: 숫자 산정 SoT
  • plugins/cc-dev/skills/agent-teams/SKILL.md — Effort Routing Convention(tier:), Fallback Principle
  • plugins/cc-dev/skills/discovery-audit/SKILL.md — Workflow 기질의 운영 사례
  • plugins/cc-serverpod/skills/serverpod-worktree-parallel/SKILL.md · plugins/cc-dev/skills/parallel-test-env/SKILL.md — 런타임 격리(직교)