코칭 엔진 v3 — 개발 원자단위 WBS (빌드 플랜 정본)

2026-06-12 · 설계 정본 = coaching-engine-v3.html · 본 문서는 그 설계를 구현 가능한 원자 단위로 분해한 작업 명세서다. 각 원자는 {목적·요구사항·구현 명세·유즈케이스·테스트 항목·완료 기준(DoD)·의존·규모}를 갖는다. 순차 개발 전제 — 원자 ID로 커밋 메시지·테스트 ID를 추적한다(예: 커밋 "B-07: …", 테스트 tests/curriculum.test.ts > B-07-*).

전체 규모 요약 — EPIC 10개 · 원자 131개(12유닛 반복 상세를 펼치면 실질 ~180 작업 단위) · 마일스톤 매핑 D1~D5(부록 1) · 신규 파일 6(lib/curriculum.ts·lib/curriculumUnits.ts·lib/letter-blocks.json·lib/assembleLetter.ts·tests/curriculum.test.ts·tests/assemble.test.ts) + SQL 1 + 기존 수정 5(coachWeekly.ts·cron route.ts·coach.ts·어드민·page.tsx). 불변 원칙: ① 모든 원자는 머지 전 npm test 통과(prebuild 게이트) ② 결정론 로직은 전부 순수 함수로 작성해 단독 테스트 가능 ③ 엣지케이스 발견 시 해당 원자의 테스트에 케이스 추가 후 수정(복리 원칙) ④ LLM이 결정하는 것은 주간 종합(Sonnet)과 자유작문 잔존 표면뿐 — 일간 편지 표면은 전부 코드.
📌 개발 지시 프로토콜 — 이 문서가 작업 지시의 단일 진실이다
📊 진척 대시보드 (원자 완료 시 갱신 — 마지막 갱신: 2026-06-13 D2~4 완료)
EPIC원자완료상태
A 데이터·SQL88✅ 완료 — SQL 실행·프로브 검증(`122abbe`·`0aeb526`)
B 판정 상태기계2626✅ 완료 `0aeb526` — 테스트 78 신규(전체 129)·리플레이가 실버그 2건 적발(이중 진화·goals 미플립)
C 블록 라이브러리2020✅ 완료 `8ba53ae`·`876ad3d` — 풀 431(제작 워크플로 4런 수확·적대검수 6렌즈 26삭제·린터 0 이슈·prebuild 게이트)
D 조립기1212✅ 완료 `8ba53ae`(+`876ad3d` D-05 문장예산 보강) — 테스트 34·실풀 252회 조립 폴백 0·경고 0
E 주간 포트폴리오1010✅ 완료 `763aa94`(E-01은 `0aeb526`) — goals 산출·온보딩 게이트·피로 캡·크론 E-07 배선(부분 — 메모 신호·진도·주차는 H-02)
F 일간 전개1010✅ 완료 `763aa94`·`fecd24b` — decideDailyV3 결정표·시급 3종·10일 통주(F-09 플래그 분기는 H-01과 함께 검증)
G 질문 정렬1010✅ 완료 `763aa94` — 측정공백·쿨다운/캡·파서·답변 블렌딩 6유닛·피드백 루프 4일 통주. ⭐B-04 잠복버그(매핑 키 비교) 수정
H 컷오버1211✅ 코드 완료 `87b9747`(플래그·v3 메인 루프·회고·reuse 보존·어드민 칩/진도 보드) — H-11만 ⏸ 운영 절차 대기(아래 컷오버 절차)
I 리플레이·지표88✅ 완료 `7ed2689`·`7730da2` — 30가정 통주 prebuild 게이트(suite 0.7s=I-08 충족)·아린 14통 4사고 재현 0·TH 보정 불요 판정. 리플레이+정독이 통합 버그 5종 적발(원장 스코프·intro 재등장·lowdata 고갈·동일 조합·사실-유닛 결)
J 운영60⬜ 컷오버 후(D5+) — 단 J-01/J-02/J-05의 원료(v3 ctx.llmCalls·경고 issues·폴백 플래그)는 H-02가 이미 기록
합계131124✅ D1~D5 코드 완료(테스트 229·빌드 그린) → 남은 것: H-11 카나리아 컷오버(운영) + J(컷오버 후)
🚀 H-11 — 컷오버 절차(준비 완료 · 운영 대기 2026-06-13)
  1. 게이트 확인(완료): I-06 수치 게이트 전부 통과 — 30가정×14일 + 아린 실데이터 14통(4사고 재현 0·블록/조합 중복 0·재서술 0·폴백 0·LLM 0콜). 매 배포 prebuild가 같은 리허설을 재실행.
  2. 아린 카나리아: Vercel env COACH_V3_CHILDREN=43942d34-b339-4bbd-978a-ec3f6a877031 설정 → 다음 새벽 크론부터 아린만 조립식. QA는 /api/cron/coach?child=<id>&force=1 즉시 실행 + 어드민 스레드 🧱조립식 칩·/admin/curriculum 진도 보드 확인.
  3. 3일 모니터: 어드민 칩(폴백·시급·경고)·cron_runs.issues(v3 경고/폴백/진도 저장 실패)·편지 육안. 이상 시 env 제거=즉시 레거시 복귀(데이터 additive·무손상).
  4. 전체 ON: COACH_V3=1주중(월~목) 아침 크론 전(이사님 확정·주말 회피). 48h 모니터 후 안정 선언.
  5. 각 단계 일지 기록. 컷오버 후 J 에픽(모니터 집계·문서 동기화 J-03: plan-engine 현행 절 v3 갱신·구 api/coach 410 검토).
🔧 구현 비고(2026-06-13 — 명세 대비 확정 사항, 문서≒코드 정합)
목차

EPIC A — 데이터 모델·SQL D1

원칙: 전부 additive(기존 컬럼·테이블 무변경), 멱등 DDL(if not exists), RLS는 부모 비노출(§13 원칙 유지 — 진도·목표는 내부용, 부모는 편지로만 체감).

A-01 · curriculum_progress 테이블 DDL SQL · 0.5h

목적자녀×유닛별 진도 상태의 단일 진실. "지금 상태가 어느 정도인지 DB로 저장"(이사님 요구).
명세sql/2026-06-12_curriculum.sql에 작성:
유즈케이스①크론이 매일 판정 후 upsert ②주간 종합이 포트폴리오 선발 시 read(미이수·재발 우선) ③어드민 진도 보드(H-09) read ④재발 모니터(J-01) read.
테스트A-01-1 DDL 멱등(2회 실행 무에러 — 실행은 이사님, 검증은 컬럼 프로브 curl) · A-01-2 PK 충돌 시 upsert 갱신 확인 · A-01-3 CHECK 위반 insert 거부.
DoDSQL 파일 커밋 + 이사님 실행 + REST 프로브로 컬럼 7종 확인(점심 사고 때처럼 실행 확인 없이 다음 단계 진행 금지 — weekly_coaching.sql 교훈).
의존없음(첫 원자).

A-02 · 인덱스·갱신 트리거 SQL · 0.2h

명세create index if not exists idx_cp_status on curriculum_progress(child_id, status);(활성·재발 조회용). updated_at은 앱이 명시 갱신(트리거 미사용 — 기존 테이블들과 일관, supabase 트리거 관리 부담 회피).
테스트A-02-1 EXPLAIN으로 status 조회가 인덱스 사용(선택 — 수동).
DoDA-01과 같은 SQL 파일에 포함.

A-03 · RLS 정책 — 부모 비노출 SQL · 0.3h

목적§13 원칙: '수업·진도·목표' 단어와 데이터는 부모에게 절대 노출 금지 — 부모는 자연스러운 편지만 본다.
명세alter table curriculum_progress enable row level security;select 정책을 만들지 않는다(weekly_plans와 동일 패턴: 정책 없음=anon/auth 접근 0행, 서버(서비스/세션 서버클라이언트)만 접근). insert/update 정책도 미생성 — 크론은 서버 권한.
유즈케이스악의/호기심 사용자가 REST로 curriculum_progress 조회 → 0행.
테스트A-03-1 publishable 키로 select → 0행(기존 scripts/rls-audit.mjs에 테이블 추가) · A-03-2 서버 경로 upsert 성공.
DoDrls-audit 통과 로그.
의존A-01.

A-04 · weekly_plans.goals 컬럼 추가 SQL · 0.2h

목적주간 닻이 단일 lever → 목표 2~3개 포트폴리오를 갖는다.
명세alter table weekly_plans add column if not exists goals jsonb; — 스키마(E-01): [{"unit_id":"table-stage","priority":1,"source":"weekly_llm","status":"focus"},{"unit_id":"hunger-rhythm","priority":2,"status":"standby"},{...3순위 optional}]. status ∈ focus(오늘의 주력)·standby(피벗 대기)·stopped(이번 주 중단, stop_reason은 curriculum_progress에).
테스트A-04-1 컬럼 프로브 · A-04-2 goals 없는 구닻 read 시 null 허용(하위 호환 — E-09 heal이 보충).
DoD실행 확인 프로브.
의존A-01과 동일 SQL 파일.

