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)과 자유작문 잔존 표면뿐 — 일간 편지 표면은 전부 코드.
📌 개발 지시 프로토콜 — 이 문서가 작업 지시의 단일 진실이다
지시 방법: "WBS 보고 B-07 구현해" / "EPIC C 진행해" — 원자 ID만으로 지시 가능. 구현자는 해당 원자의 {명세·테스트·DoD}를 그대로 따른다.
완료 절차(필수 4종): ①명세대로 구현 ②해당 테스트 ID 그린(+npm test 전체 그린 — prebuild 게이트) ③커밋 메시지 맨 앞에 원자 ID ④본 문서 상태칩 갱신(⬜→✅ + 커밋 해시 — 아래 진척 대시보드 동시 갱신).
명세와 다르게 만들고 싶을 때: 코드 먼저 금지 — 본 문서의 해당 원자 명세를 먼저 수정·커밋(사유 1줄)한 뒤 구현. 문서≠코드 드리프트 방지.
엣지케이스 발견 시: J-04 복리 규칙 — fixture+테스트(red→green)+수정+일지 1줄. 새 원자가 필요하면 해당 EPIC 말미에 ID 추가(예 B-27).
상태칩: ⬜ 대기 · 🔨 진행 중 · ✅ 완료(커밋) · ⏸ 외부 대기(SQL 실행 등) · ❌ 드랍(사유 기입).
📊 진척 대시보드(원자 완료 시 갱신 — 마지막 갱신: 2026-06-13 D2~4 완료)
⬜ 컷오버 후(D5+) — 단 J-01/J-02/J-05의 원료(v3 ctx.llmCalls·경고 issues·폴백 플래그)는 H-02가 이미 기록
합계
131
124
✅ D1~D5 코드 완료(테스트 229·빌드 그린) → 남은 것: H-11 카나리아 컷오버(운영) + J(컷오버 후)
🚀 H-11 — 컷오버 절차(준비 완료 · 운영 대기 2026-06-13)
게이트 확인(완료): I-06 수치 게이트 전부 통과 — 30가정×14일 + 아린 실데이터 14통(4사고 재현 0·블록/조합 중복 0·재서술 0·폴백 0·LLM 0콜). 매 배포 prebuild가 같은 리허설을 재실행.
아린 카나리아: Vercel env COACH_V3_CHILDREN=43942d34-b339-4bbd-978a-ec3f6a877031 설정 → 다음 새벽 크론부터 아린만 조립식. QA는 /api/cron/coach?child=<id>&force=1 즉시 실행 + 어드민 스레드 🧱조립식 칩·/admin/curriculum 진도 보드 확인.
3일 모니터: 어드민 칩(폴백·시급·경고)·cron_runs.issues(v3 경고/폴백/진도 저장 실패)·편지 육안. 이상 시 env 제거=즉시 레거시 복귀(데이터 additive·무손상).
전체 ON: COACH_V3=1 — 주중(월~목) 아침 크론 전(이사님 확정·주말 회피). 48h 모니터 후 안정 선언.
각 단계 일지 기록. 컷오버 후 J 에픽(모니터 집계·문서 동기화 J-03: plan-engine 현행 절 v3 갱신·구 api/coach 410 검토).
🔧 구현 비고(2026-06-13 — 명세 대비 확정 사항, 문서≒코드 정합)
D-01 시그니처: noteCards·progress 인자 제외(사실은 factCards로 일원·step은 decision에서), factsCited·urgent·avoidTags·tones·introNeeded·detForbid 추가. observe(무신호일) 시퀀스 확정 = 사실 있으면 observe→why, 없으면 opener-weekday→why(수업 톤). celebrate = graduate→praise. lowData(F-07)는 H-02에서 opener-weekday→lowdata로 배선.
D-04 확장: FactCard에 prose(편지 산문형) 필드 — text는 프롬프트용 구조 문자열이라 그대로 조립하면 어색. 원장 키 = 유닛:카드키, 재활성 시 resetFactsCitedFor로 그 유닛만 리셋.
C-03 규칙 추가분: 슬롯 뒤 조사 하드코딩 금지(조사 토큰 강제)·줄표 금지·거래어휘 금지·공용 블록 {name} 외 슬롯 금지·블록 문장 ≤3·내부 종결 3연속 금지. '늘' 단정어는 어절 시작만(린터 픽스처가 '오늘 ' 오탐 적발).
D-05 보강: 어미 3연속+문장 예산(3~5)을 블록 '선택 시점'에 통합(옵션 위치는 예산 미달 시 생략) — 사후 드랍보다 우선.
F-03 현황: 제작 풀에 push성(섞기·곁들) 표현 0건 — 검수가 저압력 표현만 산출. forbids:['push'] 필터는 미래 블록 대비 유지.
C-04 운영 교훈: 세션 리밋이 워크플로를 3회 중단 — 스크립트에 produce/finish 모드·skipUnits·args 문자열 방어 체크포인트 추가(4런 수확 병합으로 완성). C-18 적대검수는 메인 세션 6렌즈 에이전트로 수행(4렌즈 통과·반복감 18·정직 8 삭제 — '{fact} 없이 어제 단정' 거짓칭찬 위험 봉쇄).
E-07 부분 배선: 일요일 종합에 candSignals(구조화 신호) 주입·goals 저장·heal 상시 — 메모 통계·진도·주차·focus 이력 정밀화는 H-02(v3 메인 루프)에서.
목차
EPIC A — 데이터 모델·SQL (A-01~A-08) D1
EPIC B — 유닛 레지스트리·판정 상태기계 (B-01~B-26) D1
EPIC C — 블록 라이브러리 (C-01~C-20) D2~3
EPIC D — 조립기 composeAssembled (D-01~D-12) D2~3
EPIC E — 주간 목표 포트폴리오 (E-01~E-10) D4
EPIC F — 일간 전개 선택기 (F-01~F-10) D4
EPIC G — 질문 정렬·피드백 루프 (G-01~G-10) D4
EPIC H — 크론 통합·컷오버 (H-01~H-12) D5
EPIC I — 리플레이·지표·검증 (I-01~I-08) D5
EPIC J — 운영·모니터링·문서 (J-01~J-06) D5+
부록 — ①마일스톤 매핑 ②의존성 그래프 ③리스크 톱10 ④테스트 ID 인덱스 ⑤용어
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에 작성:
child_id uuid not null · unit_id text not null(B-02 레지스트리의 12키: pressure-off·hunger-rhythm·table-stage·exposure-savings·fullness-respect·parent-model·no-bargain·table-talk·sensory-texture·food-bridge·autonomy-part·link-rhythm) · PK (child_id, unit_id)
status text not null default 'not_started' — CHECK in (not_started·active·progressing·maintenance·mastered·pivoted·relapsed) (B-23 상태기계의 상태 집합과 1:1)
step smallint not null default 0 — 과제 사다리 현재 단(0=미시작, 유닛별 최대 단은 레지스트리가 정의)
evidence jsonb not null default '{}' — 신호 카운터(B-16 병합기 산출물: 예 {"envTablePct7d":0.42,"hitDays":["2026-06-10"],"qAnswers":2,"streakWeeks":1})
started_at date · mastered_at date · last_signal_at date · stop_reason text(피벗 사유: stalled·interrupted·replaced) · relapse_count smallint default 0 · updated_at timestamptz default now()
유즈케이스①크론이 매일 판정 후 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행.
명세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)로 보정 가능하게.
목적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와 모순 없음.
테스트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 재사용).
간식 slot 시각이 없으므로 proxy: 같은 날 간식 slot 수·저녁 직전 간식 메모 + 간식 엔진 meal_time 간섭 신호 재사용(evaluateSnacks 산출)
간식 미기록 가정(과소 추정 명시 — '기록 없음≠없음' 라벨)
B-06
table-stage
envTablePct7d·tableDays·streakWeeks
environment 칩 기반 — table/(전체 env 기록) 7일 창 + 주 단위 streak(연속 충족 주 수)
env 기록 3건 미만 주=판정 보류(G-01이 질문 보충 발동)
B-07
exposure-savings
targetExposeDays·refuseTrend·selfEatCount
타깃 식재료가 ingredients에 등장한 '일수'(주 단위), 거부율 추이(노출곡선 4회 변화 안내용), 자발 섭취(ate_well true+타깃) 카운트
타깃 미정 시 no-op / 사촌 일반화(같은 식품군 catOf)는 별도 키
B-08
fullness-respect
over30Pct·forceMemoDays
meal_time≥30 비율 + 완식 강요 메모 키워드 일수
meal_time 기록 적은 가정 보류
B-09
parent-model
familyDinnerDays·modelAnswerYes
저녁 home 끼니 기록 일수(가족식 proxy) + 질문 답("같이 드셨어요?") 카운트
proxy 한계 명시 — 질문이 1차 신호(G-02)
B-10
no-bargain
bargainMemoDays·dessertNeutralDays
거래 키워드(먹으면·줄게·사줄게) 메모 일수 — 감소 추세가 진전
인용된 과거 회상 메모 오탐(최근 2일 메모만 1차)
B-11
table-talk
objectTalkPct·banWordDays
거의 전적으로 질문 기반(G-02 probe 답 적립) + 금지어 메모 스캔
표본 4건 미만 주 판정 보류
B-12
sensory-texture
texStepNow·texUpDays·regressDays
texture 칩 최빈값의 주간 이동(puree→mashed→finger→table 서열화) + 후퇴 감지
칩 혼재 주(50:50)는 보수적으로 하위 단계로
B-13
food-bridge
newFoodCount·chainPos
신규 식재료(28일 내 첫 등장) 카운트 + 사슬 위치(F-02가 갱신하는 step과 동기)
기관 급식발 신규는 place 분리 집계(M5 교훈)
B-14
autonomy-part
selfPct7d·roleAnswerYes
autonomy 칩 self 비율 + 역할 질문 답
칩 4건 미만 보류
B-15
link-rhythm
dcRefuseHomeRetry·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_at이 TH.stallDays(기본 6일) 이상 과거 그리고 그 기간 코칭이 실제 나갔음(편지 블록 원장에서 해당 유닛 블록 ≥3회 — D-03 원장 참조: "가르쳤는데 안 됨"과 "안 가르쳤음"을 구분). 후자는 정체 아님(코칭 재개 대상).
명세조건 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-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에서 전수.
requires/forbids: 조립 조건(예 observe 블록은 requires:["fact"] — 인용할 사실 없으면 선택 불가)
테스트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}." → "아린이가 어제 아침을 화면 없이 식탁에서 먹었어요."
목적기존 런타임 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-05
pressure-off
sDOR 역할 분리·"어른은 방향 아이는 속도"·환호도 압박(담담한 인정)
"한 입만" 류 문구가 예시로도 등장 금지(따라 말할 위험)
C-06
hunger-rhythm
2~3시간 간격·식간 물·컷오프 30분(WIC·식약처 #5)
"굶기세요" 어감 0 — '배고픔이 살아나게'
C-07
table-stage
화면·자리·시작 의식(식약처 #6·7) — 관찰 사실 언급은 intro 블록에만 존재
화면 단어가 intro 외 stage 블록에 등장 금지(주제 수렴 사고의 구조적 봉쇄)
C-08
exposure-savings
노출 적금·곡선(4회 변화·5~6 정체 예고)·taste-first 티스푼·감각 위계 언어
"꼭 먹여야" 0 · 횟수 숫자는 {fact}로만
C-09
fullness-respect
적게 담고 기다리기(홍콩 FHS)·30분 자율 종료
"남기면 안 돼" 0
C-10
parent-model
모델링=단일 최강 예측변수(HabEat)·가족식은 관계로 정의(배달 OK)
"주 5회 의무" 압박 어조 0
C-11
no-bargain
보상·수단화·제한 3역효과·디저트 중립화
금지 식품 악마화 0
C-12
table-talk
객체 중심("당근이 어땠어?")·금지어 3종 대체문
정체성 라벨링 예문 금지
C-13
sensory-texture
SOS 6분류(라이선스: 32단계 상세 표기 금지)·후퇴 룰·Big Body 웜업
'치료' 단어 0
C-14
food-bridge
체이닝 4축 한 축씩·찍어먹기(맛-맛 연합) — {food}는 화이트리스트만
목록 밖 음식명 0(블록에 음식명 하드코딩 금지)
C-15
autonomy-part
둘 중 고르기→셀프서빙→역할(SDT)
"흘리면 안 돼" 0
C-16
link-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).
명세편지 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 패턴 동일).
명세기존 회진 프롬프트 확장(전면 재작성 아님): 출력 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-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 보충은 유지(구코드 호환 기간).
크론의 "오늘 무엇을 말할까"를 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 명문화):
1 안전 인터럽트(B-24 캡 적용) → celebrate/적신호/공백
2 재발 유닛 존재 → 그 유닛 재개(mode=advance, 재개 단 B-21)
3 focus 진전(B-18) → advance(어제 사실 인용+다음 단) 또는 단 유지 중이면 deepen
4 focus 정체(B-19) → pivot(B-22) — 단 주당 피벗 ≤1(휙휙 방지 — TH.maxPivotsPerWeek=1, 초과 시 plateau)
5 maintenance 유닛만 존재 → 공용/차기 후보 observe
6 무데이터일 → 기록 권유(공용 lowdata)
유즈케이스이사님 시나리오 그대로: 월 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 원장 우회) + 전문가 안내 블록. 목록은 레지스트리 상수(확장은 이사님 승인 항목).
명세전 목표 정체+피벗 캡 소진 → 공용 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 경유). '잘 모르겠어요'=표본 미적립(무지 존중). 자유 텍스트 답은 키워드 매핑 보수적(미매칭=미적립).
명세자유작문 잔존면 전용 buildIntroUser()(신규·300자급): 사실 카드 5장+behavior_goal+금지 6줄만. 기존 buildLetterUser는 컷오버 후 휴면 경로 전용으로 강등(삭제는 안정화 후 — J-03). 검증자·det 가드는 잔존면에 유지.
명세아린 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종 세트 없는 핫픽스 머지 금지 — 커밋 템플릿에 명시.
명세v3 정본 부록으로 '이상적 식습관 페르소나' 지표 정의: 12유닛 체득 벡터 + 식품 다양성(도감·사촌) + 영양 신호등 — 자녀별 갭 벡터가 주간 후보 산출(E-03)의 상위 개념임을 명문화. 어드민 레이더 차트는 후속.
부록
부록 1 — 마일스톤 매핑
마일스톤
원자
완료 게이트
D1 데이터·상태기계
A-01~08 · B-01~26
SQL 실행 확인 + 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~06
I-06 수치 게이트 + 아린 카나리아 3일 + 전체 ON
부록 2 — 의존성 요점
A-01(SQL 실행)이 D1의 외부 의존(이사님) — 단 B 전체는 SQL 없이 개발·테스트 가능(순수 함수). 병렬 전략: SQL 대기 중 B 진행.
C(블록)와 B(판정)는 상호 독립 — 에이전트 병렬 제작 가능. D(조립기)는 C-01 스키마만 있으면 미니 풀로 개발.
E·F·G는 B 완료 의존. H는 전부 의존. I-02(합성)는 D1 직후 선행 가능(보정 데이터 준비).
부록 3 — 리스크 톱10
#
리스크
대응
1
블록 350개 품질 편차(어색한 한국어)
C-18 적대검수+아린 14통 정독(I-04)+윤문 옵션(D-08) 예비
2
조립 편지의 '기계 냄새'(연결 어색)
D-05 결합 규칙+변형 다양성 의무(C-04)+컷오버 게이트에 사람 정독
3
evidence proxy 한계(간식 시각 등 미기록 신호)
보류 판정 명문화(B 공통)+질문 보충(G)이 1차 해법 — 과추정 금지
4
임계 오설정(과민 피벗/둔감 정체)
I-03 보정 리플레이+TH 단일 소스+운영 J-01 분포 감시
5
SQL 미실행 채로 진행(전례 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)
10
WBS 자체의 드리프트
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 — 용어
유닛=커리큘럼 12종의 1개 · 단(step)=유닛 내 과제 사다리 위치 · goal=주간 포트폴리오 항목(focus/standby) · mode=오늘의 전개 결정(advance/deepen/pivot/maintain/celebrate/observe) · 블록=검수된 문장 뭉치 · 유지 주=코칭 중단 후 신호 유지 검증 기간 · 진단성/일일성 카드=사실 카드의 재서술 통제 등급.
✅ 이사님 결정 확정 3건 (2026-06-12 — "추천 방향대로") — ① F-04 시급 예외 = ICFQ 적신호 누적·질식 위험 메모·3일+ 전량 거부 3종 한정 확정(확장은 별도 승인) ② D-08 윤문 기본값 = OFF 확정(순수 조립 품질을 I-04에서 측정 후 필요 시 ON) ③ H-11 컷오버 = I-06 게이트 통과 즉시 아린 카나리아 시작, 전체 ON은 주중(월~목) 아침 크론 전(주말은 기록 패턴 왜곡+대응 지연 회피). 전 원자 착수 가능.