코칭 케이던스 엔진 — 일·주·월 3층 설계

코칭 엔진 정본 ③ — 일·주·월 케이던스 (✅ 2026-06-10 구현·배포) · 설계 2026-06-09 → 배포 06-10(99e2c50·6bfc65a·84fbad9) → 06-11 변주 핫픽스(아래 부록) · 일간 실행(전술) + 주간 초점(작전) + 월간 점검(전략). 라이브 정본 plan 엔진·시나리오 엔진 위에 '한 주'·'한 달' 시간축을 얹는다. 본문 §1~§14는 설계 당시 원문(일부 '미구현' 표기는 설계 시점 기준)이며, 현행 코드와의 차이는 아래 부록(06-11)이 정본이다.

⚠️ 부록 (2026-06-11) — '3일 복붙 편지' 사고와 핫픽스 (현행 코드 기준 정본)

사고: 비-food 주력 레버(environment) 주간에 6/9·6/10·6/11 편지가 같은 진단+같은 처방의 패러프레이즈로 3일 연속 발행. 원인 3겹: ① planFromWeekly 비-food 분기가 frame 고정 + target/move=null로 시그니처가 매일 동일(mealtime-atmosphere|-|-) → §5가 약속한 "frame·move·도입 회전" dedup이 볼 게 없었음 ② 커리큘럼 컬럼(weekly_coaching.sql) 미실행으로 닻의 behavior_goal이 없어 arc(변주축)=null — 6/10 부모가 실제로 화면 끄고 식탁 식사를 실행(progress=true 계산까지 됨)했는데 reinforce(칭찬) 대신 같은 잔소리 발송 ③ 어휘 유사도 가드는 의미 중복을 못 봄(실측 0.27~0.36 < 임계 0.45 — plan 엔진 문서가 0.90 확신으로 예언한 한계). 발견은 사람(이사님) — 자가 탐지 부재.

핫픽스(06-11 배포):구조 무브 메뉴 SCEN_MOVES — 환경 5종(간식 컷오프·영상 끄기·모델링·20분 정리·식기 놓기 신호)·자율성 4종·식감 4종을 buildCoachPlan이 결정론 회전(최근 시그니처 회피·전부 겹치면 plateau로 쉬어가기) → 프레임은 잠겨도 시그니처가 매일 달라짐 ② 가르치는 아크 5단계 — intro(주 첫 편지·진단+왜는 주 1회만)→how(장면)→obstacle(막힐 때)→observe(어제 사실)→reinforce(관측된 실행 칭찬·이틀 연속 금지) — 주간 진단 재서술 금지(한 구절 리마인드만)가 intro 이후 모든 단계에 걸림 ③ 닻 치유 healAnchor — 구버전/컬럼 미적용 닻의 behavior_goal을 결정론 폴백으로 메모리 보충 ④ upsert 에러 검사 — 닻 저장 실패 시 신규 컬럼 빼고 재시도(이전엔 조용히 실패 → 닻 미저장·매일 Sonnet 재종합·ledger 사망 위험. "SQL 미실행 시 planFor 폴백=안전"은 오판이었음) ⑤ 자동 반복 경보 — 발행 시점에 직전 편지 유사도·시그니처 연속을 자가 측정(context.simToPrev·repeatAlert·cron issues + 어드민 칩) ⑥ 유사도 가드 재생성도 구조 프레임에선 SCEN_MOVES 안에서 회전(음식 무브 주입 금지) ⑦ 하루치 메모(예: 주말 뷔페 1회)를 '반복 리듬'으로 일반화 금지 가드.

교훈(프로세스): 새 편지 경로는 배포 전 동일 데이터 다일(7일) 시뮬 의무 — 1일 스냅샷 QA는 '처음 깨어나는 경로'의 반복을 못 잡는다. supabase 쓰기 결과의 error 무시 금지.

🎯 한눈에

1. 왜 주간 계획인가 — '매일 재계산'의 한계

현재 cron/coach는 매일 새벽 2시(KST) planFor()최근 7일 윈도우를 처음부터 새로 보고 그날의 시나리오·타깃·무브를 즉석에서 뽑는다. plan 엔진이 '하루 안에서의 중복'은 막았지만(시그니처 dedup·채근 방지), '한 주에 걸친 일관성'을 잡는 시간축이 없다.

현재 — 매일 독립 재계산

  • 월: 콩류 · 화: 생선 · 수: 과일 · 목: 콩류… 타깃이 휙휙
  • 같은 타깃 이틀 연속 = 무조건 금지(채근 0) → 반복 노출 불가
  • 부모 입장: 매일 다른 숙제 → 서사·안정감 없음, 뭘 꾸준히 해야 할지 모호
  • 재노출 과학(주 2~3회 같은 음식)과 정면 충돌

제안 — 주간 닻 + 일간 실행

  • 이번 주 미션 = "콩류와 친해지기" 하나. 월·수·금에 콩류, 다른 날은 변주
  • 같은 타깃이 여러 날 = 설계된 반복 노출(휙휙 아님)
  • 부모 입장: "이번 주는 이것" — 한 주 내내 같은 방향, 매일 표현만 다름
  • 주 1회 '좀 더 밀기'(채근)를 허용 = 이사님 정책

핵심 통찰: '매일 분석'은 반응성은 좋지만 안정성이 없고, '주간 고정'은 안정적이지만 그날 긴급 신호(전환·적신호)를 놓친다. 둘을 층으로 분리한다 — 주간이 방향을 잡고, 일간이 그 안에서 반응한다.

2. 2층 케이던스 아키텍처

크론은 지금처럼 하나(/api/cron/coach, 0 17 * * * = KST 02시). Vercel Hobby 2-크론 한도를 지키기 위해 새 크론을 추가하지 않고 라우트 안에서 KST 요일로 분기한다.

일요일(dow=0)주간 종합 + 회고
① 자녀 루프 진입 전 runWeeklyPlanning() — LLM 1콜로 지난 28일 분석 → weekly_plans upsert(닻 = 타깃·타깃 풀·보조 축·예산·아크 주차). 요일별 슬롯은 안 짠다. ② 일요일 편지 = '지난 주 회고 + 다음 주 미션' 프레임(weekly-review 시나리오, target/move 없음·PRAISE_ONLY 계열). ③ 월 첫 일요일은 여기에 포트폴리오 감사(trailing-84) 한 분기 더 — 월간 점검(§11).
월~토(dow=1~6)닻 안에서 일간 phase 결정
weekly_plans에서 닻(타깃·예산)을 읽고, 이번 주 누적(week-to-date) 데이터로 오늘 phase(노출/공고화/축하/휴식/안전)·무브·예산 소비를 매일 결정. 타깃은 주간 고정, HOW·WHEN만 일간이 그날 데이터로(§4).
# /api/cron/coach — 요일 분기 (단일 크론, 새 크론 0) const dow = kstDow(today) # 0=일 … 6=토 if (dow === 0) { # 일요일 = 주간 종합 for child of active: runWeeklyPlanning(child) → weekly_plans upsert # LLM 1콜/자녀 편지 = composeLetter({ scenario:'weekly-review', recap, mission }) } else { # 월~토 = 닻 안에서 일간 결정 for child of active: wp = loadWeeklyPlan(child, weekKey) # 없으면 planFor 폴백 wtd = weekToDate(child, weekStart) # 이번 주 월~어제 (토→월~금 5일) hist = trailing28(child) # 다주 노출 누적·distinct dish phase = decidePhase(wtd, hist, wp.budget, wp.ledger, dow) # 노출/공고화/축하/휴식/안전 plan = planFor({ weekly: wp, phase, signals }) # 타깃 고정·무브만 회전·예산 적기 소비 편지 = composeLetter({ ...plan }) # 작문·안전·유사도 가드 그대로 }