A-05 · 레거시 호환 전략 — lever ↔ goals 병행기 설계 · 0.3h

요구컷오버(H-11) 전까지 구코드(lever 단일)와 신코드(goals)가 같은 테이블을 읽어도 안 깨질 것.
명세신코드 작성 규칙: goals 생성 시 budget.lever도 1순위 goal의 레버 축으로 계속 채운다(구코드 호환). 읽기 규칙: goals 없으면 lever 1개를 goals 1개로 승격하는 변환 함수 goalsOf(anchor): Goal[](coachWeekly에, 순수 함수).
테스트A-05-1 goals null+lever=environment → goalsOf가 [table-stage focus] 반환 · A-05-2 goals 존재 시 그대로 반환 · A-05-3 양쪽 다 없으면 [](관찰 주).
DoDtests/curriculum.test.ts에 3케이스 그린.
의존A-04, B-02(unit↔lever 매핑).

A-06 · 블록 저장소 결정 — 정적 JSON 채택 설계 · 0.2h

결정블록은 DB가 아니라 리포 내 정적 lib/letter-blocks.json. 근거: ①블록=코드와 같은 수명(검수·테스트·버전이 git으로) ②prebuild 테스트(C-19 린터)가 블록을 빌드 시점에 전수 검증 가능 ③운영 중 무단 변경 차단. DB화는 비개발자 편집 needs 생기면 후속(J-03에 기록).
테스트(C-19에서) 스키마 전수 검증이 prebuild에 물림.
DoD본 문서 결정 기록 + v3 정본 §4와 일치.

A-07 · 질문-증거 저장 설계 — daily_questions.context 확장 설계 · 0.3h

목적보라 박스 질문의 답이 유닛 evidence로 적립되려면 "이 질문이 어느 유닛의 어느 신호를 측정했는지"가 저장돼야 함.
명세신규 테이블 없이 기존 daily_questions.context(jsonb)에 추가: {"unitProbe":{"unit_id":"table-talk","signal":"objectTalk","step":2}}. 답변 파서(G-04)가 q_date·answer·unitProbe를 읽어 evidence 적립. ICFQ(context.icfq)와 키 분리로 공존(G-05).
유즈케이스유닛8(식탁의 말) 활성인데 태그 표본 부족 → 질문 "오늘 식탁에서 어떤 말을 건넸어요?" 발행(context.unitProbe 기록) → 칩 답변 → 다음 크론이 objectTalk 카운터 +1.
테스트G-04 테스트에 포함(A-07-1 unitProbe 왕복 직렬화).
DoD스키마 변경 0으로 확정(컬럼 추가 불필요 — 이사님 "컬럼까지 해야 할 정도인가" 원칙 부합).

A-08 · 마이그레이션 실행·롤백 절차 문서 · 0.2h

명세SQL 파일 헤더에 명시: 실행=Supabase SQL Editor 1회(이사님)·pbcopy 명령 제공. 롤백: 컬럼/테이블 drop 문 주석으로 동봉(단 운영 데이터 생기면 drop 금지 — status를 not_started로 일괄 update하는 소프트 리셋 쿼리 제공). 실행 후 본 세션이 REST 프로브로 검증(A-01 DoD)하고 일지에 기록.
테스트해당 없음(절차).
DoD일지에 "실행 확인" 라인.

EPIC B — 유닛 레지스트리·판정 상태기계 D1

전부 순수 함수(lib/curriculumUnits.ts=정적 정의, lib/curriculum.ts=판정·전이 로직). LLM 0콜. 입력은 크론이 이미 가진 것만: meal_logs 행(trailing 7~28일)·daily_questions 답·기존 구조화 칩(environment·autonomy·texture·meal_time)·메모. 모든 임계는 B-03 상수 테이블에 모아 리플레이(I-03)로 보정 가능하게.

B-01 · 타입 정의 코드 · 0.5h

명세lib/curriculum.ts 상단:
테스트B-01-1 tsc 통과 + UnitId 12종 = A-01 CHECK 목록과 일치하는 정합 테스트(문자열 배열 비교 — 명단 단일 소스 원칙).
DoDtsc 0 · 정합 테스트 그린.

B-02 · 유닛 레지스트리 골격 — lib/curriculumUnits.ts 코드 · 1h

목적12유닛의 선언적 정의(설계 정본 §2 표를 코드로 옮김). 가드 레지스트리 재편의 실체 — 유닛별 금지·검수 기준이 여기 모인다.
명세export const UNITS: Record<UnitId, UnitDef>. 각 유닛: 사다리 단 정의(step 0..maxStep, 단별 부모 행동 한 줄), 발동 신호(trigger: 주간 종합이 이 유닛을 후보로 올리는 조건 — 예 table-stage는 envBadPct≥0.4·envCount≥4), lever 매핑(A-05 호환), 관측 질문 풀(G-02 참조 키). 온보딩 게이트 필드 minWeek(E-05): 신뢰(1주차)=코칭 강도 제한 유닛 표시.
유즈케이스주간 종합이 candidateUnits(signals, progressRows)로 후보를 얻고, 일간이 UNITS[unit].advance로 판정한다.
테스트B-02-1 12유닛 전부 maxStep≥2·probes≥1·signals≥1 보유(완결성 스캔) · B-02-2 lever 매핑이 기존 LEVER_SCENARIO와 모순 없음.
DoD완결성 테스트 그린.
의존B-01.

B-03 · 임계 상수 테이블 코드 · 0.5h

목적모든 매직 넘버를 한 곳에 — 리플레이 보정(I-03)·향후 A/B의 단일 진실.
명세export const TH = { stallDays: 6, maintenanceWeeks: 1, streakWeeksToMaster: 2, relapseWindowDays: 14, envTableStep1: 0.4, envTableStep2: 0.6, exposurePerWeek: [2,3], exposureTotal: [8,15], mealOver30Cap: 0.3, selfPctTarget: 0.5, familyMealsPerWeek: 5, negTagCap: 0.2, ... } — 설계 정본 §2 표의 수치 전부. 각 키에 출처 주석(USDA NESR·Satter·WIC·자체).
테스트B-03-1 TH 키가 B-04~15에서 참조하는 키 집합과 일치(미사용/누락 키 0 — 데드 상수 방지).
DoD그린 + 주석 출처 전수.

B-04~B-15 · 유닛별 신호 추출기 12종 코드 · 각 0.5~1h

공통 명세각 유닛에 extract_<unit>(rows: FactRow[], answers: ProbeAnswer[], today: string): Evidence — 입력은 기존 컬럼만(meal_logs: log_date·slot·menus·refused·note·environment·autonomy·texture·meal_time·place·ate_well + 질문 답). 출력 evidence는 날짜 단위 집계(M6 교훈: 행 단위 부풀림 금지 — 하루 2슬롯=1일). 전부 결정론·시계열 라벨(coachFacts recurrenceLabel 재사용).
원자유닛evidence 키(예)핵심 계산 명세엣지(테스트 필수)
B-04pressure-offnegTagPct7d·pressureMemoDays·icfqStressQ5 긍/부정 태그 비율(질문 답), 메모에서 압박 키워드(한 입만·먹어야·혼냈) 등장 '일수', ICFQ stress 최근값메모 0건 주(분모 0)→null 유지·판정 보류 / 긍정 단어 포함 부정문
B-05hunger-rhythmpreMealSnackDays·gapOkPct간식 slot 시각이 없으므로 proxy: 같은 날 간식 slot 수·저녁 직전 간식 메모 + 간식 엔진 meal_time 간섭 신호 재사용(evaluateSnacks 산출)간식 미기록 가정(과소 추정 명시 — '기록 없음≠없음' 라벨)
B-06table-stageenvTablePct7d·tableDays·streakWeeksenvironment 칩 기반 — table/(전체 env 기록) 7일 창 + 주 단위 streak(연속 충족 주 수)env 기록 3건 미만 주=판정 보류(G-01이 질문 보충 발동)
B-07exposure-savingstargetExposeDays·refuseTrend·selfEatCount타깃 식재료가 ingredients에 등장한 '일수'(주 단위), 거부율 추이(노출곡선 4회 변화 안내용), 자발 섭취(ate_well true+타깃) 카운트타깃 미정 시 no-op / 사촌 일반화(같은 식품군 catOf)는 별도 키
B-08fullness-respectover30Pct·forceMemoDaysmeal_time≥30 비율 + 완식 강요 메모 키워드 일수meal_time 기록 적은 가정 보류
B-09parent-modelfamilyDinnerDays·modelAnswerYes저녁 home 끼니 기록 일수(가족식 proxy) + 질문 답("같이 드셨어요?") 카운트proxy 한계 명시 — 질문이 1차 신호(G-02)
B-10no-bargainbargainMemoDays·dessertNeutralDays거래 키워드(먹으면·줄게·사줄게) 메모 일수 — 감소 추세가 진전인용된 과거 회상 메모 오탐(최근 2일 메모만 1차)
B-11table-talkobjectTalkPct·banWordDays거의 전적으로 질문 기반(G-02 probe 답 적립) + 금지어 메모 스캔표본 4건 미만 주 판정 보류
B-12sensory-texturetexStepNow·texUpDays·regressDaystexture 칩 최빈값의 주간 이동(puree→mashed→finger→table 서열화) + 후퇴 감지칩 혼재 주(50:50)는 보수적으로 하위 단계로
B-13food-bridgenewFoodCount·chainPos신규 식재료(28일 내 첫 등장) 카운트 + 사슬 위치(F-02가 갱신하는 step과 동기)기관 급식발 신규는 place 분리 집계(M5 교훈)
B-14autonomy-partselfPct7d·roleAnswerYesautonomy 칩 self 비율 + 역할 질문 답칩 4건 미만 보류
B-15link-rhythmdcRefuseHomeRetry·retryGapDays기관 거부(place=daycare·refused) 식재료가 N일 내 home ingredients에 재등장한 매칭 수(코드 계산 — 기존 P2 연계 로직 재사용)비등원 가정 no-op
공통 테스트각 유닛 3케이스 고정: ①진전 시나리오(합성 rows→evidence 기대값 일치) ②보류 시나리오(표본 부족→null 키) ③오탐 방지(엣지 칼럼). 테스트 ID B-0X-1..3. 총 36케이스.
DoD36케이스 그린 · 각 추출기 JSDoc에 evidence 키 사전 명시.
의존B-01·B-03·coachFacts(FactRow 타입 재사용).

