Part of epic #1725. Depends on #1726. Phase 1 — Mode-as-data refactor.
Objective
Replace the scattered mode-aware constants and conditionals in plan.ts with reads from MODE_DEFINITIONS. Must produce zero visible behavior change — snapshot tests in #1.4 will prove this.
Files
Modify
app/src/services/guidedStudy/plan.ts
Do NOT modify
MODE_DEFINITIONS itself (this card consumes it; Phase 2 extends it)
types.ts
- Any UI component
What to remove from plan.ts
const DEFAULT_MODE
const MODE_RECOMMENDATION_LIMIT
const MODE_TRAIL_ORDER
function normalizeMode body (replace with call to getModeDefinition)
function modeLabel body (replace with getModeDefinition(mode).label)
- The mode-specific switch statement inside
betterQuestionPrompt — keep the function but make it pure-of-mode for now (covered fully in Phase 2.1)
What to add
import { getModeDefinition } from './modes/definitions';
Replace MODE_RECOMMENDATION_LIMIT[mode] with getModeDefinition(mode).recommendationLimit.
Replace MODE_TRAIL_ORDER[mode] with getModeDefinition(mode).trailOrder.
Replace normalizeMode(mode) with getModeDefinition(mode ?? 'deep').key where it's currently used to validate, and a thin shim function that just delegates.
buildRecommendations() panel weights wiring
Add panelWeights parameter to buildRecommendations, but default to {} and apply no behavior in Phase 1. The signature must accept weights so Phase 2.4 can flip them on without another plan.ts refactor. When weights are empty, sort order is exactly the existing RECOMMENDATION_ORDER array (no change).
function buildRecommendations(
input: GuidedStudyPlanInput,
panelWeights: Record<string, number> = {},
): GuidedPanelRecommendation[];
Inside buildGuidedStudyPlan, call:
const def = getModeDefinition(mode);
const allRecommendations = buildRecommendations(input, def.panelWeights);
def.panelWeights is {} for all four modes after Phase 1, so this is a no-op behaviorally.
Acceptance
MODE_RECOMMENDATION_LIMIT and MODE_TRAIL_ORDER constants are deleted.
plan.ts references getModeDefinition exclusively for mode-shaped values.
buildRecommendations accepts a panelWeights parameter with {} default.
tsc, lint, test all clean.
- Phase 1.4 snapshot tests pass without modification.
Out of scope
- Adding mode-specific
betterQuestionPrompt per-mode-via-MODE_DEFINITIONS (that's Phase 2.1).
- Implementing actual weight bias logic (that's Phase 2.4).
Part of epic #1725. Depends on #1726. Phase 1 — Mode-as-data refactor.
Objective
Replace the scattered mode-aware constants and conditionals in
plan.tswith reads fromMODE_DEFINITIONS. Must produce zero visible behavior change — snapshot tests in #1.4 will prove this.Files
Modify
app/src/services/guidedStudy/plan.tsDo NOT modify
MODE_DEFINITIONSitself (this card consumes it; Phase 2 extends it)types.tsWhat to remove from plan.ts
const DEFAULT_MODEconst MODE_RECOMMENDATION_LIMITconst MODE_TRAIL_ORDERfunction normalizeModebody (replace with call togetModeDefinition)function modeLabelbody (replace withgetModeDefinition(mode).label)betterQuestionPrompt— keep the function but make it pure-of-mode for now (covered fully in Phase 2.1)What to add
Replace
MODE_RECOMMENDATION_LIMIT[mode]withgetModeDefinition(mode).recommendationLimit.Replace
MODE_TRAIL_ORDER[mode]withgetModeDefinition(mode).trailOrder.Replace
normalizeMode(mode)withgetModeDefinition(mode ?? 'deep').keywhere it's currently used to validate, and a thin shim function that just delegates.buildRecommendations() panel weights wiring
Add
panelWeightsparameter tobuildRecommendations, but default to{}and apply no behavior in Phase 1. The signature must accept weights so Phase 2.4 can flip them on without anotherplan.tsrefactor. When weights are empty, sort order is exactly the existingRECOMMENDATION_ORDERarray (no change).Inside
buildGuidedStudyPlan, call:def.panelWeightsis{}for all four modes after Phase 1, so this is a no-op behaviorally.Acceptance
MODE_RECOMMENDATION_LIMITandMODE_TRAIL_ORDERconstants are deleted.plan.tsreferencesgetModeDefinitionexclusively for mode-shaped values.buildRecommendationsaccepts apanelWeightsparameter with{}default.tsc,lint,testall clean.Out of scope
betterQuestionPromptper-mode-via-MODE_DEFINITIONS (that's Phase 2.1).