호환성: composeLetter·안전가드·유사도가드는 손대지 않는다. precomputed(plan)만 '매일 타깃 재선택'→'주간 고정 타깃 + 일간 phase'로 바뀐다. planFor는 폐기하지 않고 닻 부재 시 폴백으로 남긴다(점진 배포·무회귀).

3. weekly_plans — 주간 닻 (데이터모델)

자녀 × ISO주 1행. 일요일 종합이 쓰고(service_role) 월~토가 읽는다. plan 엔진context.plan 원장을 '하루'에서 '한 주'로 끌어올린 것.

-- sql/2026-06-09_weekly_plans.sql create table public.weekly_plans ( child_id uuid not null references children(id) on delete cascade, week_key text not null, -- isoWeekKey(today+1) — 일요일 종합은 다가올 주 키로 저장(§9 Q1·§13) status text default 'active', -- active|degraded|stale_carried|cold_synth source text not null, -- weekly_llm|carried|cold_synth|none mission text, -- 이번 주 미션 1줄 (부모용·편지마다 일관 주입) mission_target text, -- 초점 타깃 1개 (주 내내 불변) target_pool text[], -- 같은 식품군 대체 식재료 (일간 교정은 이 풀 안에서만) secondary_axis text, -- 보조 축 1개 (환경/자율성, 식품 아님) budget jsonb not null, -- {expose:2~3, push:0|1, cadenceMinGap:1, pushWindow:[화,수,목]} ⭐ 슬롯 대신 예산 ledger jsonb, -- {pushUsed,exposeCount,lastExposeDow,arcWeek,reanchorUsed, -- adviceGivenAt,firstServeDow,progressWeek} ⭐ 소비·다주 메모리·행동지연(§13) arc_week int default 1, -- 이 미션 몇 주째 (다주 아크 3~5주) basis jsonb, -- 종합 근거 스냅샷(지난주 metrics·reds·favoriteFoods) basis_hash text, -- 7일 입력 지문(동일하면 LLM 스킵·carry) basis_attends_daycare boolean, -- 등원 변경 감지(§8 ⑧) generated_at timestamptz default now(), primary key (child_id, week_key) ); alter table public.weekly_plans enable row level security; create policy weekly_plans_read on public.weekly_plans for select using (child_id in (select id from children where parent_id = auth.uid()));
저장물역할
mission / mission_target이번 주 단일 방향. 모든 평일 편지 하단에 일관 주입 → 서사 연속. 일요일 회고 편지에 재사용.
budget + target_pool슬롯(요일 대본)을 대체. 주간은 '얼마나(노출 2~3·채근 1·간격)'와 '풀'만 잠그고, 어느 요일에 무엇을은 일간이 그날 데이터로 결정(§4).
ledger.arcWeek · exposeCount다주 메모리. 일간이 못 보는 '이 미션 몇 주째·누적 노출 N/10회'를 보존 → 8~10회 도달 전 끊기 방지.
ledger.pushUsed채근 1회/주의 단일 진실. DB 컬럼 하나로 하드 캡(코드가 절대 2회 못 넘김).
basis_hash지난주 입력이 같으면 일요일 LLM 스킵·carry(비용 절감). plan 엔진의 srcHash 패턴 동일.

스키마 변경 최소: 신규 테이블 1개 + coach_letters.context(기존 jsonb)에 weekKey·phase·exposeCount·pushApplied·arcWeek 키 추가(DDL 불필요). 새 RPC 불필요 — 크론이 직접 upsert.

미션 개수 · 수명 · '완료'의 의미 설계 결정

핵심 전제 — 단위는 '주당 음식 수'가 아니라 '음식당 주 수'다. 새 음식 1개 수용엔 8~10회 노출(까다로운 아이 15~20회)이 격일~주 2~3회 간격으로 필요 → 음식당 보통 3~5주. 한 주엔 2~3회만 적립하므로 한 주로는 한 음식의 노출 예산을 절대 못 채운다.

질문결정
미션 몇 개?부모 체감 1개 = 엔진 내부 1 + 0.5. 주력 식품 1개(주 2~3회 노출) + 보조 1개 — 보조는 식품이 아니라 다른 축(환경·자율성). 식품 타깃 2개 동시 = 금지("한 번에 하나" 위반·반복 노출 흩어짐 → 옛 '휙휙' 회귀). 상한 1.5 · 하한 1.
미션 수명미션 ≠ 한 주에 끝내는 과제. "이번 주에 이 음식에 2~3회 적립" → 같은 미션을 2~3주+ 지속(다주 아크). '주'는 미션 수명이 아니라 재진단 주기.
'완료' 정의이벤트가 아니라 누적 상태. 1~2회 먹음 ≠ 완료(변동성 범위 안·내리면 수용 붕괴). 판정 = distinct dish 2+(마스터리 배지 기준) 또는 연속 2~3회 거부 없는 수용. 판정은 일요일 회고 권한, 평일은 미션 못 끔.
주중 조기 수용미션 종료 ❌ → 유지(공고화) 모드로 격하: 그날 축하(행동 0) → 남은 날 push 끄고(채근 예산 선소진) 변주·칭찬으로 노출만 유지. 새 하드 미션 끼워넣기 = 안티패턴(축하 직후 새 숙제는 강화 파괴). 부모에겐 "정복/끝" 금지 → "친해지기 시작".

채근(주 1회) ≠ 노출(주 2~3회). 두 축을 혼동해 '주 1회만 노출'로 줄이면 노출 예산 부족으로 오히려 너무 느려진다. 노출(예산 배정)은 2~3회, 적극적 들이밀기(push 무브 mix/beside)만 1회.

4. 주간 ↔ 일간 역할 분담 — 그날 데이터 반응 + '토요일 문제'

핵심 경계선: 주간은 무엇을(WHAT) 잠그고, 일간은 어떻게·언제(HOW·WHEN)를 매일 그날 데이터로 결정한다. 요일별 슬롯(고정 대본)은 만들지 않는다 — 그게 일간의 반응성을 죽이기 때문(이 보고서 초안의 결함이었다).

결정 대상주간(느린 축·일 1회)일간(빠른 축·매일)
초점 타깃 1개 · 타깃 풀 · 보조 축🔒 잠금
예산(노출 2~3 · 채근 1 · 케이던스 간격) · 졸업 판정권 · 다주 메모리🔒 발급·독점
오늘 phase(노출/공고화/축하/휴식/안전)✅ 매일
무브 · 각도 · 음식 · 문장 · 예산 적기 소비✅ 매일

