# Exercise Question Types Expansion - Implementation Plan ## Scope Lock (v1) 1. Keep legacy `RADIO`, `CHECKBOX`, `TEXTAREA` behavior backward compatible. 2. Deliver P0 question types first: `TRUE_FALSE`, `SHORT_ANSWER`, `NUMERIC`, `FILL_BLANK`, `FILE_UPLOAD`. 3. Manual grading is required when exercise includes `SHORT_ANSWER` or `TEXTAREA`. 4. Attempt rules are exercise-level: `maxAttempts`, `attemptScoringRule` (`highest`, `latest`, `average`). 5. Correct-answer visibility is exercise-level policy. 6. File upload is one file per question in v1. 7. Upload limits are plan based: free `2MB`, paid `10MB`. 8. Use one canonical question-type registry to remove scattered hardcoded numeric IDs. 9. Existing submissions continue to read from `answers[]` and `openAnswer` during migration. 10. New type-rich answers write to `question_answer.payload`. 11. DB update policy for this plan: apply DB changes only in `packages/db/src/schema.ts`; do not generate new files in `packages/db/src/migrations`. 12. Question-type registry/contracts/state/serialization logic must live in `@cio/question-types`; `@cio/ui` must stay components-only. 13. Shared packages must be app-agnostic (no dashboard-local imports), so other apps can build exercise/form products from the same package APIs. 14. Exercise-level `auto-grade` toggle can be enabled only when all active questions are auto-gradable; if not, it stays disabled with a hint message. 15. Grading engine remains in `apps/api` as backend source of truth and is locked behind API contracts; no standalone grading package is introduced. 16. Submission grading workflow truth uses explicit `submission.gradingState` and `submission.overallStatus`; legacy `submission.statusId` is retained as board compatibility projection only. 17. Initial grading state on student submit is always `queued`. 18. Server-owned grading transitions are: `queued -> processing -> completed`, `queued -> processing -> awaiting_manual -> completed`, and `queued -> processing -> failed -> queued` (retry path). 19. `overallStatus` classification values are `auto_graded`, `manual_required`, and `hybrid`, computed deterministically from attempt snapshot and question capabilities. 20. Client routes do not directly own grading workflow transitions; API/service layer validates and applies allowed transitions. 21. Renderer filenames must not repeat mode suffix; canonical naming is `renderers/{question-type}/{mode}.svelte` (for example `renderers/true-false/edit.svelte`, not `true-false-edit.svelte`). 22. `@cio/ui/custom/exercise-question` must not contain hardcoded user-facing English strings (labels, helper text, placeholders, tooltips, validation messages, sr-only text); host app provides copy through render contracts/props. ## Delivery Phases 1. Phase A: Foundation (schema, seeds, shared registry, validation contracts) 2. Phase B: API + grading + submission state engine for P0 3. Phase C: Dashboard authoring/taking/grading UX for P0 4. Phase D: P1 types (`MATCHING`, `ORDERING`) 5. Phase E: P2 type (`HOTSPOT`) 6. Phase F: Regression hardening, rollout, and monitoring ## Shared Package File Contract (Cross-App Reuse) These files are required deliverables so any app in the monorepo can consume shared packages and build a standalone exercise/forms product. ### `@cio/question-types` package files 1. `packages/question-types/src/question-type-keys.ts` 2. `packages/question-types/src/question-type-types.ts` 3. `packages/question-types/src/question-type-registry.ts` 4. `packages/question-types/src/question-type-lookup.ts` 5. `packages/question-types/src/question-type-capabilities.ts` 6. `packages/question-types/src/question-answer-payload.ts` 7. `packages/question-types/src/question-scoring-rules.ts` 8. `packages/question-types/src/exercise-types.ts` 9. `packages/question-types/src/render-contract.ts` 10. `packages/question-types/src/form-state.ts` 11. `packages/question-types/src/answer-serializer.ts` 12. `packages/question-types/src/renderer-registry.ts` 13. `packages/question-types/src/index.ts` 14. `packages/question-types/package.json` (package name: `@cio/question-types`) ### `@cio/ui` shared exercise-question component files (core) 1. `packages/ui/src/custom/exercise-question/question-form-root.svelte` 2. `packages/ui/src/custom/exercise-question/question-navigation.svelte` 3. `packages/ui/src/custom/exercise-question/question-renderer.svelte` 4. `packages/ui/src/custom/exercise-question/question-list.svelte` 5. `packages/ui/src/custom/exercise-question/renderer-contract.ts` 6. `packages/ui/src/custom/exercise-question/index.ts` 7. `packages/ui/src/index.ts` (exports `./custom/exercise-question`) ### `@cio/ui` P0 renderer files 1. `packages/ui/src/custom/exercise-question/renderers/radio/edit.svelte` 2. `packages/ui/src/custom/exercise-question/renderers/checkbox/edit.svelte` 3. `packages/ui/src/custom/exercise-question/renderers/textarea/edit.svelte` 4. `packages/ui/src/custom/exercise-question/renderers/radio/take.svelte` 5. `packages/ui/src/custom/exercise-question/renderers/checkbox/take.svelte` 6. `packages/ui/src/custom/exercise-question/renderers/textarea/take.svelte` 7. `packages/ui/src/custom/exercise-question/renderers/radio/preview.svelte` 8. `packages/ui/src/custom/exercise-question/renderers/checkbox/preview.svelte` 9. `packages/ui/src/custom/exercise-question/renderers/textarea/preview.svelte` 10. `packages/ui/src/custom/exercise-question/renderers/true-false/edit.svelte` 11. `packages/ui/src/custom/exercise-question/renderers/short-answer/edit.svelte` 12. `packages/ui/src/custom/exercise-question/renderers/numeric/edit.svelte` 13. `packages/ui/src/custom/exercise-question/renderers/fill-blank/edit.svelte` 14. `packages/ui/src/custom/exercise-question/renderers/file-upload/edit.svelte` 15. `packages/ui/src/custom/exercise-question/renderers/true-false/take.svelte` 16. `packages/ui/src/custom/exercise-question/renderers/short-answer/take.svelte` 17. `packages/ui/src/custom/exercise-question/renderers/numeric/take.svelte` 18. `packages/ui/src/custom/exercise-question/renderers/fill-blank/take.svelte` 19. `packages/ui/src/custom/exercise-question/renderers/file-upload/take.svelte` 20. `packages/ui/src/custom/exercise-question/renderers/true-false/preview.svelte` 21. `packages/ui/src/custom/exercise-question/renderers/short-answer/preview.svelte` 22. `packages/ui/src/custom/exercise-question/renderers/numeric/preview.svelte` 23. `packages/ui/src/custom/exercise-question/renderers/fill-blank/preview.svelte` 24. `packages/ui/src/custom/exercise-question/renderers/file-upload/preview.svelte` ### `@cio/ui` P1/P2 renderer files (created when phase starts) 1. `packages/ui/src/custom/exercise-question/renderers/matching/edit.svelte` 2. `packages/ui/src/custom/exercise-question/renderers/matching/take.svelte` 3. `packages/ui/src/custom/exercise-question/renderers/matching/preview.svelte` 4. `packages/ui/src/custom/exercise-question/renderers/ordering/edit.svelte` 5. `packages/ui/src/custom/exercise-question/renderers/ordering/take.svelte` 6. `packages/ui/src/custom/exercise-question/renderers/ordering/preview.svelte` 7. `packages/ui/src/custom/exercise-question/renderers/hotspot/edit.svelte` 8. `packages/ui/src/custom/exercise-question/renderers/hotspot/take.svelte` 9. `packages/ui/src/custom/exercise-question/renderers/hotspot/preview.svelte` ### Renderer Filename Convention (Locked) 1. Question type is expressed by folder: `renderers/{question-type}/`. 2. Mode is expressed only by filename: `edit.svelte`, `take.svelte`, `preview.svelte`. 3. Do not use mode-first folders (`renderers/edit/*`) or redundant suffixes like `-edit`, `-take`, or `-preview`. ### UI Copy Ownership (Locked) 1. Shared UI renderers are copyless primitives and must not embed end-user copy. 2. All labels/help/error/tooltip/placeholder/sr-only text must be provided by host app (dashboard) via typed contract props. 3. Translation key resolution stays in host apps (`apps/dashboard`), not in `packages/ui`. 4. Renderer utilities in `packages/ui` must not generate user-facing fallback words (for example `Step`); fallback semantics must come from host-provided copy. ## Ticket Breakdown | ID | Phase | Area | Task | Key Files | Dependencies | Done When | | --- | --- | --- | --- | --- | --- | --- | | EX-A1 | A | DB Schema | Add new columns for `exercise`, `question`, `question_answer`, `submission` (attempt controls, settings, payload, grading metadata including workflow/classification fields) in schema only | `packages/db/src/schema.ts` | None | Schema changes compile via package builds and no new migration files are generated | | EX-A2 | A | DB Seed | Extend `question_type` seed with new typenames/labels | `packages/db/src/utils/seed/questionType.ts` | EX-A1 | Re-seeding inserts missing types idempotently | | EX-A3 | A | Shared Registry | Create canonical question type registry and metadata in dedicated package (`@cio/question-types`) | `packages/question-types/src/question-type-keys.ts`, `packages/question-types/src/question-type-types.ts`, `packages/question-types/src/question-type-registry.ts`, `packages/question-types/src/question-type-lookup.ts`, `packages/question-types/src/question-type-capabilities.ts`, `packages/question-types/src/question-answer-payload.ts`, `packages/question-types/src/question-scoring-rules.ts`, `packages/question-types/src/exercise-types.ts`, `packages/question-types/src/render-contract.ts`, `packages/question-types/src/form-state.ts`, `packages/question-types/src/answer-serializer.ts`, `packages/question-types/src/renderer-registry.ts`, `packages/question-types/src/index.ts`, `packages/question-types/package.json` | EX-A2 | API and dashboard consume one source of truth for type keys/traits and all shared registry files exist | | EX-A4 | A | Validation | Replace fragile numeric-only assumptions with registry-backed schemas and enums | `packages/utils/src/validation/constants.ts`, `packages/utils/src/validation/exercise/exercise.ts` | EX-A3 | Validation accepts legacy + new types and rejects invalid per-type payloads | | EX-A5 | A | DB Queries | Add read/write support for new columns and typed answer payload in query layer | `packages/db/src/queries/exercise/exercise.ts`, `packages/db/src/queries/submission/*` | EX-A1 | Query methods persist and return settings/payload/attempt fields | | EX-A6 | A | Clone Safety | Fix clone question type mismatch (`TEXTAREA` id bug) using shared constant/typename | `apps/api/src/services/course/clone.ts` | EX-A3 | Exercise clone preserves paragraph questions and options correctly | | EX-A7 | A | Shared Copy Contract | Add typed i18n/copy contract for exercise-question renderer labels and messages in shared question-types package | `packages/question-types/src/render-contract.ts`, `packages/question-types/src/exercise-types.ts`, `packages/question-types/src/index.ts`, `packages/question-types/src/renderer-registry.ts` | EX-A3 | Host apps can pass a strongly typed labels object that covers shared renderer copy needs | | EX-B1 | B | API Validation | Split exercise schemas into base + per-type authoring/submission schemas | `packages/utils/src/validation/exercise/*` | EX-A4 | Route-level validation is discriminated by question type | | EX-B2 | B | Exercise Service | Support create/update with `question.settings`, `scoringMode`, `manualGradingRequired`, and exercise attempt settings | `apps/api/src/services/exercise/exercise.ts`, `apps/api/src/services/exercise/utils.ts` | EX-A5, EX-B1 | Create/update persists all new fields and returns normalized exercise DTO | | EX-B3 | B | Submission Service | Accept typed answers into `question_answer.payload` while preserving legacy fallback fields | `apps/api/src/services/submission/submission.ts` | EX-A5, EX-B1 | Submission creation supports mixed legacy/new question payloads | | EX-B4 | B | Grading Engine | Implement auto-grading for P0 objective types (`TRUE_FALSE`, `NUMERIC`, `FILL_BLANK`) | `apps/api/src/services/submission/submission.ts`, `apps/api/src/services/exercise/utils.ts` | EX-B3 | Auto-grade computed at question level with configurable scoring mode | | EX-B5 | B | Attempt Rules | Enforce `maxAttempts` and compute final score by `attemptScoringRule` | `apps/api/src/services/submission/submission.ts`, `packages/db/src/queries/submission/*` | EX-B3 | New attempts blocked when limit reached; final score follows configured rule | | EX-B6 | B | Upload Handling | Add file-upload answer flow with allowed type/size validation and asset reference storage | `apps/api/src/routes/course/exercise.ts`, `apps/api/src/services/submission/submission.ts` | EX-B3 | Invalid upload types/sizes rejected; valid uploads linked in answer payload | | EX-B7 | B | Routes | Update exercise and submission routes for new request/response contracts | `apps/api/src/routes/course/exercise.ts`, `apps/api/src/routes/course/submission.ts` | EX-B2, EX-B3 | RPC contract supports new fields without breaking legacy clients | | EX-B8 | B | Manual Grading | Route mixed/manual exercises to manual grading path and allow override of auto score | `apps/api/src/services/submission/submission.ts`, `apps/api/src/services/mark/gradebook.ts` | EX-B4 | Teacher can override and persist final grade on submission | | EX-B9 | B | Auto-Grade Guardrail | Enforce exercise-level `auto-grade` eligibility in API/service layer using question-type capability checks | `apps/api/src/services/exercise/exercise.ts`, `apps/api/src/services/submission/submission.ts`, `packages/question-types/src/question-type-capabilities.ts` | EX-A3, EX-B2 | API rejects `auto-grade=true` when any active question type requires manual grading | | EX-B10 | B | Submission Workflow | Implement API-owned grading state machine and deterministic `overallStatus` computation on submission lifecycle | `packages/db/src/schema.ts`, `packages/utils/src/validation/submission/submission.ts`, `packages/db/src/queries/submission/submission.ts`, `apps/api/src/services/submission/submission.ts`, `apps/api/src/routes/course/submission.ts` | EX-A1, EX-B3, EX-B4, EX-B8 | Submit starts `queued`, transitions are validated server-side, and invalid direct state jumps are rejected | | EX-B11 | B | Board Compatibility | Preserve existing 3 board columns by projecting workflow state to legacy `statusId` (`1=Submitted`, `2=In Progress`, `3=Graded`) in API responses | `apps/api/src/services/submission/submission.ts`, `apps/api/src/routes/course/submission.ts`, `packages/db/src/queries/submission/submission.ts` | EX-B10 | Submission board endpoints continue returning stable `statusId` while grading truth lives in workflow/classification fields | | EX-C1 | C | Dashboard Types | Add request/response/data types for new exercise payloads in feature types | `apps/dashboard/src/lib/features/course/utils/types.ts` | EX-B7 | Frontend types inferred from API contract only | | EX-C2 | C | Dashboard API | Update exercise API class to validate/send new create/update/submit payload shapes | `apps/dashboard/src/lib/features/course/api/exercise.svelte.ts` | EX-C1 | Create/update/submit flows pass new schema checks client-side | | EX-C3 | C | Renderer Registry | Implement registry logic in `@cio/question-types` and wire dashboard to map question-type keys to shared `@cio/ui` renderer components | `packages/question-types/src/renderer-registry.ts`, `packages/question-types/src/exercise-types.ts`, `packages/question-types/src/index.ts`, `packages/ui/src/custom/exercise-question/index.ts`, `packages/ui/src/index.ts`, `apps/dashboard/src/lib/features/course/components/exercise/edit-mode.svelte`, `apps/dashboard/src/lib/features/course/components/exercise/view-mode.svelte` | EX-A3 | No hardcoded `if RADIO/CHECKBOX/TEXTAREA` branching remains; registry/contracts come from `@cio/question-types` and components from `@cio/ui` | | EX-C4 | C | Edit UX | Refactor authoring UI to use registry and per-type settings editors | `apps/dashboard/src/lib/features/course/components/exercise/edit-mode.svelte` | EX-C3, EX-C2 | Instructor can configure P0 types and per-type settings in one flow | | EX-C5 | C | Take UX | Refactor learner view to use registry and typed answer serializers | `apps/dashboard/src/lib/features/course/components/exercise/view-mode.svelte`, `apps/dashboard/src/lib/features/course/components/exercise/functions.ts` | EX-C3, EX-C2 | Student can answer mixed-type exercises and submit valid payload | | EX-C6 | C | Store | Update exercise store/autosave model for typed answers and migration-safe hydration | `apps/dashboard/src/lib/features/course/components/exercise/store.ts` | EX-C5 | Autosave/restore works for legacy and new type payloads | | EX-C7 | C | P0 Components | Add new UI question components (`true-false`, `short-answer`, `numeric`, `fill-blank`, `file-upload`) in shared UI package | `packages/ui/src/custom/exercise-question/renderers/true-false/edit.svelte`, `packages/ui/src/custom/exercise-question/renderers/short-answer/edit.svelte`, `packages/ui/src/custom/exercise-question/renderers/numeric/edit.svelte`, `packages/ui/src/custom/exercise-question/renderers/fill-blank/edit.svelte`, `packages/ui/src/custom/exercise-question/renderers/file-upload/edit.svelte`, `packages/ui/src/custom/exercise-question/renderers/true-false/take.svelte`, `packages/ui/src/custom/exercise-question/renderers/short-answer/take.svelte`, `packages/ui/src/custom/exercise-question/renderers/numeric/take.svelte`, `packages/ui/src/custom/exercise-question/renderers/fill-blank/take.svelte`, `packages/ui/src/custom/exercise-question/renderers/file-upload/take.svelte`, `packages/ui/src/custom/exercise-question/renderers/true-false/preview.svelte`, `packages/ui/src/custom/exercise-question/renderers/short-answer/preview.svelte`, `packages/ui/src/custom/exercise-question/renderers/numeric/preview.svelte`, `packages/ui/src/custom/exercise-question/renderers/fill-blank/preview.svelte`, `packages/ui/src/custom/exercise-question/renderers/file-upload/preview.svelte`, `packages/ui/src/custom/exercise-question/index.ts`, `packages/ui/src/index.ts` | EX-C3 | Edit/view/preview components exist for all P0 types and are exported from `@cio/ui` | | EX-C8 | C | Grading UI | Update submission/grading views to display typed answers from payload | `apps/dashboard/src/lib/features/course/components/exercise/submissions/*` | EX-B8, EX-C5 | Teacher can review and grade all P0 answer shapes | | EX-C9 | C | i18n | Add translation keys for new types/settings/status copy | `apps/dashboard/src/lib/utils/translations/en.json` | EX-C4, EX-C5 | No new hardcoded user-facing text | | EX-C10 | C | Accessibility | Validate keyboard navigation and mobile rendering for new question interactions in shared UI component package | `packages/ui/src/custom/exercise-question/*` | EX-C7 | P0 UI passes accessibility checks and mobile smoke tests | | EX-C11 | C | Edit UX Guardrail | Add exercise-level `auto-grade` toggle, disable state, and hint copy when question mix includes manual-only types | `apps/dashboard/src/lib/features/course/components/exercise/edit-mode.svelte`, `apps/dashboard/src/lib/features/course/components/exercise/question-type-utils.ts`, `apps/dashboard/src/lib/utils/translations/en.json` | EX-C3, EX-B9 | Toggle enables only when all active question types are auto-gradable; hint appears under toggle when blocked | | EX-C12 | C | Submission Board UX | Consume API workflow data while keeping 3-column board UX, and surface failed/manual-needed hints under In Progress column | `apps/dashboard/src/lib/features/course/pages/submissions.svelte`, `apps/dashboard/src/lib/features/course/components/exercise/mark-exercise-modal.svelte`, `apps/dashboard/src/lib/features/course/utils/types.ts`, `apps/dashboard/src/lib/features/course/api/submission.svelte.ts`, `apps/dashboard/src/lib/utils/translations/en.json` | EX-B10, EX-B11 | Board still behaves as `1/2/3` columns, with additional workflow clarity from API-provided grading fields | | EX-C13 | C | Renderer Structure Refactor | Refactor shared renderer files to question-type folders with mode file names and update imports/registry/storybook references | `packages/ui/src/custom/exercise-question/renderers/*/edit.svelte`, `packages/ui/src/custom/exercise-question/renderers/*/take.svelte`, `packages/ui/src/custom/exercise-question/renderers/*/preview.svelte`, `packages/ui/src/custom/exercise-question/index.ts`, `packages/question-types/src/renderer-registry.ts`, `packages/storybook/src/molecules/exercise-question/*`, `apps/dashboard/src/lib/features/course/components/exercise/*` | EX-C3, EX-C7 | Renderer paths use `renderers/{question-type}/{mode}.svelte`; no mode-first folders or mode-suffixed filenames remain | | EX-C14 | C | Copy Externalization | Remove hardcoded user-facing copy from shared question renderer files and wire host-provided labels from dashboard translations | `packages/ui/src/custom/exercise-question/question-renderer.svelte`, `packages/ui/src/custom/exercise-question/question-navigation.svelte`, `packages/ui/src/custom/exercise-question/renderers/*/edit.svelte`, `packages/ui/src/custom/exercise-question/renderers/*/take.svelte`, `packages/ui/src/custom/exercise-question/renderers/*/preview.svelte`, `packages/ui/src/custom/exercise-question/renderers/ordering-utils.ts`, `apps/dashboard/src/lib/features/course/components/exercise/edit-mode.svelte`, `apps/dashboard/src/lib/features/course/components/exercise/view-mode.svelte`, `apps/dashboard/src/lib/features/course/utils/types.ts`, `apps/dashboard/src/lib/utils/translations/en.json`, `packages/storybook/src/molecules/exercise-question/*` | EX-A7, EX-C3 | Shared renderer package has no hardcoded end-user copy; dashboard injects all labels/messages from translation keys through typed contracts | | EX-D1 | D | Matching Type | Implement `MATCHING` schema, authoring/taking UI in shared UI component package, serializer, and grader | validation, API service, `packages/ui/src/custom/exercise-question/renderers/matching/edit.svelte`, `packages/ui/src/custom/exercise-question/renderers/matching/take.svelte`, `packages/ui/src/custom/exercise-question/renderers/matching/preview.svelte` | EX-C7, EX-B4 | Matching questions can be authored, submitted, and auto-graded | | EX-D2 | D | Ordering Type | Implement `ORDERING` schema, drag-sort UI in shared UI component package, serializer, and grader | validation, API service, `packages/ui/src/custom/exercise-question/renderers/ordering/edit.svelte`, `packages/ui/src/custom/exercise-question/renderers/ordering/take.svelte`, `packages/ui/src/custom/exercise-question/renderers/ordering/preview.svelte` | EX-C7, EX-B4 | Ordering questions can be authored, submitted, and auto-graded | | EX-D3 | D | Partial Credit | Add partial-credit policy controls and explainability in grading details | `apps/api/src/services/submission/submission.ts`, submissions UI | EX-D1, EX-D2 | Teacher sees per-question partial-credit breakdown | | EX-E1 | E | Hotspot Type | Implement `HOTSPOT` schema, image coordinate payload, authoring/taking UI in shared UI component package, grader | validation, API service, `packages/ui/src/custom/exercise-question/renderers/hotspot/edit.svelte`, `packages/ui/src/custom/exercise-question/renderers/hotspot/take.svelte`, `packages/ui/src/custom/exercise-question/renderers/hotspot/preview.svelte` | EX-C7 | Hotspot is fully supported end-to-end | | EX-E2 | E | Hotspot Hardening | Improve responsive/touch behavior and precision tolerances for hotspot grading | hotspot UI + grading utilities | EX-E1 | Hotspot works reliably on mobile/tablet and desktop | | EX-F1 | F | API Tests | Add create/update/submit tests per type, attempt rules, manual/auto grading paths | `apps/api/tests/*`, `packages/utils/src/validation/*` | EX-B8, EX-D3, EX-E1 | Core API behavior covered with passing tests | | EX-F2 | F | Dashboard Tests | Add component/store/integration tests for authoring, taking, autosave, submission payloads across dashboard integration and shared UI component package | `apps/dashboard/src/lib/features/course/components/exercise/*`, `packages/ui/src/custom/exercise-question/*` | EX-C10, EX-D2, EX-E2 | UI and store behavior stable for legacy + new types | | EX-F3 | F | Regression | Add clone and backward-compat regression tests for legacy question data | API/services/db test files | EX-A6, EX-F1 | Existing exercises run unchanged after rollout | | EX-F4 | F | Rollout | Ship rollout checklist, migration runbook, and monitoring for submission/grading errors | `prd/exercise-question-types/README.md`, ops docs/config | EX-F1, EX-F2, EX-F3 | Controlled release enabled with rollback steps documented | ## Acceptance Criteria by Phase ### Phase A 1. Schema changes are limited to `packages/db/src/schema.ts` and no migration files are generated. 2. `question_type` seed is idempotent and includes all planned typenames. 3. Shared registry is imported by both API and dashboard. 4. Clone service no longer depends on mismatched hardcoded paragraph ID. 5. Shared renderer copy-contract types are defined in `@cio/question-types` and exported for host apps. ### Phase B 1. Exercise create/update APIs accept and return new settings fields. 2. Submission API accepts typed payloads and still reads legacy answer shape. 3. Attempt limits and scoring rules are enforced server-side. 4. File-upload answer validations enforce plan limits and allowed types. 5. API/service layer blocks exercise-level `auto-grade` when question mix includes any manual-only type. 6. Student submit always creates submission with workflow state `queued` and board-compatible `statusId=1`. 7. API enforces valid grading transitions only (`queued -> processing -> ...`), with retry path (`failed -> queued`), and rejects invalid direct client transitions. 8. `overallStatus` is computed deterministically as `auto_graded`, `manual_required`, or `hybrid`. 9. API projects workflow states to board `statusId` mapping (`queued=1`, `processing|awaiting_manual|failed=2`, `completed=3`). ### Phase C 1. Authoring and learner UIs support all P0 types. 2. Renderer registry removes hardcoded question-type branching in core screens. 3. Dashboard consumes registry/contracts from `@cio/question-types` and renderer components from `@cio/ui`, not dashboard-local question renderer files. 4. Grading UI can render typed payloads for teacher review. 5. No hardcoded UI copy is introduced. 6. Shared package files listed in `Shared Package File Contract` are implemented and exported. 7. Exercise-level `auto-grade` toggle is disabled with a clear hint when question mix is not fully auto-gradable. 8. Submission board continues to use 3 columns from backend-projected `statusId`, while exposing workflow hints (`failed`, `awaiting_manual`) without changing board IA. 9. Renderer files/imports follow canonical type-first paths: `renderers/{question-type}/{mode}.svelte`. 10. `@cio/ui/custom/exercise-question` does not contain hardcoded end-user copy; dashboard passes translated labels/messages via typed render contracts. ### Phase D 1. Matching and ordering are fully authorable/submittable/gradable. 2. Partial-credit configuration and score explanation are visible to instructors. ### Phase E 1. Hotspot works with responsive image rendering and touch input. 2. Hotspot grading handles coordinate tolerances consistently. ### Phase F 1. Regression tests cover legacy three types and mixed-type exercises. 2. Rollout checklist includes migration, monitoring, and rollback steps. ## Verification Commands 1. `pnpm --filter @cio/db build` 2. `pnpm --filter @cio/utils build` 3. `pnpm --filter @cio/api build` 4. `pnpm --filter @cio/dashboard build` ## Suggested First Sprint Slice 1. EX-A1 to EX-A7 (schema, seed, registry, validation foundation, clone fix, shared copy contract) 2. EX-B1 to EX-B5 + EX-B10 + EX-B11 (P0 API contracts, typed submission, attempt/scoring enforcement, workflow state machine, board projection compatibility) 3. EX-C1 to EX-C6 + EX-C14 (frontend types/API + renderer registry + core edit/view/store refactor + shared copy externalization) This slice establishes the core contracts and de-risks the hardest migration paths before adding all P0 components and grading UI polish. ## Implementation Progress (Updated Feb 26, 2026) Completed ticket IDs: 1. `EX-A3` Shared question-type package (`@cio/question-types`) with registry/contracts/helpers. 2. `EX-A6` Clone safety bug fix using shared question type IDs. 3. `EX-A7` Shared copy contract typing for renderer labels/messages (`ExerciseQuestionLabels`) in `@cio/question-types`. 4. `EX-C3` Dashboard/UI renderer registry centralization (`@cio/question-types` contracts + `@cio/ui` components). 5. `EX-C7` Shared `@cio/ui/custom/exercise-question` renderer component set delivered (includes P0 and additional renderer files used by current branch). 6. `EX-C13` Renderer structure refactor completed with canonical type-first paths (`renderers/{question-type}/{mode}.svelte`) and updated imports/exports/storybook wiring. 7. `EX-C14` Shared renderer copy externalization completed; dashboard now injects labels/messages from translations through typed contracts. In-progress ticket IDs: 1. `EX-A4` Validation migration is partial (constants wired to shared IDs; full per-type schema split still pending). 2. `EX-C4` Authoring UX refactor is mostly done, but exercise-level grading controls/guards are not complete. 3. `EX-C5` Learner UX refactor is active; answer submission/grading remains mixed legacy behavior. 4. `EX-B10` Submission workflow state machine has been started (schema fields + transition validation + API projection wiring); lifecycle hardening and tests remain. 5. `EX-B11` Board compatibility projection has been started (workflow-to-legacy `statusId` projection in submission service); endpoint/test hardening remains. 6. `EX-C12` Dashboard board workflow hint wiring has been started (workflow hints + guarded transitions in board UI); full UX/polish and edge-case handling remain. 7. `EX-D1`, `EX-D2`, `EX-E1` UI renderers exist, but full API validation/submission/grader paths are not complete.