107 lines
6.6 KiB
Markdown
107 lines
6.6 KiB
Markdown
# 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<T>`). 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<StudioSentenceKey, …>` → `Record<string, …>`
|
|
(`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`; `<p>` 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.
|