"그럼 주간이 무용해지는 것 아닌가?" — 아니다(단 조건부). 주간이 'target 한 줄 + 카운터'만 주면 진짜로 '타깃 힌트 붙은 일간 엔진'으로 붕괴한다. 주간이 살아있는 건 일간이 구조적으로 못 보는 것을 쥐기 때문 — ① 다주 메모리(일간은 이번 주만 봄 → '콩류 3주째 7/10회 노출'은 주간 닻에만 존재) ② 케이던스 잠금(격일~주 2~3회는 주 단위 분배 → 일간이 매일 자유 판단하면 연타·권태) ③ 졸업 판정 독점(1~2회 먹음은 변동성 → 평일이 끄면 수용 붕괴). 이 셋이 빠지면 붕괴 맞다. 그래서 주간 = 일간의 행동 경계를 정의하는 헌법.

'토요일 문제' 해소 — 이중 데이터 창

현행 결함: 데이터 창이 trailing-7(어제까지 7일)이라 일요일 종합이 사실상 '지난주'만 본다 → 토요일에 월~금 5일이 0% 반영. (슬롯 미리 박기도 같은 병.) 해결 = 창을 둘로 분리.

week-to-date (주 시작 월 ~ 어제, 가변 길이) # 이번 주 진행 전담 → 오늘 phase · 예산 소비 · 무브 결정. 토요일엔 월~금 5일이 전부 들어온다. trailing-28 (어제 기준 28일 고정) # 다주 누적 전담 → 누적 노출 횟수 · distinct dish · 재노출 적기. 노출 이력 절대 보존.

토요일의 '오늘 뭐 할지'는 일요일이 박은 게 아니라 토요일 새벽에 이번 주 누적을 직접 보고 내린다. 슬롯이 없으니 미리 박힌 결정이 없다.

그래도 휙휙이 재발 안 하는 이유: 매일 재계산하는 건 HOW(phase·move·timing)뿐 — WHAT(타깃)은 주간 고정이라 일간 입력이 아니다. 게다가 phase는 누적의 단조(monotone) 함수(노출 횟수는 줄지 않음 → 노출일→공고화일→축하일로 전진만, 진동 없음).

5일 워크스루 — 미션=콩류, 예산 노출3·채근1

요일phase일간이 그날 데이터로 내린 결정
노출두부 · 함께 요리
노출자율성 무브로 격일 유지(케이던스 간격)
축하화요일 콩 수용 감지 → 행동 0 · push 미소비
공고화누적 노출 2·수용 1 · 가지 새거부 떴음 → 하단 한 줄 안심만(타깃 불변) · push 적기 아님(보류)
공고화+소비push 1회 소비(pushUsed=false) — 금요일 고정이 아니라 수·목이 적기가 아니어서 금에 떨어진 것
휴식/유지WTD=월~금 5일 전부 봄(노출2·수용1·push 소진·가지거부1) → 변주·칭찬

현행이면 토요일도 trailing-7로 콩을 또 밀거나(휙휙), 슬롯 모델이면 일요일에 박은 '환경' 슬롯을 데이터 무시하고 실행. 예산 모델은 화/축하·목/새거부·금/예산소비가 전부 그날 데이터로 갈린다.

일관성 · 충돌 우선순위

dormancy(휴면 복귀) > shock(데이터 쇼크) > ARFID 게이트 > 닻 타깃 준수 > planFor 자율(폴백)

닻(타깃·예산) 전체 재작성은 오직 다음 일요일. 월~토는 닻 안에서 phase만 고르고 타깃을 못 바꾼다. 예외 — 주중 결핍 구조가 근본 변동(red 2개+ 신규 && 타깃 풀이 하나도 안 다룸 && 이번 주 첫 발생)이면 남은 주의 풀만 갱신(과거 노출 카운트·pushUsed 보존). ledger.reanchorUsed주 1회 하드캡 → 재닻 남발=옛 휙휙 차단. 단순 새거부 1건은 재닻 아님(편지 하단 한 줄).

5. ⭐ 채근 정책 변경 — 주 0회 → 주 1회

이사님 지시: "채근도 이제는 아예 안 하기보다 주당 1회 정도는 하는 걸로 하자." 이 한 줄이 옛 plan 엔진의 핵심 가드를 뒤집는다.

옛 규칙 (plan 엔진)

  • planFor: 어제와 같은 타깃이면 무조건 다른 각도(정체기)로 전환
  • = 같은 걸 이틀 연속 미는 채근 0회
  • 부작용: 반복 노출이 구조적으로 불가능(같은 음식 주 2~3회를 못 함)

새 규칙 (주간 닻)

  • 예산으로 노출 2~3회 배정(요일은 일간이 데이터로) = 반복 노출 = 정상
  • '직접 밀기(push)'는 ledger.pushUsed정확히 주 1회
  • 나머지 날은 같은 타깃이라도 부드러운 변주(조리법·궁합·환경·칭찬)

'채근(push)'의 정의 — 코드 판정

채근 = 같은 타깃을 적극적으로 들이미는 행동. 구체적으로 moveKey ∈ {mix, beside}('좋아하는 음식에 섞기'·'옆에 곁들이기')로 같은 타깃을 한 번 더 제안하는 것. 나머지 무브(recook·pair·cook-together·model·sensory)와 환경/자율성/칭찬은 변주이지 채근이 아니다.

주 1회 강제 — 결정론 카운트

장치규칙
상한(주 1회)주간 예산에 push:1 + pushWindow(화~목) 발급. ⭐ 1순위 게이트(행동 지연 §13): firstServeDow=null(이번 주 한 번도 안 차림)이면 push 선잠금=0 — 못 산 걸 채근 못 함. 그 위에 일간이 적기(daysSinceAdvice≥grace && exposeCount≥1 && 수용 징후)에 1회 소비 — pushUsed=false면 적용 + 기록. 이미 true면 강등(moveKey push계열→비push계열, 타깃 유지). 예산이 2여도 코드가 1회로 클램프.
하한(0 허용)그 주 적기를 못 만나거나(수용 징후 없음) 안전 인터럽트에 밀리면 그 주 0회 — '최대 1회'이지 '의무 1회'가 아니다. 금요일 고정이 아니라 적기에 떨어진다(§4 워크스루).
0회 강제잠금ARFID 의심(icfqRiskCount≥2·증상 메모·먹는 종류 감소) 또는 휴면 복귀 주는 종합 시점에 pushUsed=true 선잠금 → 그 주 채근 0회. 압박이 이탈·역효과인 아이를 보호.

왜 DB 컬럼 하나로 충분한가: 채근 카운트가 weekly_plans.ledger.pushUsed 한 곳에만 있어(단일 진실), 일일 재실행(force/QA)에도 멱등이고 별도 카운터가 필요 없다. '주 1회'가 데이터 구조에 박혀 자연히 강제된다.

시그니처 dedup은 유지. 같은 타깃이 여러 날 와도 (frame·move·도입)이 최근 3일과 겹치면 무브·도입을 회전 → 복붙 편지 방지. dedup의 차단 대상이 '의미(같은 처방)'에서 '표현(같은 문장틀)'으로 좁아진다(타깃 반복은 주간 미션이 책임지므로 허용).

