하나의 흐름(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.5
와 S11 ==> S8.5 는
서로를 먹이는 진짜 무제한 순환이다(runStep10_5() → CI 실패 → 수정 → runPrePushGate()
→ re-push).
그리고 세 개의 형제 skip 엣지 중 S8.3 ~~> S8.5 만 콘솔에 기록하고 나머지는 PR body 에 남긴다.
이 블록은 §7 체크리스트를 아직 통과하지 못한다 — 의도적이다. S7.2·S7.7 은 fail: 만 있고 대응
==> 줄이 없어 규칙 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 3BLOCKED('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: throw → PR 생성 차단 (경고 후 계속 금지)
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 다#
- 모든
GATE의 verdict 는 pass / fail / undetermined 세 값이다. undetermined의 기본값은fail이다. 판정 불가 ≠ 통과.-
정본 구현은
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 -c 가 grep 의 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문, 첫 일치 우선)#
- 경계가 분명한 단일 작업인가? → inline, 위임된 판단 하나면 Task 서브에이전트.
- 워커가 읽기 전용인가? → N≤5 이고 역할 단위면 Agent Teams, 아니면 Workflow.
- 워커가 자기 브랜치/커밋/PR 을 가져야 하나? → 워크트리 격리 필수, Agent Teams 탈락. 작업 단위가 보드 상태를 가진 ZenHub 이슈면 Orca, 코드 수준 발견 항목이면 Workflow.
-
N 이 데이터 주도이고 5 초과인가? → Workflow, 그리고
pipeline()을 기본으로 쓴다.parallel()(barrier)은 §4.2 의 정당화 사유가 있을 때만. - 머지 전에 사람이 승인해야 하나? → 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.md 와
plugins/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.md 의 B2a ↔ B2b.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 다 — 전수 재조회가 교차 항목이기 때문이다. B2d 와 B3.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.py 의 WARN_KINDS
분류와
같은 축이다.
| # | 심각도 | 규칙 |
|---|---|---|
| 1 | error |
모든
GATE
에 실패 경로가 있다 —
fail:
그리고
대응하는
==>
줄. 실패 경로 없는 게이트는 게이트가 아니다.
|
| 2 | error |
모든
GATE
가
undet:
를 선언한다(기본
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 |
모든
WAIT
에
timeout:
+
on-timeout:
; 에이전트/세션 스폰 노드에
tier:
.
|
| 10 | warn |
모든
ASK
(그리고
record:ASK…
를 쓰는
~~>
skip-edge)가
unattended:
를 선언한다 — 답할 사람이 없을 때의
선언된 기본값
.
AskUserQuestion
은 워커 안에서 무효이므로(§2), 선언이 없으면 그 자리에서 조용히 멈춘다(§0 consumers 중 Orca 워크트리·헤드리스 디스패치가 실제로 도달하는 흐름에 적용 —
commands/run.md
"Unattended & Approval Contract"가 실 사례).
|
| 11 | error | 흐름당 규범 선언 정확히 하나. 나머지 뷰는 §6 대로 비규범 표시 + 블록 지시. |
8. 도입 순서 — 11개 규칙이 첫날부터 강제되는 것이 아니다#
- 이 문서 + 흐름 블록 두 개(
run.md,batch.md)만 먼저. -
§3.2 fail-open 중 확인된 3건 수정(
no-checks무음 통과, 미정의finalCheck,--skip-bdd비내구 기록). - 전제 검사(
validateStepPrerequisites)가 back-edge 를 실제로 되돌리게 만든다. - 피드백 엣지에
bound:·invalidates:주석 채우기. - 루프 계약 7 필드를 기존 루프에 채우기(값은 각 루프 자리에).
- 린터에 warn kind 로 먼저 얹는다 — 기존 위반은 baseline 으로 grandfathering.
- 위반 수가 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.md—budget:숫자 산정 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— 런타임 격리(직교)