🧩 코칭 엔진 모듈 명세 · ①/⑧

🧠 ① 신호선택기 (시나리오 두뇌)

매일 아이 데이터에서 신호를 읽어 오늘 편지의 시나리오(각도)와 행동을 고르는 두뇌 — 선호계량화·P0-D 프로브 신호 입력 지점

← 문서 허브 · 통합 아키텍처 지도 · 코칭 엔진 모듈 명세
🗺️ 이 문서는 통합 아키텍처 지도의 모듈 ①를 코드 단위로 펼친 명세입니다 — 데이터 파이프라인·입출력 예시·가드. 코드 근거: route.ts · coachBrain.ts · coachWeekly.ts · coachDaily.ts · preferenceQuantification.ts (실측 2026-06-20).
🧠 이 모듈이 '뇌'입니다. 매일 아이 데이터에서 신호를 읽어 오늘 편지의 시나리오(각도) 1개 + 음식을 다룰지(useFood) + 추천 검수(approvedRecs)를 고릅니다. 편지 글 자체는 쓰지 않습니다(작문은 ⑥ 작문기). 모델: 뇌 = DeepSeek V4-Pro(BRAIN_MODEL 역할·실패 시 결정론 폴백). 주간 목표 포트폴리오(커리큘럼 유닛) 선택은 일요일 runWeeklyPlanning가 하고, 유닛의 정의·사다리·진척 측정은 ⑤ 진척·커리큘럼이 담당합니다(선택=①, 정의·측정=⑤). P0-D 프로브 신호 입력 지점(pickUnitProbe)도 이 모듈에 있습니다.

비개발자용 — 이 두뇌가 무엇을 왜 하나

이 모듈은 '오늘 부모에게 보낼 코칭 편지를 어떤 각도로 쓸까'를 고르는 두뇌입니다. 식당 셰프에 비유하면, 이 모듈은 '오늘은 콩류 얘기를 할까, 식사 분위기 얘기를 할까, 칭찬만 할까'를 정하는 주방장이고, 실제 요리(편지 문장 쓰기)는 다른 엔진이 합니다. 왜 필요하냐면, 같은 아이의 데이터로 매일 편지를 쓰면 LLM이 자꾸 같은 해법으로 수렴해 '매일 똑같은 잔소리'가 되기 때문입니다. 그래서 13개의 미리 짜둔 '각도(시나리오)'를 두고, 그날 데이터 신호로 딱 1개만 골라 그 각도로만 쓰게 합니다.

고르는 방식은 두 층입니다. 주간 닻(작전층): 일요일마다 더 똑똑한 모델이 지난 한 주를 의사처럼 종합해 '이번 주 주력 축(레버)'을 1개 정합니다 — 음식이냐, 식사 환경이냐, 자율성이냐, 질감이냐. 월~토는 그 축에 맞춰 일관되게 가되, 매일 표현만 바꿉니다. 일간 두뇌(전술층): 매일 두뇌(DeepSeek V4-Pro)가 '발동 가능한 시나리오 목록·최근 편지·영양 평가·이번 주 닻'을 한꺼번에 보고 오늘의 각도를 고릅니다. 두뇌의 제1원칙은 '다양성'과 '없는 문제 지어내지 않기'입니다. 그래서 두뇌가 고른 각도라도 '이번 주 닻이 음식이 아니면 음식 잔소리를 못 넣고', '데이터에 그 신호가 없으면 강제 못 하고', '며칠 연속 음식날이면 휴지'하도록 코드가 잠급니다(프롬프트 호소가 아니라 코드 게이트).

🥗 영양·편식 정보는 이렇게 처리합니다
결핍 판정은 '추측'이 아니라 우리가 계산한 수치만 씁니다. 신호에는 부족 영양소·비는 식품군·'집 끼니에서만 비는 것(homeMissing)'·거부한 음식이 구분돼 들어옵니다. 어린이집 급식 덕에 전체 영양은 채워졌어도 집에서 비면 '집-기관 격차' 각도로 가서 '기관이 챙겨준 덕'이라고 솔직히 인정하고 집 끼니 한 가지에만 행동을 둡니다(기관 메뉴 변경 요청 금지).