6. 그날그날 데이터 상황대응 — 작은 교정 vs 변경 시점

일간의 역할은 주계획을 안 뒤집고 그 위에 그날 반응을 얹는 것. '작은 교정'과 '변경 시점'을 경계표로 가른다.

그날 델타처리타깃 변경?시나리오채근 카운트
없음phase 진행(누적 단조)X닻 타깃예산 따름
전환 감지(거부→수용)작은 교정 · 그날만 축하보존progress-celebrate영향 없음(행동 없음)
새 거부(타깃 아님)작은 교정 · 한 줄 안심유지닻 타깃 유지영향 없음
icfq-risk 누적 ≥2변경 시점 · 안전 override그날만 무시neophobia-arfid-watchpush 강등
휴면 2~7일휴면 경로 우선소비 정지reengagepush 보류

원칙: 안전·즉시성이 필요한 신호(전환 축하·적신호)는 미루면 안 되므로 그날만 타깃 위에 얹는다 — 단 이건 weekly와 '충돌'이 아니라 '안전 인터럽트'로 기록되고, 다음 날은 다시 닻 타깃으로 복귀한다(주계획 보존). 일반적인 새 거부·메모는 편지 하단 한 줄로만 반영(타깃 전환 안 함) → 닻이 헐거워지지 않는다.

예산 페이싱(몰빵 방지): 예산을 단순 카운터로 두면 화요일에 노출·채근을 다 써버려 주 후반이 빈다. ledger.lastExposeDow + cadenceMinGap=1(노출 간 최소 1일)로 강제 → 격일 리듬(반복노출 과학)을 일간이 어겨도 코드가 막는다.

'변경 시점'은 일일이 절대 주간 타깃을 다시 쓰지 않는다는 뜻. 그날만 무시할 뿐. 닻(타깃·예산) 재작성은 다음 일요일 종합의 권한(또는 드문 주중 재닻 §4).

7. 35 시나리오 — 분배 · 선택 시점 · 변경 시점

13 시나리오(설계상 35 이론) 위에 '주간 아크'를 얹는다. 시나리오를 3티어로 나눠 분배 규칙을 다르게 준다.

티어시나리오주간 배치
MISSIONnutrient-gap · home-daycare-gap · new-refusal · re-exposure-timing · repeat-menu · texture-refusal · autonomy-power-struggle · mealtime-atmosphere · reward-bribe-backfire한 주를 끌 수 있는 행동 테마. 일요일에 최우선 2개 = 주테마·보조테마로 고정.
INTERRUPTprogress-celebrate(100) · neophobia-arfid-watch(92) · low-data-gap(84)주중 어디든 끼어드는 우선 신호. 그날만 타깃 위에 얹음(전환·적신호·기록공백).
RESTplateau(30)일요일 회고 + 주중 데이터 빈약 시 폴백(칭찬·쉬어가기).

한 주 예시 — 예산(노출3·채근1)을 일간이 배분한 결과

⚠️ 이건 일요일에 미리 박은 대본이 아니다. 주간은 '콩류·노출3·채근1'만 잠그고, 아래 요일 배치는 일간이 그날 데이터로 만든 한 가지 예시 결과다. 데이터가 다르면 같은 예산이라도 요일이 달라진다(§4 워크스루).

주간 회고
+미션 예고
콩류
cook-together
생선
보조테마
콩류
sensory(변주)
재노출/전환
후보
콩류
⭐ push
(주 1회 채근)
환경
식사분위기
무압력

콩류가 월·수·금 3회(반복 노출) — 단 월=함께 요리, 수=감각 사다리, 금=주 1회 직접 밀기. 같은 타깃, 매일 다른 무브 → 권태 없이 노출 누적. 화·토는 다른 축으로 환기.

선택 시점 vs 변경 시점

시점무엇주체
선택일요일 종합 — 28일 분석 → 초점 타깃 + 예산(노출·채근·간격) 발급(요일 슬롯 아님)LLM 1회(적소)
매일 배분그날 phase(노출/공고화/축하/휴식)·무브·예산 소비를 week-to-date로 결정(타깃은 고정)코드
변경매일 trigger 게이트로 '오늘 적합성'만 재확인 — 못 따르면 그날만 자율, 닻 자체는 안 바꿈코드
재작성주테마 신호 완전 소멸·진부화(부모 2주 미션 무시) → 다음 일요일 각도 전환LLM(다음 주)

구조화 입력 → 주간 미션으로 승격

식감·자율성·식사환경·식사시간 4개 구조화 입력을 '오늘 한 줄'이 아니라 '이번 주 미션'으로 끌어올린다(지난 7일 분포 집계 = notes 키워드보다 강한 데이터 기반).

입력임계(지난주)주간 미션
식감(texture)최빈이 puree/mashed에 머묾 & 연령상 위 단계 가능 & 기록 ≥3끼"이번 주 = 한 끼만 한 단계 위 질감(으깬→손가락)"
자율성(autonomy)스스로 self 비율 <30% & 기록 ≥4끼"이번 주 = 스스로 떠먹게 둘 끼니 하나"
식사환경(environment)(영상+돌아다님+놀이) 비율 >40% & 기록 ≥4끼"이번 주 = 한 끼는 영상 끄고 식탁에서"
식사시간(meal_time)30분+ 비율 높음자율성/환경 테마의 보조 근거(단독 테마는 과함)

⚠ 선행 수정 필요: 현재 cron이 구조화 입력 중 autonomy·environment를 select조차 안 함 → 코칭에 0% 반영 중. 이 주간 설계의 전제는 cron select에 두 컬럼 추가다.

8. 적대 엣지케이스 10종 + 결정론 안전망

2층을 깨려고 시도한 10개 케이스. 안전 제1원칙 = 일간 결정론은 닻 유무와 무관하게 항상 단독으로 편지를 낼 수 있어야 한다(닻은 '있으면 좋은' 안정 장치). 모든 완화책은 weekly_plans 1개 테이블 + planFor 폴백에 수렴한다.