B-16 · evidence 일일 병합기 코드 · 0.5h

명세mergeEvidence(prev: Evidence, today: Evidence, todayStr): Evidence — 카운터는 재계산 대체(스냅샷 방식: 7일 창 지표는 매일 새로 계산해 덮어씀 — 누적 오염 방지), 일수 배열(hitDays)은 union 후 28일 컷, last_signal_at 갱신 조건 = 유닛의 1차 신호가 오늘 관측됨.
유즈케이스크론이 추출(B-04~15)→병합→A-01 upsert.
테스트B-16-1 스냅샷 덮어쓰기 · B-16-2 hitDays 28일 컷 · B-16-3 신호 없으면 last_signal_at 불변.
DoD그린.

B-17 · 사다리 단(step) 정의·전이 코드 · 0.5h

명세UnitDef.steps: [{step:1, behavior:'하루 한 끼 화면 끄기', passWhen:(e)=>e.envTablePct7d>=TH.envTableStep1, holdWeeks:1}, {step:2, behavior:'주 5끼+', passWhen: ≥TH.envTableStep2, holdWeeks:2}, ...]. 전이 함수 nextStep(def, row): number — passWhen이 holdWeeks 연속 충족(주 단위 streak은 evidence.streakWeeks)일 때만 +1. 한 번에 한 단만(점프 금지 — 물 흐르듯 원칙).
테스트B-17-1 충족 1주차(holdWeeks=2)는 미전이 · B-17-2 2주 연속 충족 시 +1 · B-17-3 max 단에서 불변.
DoD그린.

B-18 · 진전 판정 isProgressing 코드 · 0.3h

명세isProgressing(def, row, today): boolean = 최근 TH.stallDays일 내 1차 신호 관측(last_signal_at 기준) 또는 step 전진 발생. 일간 선택기(F-02)의 '딥다이브 계속' 조건.
테스트B-18-1/2 경계(5일/7일).

B-19 · 정체 판정 isStalled 코드 · 0.3h

명세last_signal_atTH.stallDays(기본 6일) 이상 과거 그리고 그 기간 코칭이 실제 나갔음(편지 블록 원장에서 해당 유닛 블록 ≥3회 — D-03 원장 참조: "가르쳤는데 안 됨"과 "안 가르쳤음"을 구분). 후자는 정체 아님(코칭 재개 대상).
유즈케이스"개선이 전혀 안 되는 목표는 스탑"(이사님) — F-02가 이 판정으로 피벗.
테스트B-19-1 신호無+코칭有=정체 · B-19-2 신호無+코칭無=비정체 · B-19-3 경계일.

B-20 · 졸업 판정 isMastered — 유지 주 테스트 코드 · 0.5h

명세조건 AND 3: ①최종 단 passWhen 충족 streak ≥ TH.streakWeeksToMaster(2주) ②유지 주: 직전 TH.maintenanceWeeks(1주)간 이 유닛 블록이 편지에 0회(코칭 없이 유지 — D-03 원장으로 확인) ③그 유지 주에도 신호 유지. 충족 시 status=mastered·mastered_at 기록.
유즈케이스"달성되면 더 이상 안 가르치고"(이사님). maintenance 상태가 유지 주를 표현: 최종 단 도달 → maintenance(코칭 중단·관찰만) → 1주 후 판정 → mastered or 재코칭.
테스트B-20-1 유지 주에 블록 나가면 판정 리셋 · B-20-2 유지 주 신호 붕괴→active 복귀 · B-20-3 정상 졸업.

B-21 · 재발 감지 isRelapsed 코드 · 0.5h

명세mastered 유닛 한정: UnitDef.relapse 조건(설계 §2 표 — 예 table-stage: 2주 연속 envTablePct<0.4) 충족 시 status=relapsed·relapse_count+1. 재개 규칙: 처음(step 0)이 아니라 max(1, 최종단-1)부터 — "무너지면 다시 감지해서 코칭"하되 기억을 존중. 재발 유닛은 주간 포트폴리오 선발에서 우선순위 가산(E-03).
테스트B-21-1 임계 미만 1주는 비재발 · B-21-2 2주 연속 재발+카운트 · B-21-3 재개 step 계산.

B-22 · 피벗 선택기 pickPivot 코드 · 0.3h

명세pickPivot(goals, progressMap): UnitId | null — focus가 정체(B-19)일 때 standby 중 priority 순으로 판정 보류 아닌(신호 측정 가능한) 유닛 선택. 전부 불가면 null(F-08 plateau). 피벗 시 구 focus는 stop_reason='stalled'·status='pivoted'(주간 재진단 때 부활 후보).
테스트B-22-1 standby 1개 정상 피벗 · B-22-2 전부 보류→null · B-22-3 stop_reason 기록.

B-23 · 상태기계 통합 advanceProgress 코드 · 1h

목적크론이 부르는 단일 진입점 — 하루치 판정 전체를 결정론으로.
명세advanceProgress(p: { goals; progressMap; rows; answers; blockLedger; today }): { updates: ProgressRow[]; decision: DailyDecision }. 내부 순서: 추출(B-04~15, goals 유닛만)→병합(B-16)→재발 스캔(B-21, mastered 전체)→focus 판정(전진 B-17/진전 B-18/정체 B-19→피벗 B-22/졸업 후보 B-20)→DailyDecision = { unit, step, mode: 'advance'|'deepen'|'pivot'|'maintain'|'celebrate'|'observe', pivotTo? }. 상태 전이 매트릭스를 JSDoc 표로 명문화(7상태×이벤트 — 불법 전이는 throw가 아니라 무시+경고 카운트).
유즈케이스①어제 A 실행 관측 → mode=advance(다음 단 B) ②이틀 정체 → mode=pivot ③유지 주 → mode=maintain(편지에서 이 유닛 침묵) ④재발 → goals에 재투입 제안 플래그.
테스트B-23-1..8 전이 매트릭스 대표 8경로(리플레이 I-05가 다일 검증 보완).
DoD8케이스+JSDoc 매트릭스.
의존B-16~22.

B-24 · 안전 인터럽트 연동(S3 수술) 코드 · 0.5h

명세인터럽트(progress-celebrate·neophobia-arfid-watch·low-data-gap)는 진도를 멈추지 않는다 — 그날 편지만 인터럽트가 가져가고 advanceProgress는 정상 수행(판정·evidence 적립 계속). 인터럽트 연속 발화 캡: 같은 인터럽트 id가 직전 2일 발화면 3일째 억제(축하·적신호 제외 조건은 기존 selectScenario 예외 유지하되 발화 횟수 원장을 context에 기록해 사실상 연속 3일 차단 — S3의 '7일 연속 축하' 봉쇄).
테스트B-24-1 인터럽트 날에도 evidence 적립 · B-24-2 3일째 억제 · B-24-3 적신호는 억제 예외(안전 우선).