반복 노출이 중심 철학입니다. 거부한 음식은 '실패'가 아니라 '아직 처음 보는 것'으로 재정의하고, 격일~주2~3회로 좋아하는 음식 옆에 작게 다시 올려두라고 안내합니다. 괴식·강요는 코드와 프롬프트 양쪽에서 차단합니다 — 음식 추천은 LLM이 새로 지어내지 못하고 'food-graph가 계산한 실제 후보' 안에서만 두뇌가 취사선택합니다(후보 밖 추가 금지 = 환각 0).
🆕 이번에 추가된 것(이사님 2026-06-19~20)
선호계량화 — 지금까지는 '아이가 잘 먹었다(ate_well)'만 봐서, 4번 다 다른 음식을 한 번씩 잘 먹어도 '4개를 좋아한다'고 오판했습니다. 이제 같은 음식을 2번 이상 완식 + 통계적으로 확실해야만 '확신하고 좋아함(liked)'으로 칩니다. 아린은 이 기준으로 '확신 liked = 0' → 그래서 '잘 먹는 것 위에 다리 놓기'가 아니라 '콜드스타트(자주 차려진 음식으로 출발)'로만 작동합니다.
P0-D 프로브 — 코치가 매일 던지던 질문이 '답해도 신호로 흐르지 않던' 다리를 잇기 위해, 오늘 가르치는 유닛의 정식 질문(정규 칩)을 매일 던져 부모 답변을 신호 표본으로 쌓습니다(졸업 판정의 연료). 단, 출력(질문 내보내기) 다리만 깔렸고 입력(답변이 신호로 들어오는 흐름)은 아직 비어 있습니다 — ⑤ 남은 문제 참고.
🧒 운영 예시(아린·5세) — 입력: 콩류·채소가 집 끼니에서 비고(homeMissing), 미역국·소고기는 잘 먹지만 전부 한 번씩이라 '확신 liked=0'(콜드스타트), 어린이집 급식 덕에 전체 영양은 채워짐(missing=0), 메모에 "저녁마다 유튜브 보며 먹어요"(envBadPct 0.55). 일요일 주간 닻은 "이번 주 주력 레버=environment(식사 환경)"로 잡혔습니다. 처리: 두뇌가 13개 시나리오 중 오늘 1개를 고르는데, 닻이 비-food이므로 음식 시나리오는 '며칠에 한 번·캡 안에서만' 허용되고 나머지는 환경 각도(mealtime-atmosphere)로 갑니다. 콩류 추천은 food-graph가 계산한 실제 음식(단호박찜·치즈스틱 등 슬롯 dish)만 후보로 두고, 두뇌가 useFood로 검수합니다. 결과: 매일 '콩 먹여라'가 아니라 '저녁 한 끼만 화면 끄고 식탁에 앉아 함께 먹기' 각도가 그날 편지의 뼈대가 됩니다.

구현된 내용 — 함수·데이터흐름·입출력 (실측)

현재 코드에 실제로 존재하는 함수·타입·file:line·데이터흐름입니다. 환각 없이 실측만.

핵심 함수