#케이스결정론 안전망
신규 자녀(일요일에 7일 이력 없음)LLM 미호출. cold_synth — 결정론 온보딩 4주 아크 닻(관찰·신뢰, 거부 단정 금지). mission=null. 비용 0.
휴면 → 복귀(주중)휴면 경로(reengageBlock)가 닻보다 우선. 복귀일 shockResetAt 기록 → 남은 주 준수 강제 안 함. push 선잠금(복귀 직후 채근=이탈 위험).
저데이터 주(기록 듬성)닻 타깃 trigger가 통과 못 함 → planFor 자율. 주간은 degraded, 미션=행동 금지·"기록 채우기 한 줄". LLM이 미션 만들어도 화이트리스트 교집합 비어 target 전부 null 강등 → plateau 수렴.
주중 데이터 쇼크(새 거부 폭증·전환)쇼크 게이트(신규 거부 ≥3 OR 전환 발화 OR icfqRisk 점프) → 닻 무시·그날 신호로 자율. progress-celebrate(100)/neophobia(92)가 어차피 닻을 이김.
주계획 진부화(부모 미션 무시)진부화 타이머(부모 무시)와 진척 타이머(수용 누적 중)를 분리. missionTouch 있고 수용 오르는 중 = 3주 캡 해제·같은 미션 지속(8~10회 도달 직전 끊기 방지). 부모가 2주째 손도 안 댄 미션만 같은 타깃 반복 금지·각도 전환(섞기 실패→함께 요리).
다자녀weekly_plans는 child_id PK라 자녀별 독립 닻. 알림은 현행 notifiedParents로 부모당 1건. 미션은 자녀 탭별 표시.
일요일 종합 LLM 실패/비용try/catch → 지난주 닻 있으면 carry(stale_carried, 안정감>최신성). 없으면 cold_synth. basis_hash 동일 시 LLM 스킵. 일간은 절대 영향 없음.
기관 변경·등원토글basis_attends_daycare ≠ 현재면 집-기관 관련 타깃(home-daycare-gap·nutrient-gap) 무효화·그날만 자율. 등원 ON 직후 첫 주는 re-exposure 보류(신호 미성숙).
주 경계(부모가 주중 가입)그 주 닻 없음 = planFor 폴백(정상·무편지 0). 첫 일요일에 7일 이력으로 정식 점화(이력<3이면 cold_synth). "가입 주는 닻 없이 일간, 첫 일요일에 닻 점화."
채근 1회가 역효과인 아이(ARFID)icfqRiskCount≥2 등 ARFID 신호 → 종합 시점에 pushUsed=true 선잠금(채근 0회). neophobia는 PRAISE_ONLY라 target=null = 밀 게 없음. 2층이 채근을 못 켠다.

결정론 안전망 요약: ① 닻 부재·LLM 실패 → planFor 폴백(무편지 0) ② LLM 환각 target → 화이트리스트 교집합 강등 ③ 환각 scenarioId → 13개 검증 실패 시 드롭 ④ ARFID·휴면 → push 선잠금 ⑤ 쇼크 → 닻 무시·자율 ⑥ 신규/저데이터 → cold_synth·degraded(LLM 미호출) ⑦ push 상한 → 코드 클램프 1회.

9. 결정 필요 — 이사님 확인

Q1. 주 경계. ✅ 확정 — 일요일. 이사님: "월요일 오전은 바빠서 못 본다. 엄마가 일요일에 회고+다음 주 안내를 받아 주말에 계획·장보기." → 일요일 종합이 쓰는 닻 week_key = isoWeekKey(today+1)(다가올 월요일 기준, +1 보정). 월~토는 자기 날짜 키로 읽어 일치. "닻 쓰기(일요일)만 +1, 읽기(월~토)는 자기 날짜." 일요일 편지 보관 3→7일(월요일 늦게 봐도 안내 살아있게). 상세 §13.
Q2. 일요일 부하. 자녀당 종합 LLM +1콜이 일요일에 몰림(현재 활성 ~4명은 무해, 키트 캠페인 유입 후 재검토). 못 끝낸 자녀는 월요일 lazy 폴백(품질 동일, 분배일만 다름). → 권장: 허용, 스케일 시 self-fetch 배치 체이닝.
Q3. 미션을 부모에게 노출? ✅ 확정 — 노출 금지. 이사님: "미션은 부모에게 노출하지 마. 그냥 코치 편지로만 움직이게 해." → 미션은 엔진 내부 나침반일 뿐, '이번 주 미션' 카드·진척 게이지·숙제 목록 0. 부모가 보는 건 매일 코치 편지 하나 + 일요일 회고. 일요일 장보기 힌트도 미션 카드가 아니라 권유 한 줄(§13). ⚠️ RLS 누수 주의: weekly_plans parent-read가 mission_target/target_pool/ledger 노출하면 부모 클라가 직접 fetch=누수 → 그 컬럼 select 제외(안전 뷰). 도감 '추천' 배지는 코칭 타깃과 분리.
Q4. 채근 톤. '주 1회 같은 타깃 한 번 더 밀기'가 무압력 원칙(Satter 식사 분담)과 충돌 안 하게 promptHint 문구를 짜야 함. → 권장: 적대검수 워크플로 1회(채근·장보기 권유 톤 둘 다 검수).
Q5. 구조화 입력 신뢰도. ✅ 확정 — 가볍게 주 1회 권유. 이사님: "가볍게 권유해, 주 1회 정도." → autonomy/environment 비면 주 1회 PS 한 줄로 가벼운 입력 권유(profileNudge 패턴, 매일=잔소리 금지). 임계 미달이면 환경 테마는 보수적으로 안 띄움(데이터 부족 단정 금지·P4).

10. 구현 범위 — 이 문서 이후

이 문서는 설계 시점엔 설계만이었다(코드 변경 0). 아래 작업 단위는 2026-06-10 전부 구현·배포됨(43f8e75·99e2c50·6bfc65a·84fbad9) — 현행과의 차이는 상단 부록(06-11) 참조. 구현 시 작업 단위:

단계작업선행
선행cron select에 autonomy,environment 추가(구조화 입력 0% 반영 버그) + 데이터 창 이중화: 현행 trailing-7 옆에 week-to-date(weekStart 필터·추가 쿼리 0) 신설독립
1sql/2026-06-09_weekly_plans.sql 실행(이사님 승인 — slots 없이 mission_target·target_pool·budget·ledger·arc_week) + lib/coachWeekly.ts(runWeeklyPlanning·kstDow·decidePhase·cold_synth)선행
2cron/coach 요일 분기 + 닻 로드 + decidePhase(wtd·trailing28·budget·ledger) + shockDetect + ledger 소비. weekly-review 시나리오 1개 추가1
3planFor에 옵셔널 weekly?·phase?·pushAllowed? 인자(하위호환·점진 배포). 타깃 고정·무브만 회전 + 채근 캡·예산 페이싱 배선2
4QA: ?date=토요일월~금 5일 누적 반영 검증 + 아린 한 주 시뮬(채근 ≤1·휙휙 0·예산 몰빵 0). 적대 톤 검수3

호환성: composeLetter·안전가드·유사도가드 무변경. planFor 폴백 보존(닻 없으면 현행 100% 동작). Vercel 크론 한도 유지(추가 0). 잔여 의존: 크론 아키텍처(2-크론 한도)·CRON_SECRET 미설정.

11. 월간 점검 — 3층으로 확장 (포트폴리오·전략) 설계 결정

주간이 일간을 제약하듯, 그 위에 월간이 있다. 단 후보를 엄격히 거르면(테스트: 월간에만 보임 AND 주간이 소유 안 함) 진짜 월간 '결정'은 딱 두 가지다. 나머지(성장 곡선·repertoire·KDRI 추세)는 월간이 보는 계기판이지 결정이 아니다.

비유결정데이터창주기
일간전술HOW·WHEN — 오늘 phase·무브·예산 소비week-to-date + trailing-28매일
주간작전WHAT·얼마나 — 초점 타깃·예산·졸업 판정trailing-28일 1회
월간전략어느 군 순서로·언제 포기 — 포트폴리오·stuck 전환trailing-84(2~3 아크)월 1회

