# Studio Curriculum Refactor — Notes Branch: `feat/studio-curriculum` ## Phase 1 (prior, by remover) - `c977e0b chore: remove gamification curriculum/lesson feature` — removed the unrelated gamification Curriculum/Lesson feature, freeing the `Curriculum` name and `src/types/curriculum.ts` path. tsc/build green. ## Phase 2 (this work) — data-driven studio Curriculum (회차) entity Three green-gated steps, commit after each (tsc `--noEmit` = 0): - **2-1** `a4aa1db feat(studio): add Curriculum entity, role icon registry, 3학년 1회차 seed` - `src/types/curriculum.ts` — `Curriculum` + `CurriculumRole/Meta/AI/Rewards/Copy` (+ `Localized`). Stages are NOT modeled here (fixed at 6 — locked decision). - `src/components/writing/studio/roleIconRegistry.ts` — string icon key → `IconType` (`mapPin`/`heart`/`question`/`default`). - `src/data/studioCurricula/grade3-session1.ts` — seed; role label/guide/placeholder/examples copied verbatim from `messages/*.json`; `ai.keywordCount = 5`; `meta` from the lesson plan (ko; en/ja = ko + `// TODO i18n`). - Pure addition; studio did not consume it yet. - **2-2** `3536c8d feat(studio): make writing frame data-driven via Curriculum roles` - Removed the `StudioSentenceKey` union + `STUDIO_SENTENCE_ORDER`. - Generalized every `Record` → `Record` (`StudioSegment/SuggestResponse.bySentence`, `StudioResult.sentencesV1/V2`, `StudioCompleteRequest.sentencesV1/V2`, segment/suggest request `key`, flow state `sentencesV2`, `tokensBySentence`, stage props). - `StudioBlank.sentenceKey` → `roleKey: string` (no back-compat — locked decision). - `templates.ts` (`studioBlankId`/`getStudioBlanks`/`composeStudioSentences`) now takes `CurriculumRole[]` (slotId-driven). `studioRoleMeta.ts` deleted. - `studioService.ts`: `SEGMENT_SCHEMA`/`SUGGEST_SCHEMA` built **at runtime** from role keys (Gemini structured output); `emptyTokenMap`/`emptySuggestionMap` and all loops derive from the role keys carried by the request; `keywordCount` injected (was `STUDIO_KEYWORD_COUNT`, `pickFiveKeywords`→`pickKeywords`). - `segmentation.ts`/`suggestion.ts`: output-key instruction generated from role keys. - Routes resolve the Curriculum from `curriculumId` and add **manual runtime key-validation** (repo's existing `validationErrorResponse` style, not zod): - `analyze` — feeds `ai.keywordCount`. - `segment`/`suggest` — reject sentences whose `key` is not a curriculum role key. - `complete` — require all role keys present in `sentencesV1/V2`; `

` body assembly + `safeSentences` iterate role keys in frame order; `roleKey` saved. - `page.tsx` + stages render from `state.curriculum.frame.roles` via `roleIconRegistry`; studio loads the Curriculum with `getCurriculum(?curriculumId=)` → falls back to the `grade3-session1` seed. - 3학년 1회차 renders identically to before. tsc 0, `npm run build` exit 0. - New code-seed registry: `src/data/studioCurricula/index.ts` (`getCurriculum`, `getCurriculumRoleKeys`, `DEFAULT_CURRICULUM_ID`). - **2-3** `feat: data-driven studio Curriculum entity (3학년 1회차 seed)` - ICU-templated copy in `messages/{ko,en,ja}.json`, values fed from data: - `complete.title` `{session}` ← `meta.sessionOrdinal` - `keywords.title` `{count}` ← `ai.keywordCount` - `secondWriting.subtitle` `{count}` ← `frame.roles.length` - Removed migrated keys from all three locales: `studio.roles.*`, `studio.firstWriting.{guides,placeholders,examples}`. Platform-chrome keys kept (stepper, reward, actions, imageUpload, etc.). CRLF + tab + trailing newline preserved; key sets identical across the 3 locales (2080 each). - tsc 0, build exit 0. ## What is data-driven now - Writing-frame roles (key/slotId/icon/label/guide/placeholder/examples) — variable count & names, sourced from `Curriculum.frame.roles`. - AI keyword count (`ai.keywordCount`), AI structured-output schemas & prompt output keys (built from role keys at runtime). - Session/count copy (`complete.title`/`keywords.title`/`secondWriting.subtitle`) via ICU + data. - Lesson-plan `meta` (sessionLabel/topic/objective/achievementStandard/flow/materials/ emotionCards) — present as data on the entity. ## What is intentionally FIXED (not data-driven) - The 6-stage flow `STUDIO_STAGE_ORDER` / `goNext` / `StageStepper` and `stepper.*` i18n labels (locked decision: stages fixed at 6). - Reward UI engine + default motion/sprite preset pools (`rewards` left undefined → shared platform pool). ## STOP points (product decisions — NOT built, per locked decisions) 1. **Management layer** — no Firestore `curriculums` collection, CRUD API, teacher authoring console, or security rules. Curricula live only as code seeds today. A real authoring flow needs a schema + rules + UI decision. 2. **Curriculum entry/routing** — studio reads `?curriculumId=` and falls back to `grade3-session1`. How students actually receive a `curriculumId` (team assignment link / home card / Topic linkage) is undecided. > **Resolved 2026-08-10** — a student arrives from the class session. The notice > board and the classroom both link with the in-progress session's `teamId`+`topicId`, > and `_flow/liveCurriculum.ts` overlays the live week/topic onto the code seed. Note > that `meta.sessionOrdinal` (2-3 above) is now **seeded from `TeamSession.order`** — > the confirmed class number — rather than being the seed's own constant. There is no > "assignment link"; assignments were deleted (see `DATA_MODELS.md` → TeamSession). 3. **Existing published works** — NO back-compat/migration was built (locked decision #2). Old studio writings stored with `situation/emotion/reason` keys and `blanks[].sentenceKey` may not render correctly under the new `roleKey` shape. If preserving old works matters, a migration/back-fill is required. 4. **Multilingual authorship for `meta`** — en/ja for `meta` are ko + `// TODO i18n` (meta is data-only, not surfaced in UI yet). Translation authorship is out of scope. 5. **`copy` overrides** — `CurriculumCopy` type exists but the seed leaves it undefined; ICU templates live in i18n. Per-curriculum copy override wiring is not built (not needed for the seed). 6. **Topic relationship** — `Curriculum.topicId?` left as an optional aggregation FK; Topic itself unchanged. ## Verify - `npx tsc --noEmit` → 0 errors. - `npm run build` → exit 0 (run after `Remove-Item -Recurse -Force .next` once to clear a stale route-validator cache). - Pre-existing unrelated lint warnings may remain; none introduced.