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

✍️ ⑥ 작문기 (손)

두뇌가 고른 모든 재료를 받아 실제 부모용 편지 한 통을 LLM으로 쓰는 손 — slotMandate 봉합 · #6 도입 소재 회전 · #5 거울 쿨다운 anti-leak · 발행 전 3겹 가드

← 문서 허브 · 통합 아키텍처 지도 · 코칭 엔진 모듈 명세 · 코드 근거 2026-06-20
🗺️ 이 문서는 통합 아키텍처 지도의 모듈 ⑥를 코드 단위로 펼친 명세입니다 — 비개발자 설명 · 구현 실측(file:line) · 파이프라인 그림 · before/after · 남은 천장(랄프위검 47점).
1

비개발자용 — 이 모듈은 '무엇을 왜' 하나

밀프레드 코칭은 머리(무엇을 말할지 결정)와 (그걸 따뜻한 편지로 풀어 쓰기)으로 나뉩니다. 이 모듈이 바로 '손'입니다. 머리(②추천엔진·③사실거울·④영양평가·⑤진척·⑧주간계획)가 미리 정해 넘긴 재료 — 오늘 다룰 단 하나의 타깃(예: 부족한 콩류), 오늘 권할 구체적인 음식 하나(예: '단호박찜'), 영양 거울 한 줄, 검증된 추천 음식 목록, 이번 주 커리큘럼 단계 — 를 받아서, AI에게 '이 사실 안에서만, 이런 규칙을 지켜서 부모에게 가는 편지를 써라'라고 시킵니다. 손은 새로운 사실을 만들지 않습니다. 머리가 준 재료를 따뜻한 한국어 문장으로 옮기는 일만 합니다.

핵심은 '작문은 AI가 자유롭게, 그러나 안전벨트는 코드가 단단히' 입니다. AI가 글을 쓴 뒤 곧바로 발행하지 않고, 세 겹의 검사를 통과해야만 부모에게 갑니다. (1) 정규식 안전망: '지난달·N개월 전' 같은 없는 시점, 김치에 다른 재료 섞기, 바나나에 콩가루 뿌리기(괴식), 튀김·돈까스 권유, 짠맛 칭찬 같은 위험 표현이 들어 있으면 즉시 다시 쓰게 합니다. (2) 어휘 반복 검사: 어제·최근 편지와 도입부나 전체가 너무 비슷하면(잔소리가 되니까) 행동 방식을 바꿔 다시 씁니다. (3) AI 검증자: 또 다른 AI가 '이 편지의 사실 주장이 진짜 데이터에 근거하나, 행동을 2개 시키지 않나, 체중·점수·진단 같은 금지어가 없나'를 의미 수준에서 검사하고, 걸리면 위반 목록을 돌려줘 1회 더 고쳐 씁니다.