함수 · file:line역할 (실측)
pickActionByBrain(ctx, candidates, model)
coachBrain.ts:115
DeepSeek V4-Pro 두뇌 호출 → BrainAction 반환. scenarioIdSCEN_IDS 화이트리스트(coachBrain.ts:32) 검증 — 목록 밖이면 ''(빈값)으로 두고 호출측이 v2 폴백. useFood boolean·approvedRecs 방어 파싱.
buildBrainContext(p)
coachBrain.ts:48
13개 시나리오 메뉴(발동가능·최근사용 플래그) + 영양거울 + 추천후보 + 최근 3주 닻(weeklyEchoes) + 최근편지 + anchorBlock(coachBrain.ts:79 — 닻 lever가 비-food면 '음식 시나리오로 매일 갈아끼우지 마라' 지시) + useFoodBlock(coachBrain.ts:82 — 최근 음식날 시퀀스 '음식→비음식→…' + 직전 2일 음식이면 다른 각도로) + curriculumSummary를 JSON 지시 프롬프트로 조립.
anchorOverrideAllowed({anchorLever,sid,fov,triggerOk})
coachWeekly.ts:121
두뇌 override 게이트(핵심). {allow, isFoodOverride} 반환. SAFE_INTERRUPT_SCENARIOS(progress-celebrate·neophobia-arfid-watch·low-data-gap)거나, food주에선 비구조 시나리오, 비-food주에선 그 레버의 LEVER_SCENARIO[lever] 시나리오면 호환. food주 아닌 식 override는 FOOD_OVERRIDE_CAP=2(coachWeekly.ts:110) 캡 내에서만. triggerOk=false(데이터에 신호 없음)면 불허.
planFromWeekly(p) / weeklyReplan(sid)
route.ts:740 / 750
월~토 닻 실행 — mission_target 잠금·채근캡·아크 단계 회전. weeklyReplan은 두뇌 override 허용 시 '닻 안에서 시나리오만 교체'(타깃·채근캡·effectiveLever 보존).
quantifyPreferences(rows, asOf, {homeOnly})
preferenceQuantification.ts:51
선호계량화. 수용 5단계(0거부·1만짐·2한입·3조금·4완식)로 식재료별 FoodPref(state·score·confidence·trend) 산출. homeOnly 기본 true(기관 급식 제외). state: 신호<2면 unknown, 거부≥2&수용률<0.34면 disliked, 완식≥2&Wilson≥0.5면 liked, 나머지 exploring.
acceptanceLevel(r) / wilsonLower(pos,n)
preferenceQuantification.ts:32 / 40
acceptance_level(5단계 컬럼) 우선, 없으면 ate_well 폴백(true→4·false→0·null→미상). wilsonLower=Wilson 95% 하한(표본 적을수록 보수적 — 1/1 완식을 '확실'로 단정 안 함).
confidentLiked(prefs) / dislikedFoods(prefs)
preferenceQuantification.ts:91 / 95
state==='liked'만 추천 앵커·콜드스타트 게이트로(미상·탐색 제외). dislikedFoods=확실 거부 → 추천 후보에서 제외(거부한 걸 또 권하지 않기).
pickUnitProbe(unitId, seed)
coachDaily.ts:71
P0-D 유닛 프로브 회전. 오늘 focus 유닛의 정규 프로브(passWhen 신호) 1개를 seed % probes.length로 일별 회전 emit. 반환 {question, topic, chips, unitProbe:{unit_id,signal,probeId}}. 유닛/프로브 없으면 null(→ LLM 질문 폴백).
parseProbeAnswers(qs) / buildCandSignals(rows,…)
coachDaily.ts:52 / 17
parseProbeAnswers=부모 답변 → evidence. context.unitProbe 있고 답이 정규 칩과 정확 일치할 때만 적립('잘 모르겠어요'·자유텍스트 미적립). buildCandSignals=주간 후보 신호(envBadPct·refusedCount 등) rows에서 계산.
SCENARIOS / selectScenario(s,recentIds)
coachScenarios.ts
13개 시나리오 상수(라이브러리) + 결정론 폴백 셀렉터 — trigger 충족분을 priority 내림차순, 최근 2일 1차 제외, fresh→fired→plateau 폴백. 두뇌 실패·빈값 시 안전망.

일간 두뇌 데이터흐름 (route.ts 실측 라인)