판별 한 줄: 같은 음식 며칠째냐 = 일간 / 어느 음식 몇 주째냐 = 주간 / 어느 군 차례냐·지난 달 대비 늘었냐 = 월간. 위층이 아래층을 제약(잠금), 아래층이 위층에 신호(누적)를 올린다.

월간이 소유하는 것 — 딱 2개(결정) + 계기판

결정왜 월간만 가능
① 식품군 포트폴리오·시퀀스주간은 '이번 주 타깃 1개'만 봄. '8군 중 어느 군을 어떤 순서로, 치우침 없이'는 음식당 3~5주 아크라 주 단위로 못 봄. 지금 targetPoolForScenario는 결핍 전체를 매주 평평히 회전 → 군 사이를 산만하게 튐.
② 전략 효과·회귀(stuck) 감지졸업은 3~5주 걸림 → 주간은 표본 1개. '최근 4주 4개 타깃 돌렸는데 졸업 0 = 이 아이에게 표준 무브가 안 먹힘'은 월 창에서만 보임. 전환율(시도÷졸업)은 어디서도 집계 안 됨.
계기판(결정 아님)성장 곡선(키·몸무게/BMI 추세)·repertoire 곡선·KDRI red→green 추세 = 월간이 보는 신호이자 부모 성장곡선. ①②의 입력일 뿐 독립 결정 아님.

제약 계층 — 월간이 주간을 잠근다 (일간 직접 X)

월간: 포트폴리오 풀 · abandon 플래그 · 성장 모드 # 잠금만, 편지 안 씀 ▼ (반드시 주간 경유 — 2칸 건너뛰기 금지) 주간: 좁혀진 풀 ∩ 결핍에서 이번 주 타깃 선택 ▼ 일간: 그 타깃에 오늘 phase·무브

월간이 잠그는 3가지: ① 포트폴리오 풀(소외 군 우선 화이트리스트 → 주간 타깃 후보 좁힘) ② abandon 플래그(stuck 아크 강제 종결 → 주간이 arc_week++ 무한 연장을 멈추고 전환 — 주간 혼자선 못 끊는 결정을 월간이 풀어줌) ③ 성장 모드(저체중 추세면 energy-dense 우선·과한 채소 채근 자제 / 과체중이면 반대 — 처방 아니라 예산 톤 경계). 우선순위: dormancy > shock > ARFID > 월간 abandon/floor > 주간 닻 > 일간 자율.

월간이 막는 단 하나의, 그러나 결정적인 실패없으면 새는 것
「한 번에 하나」 + 아크 무한 연장 → 한 식품군에 영원히 갇힘. 예: 콩류 미션 5주째, distinct dish 1개(두부만)·노출 14회·수용 0인데, 주간은 '8~10회 도달 전 끊기 방지'로 arc_week++만 하고 생선·비타민A 채소가 red로 방치돼도 못 본다. 일간=이번 주만, 주간=이번 아크만 → 둘 다 구조적으로 못 봄. 월간 포트폴리오 감사 1개에서만 잡힌다.

과설계 판정 — '별도 층'이 아니라 '주간의 월 첫 주 분기'

지금(MVP) — 거의 공짜·부모 노출 0

  • 새 크론·새 LLM·새 화면 0. 일요일 runWeeklyPlanningif(월 첫 주) runPortfolioAudit() 한 분기. 그날만 trailing-84를 본다(주간 trailing-28과 유일한 차이).
  • ① 8군 미션 이력 + computeGroupWeekly red 교차 → 소외 군 1개 → 다음 아크 풀 '소외 군 우선' 플래그
  • ② stuck(progressWeek≥5 && distinct≤1 && expose≥12 && firstServe 존재) → abandon=true. ⭐ 달력주가 아니라 노출 진척주(실제 차려진 주)로 판정 — 시차로 '못 사서 노출이 안 쌓인 것'을 거짓 stuck으로 오판 안 함(§13)
  • 저장 = weekly_plans.monthly_audit jsonb 1컬럼(별도 테이블 X). 결정론(LLM 0).

나중(스케일 후) — 보류

  • 부모 대면 월간 wins-only 리포트 + 성장 곡선(자기 추세만)
  • 월간 LLM 종합·전략 플래그가 주간 예산을 실제 제약
  • 만성질환 호전 자동 재평가·제철 자동 풀 회전·별도 monthly_plans 테이블
  • 트리거 = 주간 층이 먼저 서고 + 키트 유입으로 trailing-84 표본이 의미를 가질 때

왜 지금 풀스펙은 과설계: 주간조차 미구현(설계만)·활성 ~4명이라 월 단위 표본이 통계적으로 무의미·Hobby 2-크론 한도 만석. 그래서 '관점'은 지금 심되(감사 분기), '층'은 주간 다음에.

부모 월간 리포트 = 보류(현 단계). 필요성은 있으나(재측정 가시화·리텐션) 죄책감 위험이 미션 카드보다 크다(성장은 부모 통제 밖이라 불안↑). 만들 때 원칙 = '거울이 아니라 앨범' — 그 달 새로 친해진 음식·distinct dish 늘어난 것만, 성장은 또래/백분위 0·자기 추세 우상향만, 저체중/과체중 라벨·0졸업·stuck은 내부 전략 신호로만(부모 노출 절대 금지). 편지에 녹이기 우선.

12. 코칭 차트 — 롤업 메모리 계층 (L1·L2·L3) 설계 결정

질문: 병원 차트처럼 각 층이 자기 판단을 기록으로 남기고, 상위 층은 raw를 매번 다시 읽는 대신 하위 층의 요약 + 자기 층 이력을 읽는가(L1·L2·L3 캐시/DNS 리졸버)? 정직한 답 — 지금은 아니다. 그런데 차트 자체는 이미 매일 만들고 있다(안 읽을 뿐).

현재 상태 — 정직한 진단(코드 grep 기준)

조각상태실체
L1→L1 (일간이 자기 최근 판단 읽기)✅ 작동context.planrecentPlans(3일) → 중복 회피. raw 아닌 '기록'을 읽는 유일한 가동 롤업.
차트 영속 기록(주/월/분기)⚠️ 있으나 안 읽음period_summaries를 크론이 매일 trailing-365 raw 재집계해 upsert. 편지 엔진은 한 번도 read 안 함 — 어드민 화면만 읽는 write-only dead-end.
L1→L2→L3 (상위가 하위 요약 읽기)❌ 0%주간 작전층·누적 노출 카운트·월간 포트폴리오 = 코드에 없음. 각 층이 자기 윈도우 raw를 따로 재집계.

목표 구조 — 가장 가까운 캐시부터, 미스 때만 raw 드릴다운

L1 일간 read = L2 주간 닻(타깃·예산 잔량) + 최근 N일 daily_note # 미스 → week-to-date raw L2 주간 read = 최근 M주 weekly_summary + 이번주 daily_note 롤업 # 미스 → trailing-28 raw L3 월간 read = 최근 4주 weekly_summary + 과거 monthly_assessment # 미스 → trailing-84 raw

