Files
emba-course-solver/openspec/changes/add-external-credits/proposal.md
T
Bill 3a5ebaa17a v1.5.0: External credits per specialization
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.
2026-05-10 11:47:22 -04:00

3.9 KiB
Raw Blame History

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 on AppState (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 ≥ 9 to ≥ 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) add external[spec] to each spec's potential.
  • The hard 3-spec cap in maximizeCount and priorityOrder is 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 (was allocated ≥ threshold).
  • Credit bar gets a new amber #f59e0b segment that fills from the left, before the existing in-program allocated/potential stripes.
  • Allocation breakdown gains an External line item when external[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 useAppState extends to externalCredits (treated like ranking/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 — extend AppState shape (via state/appState.ts); no change to Course/Specialization/AllocationResult.
  • app/src/state/appState.ts — add externalCredits to state, reducer actions (setExternalCredits or similar), localStorage load/save, and leaf-cache invalidation signature. Thread the value through to optimize and the worker.
  • app/src/solver/feasibility.ts — new optional externalCredits parameter to checkFeasibility, computeUpperBounds, and preFilterCandidates; reduce demand and add to bounds.
  • app/src/solver/optimizer.ts — thread externalCredits through maximizeCount, priorityOrder, determineStatuses, and optimize. The hardcoded 3-spec cap stays. Status determination keeps missing_required when applicable regardless of external totals.
  • app/src/solver/decisionTree.ts + app/src/workers/decisionTree.worker.ts — accept externalCredits in the worker request and propagate to feasibility/upper-bound calls during search.
  • app/src/components/SpecializationRanking.tsxCreditBar accepts external and renders the amber segment as the leftmost stripe; AllocationBreakdown shows an External line; 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 verifying missing_required survives external-only achievement.
  • app/vite.config.ts — version bump.
  • CHANGELOG.md — release entry.
  • No data-file (data/courses.ts, data/specializations.ts) changes.