B-25 · 단위 테스트 묶음 — tests/curriculum.test.ts 테스트 · 1h

명세B-04~24의 케이스 전부(≈60)를 한 파일로. 헬퍼: mkRow()(coach-facts.test.ts 패턴 재사용)·mkProgress(). 명명 규칙 describe('B-17 사다리') — 원자 ID 추적.
DoDprebuild 게이트 포함 그린.

B-26 · 진도 리플레이 불변식 테스트 · 0.5h

명세tests/replay.test.ts 확장: 합성 14일 시나리오 3종(순항 졸업/정체 피벗/재발 재개)을 advanceProgress로 통주(通走)하며 불변식 검증 — ①status 역행 금지(mastered→active는 relapse 경유만) ②step 점프 금지 ③같은 날 이중 전진 금지 ④피벗 후 standby가 focus로.
DoD3시나리오×4불변식 그린.

EPIC C — 블록 라이브러리 D2~3

편지 표면의 유한화 본체. 블록 = 사전 작성+적대 검수된 한국어 문장 뭉치(1~3문장). 제작은 멀티에이전트(검증된 파이프: 커뮤니티 98·팁 36·SCEN 문구), 저장은 lib/letter-blocks.json(A-06), 검증은 린터(C-19)가 prebuild에서 전수.

C-01 · 블록 JSON 스키마 설계+코드 · 0.5h

명세{ id: "table-stage.intro.2", unit: UnitId|'common', stage: BlockStage, variant: number, text: string, slots: string[], tone: 'warm'|'praise'|'empathy', minStep?: number, requires?: string[], forbids?: string[] }
테스트C-01-1 스키마 타입 tsc · C-19 린터가 전 블록 검증.
DoD타입+샘플 블록 5개로 조립기 스파이크 통과.

C-02 · 슬롯 문법·렌더러 코드 · 0.5h

명세renderBlock(block, ctx: {name, fact?, next?, food?}): string — 단순 치환 + 한국어 조사 처리(이/가·은/는·을/를: 받침 유무 판별 함수 josa(word, pair) — progressNote '아침를' 사고의 일반해). 미충족 슬롯 있으면 null 반환(조립기가 다른 변형 선택).
유즈케이스"{name}{이가} 어제 {fact}." → "아린이가 어제 아침을 화면 없이 식탁에서 먹었어요."
테스트C-02-1 받침 4조합(이/가·을/를) · C-02-2 슬롯 미충족→null · C-02-3 이중 치환 없음.
DoD그린.

C-03 · 블록 검수 체크리스트 — 가드 레지스트리 재편 설계 · 1h

목적기존 런타임 4중 가드(정적 정규식 13·동적 detForbid·프롬프트 산문·검증자 항목)를 제작 시 1회 검수 기준으로 이관 — 적대감사 3b의 실행.
명세체크리스트 항목(블록 텍스트에 적용): ①의학 단어·체중·점수·'미션' 0 ②매운·튀김·초가공 권유 0 ③섞기 표현은 food 유닛 mix 단계 블록에만 ④과일↔끼니 페어링 0 ⑤시점·횟수 숫자 0(숫자는 {fact} 슬롯으로만 유입) ⑥'항상·매일·계속' 패턴 단정어 0(사실 라벨이 슬롯으로 옴) ⑦아이 주체높임 0 ⑧지시·압박 어조 0 ⑨한 블록 한 행동 ⑩길이 20~90자. 이 목록이 C-04 제작 프롬프트와 C-19 린터의 공통 소스(coach.ts의 가드 정규식 재사용해 린트).
테스트C-19에 기계화.
DoD체크리스트가 제작 프롬프트·린터 양쪽에 동일 적용 확인.

C-04 · 블록 제작 워크플로 설계(멀티에이전트) 설계 · 1h

명세유닛별 제작 에이전트 프롬프트 템플릿: 입력={유닛 정의(레지스트리)·근거 이론 발췌(35이론 표)·체크리스트(C-03)·기존 SCEN_OPEN/ACT·MOVE 문구(톤 기준)·금지 예시}. 출력=stage 8종×변형 3~5(JSON, C-01 스키마). 제작 후 적대 검수 에이전트가 체크리스트로 공격(위반 블록 리젝→재작성). 변형 간 도입 어절·문형 다양성 의무(같은 첫 어절 금지 — 조립 후 반복감 방지).
유즈케이스"/v3 블록 제작 table-stage" 1회 실행 → 32~40블록 산출 → 린터 통과분 머지.
테스트산출물이 C-19 린터 통과.
DoD템플릿 확정 + 파일럿 1유닛 산출 검수 통과.

C-05~C-16 · 유닛 12종 블록 세트 제작 콘텐츠 · 각 1~1.5h(에이전트 병렬)