기록 구조 — 신규 테이블 금지. 이미 있는 period_summaries에 흡수하고 metrics{retro(후향=경과기록) · plan(전향=처방)}으로 양분한다. daily_note = coach_letters.context(plan + 오늘 판정 스냅샷, 이미 있음). 병원 차트가 '오더(plan)'와 '경과기록(progress note)'을 둘 다 남기듯, 전향/후향을 한 행에 묶는다.

두 철칙

① 요약은 raw를 대체하지 않는다병원 차트 원칙
meal_logs = 원본 랩(영구 단일 진실). 요약 = 읽기 캐시일 뿐, 항상 드릴다운 포인터(exposeCount·acceptCount·원천 log_date)를 동봉. 우리 실패 모드는 데이터 부족이 아니라 '약한 신호를 강한 판단으로 뭉갬'(카레유령=거부메모를 가짜 전환으로 날조·'1회 수용≠완료') — 요약이 "콩류 수용 시작"으로 뭉개면 월간이 그 아래 '사실 한 번뿐'을 못 본다. raw 동봉이 거짓 확신을 막는다.
② 상위 = 하위의 순수 함수(fold), 독립 재판단 금지계층 일관성
주간은 일간 기록들을 fold해 파생될 뿐 raw를 독립 재판단하지 않는다 → 층끼리 어긋날 입구가 없다. (지금은 일간 7일창·주간 28일창·차트 365일창이 각자 raw를 따로 집계해 창-경계 불일치가 구조적으로 가능.) transition/exposure 정의는 lib 한 곳에서 import.

과설계 판정 + MVP

지금(MVP) — 거의 공짜·신규 테이블 0

  • (A) 이미 매일 써지는 period_summaries.week 행에 엔진 판단 3필드(targetsTried[]·exposeCount{음식:n}·verdict)를 daily plan들을 fold해 추가(~30줄)
  • (B) 편지 엔진이 그 week 행을 닻으로 read — 'write-only dead-end'를 깨는 핵심 한 줄. → 28일 밖 누적 노출 아크(8~10회, 까다로우면 15~20)를 plan이 참조
  • raw 동봉으로 lossy 방어. 진짜 실익 = 장기 노출 아크 하나(비용 절감 논거는 4명에선 약함)

나중(스케일 후) — 보류

  • 전향 weekly_plans(mission_target·budget·졸업판정)
  • 월간 포트폴리오 전환·증분 롤업·dirty 캐시 무효화
  • L2가 raw 대신 weekly_summary 읽기로 전환(비용 ROI는 자녀 수백·노출 누적 시점)
  • 트리거 = 활성 수십~수백 명 + 365일 raw 재집계가 느려질 때

왜 지금 풀 3층 롤업은 과설계: '정확히 판단'의 진짜 병목은 메모리 구조가 아니라 데이터 부족(활성 ~4명)이다. 이 규모에선 윈도우 raw 재집계가 더 단순하고 똑같이 정확하다(재집계 비용 무시 가능). 방향은 옳다 — 후향 요약(경과기록)부터 적립하고, 데이터가 쌓이면 전향 처방(작전)을 그 위에 얹는다.

13. 행동 지연 · 일요일 주경계 · 미션 비노출 이사님 결정

부모 대면 동작과 엔진 신호 해석을 바꾸는 세 결정. 공통 축 = "부모가 보는 건 매일 코치 편지 하나뿐" + "조언과 실행 사이엔 시차가 있다".

A. 미션 비노출 — 코치 편지로만 움직인다 확정

미션('이번 주 콩류')은 엔진 내부 나침반일 뿐, 부모에겐 절대 노출 안 한다. '이번 주 미션' 카드·진척 게이지·숙제 목록·N/10회 노출 같은 카운트 전부 0. 부모가 보는 건 매일 따뜻한 편지 하나 + 일요일 회고.

누수 경로차단
편지가 '이번 주 미션/목표/과제/숙제'라고 받아씀letterDeterministicBad에 정규식 추가(미션·목표·과제·숙제·'N회 노출'·'사세요/구매/장 보세요' 차단) + promptHint 금지어
RLS 누수: 부모 클라가 weekly_plans에서 mission_target 직접 fetchparent-read 정책에서 mission_target·target_pool·ledger 컬럼 select 제외(안전 뷰). 부모는 편지 텍스트만.
도감 '추천' 배지로 이번 주 타깃 역추론도감 추천은 빈도/영양 기준 독립 — 코칭 타깃과 분리(현행 유지)

B. 행동 지연(procurement lag) — 시차를 엔진이 인지 신규 설계

편지를 오늘 봐도 식재료 사서 차리기까지 며칠. 이 시차를 무시하면 우리 실패 모드(약신호를 강판단으로 뭉갬·카레유령)가 재발한다 — "타깃 안 나타남"을 거부로 오판하거나, 살 시간도 없는데 "왜 안 했어요" 채근. 결정론 규칙 6개(신규 테이블 0·ledger 키 3개 추가):

#규칙
R1노출 카운트 = 실제 차림만. exposeCountmeal_logs.ingredients에 타깃이 실제 등장한 끼니만 +1. 조언·편지 발행은 노출 0(조언≠노출). 8~10회 아크가 '엔진이 N번 권함'이 아니라 '식탁에 N번 차려짐'으로 정직하게 전진.
R2firstServe 게이트. 이번 주 타깃이 한 번도 안 차림(firstServeDow=null)이면 push·예산 페이싱·채근 멘트 전부 잠금. 못 산 걸 들이밀 대상 자체가 없다 → push 0회.
R3absence ≠ refusal. '안 나타남'=noExposure(중립), 거부는 refused/ate_well:false만. grace는 '거부 판정 유예'가 아니라 push 보류 게이트 — 차렸는데 거부면 grace 무관 즉시 인정(진짜 거부 안 놓침).
R4grace = 3~4일(주말 장 + 평일 장 기회). adviceGivenAt(타깃 첫 권유일)부터 카운트. firstServe 발생 시 조기 종료(달력은 안전 상한일 뿐) → 일찍 산 부모는 일찍 신호, 늦게 산 부모는 채근 안 당함.
R5아크는 progressWeek(노출 진척주) 기준, 달력주 아님. arc_week++는 노출이 실제 누적된 주만. 시차로 달력 6~10주여도 진척주 3~5 유지 → 월간 stuck/abandon이 '미조달 ≠ stuck'을 구분(거짓 abandon 방지).
R0즉시 실행 우선(시차 0). 집에 이미 있는 것(favoriteFoods·최근 등장·박스 구성품) 먼저 소비. 새로 살 타깃은 일요일 리드타임으로 안내하고, 그 사이 평일은 보유 식재료로 아크를 굴림.

C. 일요일 주경계 + 리드타임 Q1 확정

이사님: "월요일 오전은 바빠서 못 본다. 엄마가 일요일에 우리 편지 보고 다음 주 계획 세워야지." → 주 경계를 일요일로.

일요일 회고(지난 주) + 다음 주 안내(장보기 힌트) # 닻 write: week_key = isoWeekKey(today+1) ▼ 주말 장보기 = 시차 1차 방어선(월요일부터 집에 식재료) 월~토 닻 실행(자기 날짜 키로 read) # grace = 시차 2차 방어선