단계 · route.ts 라인무엇을 하나
선호계량화 · route.ts:368-374prefs=quantifyPreferences(rs, today)confidentLiked·dislikedIng. coldStart = confidentLiked.length < 2(route.ts:372). 콜드스타트면 coldStartSeed(servedTop,6)로 앵커 보충, 아니면 likedIng. dislikedIng는 앵커에서 제외(route.ts:374).
신호 묶음 · route.ts:547-554CoachSignals 조립 — 1주차 미성숙(loggedDays<7)이면 결핍 신호(reds/missing/homeMissing) 비움(route.ts:542-546, 결핍 단정 금지). envBadPct=집 끼니만(route.ts:529).
주간 닻 잠금 · route.ts:702-753anchor.mission_target·lever 로드 → targetAccepts(route.ts:711 — acceptanceLevel≥1만, 미상 null 배제 ⭐신호포착) → planFromWeekly(route.ts:740) → weekCtx에 lever·targetPool·ledger 노출(route.ts:745, 두뇌 게이트 입력).
연속 food날 캡 · route.ts:774-775food주=3일·비-food주=2일 연속 useFood면 brainPick.useFood=false 강제(프롬프트 신뢰 대신 코드 보증).
override 게이트 · route.ts:776-799두뇌 scenarioId 있으면 safeTrigger(sid)(route.ts:778, signals 트리거 충족) + anchorOverrideAllowed(route.ts:782) 통과 시에만 weeklyReplan(sid) 또는 planFor(forceScenarioId). isFoodOverride면 arc=null(잡탕 차단·route.ts:787) + foodOverrideUsed 카운트 적재(route.ts:792-796).
슬롯-본문 봉합(F-18) · route.ts:857-872음식 시나리오(home-daycare-gap·nutrient-gap·repeat-menu)는 _noFood를 끄고 슬롯 음식을 본문에 강제(route.ts:859-860, 콩류→두부 디폴트 차단). slotDish/slotFood(route.ts:864-865) must-weave. 거울 음식=항상 그날 슬롯 음식(route.ts:870-889, deficitDishFor 제거).
거울 출현빈도 쿨다운(#5) · route.ts:890-899mirrorCooldownDue(route.ts:894) — '어린이집 덕에…' 거울줄이 최근 2일 안에 나왔으면 오늘 생략(전체 결핍 2개+면 면제). 무슬롯 경로도 동일 쿨다운(골든완화).
도입 회전(#6) · route.ts:876-883거울 positive 문장을 daySeed 변주 3종(_posV·_foodClause)으로 회전 — 템플릿 축자 반복(4~11통) 차단.
macro input-starvation(#7) · route.ts:229-232성장데이터(키·몸무게·성별) 없으면 macro 트랙(저체중→고기↑)이 영영 비활성 → 성장 권유를 매 3일·효과 명시로 데이터 유입 유도, 들어오면 자동 중단.
P0-D 프로브 emit · route.ts:989-1002ICFQ 스크리너 아닌 날 pickUnitProbe(curriculumDecision?.unit, day-number)(route.ts:990) → 질문 + context.unitProbe 저장(route.ts:999-1002). 출력 다리.

A 추천 근거 — 어드민 노출(이번 세션)

두뇌·계획 산출은 coach_letters.contextbrain(scenarioId·useFood·why) + weekly(lever·effLever·missionTarget·targetPool·ledger) + planSlot(slotIndex·ingredient·dishes·group·track·via·macro) + recoIng/recoPool/recoMode + curriculum(unit·step·mode·pivotTo)로 적재(route.ts:947-960) — 어드민이 "결핍군 콩류 보급(주N회)"·"잘 먹는 두부의 검증 사촌" 같은 추천 근거를 검증.

어느 테이블에서 무엇을 뽑나

테이블 · 데이터뽑는 값운영 의미
meal_logsingredients·refused·note·place·ate_well·acceptance_level(5단계)·environment·autonomy·texture·meal_time아이 '오늘 상태표'. acceptance_level 컬럼이 선호계량화 granular 입력(없으면 ate_well 폴백)
weekly_plansmission_target·budget(lever·push)·ledger(targetAccepts·foodOverrideUsed)·goals·impression이번 주 '나침반'. 두뇌 override 게이트가 이 lever·ledger를 읽고 잠금
coach_letters.context최근 scenarioId·plan·brain.useFood·weekly·mirrorShown — 중복 회피 이력'어제 뭐라 보냈는지'로 같은 각도·연속 food날 반복 차단(다양성 장치)
daily_questionsanswer·context.unitProbe(P0-D)·icfq프로브 답변→신호 적립(parseProbeAnswers). ICFQ 위험 누적은 ARFID 신호
curriculum_progressunit_id·status·step·evidence오늘 focus 유닛 결정(pickUnitProbe·effectiveLever 입력) — 정의·측정은 ⑤
SCENARIOS (코드 상수)13개 각도의 label·priority·trigger·promptHint·avoidDB 아닌 코드 라이브러리 — 두뇌가 이 안에서만 골라 환각 0

설계 아키텍처 시각화 — 입력테이블 → 가공함수 → 출력키

데이터 파이프라인을 '그림'으로. 좌 입력테이블 → 중 가공함수 → 우 출력 컨텍스트키.

📥 meal_logs
ingredients · ate_well · acceptance_level · place · environment · note
📥 weekly_plans
mission_target · budget.lever · ledger
📥 coach_letters.context
recent scenarioId · brain.useFood · mirrorShown
📥 curriculum_progress
focus unit · step
📥 daily_questions
answer · context.unitProbe
⚙ quantifyPreferences
5단계 수용 → state(liked/exploring/unknown/disliked) · Wilson score · confidentLiked
⚙ planFromWeekly
닻 lever 잠금 · targetAccepts(≥1만) · weekCtx 노출
⚙ buildBrainContext → pickActionByBrain
13 시나리오 메뉴 + anchorBlock + useFoodBlock → 두뇌 scenarioId·useFood
⚙ anchorOverrideAllowed + safeTrigger
닻 종속 게이트(허용 시만 override) · 연속 food캡
⚙ pickUnitProbe
focus 유닛 정규 프로브 회전 emit
📤 CoachContext
scenarioId · scenarioLabel · plan · brain · weekly
📤 planSlot
slotDish · slotFood · mirrorLine(쿨다운)
📤 daily_questions
question + context.unitProbe
📤 weekly_plans.ledger
targetAccepts · foodOverrideUsed
일간 두뇌 결정 트리 (route.ts:755-799) ─ '뇌는 작문 안 함, 각도/타깃/useFood/lever 회전만 결정론 보증'

  signals + weekCtx(lever·ledger) ──▶ pickActionByBrain ──▶ brainPick{scenarioId, useFood, why}
                                                                     │
                              ┌──────────────────────────────────────┤
                              ▼                                       ▼
                   연속 food날 캡(774-775)                  scenarioId 있나?
                   food주 3일/비-food 2일                          │
                   ─▶ useFood=false 강제                  ┌────────┴────────┐
                                                          ▼                 ▼
                                              safeTrigger(sid)?         (빈값/실패)
                                              signals에 신호 있나          │
                                                   │                       ▼
                                          ┌────────┴───────┐    결정론 selectScenario 폴백
                                          ▼                ▼        (planFor precomputed 유지)
                              anchorOverrideAllowed(782)   미충족
                              food주│레버호환│안전인터럽트│cap내    │
                                   │                              ▼
                          ┌────────┴────────┐         override 무시 ─ 닻 프레임 유지
                          ▼ allow=true      ▼ allow=false  (비-food주 음식잔소리 차단)
                   weeklyReplan(sid)    닻 lever 프레임 유지
                   타깃·채근캡 보존      (precomputed=닻)
                   isFoodOverride면 arc=null + foodOverrideUsed++

  EARNED: 게이트는 코드에 박혀 프롬프트 신뢰가 아님. 트리거 미충족·비-food주·연속food = 결정론으로 막힘.
  천장: 게이트 입구의 신호(targetAccepts·envTablePct)가 거의 0 ─ 두뇌가 mealtime-atmosphere에 고착(⑤ 참조).

작동 예시 — 아린 데이터 before/after

실제 입력(아린·5세) → 이 모듈 통과 → 출력. 핵심 변화 = 선호계량화로 콜드스타트가 켜지고, 닻 게이트로 음식 override가 막힌다.

📥 INPUT (route.ts가 조립한 신호)
// meal_logs(집 끼니만, 최근 7일):
//   미역국·소고기·계란·감자 각 1회 ate_well=true (서로 다른 식재료)
//   acceptance_level 대부분 null(미상 80%)
//   environment: 5끼 중 3끼 'screen' → envBadPct 0.55
//   note: "저녁마다 유튜브 보며 먹어요"
signals = {
  homeMissing: ["콩류","비타민A채소"], missing: [],   // 기관 급식 덕 전체 OK
  homeRefused: ["당근"], daycareRefused: [],
  notes: ["저녁마다 유튜브 보면서 먹어요"],
  favoriteFoods: ["소고기","계란","감자","미역국"],
  attendsDaycare: true, envBadPct: 0.55, icfqRiskCount: 0
}
weekCtx = { lever: "environment", missionTarget: "콩류",
            ledger: { targetAccepts: 0, foodOverrideUsed: 0 } }
📤 OUTPUT
// ① 선호계량화(quantifyPreferences):
//   미역국·소고기·계란·감자 = 각 1회 → signal<2 → state="unknown"
confidentLiked = []            // ⭐ 0개! (구 누적기는 4개로 오판)
coldStart = true               // confidentLiked.length < 2
likedSeed = coldStartSeed(servedTop,6)   // 자주 차려진 음식으로 출발

// ② 두뇌 brainPick:
{ scenarioId: "nutrient-gap", useFood: true,
  why: "콩류가 집에서 비어 음식 각도 제안" }

// ③ override 게이트(anchorOverrideAllowed):
//   anchorLever="environment"(비-food) · sid="nutrient-gap"(음식)
//   → LEVER_SCENARIO[environment]="mealtime-atmosphere" ≠ sid
//   → 안전인터럽트 아님 · foodOverrideUsed(0)<CAP(2)
//   → isFoodOverride=true, allow=true(캡 내 1회 허용) ...단
//   recentUseFood 직전 2일 모두 음식이었으면 774-775서 useFood=false 선차단

// ④ 최종 CoachContext:
scenarioId: "mealtime-atmosphere"   // 환경 닻 프레임 유지(override 캡 소진 시)
plan.target: "콩류"(배경) · weekCtx.effLever: "environment"
slotFood: 슬롯 dish(콩류 supply 날엔 두부 아닌 구체 dish)
mirrorLine: 쿨다운이면 null
실제 편지 거울/슬롯 줄(예시) — 환경 코칭 날이라 본문엔 음식 액션이 없고, 거울에 슬롯 음식을 소프트 노출: "어린이집 덕에 여러 식품군을 두루 잘 챙기고 있어요. 여유 될 때 단호박찜도 한 번 만나보면 좋겠어요." + 본문 행동: "저녁 한 끼만 화면 끄고 식탁에 앉아 함께 먹어보세요."before(구 누적기): confidentLiked=4 오판 → 콜드스타트 미발동 → 두부 디폴트 반복 + 비-food주에 매일 음식 잔소리. after: confidentLiked=0 → 콜드스타트 + 닻 게이트로 환경 프레임 유지.
📖

문헌 · 이론 근거 (이 모듈이 적용하는 이론)

이론 · 문헌어떻게 적용하나
반복 노출(HabEat)·단순노출거부 음식을 격일~주2~3회 작게 다시 올려 친숙도 축적(진전축하·새거부·재노출 시나리오 근거)
ARFID 구분·감각적 음식 거부식사 적신호가 60일 내 2회+ 누적되면 진단 없이 전문가 안내 1회 + 채근 0(neophobia-arfid-watch·push=0)
자율성 지지(SDT)·Satter DOR어린 연령 광범위 거부를 발달 과업으로 보고 결과는 같되 작은 선택권(자율성·집기관격차 시나리오)
압박의 역효과·무압력 식사환경에 압박·영상 신호가 있으면 단일 레버 1개만(mealtime-atmosphere)
보상 역효과(과잉정당화)보상·디저트 협상이 장기 선호를 떨어뜨림을 알리고 거래를 끊게 함(reward-bribe-backfire)
푸드 체이닝·맛-맛 연합부족 식품군을 좋아하는 음식에 다리 놓기(nutrient-gap·repeat-menu) — 슬롯 dish로 구체화
Wilson 점수 하한(통계)⭐ 선호계량화 — 1/1 완식을 '확실히 좋아함'으로 단정 안 함(표본 적을수록 보수적, likedScore≥0.5 게이트)
편식 궤적(자연 완화)·신뢰 모델뚜렷한 신호 없으면 행동 빼고 칭찬만(plateau·low-data-gap)
Fogg 행동설계 · Gollwitzer 실행의도주간 행동목표를 '하루 한 끼'의 가장 작은 행동 + 구체 트리거 1구('저녁 6시 TV 끄고')로

남은 문제점 · 해야할 일 (랄프위검 47점·2026-06-20)

랄프위검(연속성 감사) 47점. 이 모듈 관점에서 정직하게 — 증상 → 근본원인 → 해야할일. 미화 없음. 단 사실인 성과는 EARNED로 명시.

✅ EARNED (사실인 성과) — ① 선호계량화가 아린의 '미상 80%를 liked로 오판'을 실제로 차단(confidentLiked=4 오판 → 0 정정, route.ts:366-374 실측). ② 닻 종속 override 게이트(anchorOverrideAllowed)가 코드에 박혀 비-food주 음식 잔소리·트리거 미충족 강제를 결정론으로 막음(프롬프트 신뢰 아님). ③ F-18 슬롯본문봉합으로 비-콩류 슬롯 12일 두부누수 0(실측). ④ 거울 출현빈도 쿨다운(#5)·도입 회전(#6)으로 템플릿 축자 반복 격일화. 가드는 견고하다 — 무너진 건 가드가 아니라 그 가드 입구로 들어올 신호다.
증상근본원인해야할일 (owner)
[천장1·뿌리] P0-D 환경신호가 step 전진 트리거 0 — 졸업 0, 피벗만 됨child_daily_state·envTablePct가 '다리만 깔리고' 실데이터 미입력. pickUnitProbecontext.unitProbe로 질문은 내보내나(출력 다리·route.ts:990 EARNED), 답변 파이프(parseProbeAnswers→advanceProgress)에 양성신호가 안 들어옴 — 입력 다리 공백. targetAccepts(route.ts:711)·focus 유닛 passWhen이 표본 0이라 영구 보류환경신호 '입력 UI/경로'까지 정의(현재 출력 다리만) — care 5단계/환경칩 입력이 child_daily_state·envTablePct로 흘러 parseProbeAnswers 양성표본을 쌓게 배선. owner: route.ts + ⑤ 진척(advanceProgress passWhen) + care UI
[천장5] 시나리오 mealtime-atmosphere 고착 — 9일 중 7일 동일 각도focusFatigue(정체 유닛/시나리오 강등)가 environment 레버를 강등 후에도 즉시 재선택 → 강등 면역. 강등 기준이 '직전 주 비교'라 일간 누적빈도를 못 봄. 시나리오 다양성이 도입 다양성과 직결focusFatigue 강등 기준을 '연속 정체일/시나리오 누적빈도'로 교체(직전 주 비교 → 최근 N일 시나리오 카운트). recentScenarios 윈도를 두뇌 입력에 누적빈도로 주입해 7일 중 5일+ 동일이면 강제 차순위. owner: route.ts:584-590 focusHistory + buildBrainContext
선호계량화 confidentLiked=0 → 콜드스타트만 — 영원히 콜드스타트아린 양성신호 없음(완식 반복 2회+ 없음). 미상 null을 liked 오판 차단은 정상(EARNED). 그러나 신호 입력이 0이면 콜드스타트 탈출 불가 — 천장1과 같은 뿌리(신호 0 구간)신호 0 구간 식별 후 시나리오 강제 다변화 — confidentLiked=0 + targetAccepts=0이 N일+ 지속이면 '신호 수집 모드'로 진입해 시나리오를 강제 회전(같은 각도 고착 방지)하고 프로브를 우선 emit. acceptance_level 입력 유도(care 5단계)로 양성표본을 적극 수집. owner: route.ts:372 coldStart 분기 + pickUnitProbe 우선순위
추천 풀 협소 — 콜드스타트 후보가 두부5·달걀6에 수렴콜드스타트 Tier2→Tier3(youa 큐레이션)까지 못 가 풀이 좁음 · 앵커 다양화 부족(돼지불고기 단일)P0-B/C — 추천 풀 확장(Tier3 큐레이션 배선) · 앵커 다양화. owner: ② 추천엔진(coachRecos) — 본 모듈은 confidentLiked/likedSeed 제공만
lever 태그 정합·plateau 카피풀effLever 디버그 표시와 본문 레버가 어긋나는 케이스 산발 · plateau/무브 카피 풀 빈약(반복 톤)lever 태그 정합 디버그 · plateau·무브 카피풀 확충. owner: route.ts weekCtx.effLever + ⑥ 작문기 카피풀
🧠 한 줄 요약 — 이 모듈의 가드(선호계량화·닻 게이트·슬롯봉합)는 EARNED다. 무너진 단일 뿌리는 '아린 확신 liked 0개 = 신호 0'이다 — 졸업·콜드스타트 탈출·시나리오 다양화가 전부 '양성신호 입력'에 막혀 있다. 그래서 다음 P0 = P0-D 환경신호 입력 경로(출력 다리만 깔린 것을 입력까지 잇기). 대부분 care 5단계 실데이터가 쌓이면 자연 해소된다.
🛡️

가드 — 괴식·환각·안전 차단 (실측)