3a5ebaa17a
Students can now record credits earned in courses taken outside the J27 program via an inline editable amber chip on each spec card. Values flow through the LP (per-spec demand reduces by external amount), upper-bound math, decision-tree search, and the credit bar visualization. The 9-credit threshold and the 3-spec achievement cap are unchanged; required-course gates remain authoritative — external credits never satisfy them.
3.9 KiB
3.9 KiB
Why
Students sometimes earn credits in courses taken outside the J27 program (cross-registration, transfer, etc.) that the registrar will count toward a specialization. The current solver has no way to represent these credits, so its achievement and reachability claims understate what the student actually has. We want a lightweight escape hatch that lets a student dial in external credits per specialization and have the optimizer, status determination, and credit-bar visualization reflect them.
What Changes
- New
externalCredits: Record<string, number>field onAppState(specId → credits, default 0). Persisted to localStorage with the rest of the state. Edited via per-spec inline chip on the spec card. - LP feasibility (
checkFeasibility) reduces each spec's demand from≥ 9to≥ max(0, 9 − external[spec]). Specs whose external credits already meet the threshold drop out of the LP entirely. - Upper-bound and pre-filter math (
computeUpperBounds,preFilterCandidates) addexternal[spec]to each spec's potential. - The hard 3-spec cap in
maximizeCountandpriorityOrderis retained (program policy, not a math consequence). External credits may free in-program credits or substitute for an in-program spec within that cap, but never raise the cap above 3. - "Achieved" coloring switches when
allocated + external ≥ 9(wasallocated ≥ threshold). - Credit bar gets a new amber
#f59e0bsegment that fills from the left, before the existing in-program allocated/potential stripes. - Allocation breakdown gains an
Externalline item whenexternal[spec] > 0. - Required-course gates (BRM/EMT/ENT/SBI) are unchanged — external credits never satisfy them. A spec with sufficient external credits but a missing required course stays in
missing_required. - Leaf-cache invalidation in
useAppStateextends toexternalCredits(treated likeranking/mode).
Capabilities
New Capabilities
None.
Modified Capabilities
optimization-engine: external-credit-aware demand, upper-bound, candidate pre-filter, achievement ceiling, and cache invalidation. The LP shape itself (variables, capacity constraints, S2 enumeration) is unchanged.unified-specialization-panel: per-spec inline editable external-credits chip, amber bar segment, "achieved" coloring keyed off combined credit, External line in allocation breakdown.
Impact
app/src/data/types.ts— extendAppStateshape (viastate/appState.ts); no change toCourse/Specialization/AllocationResult.app/src/state/appState.ts— addexternalCreditsto state, reducer actions (setExternalCreditsor similar), localStorage load/save, and leaf-cache invalidation signature. Thread the value through tooptimizeand the worker.app/src/solver/feasibility.ts— new optionalexternalCreditsparameter tocheckFeasibility,computeUpperBounds, andpreFilterCandidates; reduce demand and add to bounds.app/src/solver/optimizer.ts— threadexternalCreditsthroughmaximizeCount,priorityOrder,determineStatuses, andoptimize. The hardcoded 3-spec cap stays. Status determination keepsmissing_requiredwhen applicable regardless of external totals.app/src/solver/decisionTree.ts+app/src/workers/decisionTree.worker.ts— acceptexternalCreditsin the worker request and propagate to feasibility/upper-bound calls during search.app/src/components/SpecializationRanking.tsx—CreditBaracceptsexternaland renders the amber segment as the leftmost stripe;AllocationBreakdownshows anExternalline; spec card adds an inline editable credits chip.app/src/solver/__tests__/— add LP tests for the demand reduction, upper-bound boost, and the lifted ceiling. Add tests verifyingmissing_requiredsurvives external-only achievement.app/vite.config.ts— version bump.CHANGELOG.md— release entry.- No data-file (
data/courses.ts,data/specializations.ts) changes.