"닻 쓰기(일요일)만 +1, 읽기(월~토)는 자기 날짜." isoWeekKey는 월요일 시작이라 일요일을 그냥 키하면 지난주가 나옴 → isoWeekKey(today+1)로 다가올 주 키 저장(off-by-one 해결). 일요일 편지 보관 3→7일(월요일 늦게 봐도 안내 살아있게).

D. 편지 UX — 권유이지 과제가 아니다 확정

일요일 편지

  • 회고 win 1개(새로 친해진 음식·distinct dish 늘어난 것 — 진척 숫자 칭찬 금지)
  • 다음 주 권유 1줄 — 가정형: "주말에 두부 한 모 있으면, 이번 주에 ○○이 좋아하는 볶음밥에 같이 넣어볼 수 있어요"
  • 주어 = "코치가 ~해볼게요"(엄마 과제 아님). 명령형 '사세요' 금지

평일 편지

  • 이미 집에 있는 것 우선(오늘 당장 할 수 있는 것)
  • 새로 살 것은 '다음 장 볼 때' 미리·부드럽게
  • grace 안엔 '왜 안 했어요' 채근 금지 — 보유 식재료 변주·환경 축으로 환기
  • '이번 주 ~하기' 같은 주간 과제어 금지. 그날 한 걸음만

죄책감 0 경계: 권유(있으면·천천히·여유될 때)=OK / 과제(이번 주 안에·꼭·N회)=금지. 못 샀을 때 다음 편지가 추궁 안 함(R2가 firstServe=null이면 채근 멘트 데이터로 차단). 장보기 힌트는 ~주 1회 로테이션(매일 반복 X).

과설계 경계: 신규 테이블·크론·부모 UI 0. ledger jsonb 키 3개(adviceGivenAt·firstServeDow·progressWeek) + decidePhase firstServe 분기 ~10줄 + letterDeterministicBad 정규식 3종. 활성 ~4명에선 '관점만 심고' 복잡한 조달 예측은 보류.

14. 주간 종합의 깊이 — 비싼 모델·종합 신호·35이론·의사 소견 이사님 결정

이사님: "주간 계획은 비싼 모델로 종합적으로 봐서 가이드를 잡으면 좋겠다. 식단 말고도 35개 국제 편식 이론 기반으로 검토하나? 차트 남길 때 LLM으로 의사처럼 종합 소견을 남기나?" → 네 보강은 하나의 원칙으로 묶인다: 주간 = 비싼 모델이 모든 신호를 35이론 렌즈로 읽어 의사식 소견 + 방향을 남기는 회진(回診). 일간은 그 소견을 들고 그날 한 걸음을 싸게 실행. 현재는 편지가 Haiku 단일·주간 종합 미구현이라 전부 미구현 설계.

① 모델 티어 — 비싼 판단은 드물게, 싼 실행은 자주

모델빈도역할
일간결정론 + Haiku(작문만)매일닻 안에서 그날 한 걸음·톤
주간 종합Sonnet 4.6 (기본) · Opus 4.8(승급 옵션)자녀당 주 1회종합 회진·방향·소견
월간 감사Sonnet 4.6 또는 결정론월 1회포트폴리오·전략(§11)

✅ 주간=Sonnet 4.6 확정(이사님). Sonnet 4.6($3/$15)은 28일 신호+35이론 종합·의사 소견에 충분히 똑똑하고, Opus 4.8($5/$25)의 추가가치는 한계적인데 비용 1.4배. 비용 = 주간 ~₩215/child·월, 전체 스택 ~₩630/child·월(구독 ₩4,900의 13%, 일간 Haiku만 라이브인 현재는 ~₩320). 품질을 실제 편지로 모니터 후 정 아쉬우면 Opus로 승급. 일간은 계속 결정론+Haiku(저비용).

② 종합 신호 — "어떤 음식 먹었냐"는 입력 하나일 뿐

주간 종합이 보는 것: 식단(무엇/거부/전환) + 구조화 입력(식감·자율성·식사환경·식사시간) + 만성질환 경과(변비·철결핍 등) + 성장(키·몸무게/BMI 추세) + ICFQ 위험·재노출 적기 + 35이론 렌즈(아래). 식단만 보던 게 아니라 아이 상태 전체를 읽는다.

⚠️ 지금은 구조화 입력이 코칭에 거의 안 붙어 있다(개편의 명시적 버그 수정)
부모가 매번 찍는 식사환경·자율성·식감·식사시간이 — care엔 다 저장되지만 — 코칭엔 거의 미반영이다. 코드 확인: 크론 select가 texture·meal_time만 읽고 autonomy·environment는 select조차 안 함(0% 반영). 시나리오(mealtime-atmosphere·autonomy-power-struggle·texture-refusal)는 있으나 자유텍스트 메모로만 발동하고 구조화 필드 데이터엔 연결 안 됨. → 개편 = ⓐ 크론 select에 autonomy·environment 추가(§10 선행) ⓑ 지난주 분포 집계 → 개선 팁(주간 미션)으로 승격(§7: "한 끼는 영상 끄고 식탁에서"·"스스로 떠먹게"·"한 단계 위 질감") ⓒ 팁은 미션 카드 아니라 편지에 녹여서(§13). 즉 매번 찍는 그 입력들이 비로소 개선 팁이 된다.

③ 35이론 렌즈 — 방법론 진단이 주간의 핵심 산출

35이론(정본: 시나리오 엔진에 13시나리오로 구현)은 지금 일간 시나리오 선택에만 쓰인다. 보강: 주간 종합이 "이 아이의 지금 패턴에 어느 이론/방법론이 맞나"를 Sonnet으로 진단해 이번 주 미션·접근을 잡는다. 예 — 신공포 단계면 SOS 감각 사다리, 자율성 다툼이면 Satter 식사 분담, 정체면 푸드체이닝. 식단 숫자가 아니라 방법론 진단이 주간의 산출.

④ 의사식 종합 소견 — 차트에 narrative impression

병원 차트가 랩 수치 + 의사 impression을 둘 다 남기듯(§12 철칙), 주간 종합이 LLM으로 '경과 소견'을 차트에 적는다 — 숫자가 아니라 판단.

예시 소견(weekly_summary.impression)
"지난주 콩류 노출 3→5회로 늘었으나 형태는 두부 한 가지에 머묾(distinct=1). 화·목 자율성 다툼 메모 2건 — 신공포보다 통제 욕구 신호. 성장 곡선 정상 추세. 이번 주는 식감보다 식사 분위기·선택권 우선(Satter), 콩류는 형태만 바꿔 가볍게 유지."

이 소견을 일간 Haiku(편지 톤·방향 일관)와 다음 주 종합(longitudinal·전주 소견 대비)이 읽는다. 숫자=raw 드릴다운, 소견=판단 — 둘 다 보존(§12). 미션 비노출(§13): 소견은 엔진 내부용, 부모에겐 편지로만.

구현 위치: 주간 종합층(미구현)을 지을 때 함께. runWeeklyPlanningSonnet 4.6으로 종합 신호+35이론을 읽어 weekly_plans.impression(소견)·mission·budget을 산출. 일간/월간은 read. 신규 비용 = 자녀당 주 1 Sonnet 콜(~₩215/월).