2026-06 들어 손이 새로 맡은 세 가지 '봉합'이 있습니다. ① slotMandate 봉합(F-18) — 머리(⑧주간계획)가 "오늘은 단호박찜"이라고 구체 음식을 정해 줬는데도 손이 자꾸 익숙한 '두부'로 돌아가던 버릇이 있었습니다. 이제 손은 편지를 쓴 뒤 '본문에 진짜 단호박이 들어갔나'를 확인하고, 빠졌으면 그 음식을 강제로 넣어 다시 씁니다. ② 거울 쿨다운 anti-leak(#5) — '어린이집 덕에 영양은 잘 채워져요'라는 안심 한 줄이 거의 매일 박혀(24통 중 17통) 앵무새가 됐습니다. 이제 최근 2일 안에 그 줄이 나왔으면 오늘은 빼고, 손이 습관적으로 그 줄을 도로 붙이면 잘라냅니다. ③ 도입 소재 회전(#6) — 모든 편지가 '부모님,' 또는 '요즘…'으로 똑같이 시작하던 것을, 날짜 시드로 '관찰/변화/질문/장면/일상/공감' 6가지 여는 방식을 매일 돌려 씁니다.

🥗 영양·편식 정보는 이렇게 처리합니다
이 모듈은 영양·결핍을 스스로 '판정'하지 않습니다. 부족 영양소·부족 식품군은 머리(분석)가 계산해 넘겨준 우리 데이터만 쓰고, 손은 그걸 편지에 옮기기만 합니다(특정 식재료를 'OO는 고단백'이라 단정 금지). 처리 규칙: ①결핍 제시는 받은 값만 — 영양거울 한 구절을 코드가 미리 문장으로 만들어 '이걸 반드시 녹여라'로 손에 직접 전달. ②반복 노출=거부는 정상 — 단 '벌써 N번째' 같은 카운트는 절대 지어내지 못하게 막음. ③괴식 차단 — 바나나에 콩가루, 김치에 다른 재료 섞기, 해조류(미역·김)를 생선으로 혼동, 밀·쌀 날곡물 금지. ④강요 금지(Satter 역할분담) — 부모는 무엇·언제만, 행동은 한 번에 하나만. ⑤저염·연령 안전 — 짠맛 칭찬·매운 음식·튀김 금지, 질식 표시(반으로잘라·익혀) 보존. ⑥만성질환은 식이 '방향'만 한 구절, 진단·처방·정량·체중은 전부 금지.
🧒 운영 예시(아린·5세) — 집 기록을 보면 소고기·계란은 잘 먹는데 콩류·녹황색채소가 며칠째 비어 있습니다. [입력] 머리가 meal_logs를 집 끼니만 골라 '집 부족군=콩류·채소', '잘 먹는 음식=밥(16)·소고기(9)·계란말이(7)', 오늘의 슬롯 음식='단호박찜'(찐 단호박), 슬롯 트랙='challenge'(잘 먹는 감자의 사촌 확장), 영양거울 한 줄, 이번 주 커리큘럼 단계를 손에 넘깁니다. [처리] 손은 AI에게 '이 각도로, 단호박찜 하나를 본문에 반드시 직조, 감자 잘 먹으니 닮은 단호박으로 이어 권하는 톤으로, 매운 것·괴식·점수 금지로 따뜻하게 써라'고 시킵니다. AI가 쓴 글에 단호박이 안 들어갔으면 강제로 넣어 다시 쓰고, 어제 편지와 도입이 비슷하면 여는 방식을 바꿔 다시 쓰고, 검증자 AI가 '행동 2개·없는 사실'을 점검합니다. [결과] 4~6문장 편지가 어법 퇴고(국→먹다 등)를 거쳐 부모에게 발행됩니다.
2

구현된 내용 — 코드에 실제로 있는 함수·흐름·I/O

전부 web/lib/coach.ts(1190줄) + 호출부 web/app/api/cron/coach/route.ts 실측. 함수명·타입·file:line은 코드 그대로입니다.

핵심 함수 (file:line)

함수 · 타입위치역할 (실측)
composeLettercoach.ts:923-1066통합 작성기(크론·온디맨드 공유). {base, precomputed, detInput, detForbid, deadlineMs, daySeed, cidHash} 입력 → 전환사실 누수 차단 → generateLetter → det 재생성(≤2) → 유사도 재생성(≤2) → verifyLetter 의미검증 재생성(1) → 5개 must-weave 포함검증(거울·거울누수절단·성장·F-18 슬롯·F-17 step) → {letter, oneliner, plan, scenarioId, scenarioLabel, coachRegen, verify, modelUsed} 반환
buildLetterUser(b: LetterInput)coach.ts:595-808LetterInput → USER 프롬프트 문자열 조립. history·ctx·factsBlock·mirrorBlock·growthBlock·scenarioBlock·planTargetBlock·slotMandateBlock·openBlock·actionBlock·arcBlock·chronicBlock·snackBlock·moveBlock·regen/fix/reengage/safety 블록 결합. EPIC C byte-동일 회귀용 export
generateLettercoach.ts:831-837callLLM(700토큰) 편지 생성 → polishKo(결정론 어법) + proofreadLetter(LLM 퇴고 1콜) → {letter, oneliner}
callLLMcoach.ts:74-159DeepSeek 1차(DEEPINFRA/OPENROUTER 키·sonnet→V4-Pro·haiku→V4-Flash)→실패 시 dsDead 서킷브레이커로 Claude 폴백. 최대 5회 재시도(깨진 JSON 회복)·usage 적재(_usageLog)·system 캐싱
letterDeterministicBadcoach.ts:576-592발행 전 결정론 정규식 가드 — FORBID_TIME·SYMPTOM_RE·섞기누수(NO_MIX_SCEN)·김치괴식·튀김초가공·N가지칭찬·해조류↔생선·fruitSaltyMix·fruitMealPairing·날곡물·짠맛칭찬·김치칭찬 → true면 재생성
verifyLetter / verifyFactscoach.ts:886-914LLM 의미 검증자 1콜(SYSTEM_VERIFY) — ①사실근거 ②행동1개(noFoodAction) ③금지표현 ④진단재서술. verifyFacts가 '실제 받은 입력'을 압축해 전달. 장애 시 fail-open({ok:true})
mirrorCooldownDue(recentShown, opts)coach.ts:299-303#5 신규 순수함수 — 최근 cooldownDays(=2)일 안에 거울 노출 있으면 true(오늘 생략). severe(전체결핍≥2)면 면제. 출현 1회/3일로 수렴
mirrorReassureLeak(letter)coach.ts:307-316#5 신규 거울 생략 날인데 손이 '어린이집/기관 급식 덕에 영양 채워진다' 안심줄을 자체로 붙였는지 탐지(역 must-weave 트리거). 영양어 동반 require로 재노출 언급 과탐 방지
stripMirrorReassure(letter)coach.ts:323-347#5 신규 재생성으로도 누수 남으면 결정론 절단 — 순수 daycare-praise 문장은 통째, 행동/집-결핍 섞인 문장은 선행 praise 절(쉼표)만 제거. 과절단이면 원본 유지
pickOpenMaterial(seed) / OPEN_MATERIALScoach.ts:389-403#6 신규 daySeed로 도입 '여는 방식' 6종(observe·change·question·scene·daily·empathy) 중 1개. SCEN_OPEN(각도 내용)과 직교 합성. 풀 비면 null 폴백
planFor / buildCoachPlan / targetPoolForScenariocoach.ts:533·484·473시나리오 선택 + 결정론 plan(frame·target·moveKey·signature) 산출. forceScenarioId(두뇌)면 그 시나리오로 잠금. composeLetter 인자 precomputed로 주입
chronicGuidanceText / structuredTip / metaInputNudgecoach.ts(별·248·266)만성질환→식이 방향 1구절 · 구조화 입력(식감·자율성·환경·식사시간)→개선 팁 · 초기 1~3주 메타 입력 초대(#4b)
generateOnboardingLetter / generateQuestion / icfqForDatecoach.ts:845·(이하)·1081기록<3일 온보딩 편지 · 오늘의 질문(결정론 주제 회전) · ICFQ 위험 스크리너(10일 회전)

이번 세션 신규 — 손이 받는 입력·실행하는 봉합

기능손의 입력/코드 (file:line)손이 실제로 하는 일
F-18 슬롯 본문 봉합
라이브
LetterInput.slotFood / slotDish / slotTrack / slotPairLiked(coach.ts:212-215). 봉합 검증 = composeLetter:1040-1047. 주입 = route.ts:864-865·917-918slotMandateBlock(buildLetterUser:660-666)이 '오늘 권할 음식은 slotDish 하나, 본문에 반드시 직조' + '두부·콩류로 대체 금지'(slotMandateCommon). 생성 후 slotWoven(slotDish·slotFood·핵심명사 중 하나라도 본문에) 검사 → 누락 시 fixNotes로 1회 재생성. track='challenge'면 "감자 잘 먹으니 닮은 단호박" 톤, supply면 부족 채움 톤(프레이밍 분기)
#5 거울 출현빈도 쿨다운 + anti-leak
라이브
mirrorCooldown / mirrorLine / mirrorPlanned(coach.ts:209-211). 누수 절단 = composeLetter:1014-1029. 쿨다운 판정 = route.ts:894·896route가 mirrorCooldownDue로 최근 2일 노출 시 mirrorLineSel=null. mirrorBlock(coach.ts:743-786)이 라인 없으면 '안심 문구 금지' 가드만 주입. 발행 후 intendMirror=false인데 mirrorReassureLeak 매치면 → ①재생성 1회 → ②stripMirrorReassure 결정론 절단. K-03 must-weave(998-1013)는 결핍 분기에서만 거울 누락 시 보강(쿨다운 날 면제)
#6 도입 소재 회전
라이브
OPEN_MATERIALS·pickOpenMaterial(coach.ts:389-403). 합성 = buildLetterUser:643-648openMat=pickOpenMaterial(daySeed)openRotate 문장을 openBlock에 합성. SCEN_OPEN(각도)은 그대로 두고 '여는 형태'만 회전. 양 분기(varyOpener true/false) 모두 "첫 어절을 '부모님'(호명)으로 시작 금지" 명문(:647-648)
#7 macro 성장 거울
라이브(route 주도)
LetterInput.growthMirror(coach.ts:207). 손 봉합 = E-06 composeLetter:1031-1037손은 growthMirror가 오면 본문에 한 구절(숫자·BMI·체중·살 금지). E-06 must-weave가 growthWoven 누락 시 재생성. 단 macro 트랙 활성화·성장데이터 nudge 빈도(저체중→고기류↑)는 route.ts:229-230(매 3일·hasGrowth면 중단)이 결정 — 손은 받은 phrase를 녹이는 역할
F-17 step 누적 서사
라이브
weeklyArc.stepStory(coach.ts:196). 봉합 = composeLetter:1048-1063arcBlock(688-703)이 advance/celebrate/길/연속감 한 구절 지시. 손이 거의 항상 생략(메타 주입만으론 23통 중 1통)해 '자연스러운 진도 순간'(intro·advance·celebrate)에만 storyWoven must-weave 재생성
선호계량화 / hardStall 피벗 / P0-D 프로브 / A 추천근거
상류 모듈 — 손은 결과만 수신
이번 세션 신규지만 코드상 손(coach.ts)에는 직접 존재하지 않음: 선호계량화=lib/preferenceQuantification.ts(②/route), hardStall 피벗=⑤커리큘럼(route.ts:671 fallbackPivot), P0-D child_daily_state=신호입력(미구현), A 추천근거(reason/weeklyEstFreq/level)=②추천엔진/어드민. 손은 이들이 산출한 slotFood·target·bridgeFacts·arc만 받아 작문. (환각 금지 — 이 모듈 문서엔 손이 받는 인터페이스로만 표기)
모델 라우팅(실측) — composeLetter:946: weeklyArc.stage==='intro'(주 첫 진단)만 'claude-sonnet-4-6' 역할키(→DeepSeek V4-Pro 폴백 Sonnet), 평소엔 undefined→HAND_MODEL='claude-haiku-4-5-20251001' 역할키(→V4-Flash 폴백 Haiku). 1차는 항상 DeepSeek. modelUsed로 letterCtx에 기록.
3

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

데이터 파이프라인 그림. 왼쪽 입력(머리·테이블이 넘긴 재료) → 가운데 가공 함수(손) → 오른쪽 출력(coach_letters 행 + letterCtx). 점선 박스는 발행 전 가드 루프.

📥 INPUT (route.ts가 조립한 LetterInput base) target·missing·homeMissing · favoriteFreq · timeseries · factCards · bridgeFacts
slotFood·slotDish·slotTrack·slotPairLiked(F-18) · mirrorLine·mirrorPlanned·mirrorCooldown(#5) · growthMirror(#7) · weeklyArc.stepStory(F-17) · daySeed·cidHash
📥 precomputed planFor() 산출:
scenario · plan(frame·target·moveKey·signature) · varyOpener
composeLetter(coach.ts:923) 진입
① 전환사실 누수 차단 (931-944) ALLOW_TRANSITION 아닌 시나리오는 '거부→수용 전환' 시계열 제거 · sanitizeTimeseries · letterInput에 scenario/rotateMove/planTarget/varyOpener/daySeed 병합
② buildLetterUser (595-808) — USER 프롬프트 조립 history · ctx(favoriteFreq·검증추천) · factsBlock · mirrorBlock(743-786 · #5 쿨다운 분기) · scenarioBlock · planTargetBlock(슬롯 있으면 거울정보로 강등) · slotMandateBlock(660-666 · F-18 must-weave) · openBlock(643-648 · #6 회전) · actionBlock · arcBlock(F-17 ladder) · safetyBlock
③ generateLetter (831) → callLLM(700) → polishKo + proofreadLetter DeepSeek V4-Flash 1차 (intro만 V4-Pro) · 실패 시 Claude 폴백 · 깨진 JSON 5회 재시도
⛓️ 발행 전 가드 루프 (모든 재생성 산출물은 det+유사도 재통과해야 채택 — S1 대칭)
(a) det 재생성 ≤2 — letterDeterministicBad(576) || detForbid → 환각시점·괴식·튀김·날곡물
(b) 유사도 재생성 ≤2 — letterSimilarity 3-gram(전체 0.45·도입 0.40) → 무브 회전+regenAvoid
(c) 의미검증 재생성 ×1 — verifyLetter(886) 사실·행동1개·금지어·진단 → fixNotes
(d) must-weave 포함검증 ×1씩 — K-03 거울(998) · #5 거울누수 절단(1014: 재생성→stripMirrorReassure) · E-06 성장(1031) · F-18 슬롯음식(1040: slotWoven) · F-17 step(1048)
deadlineMs 넘기면(S7) 추가 콜 전부 생략 → 발행 우선 강등(SIGKILL 좀비 방지)
📤 OUTPUT (composeLetter 반환 → route.ts:962 upsert) coach_letters 행: letter · oneliner
letterCtx(context JSON): plan · scenarioId · scenarioLabel · coachRegen · verify · simToPrev · repeatAlert · modelUsed · mirrorShown(#5 쿨다운 이력) · planSlot(slotIndex·dish·track·mirror·macro) · brain · curriculum · recoIng/recoPool/recoMode
닫힌 루프 — 출력 mirrorShown·plan.signature·letter 본문이 다음 날 입력으로 되돌아온다: route.ts:167-187이 최근 편지에서 recentMirrorShown(쿨다운)·recentPlans(시그니처 회피)·pastLetters(유사도)를 적재. 손은 이 이력으로 '어제와 다르게' 쓴다.
4

작동 예시 — 아린(5세) 실데이터 before/after

실제 케이스: 슬롯이 '단호박찜'(challenge·감자 사촌)을 정했는데 손이 결핍군 대표 '두부'로 회귀하던 F-18 천장. 같은 입력으로 봉합 전/후 본문 비교.

📥 INPUT — route.ts가 조립한 base (발췌)
{
  "childName": "아린", "ageBand": "5y",
  "favoriteFreq": ["밥(16회)","소고기(9회)","계란말이(7회)","감자(4회)"],
  "missing": ["콩류","녹황색채소"],
  "homeMissing": ["콩류","녹황색채소"],
  "attendsDaycare": true,
  "slotFood": "찐 단호박",
  "slotDish": "단호박찜",
  "slotTrack": "challenge",
  "slotPairLiked": "감자",
  "mirrorLine": "어린이집 덕에 여러 식품군을 두루 잘 챙기고 있어요. 집에선 단호박찜 같은 음식도 가끔 식탁에 올리면 다양성에 좋아요",
  "mirrorPlanned": true,
  "mirrorCooldown": false,
  "weeklyArc": {"stage":"how","behaviorGoal":"저녁 첫 숟갈 전 거부 채소 한 조각 올리기"},
  "daySeed": 20259   // pickOpenMaterial → 'scene'(식탁 장면)
}
📤 손이 만든 plan/봉합 결과
slotMandateBlock(challenge·:664):
 "오늘 권할 음식은 '단호박찜'(찐 단호박) 하나 —
  '감자 잘 먹으니 닮은 단호박찜도 한 번'처럼
  잘 먹는 음식에서 이어 권하라. ❌ 두부·콩류로
  대체 금지. ❌ 부족 프레이밍 금지(다른 식품군)"

planTargetBlock(:656 강등):
 "[ℹ️ 콩류는 영양거울에서 가볍게 비추기만, 음식
  추천을 콩류로 끌어오지 마라 — 슬롯이 단일 타깃]"

openBlock(:644 #6):
 "오늘 첫 문장은 '아이가 식탁에서 보이는 모습을
  그리듯' 방식으로 열어라. ❌ 첫 어절 '부모님' 금지"

F-18 검증(:1042): slotWoven('단호박' in 본문)?
 → 1차 생성에 누락 시 fixNotes 재생성 1회
❌ BEFORE (F-18 봉합 전 · 두부 회귀) "…콩류가 집 끼니에 드무네요. 아린이가 좋아하는 밥에 두부를 아주 잘게 으깨 섞어 한 번 만나게 해보세요. 또래들은 두부볶음밥으로도 자주 먹어요."

메타(planSlot)='단호박찜'인데 본문 음식명 0회 — '메타=단호박, 본문=두부' 이중장부. 부모는 단호박을 영영 못 봄.
✅ AFTER (slotMandateBlock + slotWoven 봉합 후) "저녁 식탁에 아린이가 앉으면 좋아하는 감자부터 손이 가죠. 그 옆에 찐 단호박 한 조각만 부담 없이 곁들여 두어 보세요 — 감자와 닮은 포근한 맛이라 낯설지 않을 거예요. 또래들은 단호박찜으로도 곧잘 먹는답니다."

slotDish/slotFood 본문 직조 · challenge 톤(감자→단호박) · 도입은 #6 'scene'(식탁 장면, '부모님' 호명 없음). 콩류는 거울에만.
#5 거울 쿨다운 예시(같은 아린) — 어제 편지에 '어린이집 덕에 영양 채워져요' 줄이 나가 recentMirrorShown[아린]=[true,…]. 오늘 mirrorCooldownDue([true],{cooldownDays:2})=true → route가 mirrorLineSel=null → mirrorBlock(750)이 "오늘은 영양거울 생략 — '어린이집 덕에…' 안심 문구 금지" 가드만 주입. 그래도 Haiku가 습관적으로 그 줄을 붙이면 → intendMirror=false && mirrorReassureLeak=true(1018) → 재생성 1회 → 남으면 stripMirrorReassure로 그 문장만 절단. 결과: 안심줄 출현이 매일→3일 1회로 격일화.
5

남은 문제점 · 해야할 일 — 랄프위검 47점(연속성·2026-06-20) 실측 천장

아린 24통(가입 1일차~오늘) 연속성 감사 결과를 이 모듈(손) 관점에서 정직하게. 봉합된 것(EARNED)도 사실이면 명시하되, 남은 천장은 미화 없이 증상→근본원인→해야할일(owner)로.

증상 (날짜·증거)근본원인 (이 모듈 관점)해야할 일 (owner)등급
천장2 — F-18 봉합이 environment 분기를 우회
슬롯음식(두부/단호박/검은콩/달걀)이 메타(planSlot)엔 있는데 본문 음식명 0회. 06-11·12·13·18·19 5연속. 부모가 '오늘 뭘 먹여보라는 건지' 못 읽음
F-18 슬롯 강제(slotFood/slotDish)가 lever:food 분기에만 걸린다. route.ts:864-865는 !_noFood 조건이라 환경(brainPick.useFood===false) 날은 slotFood=null → slotMandateBlock·slotWoven 봉합이 통째로 풀림. _foodScen(home-daycare-gap·nutrient-gap·repeat-menu)만 _noFood를 끄므로(route.ts:859-860), 환경 레버로 잡힌 음식-있는 날은 슬롯이 본문에 안 들어감 environment 분기에도 slotDish must-weave 봉합 — '슬롯에 음식 걸린 모든 날 본문에 음식명 최소 1회'. mirrorBlock의 환경-날 소프트 노출(route.ts:886-887 _foodClause)이 거울에만 있고 본문 검증(slotWoven)은 _noFood면 skip(:1040)이라, 환경 날 본문 봉합 경로를 신설하거나 거울 소프트노출을 검증 대상에 포함. owner: ⑥손(slotWoven 게이트) + ⑧주간계획(슬롯↔레버 정합) 높음
천장5 — 도입 새 수렴(#6이 옮긴 자리)
'부모님,' 호명은 0/24(차단 성공처럼 보임). 대신 '요즘/며칠째/오늘 저녁 6시+식탁/화면'이 후반부 점령. 06-07≈06-08 near-duplicate
OPEN_MATERIALS 6종(observe·change·question·scene·daily·empathy)이 daySeed 단일 시드로만 회전(coach.ts:398-402)하는데, 실제 첫 문장 수사는 SCEN_OPEN(시나리오 각도)에 종속된다. 같은 시나리오가 며칠 연속이면(상류 닻 종속) 같은 각도 × 인접 daySeed → 도입 재수렴. '부모님' 차단 baseline 자체가 측정 위생 문제: 0/24가 진짜 차단인지 검증 baseline이 wf.js args 버그로 stale(아래 ⚠️) openBlock 수사장치를 시나리오와 직교화 — pickOpenMaterial 시드에 scenarioId 해시를 섞어 같은 각도라도 다른 도입 장치 강제 + 인접 N일 도입 중복 억제(pastLetters 첫 문장 25자 자카드를 openBlock 단계에서 별도 검사, 현 simBadness는 전체/도입 통합이라 도입 단독 회귀를 못 잡음). owner: ⑥손
천장6 — 본문 클로징·위로·안심 템플릿 회전 부재
도입만 손봄. modeling-line '부모님이 먼저 맛있게 드시는' ~11통, plateau-comfort '거부 없이 평온함' 3통이 거의 동일
#6 회전은 첫 문장(openBlock)에만 적용됐다. 본문 중간·클로징의 상투 슬롯(modeling-line·plateau-comfort·곁들임 권유)은 SYSTEM_COACH(coach.ts:40 '부모가 먼저 맛있게 먹는 모습') + SCEN_ACT가 매 편지 같은 슬롯으로 수렴시키는데 회전 장치가 없다. simBadness는 3-gram 자카드라 modeling-line처럼 짧은 verbatim 상투구는 전체 유사도를 0.45 밑으로 통과(어휘 통과) modeling/plateau 본문 템플릿 풀 회전 추가 — MOVE_MENU(model 무브)·plateau 칭찬 문구를 daySeed 변형 풀로(SCEN_MOVES 환경 무브가 이미 풀-회전이듯). 추가로 구조 골격 유사도 측정자(상투 슬롯 시그니처를 직전 N편과 비교)를 simBadness에 가산해 '단어만 바꾼 붕어빵'까지 재생성 트리거. owner: ⑥손
⚠️ 측정 위생 — wf.js args 미전달로 baseline stale
천장5의 '0/24' 같은 판정이 직전 라운드와 정확히 비교되지 않음
scripts/arin-continuity-review.wf.jsWorkflow({scriptPath, args})에서 args를 런타임에 전달받지 못해 스크립트 기본값(loop·file·baseline=44, :16-18)으로 매 라운드 고정 실행. 그래서 'baseline 대비 올랐나'의 baseline이 갱신 안 됨 → 봉합 효과 측정이 흐려짐 우선 args 미전달 버그 수정 — 매 라운드 loop/file/baseline 기본값을 직접 갱신하거나 Workflow args 주입 경로를 고침. 그 뒤에야 위 천장들의 봉합 효과를 신뢰성 있게 측정 가능. owner: 측정 인프라(wf.js) 선행
✅ EARNED (이번 세션 실측 봉합 — 사실)   ① F-18 food 분기 슬롯 봉합: 실측 비-콩류 슬롯 12일에서 본문 두부 누수 0(food 레버 날 한정). ② #5 거울 쿨다운: 안심줄 출현 17→8(3일 1회)로 격일화, stripMirrorReassure로 Haiku 습관 누수까지 결정론 절단. ③ #6 도입: '부모님,' 호명 24→0(단, 수사가 다른 자리로 이동 = 천장5). ④ F-17 step: 메타 주입만일 때 23통 중 1통 → must-weave로 진도 순간엔 본문 직조. tsc 0 · vitest 464 통과.
🛠️ 이 모듈의 구조적 빚 — 한 줄 정직 요약
손은 '글은 LLM이 자유롭게, 안전벨트는 코드가 단단히'의 마지막 관문이고, 이번 세션에 거울 anti-leak·도입 회전·슬롯 봉합을 EARNED했다. 그러나 셋 다 '한 자리'만 봉합했다: 슬롯은 food 분기만(environment 우회), 회전은 첫 문장만(본문 클로징 미적용), 측정은 stale baseline. 남은 일은 봉합을 분기 전체·본문 전체로 확장하고, '단어만 바꾼 붕어빵'을 잡는 구조 골격 유사도를 어휘 자카드 위에 얹는 것이다. ⚠️ 상류(②/⑧이 슬롯·레버를 정함)가 환경 날 음식 슬롯을 어떻게 본문에 흘릴지 정하지 않으면 손의 봉합만으론 천장2를 못 막는다(손은 2차 방어층).