공통유닛당 8 stage × 3~5변형 ≈ 28~40블록, 12유닛 합계 ≈ 400±80블록. 각 유닛 제작 지침의 유닛 고유 항목:
원자유닛콘텐츠 핵심 지침(근거 이론)고유 금지
C-05pressure-offsDOR 역할 분리·"어른은 방향 아이는 속도"·환호도 압박(담담한 인정)"한 입만" 류 문구가 예시로도 등장 금지(따라 말할 위험)
C-06hunger-rhythm2~3시간 간격·식간 물·컷오프 30분(WIC·식약처 #5)"굶기세요" 어감 0 — '배고픔이 살아나게'
C-07table-stage화면·자리·시작 의식(식약처 #6·7) — 관찰 사실 언급은 intro 블록에만 존재화면 단어가 intro 외 stage 블록에 등장 금지(주제 수렴 사고의 구조적 봉쇄)
C-08exposure-savings노출 적금·곡선(4회 변화·5~6 정체 예고)·taste-first 티스푼·감각 위계 언어"꼭 먹여야" 0 · 횟수 숫자는 {fact}로만
C-09fullness-respect적게 담고 기다리기(홍콩 FHS)·30분 자율 종료"남기면 안 돼" 0
C-10parent-model모델링=단일 최강 예측변수(HabEat)·가족식은 관계로 정의(배달 OK)"주 5회 의무" 압박 어조 0
C-11no-bargain보상·수단화·제한 3역효과·디저트 중립화금지 식품 악마화 0
C-12table-talk객체 중심("당근이 어땠어?")·금지어 3종 대체문정체성 라벨링 예문 금지
C-13sensory-textureSOS 6분류(라이선스: 32단계 상세 표기 금지)·후퇴 룰·Big Body 웜업'치료' 단어 0
C-14food-bridge체이닝 4축 한 축씩·찍어먹기(맛-맛 연합) — {food}는 화이트리스트만목록 밖 음식명 0(블록에 음식명 하드코딩 금지)
C-15autonomy-part둘 중 고르기→셀프서빙→역할(SDT)"흘리면 안 돼" 0
C-16link-rhythm기관 거부→집 저압력 재노출·격일 리듬(권태 효과)기관 비판 어조 0(P2)
테스트유닛별: 린터 전수 + 변형 간 첫 어절 중복 0 + 금지 항목 스캔.
DoD12유닛 × 린터 그린 · 총 블록 수 350+.
의존C-01~04.

C-17 · 공용 블록(unit='common') 콘텐츠 · 1h

명세유닛 무관 블록: plateau(쉬어가기 칭찬 — 태도·꾸준함, '충분' 금지) · celebrate(전환 축하 — 인터럽트용) · lowdata(기록 공백 따뜻한 권유) · graduate(졸업 — "이제 몸에 붙었어요", 유닛명 비노출) · pivot-bridge(전환 연결문 — "이번엔 ○○ 쪽을 먼저 잡아볼게요") · opener-weekday(보조 도입 변형 12종 — 날씨·요일 무관, 데이터 무관 워밍 문장). 각 4~6변형 ≈ 35블록.
테스트린터 + plateau 블록에 '충분·N가지' 0 스캔.
DoD그린.

C-18 · 적대 검수 패스 검증 · 0.5h(에이전트)

명세전체 블록 풀에 6렌즈 적대 에이전트(괴식·압박·의학·반복감·슬롯 오용·톤) 1회 — 위반은 수정이 아니라 삭제 후 재제작(풀 오염 방지). 발견 0이 아니라 발견→처리 기록이 산출물.
DoD검수 리포트(발견 N건·처리)와 최종 풀 확정.

C-19 · 블록 린터 — tests/blocks.test.ts 테스트 · 1h

명세prebuild에서 전 블록 기계 검증: ①스키마 적합(zod 없이 수동 가드 — 의존성 최소) ②C-03 체크리스트 정규식(coach.ts의 letterDeterministicBad 부분 재사용 + 블록 전용: 화면 단어 stage 제한(C-07)·숫자 리터럴 금지·길이 20~90) ③슬롯 참조 무결성(text의 {slot}이 slots 배열과 일치) ④유닛×stage 커버리지(각 유닛 8 stage 모두 변형≥3) ⑤변형 간 유사도: 같은 (unit,stage) 내 letterSimilarity ≥0.6 쌍 금지(복붙 변형 방지) ⑥전 블록 첫 15자 중복 캡(전체 풀에서 동일 도입 ≤2).
유즈케이스블록 1개 추가 커밋 → 린터가 자동 검수 → 통과 못 하면 배포 불가(복리: 콘텐츠도 코드처럼).
DoD린터 그린 상태로 풀 머지.

C-20 · 블록 증식 절차(엣지케이스→블록) 프로세스 · 0.3h

명세운영 중 "이 상황에 맞는 블록이 없다"(D-11 폴백 발동 로그) 또는 표현 개선 제보 → ①해당 상황을 fixture로 박제 ②블록 1~3개 제작(C-04 워크플로 단건 모드) ③린터 통과 ④폴백 로그 소거 확인. 일지·메모리에 기록 규칙(J-04).
DoD절차가 v3 정본·본 문서에 기록됨.

EPIC D — 조립기 assembleLetter D2~3

신규 lib/assembleLetter.ts. 입력=DailyDecision(B-23)+사실 카드(coachFacts)+진도+블록 풀. 출력=편지 본문+oneliner+context 스냅샷. LLM 0~2콜(윤문 옵션·기본 0).

D-01 · 조립기 시그니처·편지 구조 규격 코드 · 0.5h

명세assembleLetter(p: { decision; unitDef; progress; factCards; noteCards; blocks; blockLedger: string[](최근 3일 블록 id); name; daySeed; polish?: boolean }): { letter; oneliner; usedBlocks: string[]; factUsed: string|null }. 편지 구조 규격(mode별 블록 시퀀스 — 결정론):
테스트D-01-1 mode 6종 각 시퀀스 산출 스냅샷.

D-02 · 블록 선택 결정론 코드 · 0.5h

명세(unit,stage) 후보 변형 중: ①blockLedger(3일) 제외 ②requires 슬롯 충족 필터 ③남은 것에서 (daySeed+cidHash) 시드 회전. 후보 0이면 D-11 폴백. 같은 입력=같은 출력(재현성 — QA·리플레이 전제).
테스트D-02-1 원장 제외 · D-02-2 시드 재현성 · D-02-3 requires 필터.

D-03 · 비반복 원장 — context.blocks 코드 · 0.5h

명세편지 context에 blocks: string[](사용 블록 id) 저장. 크론이 최근 3일 편지에서 수집해 blockLedger로 주입(기존 recentPlans 수집 패턴과 동일 — reuse 경로도 보존(S6 교훈 적용)). 보장: 같은 블록 id 3일 내 재사용 0 — 확률이 아니라 집합 차감.
테스트D-03-1 3일 원장 차감 · D-03-2 reuse 경로 보존 · D-03-3 리플레이 6일 블록 중복 0(I-05).

D-04 · 사실 인용 1회 원장 — 재서술 구조 봉쇄 코드 · 0.5h

명세관찰 사실(사실 카드의 진단성 항목: 환경 통계·점심 추세 등)은 context.factsCited: string[](카드 키) 원장으로 추적 — 유닛 활성 기간 중 1회만 {fact} 슬롯에 진단성 카드 주입 가능(intro/observe). 이후 {fact}는 '어제 단일 사실'(어제 끼니 환경 등 일일 카드)만 허용. 진단성/일일성 구분은 카드 생성부(coachFacts)가 kind: 'diagnosis'|'daily' 태그로 제공(coachFacts 확장 원자 — 카드 객체화 {text, kind}, 기존 호출부 호환 어댑터 포함).
유즈케이스"화면 식사 93%"는 주 1회(intro)만 — 이후 편지엔 "어제 저녁은 식탁에서"(daily)만. 스크린샷 사고("태블릿 사실 반복")의 구조적 해법.
테스트D-04-1 진단 카드 2회째 주입 시도→차단 · D-04-2 daily 카드는 매일 허용 · D-04-3 원장 reuse 보존.

D-05 · 연결문·문장 결합 규칙 코드 · 0.3h

명세블록 join은 단순 공백·줄바꿈이 아니라: 블록 말미 종결어미와 다음 블록 도입의 충돌 검사(같은 어미 '—요.' 3연속이면 변형 재선택), 총 길이 캡 380자(현 편지 규격), 문장 수 3~5.
테스트D-05-1 어미 3연속 회피 · D-05-2 길이 캡.

D-06 · oneliner 조립 코드 · 0.3h

명세oneliner(홈 한줄 진단)도 조립: {잘하는 점(praise 계열 축약)} + {오늘의 한 걸음(next)} 템플릿 6변형 회전. LLM 0콜.
테스트D-06-1 변형 회전 · D-06-2 길이 캡 60자.

D-07 · polishKo 통합 코드 · 0.2h

명세조립 결과에 기존 polishKo(국 마시다 등 결정론 교정) 적용 — 블록이 검수돼 있어도 슬롯 치환 후 어법 안전망.
테스트D-07-1 기존 polishKo 케이스 통과.

D-08 · LLM 윤문 옵션(기본 OFF) 코드 · 0.5h

명세polish: true일 때만 기존 proofreadLetter(내용 불변 계약) 1콜. 컷오버 초기엔 OFF로 순수 조립 품질을 측정(I-04) 후 이사님 판단으로 ON. ON이어도 검증자 불필요(표면이 이미 검수됨) — 콜 상한 1.
테스트D-08-1 OFF 시 0콜 · D-08-2 ON 시 1콜·실패 시 원문.

D-09 · 안전망 — 조립 결과 최종 스캔 코드 · 0.3h

명세발행 직전 letterDeterministicBad+detForbid 1회(LLM 0콜·정규식만) — 블록 검수의 belt. 위반 시 해당 블록 id를 경고 로그+이슈에 남기고 변형 재선택 1회(이론상 발생 0이어야 — 발생=린터 구멍 신호).
테스트D-09-1 강제 위반 블록 주입 시 재선택+로그.

D-10 · context 스냅샷 스키마 v3 코드 · 0.3h

명세기존 letterCtx 유지 + 추가: blocks·factsCited·decision{unit,step,mode}·goalsSnapshot. 어드민 칩(H-08)이 읽는 계약 — 쓰기 3경로(크론 신규·크론 reuse·폴백) 동일 함수로 단일화 buildLetterCtx()(회귀 위험 #1 봉쇄).
테스트D-10-1 세 경로 호출이 같은 함수 — grep 테스트(소스 스캔으로 직접 객체 리터럴 금지).

D-11 · 폴백 — 블록 부재 시 코드 · 0.3h

명세후보 0(원장 소진+requires 미충족 동시) 시: 공용 opener-weekday + plateau 조합으로 안전 편지(어떤 상황에도 발행 가능 보장) + context.fallback:true + 이슈 로그(C-20 증식 트리거). 절대 LLM 자유 생성으로 폴백하지 않는다(6/12 사고 원칙).
테스트D-11-1 후보 0 강제 → 안전 편지+플래그.

D-12 · 조립기 테스트 묶음 — tests/assemble.test.ts 테스트 · 1h

명세D-01~11 케이스(≈25) + 통합: 6일 연속 조립 시 ①블록 중복 0 ②사실 재서술 0 ③전 편지 길이 규격 ④mode 시퀀스 정합. 헬퍼 mkBlocks()(테스트 전용 미니 풀 — 실 풀 의존 금지로 콘텐츠 변경에 강건).
DoD그린+prebuild 편입.

EPIC E — 주간 목표 포트폴리오 D4

lib/coachWeekly.ts 개편. 단일 lever → goals 2~3. Sonnet은 '선발'만 하고 검증·정규화는 코드(화이트리스트 강제 — 기존 mission_target 패턴 동일).

E-01 · Goal 타입·goals 스키마 코드 · 0.3h

명세type Goal = { unit_id: UnitId; priority: 1|2|3; status: 'focus'|'standby'|'stopped'; reason?: string }. WeeklyAnchor에 goals: Goal[] | null 추가. 불변식: focus 정확히 1개·priority 중복 0(코드 검증 함수 normalizeGoals — Sonnet 산출 정규화).
테스트E-01-1 focus 2개 입력→1개로 정규화 · E-01-2 미지 unit_id 제거 · E-01-3 0개→관찰 주(null).

E-02 · SYSTEM_WEEKLY v3 프롬프트 프롬프트 · 1h

명세기존 회진 프롬프트 확장(전면 재작성 아님): 출력 JSON에 goals:[{unit_id, priority, why(소견용 한 줄)}] 추가 — 후보는 코드가 미리 계산해 목록으로 제공(E-03)하고 Sonnet은 순위만 결정("이번 주 입력된 데이터 보고 지금 아이 상황에서 어떤 교육이 좋겠나" — 이사님 표현 그대로 프롬프트에). mission_target(배경 식품 타깃)·impression(소견)은 유지. 유닛 목록·설명은 레지스트리에서 렌더(이중 정의 금지).
유즈케이스아린: env 신호 + 점심 정상 + 콩류 갭 → 후보 [table-stage, hunger-rhythm, exposure-savings(콩류)] → Sonnet 순위: table-stage focus·hunger-rhythm standby·exposure standby.
테스트E-02-1 산출 파싱+정규화 왕복(목 JSON) · E-02-2 후보 밖 unit 강제 제거(화이트리스트).

E-03 · 후보 산출기 candidateUnits 코드 · 0.5h

명세순수 함수: signals+progressMap → 후보 Goal 풀(점수순). 점수 = 발동 신호 강도(레지스트리 trigger) + 가산: relapsed +3 / pivoted(전주 중단) +1 / mastered 제외 / maintenance 제외 / 온보딩 게이트(E-05) 위반 제외. 상한 5개(Sonnet 입력).
테스트E-03-1 재발 우선 · E-03-2 mastered 제외 · E-03-3 신호 0이면 기초 순서(코어 1→8 미이수 첫) 폴백 — "처방 없는 주엔 수업".

E-04 · coldSynth v3 코드 · 0.3h

명세LLM 실패/후보 없음 시: candidateUnits 상위 2개를 goals로(결정론) — 닻은 항상 생긴다(기존 안전 제1원칙 유지).
테스트E-04-1 LLM 목 실패→결정론 goals.

E-05 · 온보딩 아크 게이트 코드 · 0.5h

명세가입 후 주차 산정(첫 meal_log 기준): 1주차(신뢰)=goals 1개·코칭 강도 최저(블록 tone=warm만·행동 요청 최소) / 2주차(관찰)=goals ≤2·기초 유닛만 / 3주차+(개입)=정상 / 정착은 졸업 누적으로 자연 표현. 레지스트리 minWeek로 선언(B-02).
유즈케이스신규 가입 가정이 첫 주부터 행동 숙제 폭탄을 받지 않게 — 신뢰 먼저(이사님 "1~4주 큰틀 마음에 듦").
테스트E-05-1 1주차 goals 캡 · E-05-2 minWeek 필터.

E-06 · 주제 피로 캡 — 레버 연속 정책 코드 · 0.3h

명세같은 unit이 focus로 3주 연속 선발되려면 직전 2주에 step 전진 ≥1 필요(전진 중이면 딥다이브 허용 — "진전 있으면 계속") · 전진 0이면 강제 standby 강등+차순위 승격(이사님 "또 화면 주간" 방지 — 다주 아크와의 균형점). 상수 TH.focusMaxStallWeeks=2.
테스트E-06-1 정체 2주 후 3주째 강등 · E-06-2 전진 중 3주째 유지 허용.

E-07 · 닻 read/write 확장 코드 · 0.3h

명세synthAndStoreAnchor row에 goals 포함(+lever 병행 기록 A-05). upsert 에러 검사·legacy 폴백(기존 패턴)에 goals 포함/제외 양분기. loadAnchor 후 goalsOf() 정규화 1회.
테스트E-07-1 goals 미컬럼 환경 legacy 폴백 — (A-04 실행 전 모의).

E-08 · 일요일 회고 편지(자유작문 잔존면) 규격 프롬프트 · 0.5h

명세주 1회 일요일: Sonnet 자유작문(소형 프롬프트 신설 — 기존 거대 buildLetterUser 미사용): 입력=이번 주 진도 요약(전진·졸업·소견)+다음 주 goals, 규격 4~6문장, 기존 가드 스택(det+검증자) 그대로 적용(빈도 낮아 비용 무시). 유일하게 '한 주 이야기'를 회고로 묶는 자리 — 신선도 담당.
테스트E-08-1 프롬프트 스냅샷 · 가드 경유 확인(리플레이 제외 — LLM 표면).

E-09 · healAnchor v3 코드 · 0.2h

명세기존 heal에 goals 보충 추가(goals 없으면 goalsOf(lever)). behavior_goal 보충은 유지(구코드 호환 기간).
테스트E-09-1 구닻(컬럼 전) heal 왕복.

E-10 · EPIC E 테스트 묶음 테스트 · 0.5h

명세E-01~09 케이스(≈15)를 curriculum.test.ts에 편입 + 주간 리플레이: 4주 합성(정체→강등, 재발→재선발) 시나리오.

EPIC F — 일간 전개 선택기 D4

크론의 "오늘 무엇을 말할까"를 advanceProgress(B-23) 출력으로 일원화 — planFromWeekly의 v3 후계. 기존 planFor는 비커리큘럼 인터럽트·food 배경 타깃 보조로 축소.

F-01 · 일일 진료차트 대조 — 어제 델타 코드 · 0.5h

명세yesterdayDelta(progressPrev, progressNow): Delta — 신호 변화 방향(↑↓→)·신규 hit·step 변화. DailyDecision의 입력이자 observe/advance 블록의 {fact} 소스(daily 카드와 결합). "일간 계획(크론이 남긴 일일 진료차트)과 비교"(이사님)의 구현체 — 어제 context.decision과 오늘 판정을 대조해 같은 mode 3일 연속 시 변형 강제(advance 3연속=사다리가 빠른 것 — holdWeeks 검증 경고).
테스트F-01-1 델타 방향 · F-01-2 3연속 경고.

F-02 · 전개 결정표(물 흐르듯 규칙) 코드 · 1h

명세결정 우선순위(코드 표 — JSDoc 명문화):
유즈케이스이사님 시나리오 그대로: 월 intro→화 how→수(실행 관측) advance B→목(이틀 정체) pivot 간식 리듬→금(컷오프 관측) deepen.
테스트F-02-1..6 결정표 각 행 + F-02-7 주당 피벗 캡.

F-03 · food 배경 타깃 공존 코드 · 0.5h

명세mission_target(콩류 등)은 exposure-savings 유닛이 goals에 있을 때만 행동으로 등장 — 그 외 주엔 박스·추천(bridgeFacts)에만 흐르고 편지 행동으론 미등장('한 번에 하나' 유지). 채근 캡(M3 흡수): push성 블록(advance의 mix/beside 계열)은 ledger.pushUsed 주1회 게이트를 블록 선택 필터로 — 강등 시 비push 변형 선택(코드 보장).
테스트F-03-1 비노출 주 행동 미등장 · F-03-2 push 캡 필터.

F-04 · 시급 예외 정의 코드 · 0.3h

명세"사실 반복 금지의 예외=진짜 시급"(이사님) 한정 목록: ICFQ 적신호 누적·질식 위험 메모·3일+ 전량 거부. 이때만 동일 사실 재서술 허용 플래그(D-04 원장 우회) + 전문가 안내 블록. 목록은 레지스트리 상수(확장은 이사님 승인 항목).
테스트F-04-1 예외 플래그 경로 · F-04-2 비시급은 우회 불가.

F-05 · 진료차트 기록 — context.decision 코드 · 0.2h

명세D-10 스키마의 decision 필드 채움 + cron_runs.meta에 일일 집계(advance/pivot/maintain 분포) — 어드민·J 모니터의 원료.
테스트D-10 계약 테스트에 포함.

F-06 · 주 첫 편지 판정 v3 코드 · 0.3h

명세firstOfWeek(기존 recentWeekKeys 방식 유지) → intro 시퀀스는 focus 유닛이 신규 활성일 때만(전주에서 이어지는 유닛이면 intro 생략하고 advance부터 — 다주 아크에서 매주 월요일 재도입 방지).
테스트F-06-1 이월 유닛 intro 생략 · F-06-2 신규 유닛 intro.

F-07 · 무기록·휴면 경계 코드 · 0.3h

명세byDay<3(판정 불가 주)→ 진도 동결(전이 없음)+lowdata 모드. 휴면 복귀(기존 경로 유지)와의 경계: dormancy≥2(주중 갭)는 기존 reengage — 단 진도는 동결이지 리셋 아님.
테스트F-07-1 동결 불변 · F-07-2 복귀 후 이어가기.

F-08 · plateau 처리 v3 코드 · 0.2h

명세전 목표 정체+피벗 캡 소진 → 공용 plateau 모드(행동 0·태도 칭찬) + 주간 재진단 플래그(다음 일요일 종합에 stop_reason 전달).
테스트F-08-1 plateau 진입 조건.

F-09 · planFromWeekly→v3 어댑터 코드 · 0.5h

명세컷오버 플래그(H-01) OFF면 기존 planFromWeekly 경로 그대로, ON이면 F 경로. 인터페이스 호환 셰이프 유지(점진 전환·즉시 롤백 가능).
테스트F-09-1 플래그 양분기 동작.

F-10 · EPIC F 테스트 묶음 테스트 · 0.5h

명세결정표 전 행+이사님 주간 시나리오(월~금 5일 통주) 통합 케이스.

EPIC G — 질문 정렬·피드백 루프 D4

보라 박스(오늘의 질문)를 측정 기관(器官)으로 — "식단 입력으로 판단이 부족할 때 적절한 질문으로 판단"(이사님).

G-01 · 측정 공백 감지기 코드 · 0.5h

명세measurementGap(def, row, recentProbes): SignalKey | null — 활성 유닛의 판정 필수 신호 중 ①최근 7일 표본 < 최소치(B-04~15 보류 조건과 동일 임계) ②질문으로 측정 가능(probes 보유) ③최근 3일 같은 probe 미발행(쿨다운 G-06). 충족 시 그 신호 반환.
테스트G-01-1 표본 부족 감지 · G-01-2 쿨다운 차단 · G-01-3 칩 충분 시 null.

G-02 · 유닛별 관측 질문 템플릿(12세트) 콘텐츠 · 1h

명세레지스트리 probes: 유닛당 2~3문항+칩(1탭 답·"잘 모르겠어요" 필수 — 기존 규칙). 예: table-talk → "오늘 식탁에서 어떤 말을 가장 많이 건넸어요?" 칩["맛이 어떤지 물었어요","먹으라고 챙겼어요","별말 안 했어요","잘 모르겠어요"] → objectTalk 매핑(칩1=+1, 칩2=압박+1). P1 준수: 데이터로 아는 것은 절대 안 묻는다(envTablePct 있으면 환경 질문 발행 금지 — G-01이 보장). 12유닛 × 2~3 = ~30문항, 블록과 동일 린트(C-19 확장: probes 검수).
테스트G-02-1 전 유닛 probes 칩에 '잘 모르겠어요' 존재 · G-02-2 매핑 키가 evidence 키와 일치.

G-03 · 질문 선택기 개편 코드 · 0.5h

명세우선순위: ①ICFQ 주기일(기존 10일 결정론 — 불변) ②unitProbe(G-01 공백 시) ③기존 QUESTION_MOVES 로테이션(폴백). 발행 시 context.unitProbe(A-07) 기록.
테스트G-03-1 3순위 폴백 체인 · G-03-2 ICFQ일 양보.

G-04 · 답변→evidence 파서 코드 · 0.5h

명세크론이 어제 daily_questions(answered+unitProbe)를 읽어 칩→evidence 매핑 적립(B-16 경유). '잘 모르겠어요'=표본 미적립(무지 존중). 자유 텍스트 답은 키워드 매핑 보수적(미매칭=미적립).
테스트G-04-1 칩 매핑 · G-04-2 모름 무적립 · G-04-3 unitProbe 없는 답 무시.

G-05 · ICFQ 공존 코드 · 0.2h

명세ICFQ 키와 unitProbe 키 분리(A-07) — 같은 날 충돌 시 ICFQ 우선(안전 스크리너), unitProbe는 다음 날 이월.
테스트G-05-1 충돌일 이월.

G-06 · 질문 쿨다운·다양성 코드 · 0.3h

명세같은 probe 3일 쿨다운 + 유닛당 주 2회 캡(질문 잔소리화 방지). 원장=daily_questions 최근 7일 context.unitProbe.
테스트G-06-1 캡 초과 시 차순위 신호/로테이션 폴백.

G-07 · 칩 설계 규격 설계 · 0.2h

명세칩 3~5개·1탭·중립 어휘(죄책감 0)·측정 의도 은닉(부모에게 '측정' 티 안 남 — 자연스러운 안부 톤). G-02 제작 지침에 포함.

G-08 · 질문 context 스키마 코드 · 0.2h

명세A-07 확정 구조 + topic='unit-probe' 라벨(어드민 구분).

G-09 · P1 정합 감사 테스트 테스트 · 0.3h

명세속성 테스트: 임의 합성 주간에 대해 "칩 표본 충분한 신호로 unitProbe 발행 0"(데이터로 아는 것 안 묻기) — G-01 임계와 B-04~15 보류 임계의 일치 검증.

G-10 · EPIC G 테스트 묶음 테스트 · 0.4h

명세G 전 케이스(≈15) + 피드백 루프 통합: 표본 부족→질문→답→evidence→다음 날 판정 가능 — 4일 통주 시나리오.

EPIC H — 크론 통합·컷오버 D5

H-01 · 컷오버 플래그 코드 · 0.2h

명세COACH_V3=1 env(Vercel) — 일간 편지 경로 분기(F-09). 미설정=기존 경로(즉시 롤백=env 끄기). 자녀 단위 카나리아: COACH_V3_CHILDREN=id1,id2(아린 먼저).
테스트H-01-1 플래그 OFF 기존 경로 byte-호환(리플레이 비교).

H-02 · 크론 배선 — v3 메인 루프 코드 · 1h

명세자녀 루프 내 v3 분기: ①진도 로드(curriculum_progress 일괄 select — 자녀당 1쿼리) ②advanceProgress(B-23) ③assembleLetter(D) ④진도 upsert+letterCtx upsert(buildLetterCtx 단일 함수 D-10) ⑤질문(G-03). LLM 콜: 평일 0~1(윤문 ON 시), 일요일 +1(Sonnet 종합)+1(회고 E-08) — TIME_BUDGET 산수 재계산 주석(자녀당 ~1.5s 평일 → 30자녀/실행 여유 — S7 해소 수치 명시).
테스트H-02-1 모의 supabase로 루프 1자녀 왕복(읽기 fixture 주입 — I-05 러너 재사용).

H-03 · intro/회고 소형 프롬프트 분리 코드 · 0.5h

명세자유작문 잔존면 전용 buildIntroUser()(신규·300자급): 사실 카드 5장+behavior_goal+금지 6줄만. 기존 buildLetterUser는 컷오버 후 휴면 경로 전용으로 강등(삭제는 안정화 후 — J-03). 검증자·det 가드는 잔존면에 유지.
테스트H-03-1 프롬프트 스냅샷(지시 수 ≤25 — 희석 곡선 안전권).

H-04 · 휴면·복귀 경로 유지 코드 · 0.2h

명세reengage 경로 무변경(주말 갭 가드 S4 적용분 유지) — 진도 동결(F-07) 연동만 추가.
테스트기존 케이스 회귀 그린.

H-05 · 시간 예산 v3 코드 · 0.2h

명세deadlineMs 유지(잔존면용). v3 경로는 LLM 의존 낮아 per-child 가드만(50s 체크 위치 루프 상단 유지 + 일요일 Sonnet 콜 전 체크 추가).
테스트H-05-1 일요일 예산 초과 시 종합 스킵→차주 lazy(기존 안전망).

H-06 · 닻 장애 경보(M9) 코드 · 0.2h

명세loadAnchor 연속 실패/upsert legacy 폴백 발동 시 issues.push("닻 저장 장애 — 매일 재종합 모드") — 조용한 비용 누수 가시화.
테스트H-06-1 폴백 시 이슈 기록.

H-07 · reuse 경로 v3 코드 · 0.2h

명세reuse 보존 목록에 blocks·factsCited·decision 추가(S6 일반해 — buildLetterCtx가 prev에서 복원).
테스트H-07-1 reuse 후 원장 연속성(다음 날 dedup 정상).

H-08 · 어드민 칩 확장 코드 · 0.3h

명세편지 헤더 칩에 추가: 유닛·step·mode(decision) + fallback 플래그. 기존 칩(sig·sim·verify) 유지(잔존면용).
테스트수동 QA(어드민 페이지 — 스냅샷 선택).

H-09 · 어드민 진도 보드 코드 · 1h

명세/admin/curriculum: 자녀×12유닛 매트릭스(status 색·step·last_signal)·재발 하이라이트·주간 goals 표시. read-only(서버 컴포넌트·기존 어드민 게이트). "달성 시 안 가르치고 무너지면 재감지"가 한눈에 보이는 화면.
테스트수동 QA + RLS(서버만).

H-10 · 온디맨드 패리티 확인 검증 · 0.2h

명세폴백=크론 호출(기배포)이 v3 플래그를 그대로 타는지 확인만(추가 코드 0). api/coach 구 라우트는 컷오버 후 410 처리 검토(J-03).
테스트H-10-1 플래그 ON 상태 폴백 경로 호출이 v3 편지 생성.

H-11 · 컷오버 절차·롤백 절차 · 0.3h

명세순서: ①I-06 게이트 통과 확인 ②아린 카나리아 3일(COACH_V3_CHILDREN) ③전체 ON ④48h 모니터(J-01 지표) ⑤이상 시 env OFF 즉시 롤백(데이터는 additive라 무손상). 각 단계 일지 기록.
DoD절차 문서화+카나리아 결과 기록.

H-12 · EPIC H 통합 테스트 테스트 · 0.5h

명세플래그 분기·reuse·경보 케이스(≈8) + tsc/build/test 게이트.

EPIC I — 리플레이·지표·검증 D5

I-01 · v3 지표 정의 코드 · 0.5h

명세리플레이 리포트 지표(설계 §5 교체분): ①블록 3일 중복률(목표 0%) ②진단 사실 재서술 건수(목표 0) ③같은 주제 도입 연속일 p95(목표 ≤2) ④피벗 정확성(정체→피벗 결정 일치율 100% — 결정론이므로) ⑤전개 규칙 위반 0 ⑥mode 분포(advance·deepen 비중 — 흐름 건강도) ⑦LLM 콜/통 ⑧주제 피로(같은 unit focus 연속 주). 산출 함수 replayMetrics(letters[]): Report.
테스트I-01-1 지표 함수 단위 케이스.

I-02 · 합성 가정 생성기 코드 · 1h

명세scripts/gen-synthetic.mjs: 페르소나 파라미터(등원 여부·주말 기록 습관·env 나쁨·거부 잦음·정체형/순응형/재발형…)로 30가정×14~28일 meal_logs 합성(fixture JSON). 결정론 시드. 과거 사고 11건은 전용 fixture(아린 실데이터 캡처 — capture-fixture.mjs)로 별도.
테스트I-02-1 생성 재현성 · I-02-2 페르소나 신호 분포 sanity.

I-03 · 임계 보정 리플레이 검증 · 0.5h

명세30가정 통주 → 지표 리포트 → TH 상수 조정(예: stallDays 6이 순응형에서 과민하면 7) → 재실행. 보정 결과는 B-03 주석에 근거 기록(숫자가 '느낌'이 아니라 리플레이 산출).
DoD보정 전후 리포트 2장 일지 첨부.

I-04 · 실데이터 병행 검증 검증 · 0.5h

명세아린 6/1~6/12 fixture로 v3 통주 — 산출 편지 14통 전문을 사람 정독(전수 마커 스캐너 + 눈) + "사고들이 v3에서 재현 불가" 확인(뷔페·점심·복붙·주제수렴 각각 해당 fixture 날짜에서 검증).
DoD4사고 재현 0 리포트.

I-05 · 리플레이 러너 확장 코드 · 0.5h

명세tests/replay.test.ts에 v3 통주 모드(fixture→일별 advanceProgress+assembleLetter→지표 단언). LLM 0콜이므로 CI(prebuild)에 풀 포함 가능 — 매 배포가 30가정×14일 리허설을 통과해야 함(복리의 완성).
테스트자체가 테스트.

I-06 · 컷오버 게이트 기준 기준 · 0.2h

명세수치 게이트: 블록 중복 0 · 재서술 0 · 전개 위반 0 · 도입 주제 연속 p95≤2 · fallback률<3% · 아린 정독 통과. 미달 시 컷오버 보류(원자 단위 수정 후 재실행).

I-07 · 사고 박제 절차 통합 프로세스 · 0.2h

명세신규 사고 발생 시: capture-fixture→fixture 추가→재현 테스트(red)→수정(green)→지표 리포트 첨부. C-20(블록 증식)과 한 문서로(J-04).

I-08 · CI 시간 예산 검증 · 0.2h

명세prebuild 총 시간 측정 — 목표 <60s(Vercel 빌드 영향 한정). 초과 시 리플레이 가정 수 빌드용 축소 세트(10가정)+풀 세트는 npm run test:full 분리.

EPIC J — 운영·모니터링·문서 D5+

J-01 · 재발·건강도 모니터 코드 · 0.3h

명세크론 말미 집계→cron_runs.meta: 재발 감지 수·fallback률·mode 분포·진도 정체 자녀 수. 임계 초과 시 issues 경보(기존 채널).

J-02 · 미해결 케이스 큐(경량) 코드 · 0.3h

명세D-09 위반·D-11 폴백·B-23 불법 전이 무시 건을 cron_runs.meta.issues에 구조화 태그로 — 주 1회 사람 리뷰(어드민 보고서에 섹션). 전용 테이블은 발생량 보고 후 결정(과설계 회피).

J-03 · 문서 동기화 문서 · 0.5h

명세컷오버 후: plan-engine '현행 파이프라인' 절 v3로 갱신·scenarios에 조립식 관계 1줄·구 api/coach 처리 결정 기록·허브 라벨. 본 WBS 문서에 원자별 완료 체크 갱신(커밋 해시 기입).

J-04 · 복리 기록 규칙 프로세스 · 0.2h

명세엣지케이스 1건 = ①fixture ②테스트(red→green) ③블록/임계 수정 ④일지 1줄(원자 ID 참조). 이 4종 세트 없는 핫픽스 머지 금지 — 커밋 템플릿에 명시.

J-05 · 비용 계측 코드 · 0.2h

명세cron_runs.meta에 LLM 콜 수 집계(경로별) — v3 효과(14→0~2) 수치 증명·어드민 노출.

J-06 · 북극성 페르소나 부록 연결 문서 · 0.5h

명세v3 정본 부록으로 '이상적 식습관 페르소나' 지표 정의: 12유닛 체득 벡터 + 식품 다양성(도감·사촌) + 영양 신호등 — 자녀별 갭 벡터가 주간 후보 산출(E-03)의 상위 개념임을 명문화. 어드민 레이더 차트는 후속.

부록

부록 1 — 마일스톤 매핑

마일스톤원자완료 게이트
D1 데이터·상태기계A-01~08 · B-01~26SQL 실행 확인 + curriculum.test ~60케이스 그린
D2~3 블록·조립기C-01~20 · D-01~12블록 350+ 린터 그린 + assemble.test 그린 + 6일 조립 중복 0
D4 주간·일간·질문E-01~10 · F-01~10 · G-01~10이사님 주간 시나리오(월~금) 통주 그린 + 피드백 루프 4일 통주
D5 통합·컷오버H-01~12 · I-01~08 · J-01~06I-06 수치 게이트 + 아린 카나리아 3일 + 전체 ON

부록 2 — 의존성 요점

부록 3 — 리스크 톱10

#리스크대응
1블록 350개 품질 편차(어색한 한국어)C-18 적대검수+아린 14통 정독(I-04)+윤문 옵션(D-08) 예비
2조립 편지의 '기계 냄새'(연결 어색)D-05 결합 규칙+변형 다양성 의무(C-04)+컷오버 게이트에 사람 정독
3evidence proxy 한계(간식 시각 등 미기록 신호)보류 판정 명문화(B 공통)+질문 보충(G)이 1차 해법 — 과추정 금지
4임계 오설정(과민 피벗/둔감 정체)I-03 보정 리플레이+TH 단일 소스+운영 J-01 분포 감시
5SQL 미실행 채로 진행(전례 2회)A-01 DoD에 프로브 강제 — 미확인 시 D4 진입 금지
6구·신 경로 동시 운영 중 원장 분기buildLetterCtx 단일화(D-10)+플래그 분기 테스트(H-01)
7질문이 측정 도구화로 차가워짐G-07 톤 규격+발행 캡(G-06)
8다자녀 스케일 미지수H-02 산수(30/실행)+J-05 계측으로 실측 확인
9이사님 결정 대기 항목 블로킹본 문서에 결정 필요 항목 명시(시급 예외 목록 F-04·윤문 ON/OFF D-08·컷오버 시점 H-11)
10WBS 자체의 드리프트J-03: 원자 완료 시 본 문서에 커밋 해시 기입(살아있는 문서)

부록 4 — 테스트 ID 인덱스(요약)

신규 테스트 파일 4: tests/curriculum.test.ts(B·E·F ≈90케이스) · tests/assemble.test.ts(D ≈25) · tests/blocks.test.ts(C 린터 — 풀 전수) · tests/replay.test.ts 확장(I — 30가정 통주+불변식). 기존 4파일 51케이스 유지. 예상 총합 ≈ 200+ 케이스, 전부 prebuild 게이트.

부록 5 — 용어

✅ 이사님 결정 확정 3건 (2026-06-12 — "추천 방향대로") — ① F-04 시급 예외 = ICFQ 적신호 누적·질식 위험 메모·3일+ 전량 거부 3종 한정 확정(확장은 별도 승인) ② D-08 윤문 기본값 = OFF 확정(순수 조립 품질을 I-04에서 측정 후 필요 시 ON) ③ H-11 컷오버 = I-06 게이트 통과 즉시 아린 카나리아 시작, 전체 ON은 주중(월~목) 아침 크론 전(주말은 기록 패턴 왜곡+대응 지연 회피). 전 원자 착수 가능.