Files
emba-course-solver/CHANGELOG.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

140 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Changelog
## v1.5.0 — 2026-05-10
### Changes
- **External credits per specialization** — students can now record credits earned in courses taken outside the J27 program. An inline editable amber chip on each specialization card commits a non-negative number; the value is persisted to localStorage alongside the rest of the app state. Credits flow through LP feasibility (per-spec demand reduces by the external amount), upper-bound and pre-filter math (potential rises by the external amount), and decision-tree search (worker request carries the value, search uses external-credit-aware feasibility on every leaf).
- **Credit bar gains an amber segment** — the bar's existing in-program allocated/potential stripes shift right to make room for an amber `#f59e0b` external segment at the leftmost edge. The 9-credit threshold tick repositions automatically when external credits push the bar's max width past 9. The "achieved" green color now switches when `allocated + external ≥ 9` (was `allocated ≥ 9`).
- **Allocation breakdown shows External line** — when external credits exist, the per-spec allocation breakdown (mobile expand and desktop popover) prepends an italic amber `External — N.N` line above the in-program contributions.
- **3-spec cap retained** — the program policy of 3 specializations is unchanged. External credits may shift which 3 specs the optimizer picks (e.g., admitting a spec the in-program courses don't naturally support, or freeing in-program credits for a different combination), but the achieved count never exceeds 3.
- **Required-course gates remain authoritative** — external credits never satisfy a `requiredCourseId` gate. A specialization with 9 external credits but a missing required course stays in `missing_required`; the gold bar segment is informational only.
- **Leaf-cache invalidation extended** — any change to `externalCredits` clears the leaf cache (treated identically to a `ranking` or `mode` change). No-op edits (committing the same value) leave the cache intact.
## v1.4.0 — 2026-05-09
### Changes
- **Desktop layout redesigned** — specializations move from a 340px left rail into a horizontal drag-to-rank chip strip at the top of the page (left = highest priority). The previous right pane splits into two side-by-side columns: Top Plans on the left, Schedule on the right. Each column scrolls independently so the schedule no longer gets pushed below the fold when plans expand.
- **Search progress hoisted to a global strip** — the animated progress bar moves out of Top Plans into a thin strip directly below the spec strip. It stays visible regardless of which column you're scrolling. The static "Search complete · N explored" / "Search incomplete · cap hit at N" text remains inline with the Top Plans header.
- **Specialization chips with hover popover** — each chip shows rank, full specialization name (line-clamped to 2 lines), status indicator, and a micro credit bar; status is encoded by background color. Hovering (or tapping on touch) opens a popover with the full name, status word, allocated/threshold credits, and — for achieved specializations — the contributing-courses breakdown. The strip scrolls horizontally when the 15 chips don't fit in the available width.
- **Horizontal drag-to-reorder on desktop** — switched the spec sort strategy from `verticalListSortingStrategy` to `horizontalListSortingStrategy`. Mobile keeps vertical drag.
- **Schedule blocks render horizontal course buttons on desktop** — each non-pinned elective set lays its course choices out as a flex row of equal-width buttons (35 per set) instead of stacked rows. Each button shows the info icon (top-left), the recommended star (top-right when applicable), the course name with line-clamp, and a row of spec ceiling tags at the bottom. Cancelled / already-selected / per-course searching states preserve their semantics in the new layout.
- **Compact pinned-set rendering** — when a course is pinned, the elective-set card collapses to a single line: `Set Name: Course Name [Clear]`. The previous separate-row pinned-view block is gone, freeing vertical space.
- **Mobile (≤640px) layout unchanged** — vertical specialization list, stacked Top Plans + Schedule sections, in-line progress bar, MobileStatusBanner and MobileCourseBanner all behave as before.
## v1.3.3 — 2026-05-09
### Changes
- **Lexicographic priority comparison** — fixes a scoring bug where combinations of lower-priority specializations could outrank a single higher-priority specialization in priority-order mode. The comparator now uses lex-by-rank: a plan containing a higher-ranked specialization always beats a plan that doesn't, regardless of how many lower-ranked specializations the latter contains. Lower-ranked specializations only act as tiebreakers among plans that all contain the same higher-ranked specs. Same logic also tiebreaks within maximize-count mode.
- **Score display matches the comparator** — the per-plan score now shows the lexicographic rank weight in compact form (e.g. `score 24.6k`) instead of the legacy sum-of-weights. Hover the score for the full integer.
- **Cache cap retains warm entries** — when the leaf cache hits the 500k cap, new entries are now dropped instead of clearing the cache; the existing 500k stay as a starting point for subsequent pin/unpin operations.
- **Cache stays valid** across the comparator change — leaves cached under v1.3.2 still produce correct rankings under the new comparator since `achievedSpecs` (the input to lex compare) is unchanged.
## v1.3.2 — 2026-05-09
### Changes
- **Leaf cache for instant pin/unpin** — decision-tree leaf outcomes are now cached on the main thread keyed by their full 12-course assignment. Pin operations filter the cache and re-derive the top-K + per-set ceilings instantly with no worker spawn. Unpin operations show the cached subset immediately and stream improvements as a background worker fills in the missing leaves. The cache persists across pin, unpin, and adopt-plan operations.
- **Cache invalidation** — the cache is cleared only when the active mode or the specialization ranking changes. Pin/unpin alone never invalidates.
- **`skipKeys` worker contract** — workers now accept a list of cached assignment keys and skip the optimizer call for any leaf already in the cache, while still counting iterations toward the global progress percentage.
- **`leafEvaluated` worker event** — workers stream individual leaf outcomes to the main thread for cache population as the search progresses.
- **`deriveFromLeaves` shared helper** — pure function that produces the top-K and per-set ceilings from a leaf collection; used by both the main-thread cache filter and the worker's final emission for parity.
- **500,000-leaf soft cap** — the cache is cleared if it grows beyond 500k entries, bounding worst-case memory at ~150 MB. Typical sessions stay well below.
## v1.3.1 — 2026-05-09
### Changes
- **Exhaustive decision-tree search** — replaced the saturation early-termination with full enumeration of the open-set cartesian product. Per-set ceiling cells now reflect the true best outcome for every (set, course) pair instead of leaving most cells stuck at "0 specs". Top Plans surfaces all genuinely-feasible plans, including 3-spec maximize-count plans that the v1.3.0 search missed. The previous iteration cap has been removed; search runs to full completion.
- **Mode-dependent enumeration ordering** — priority-order mode keeps the priority-target-first heuristic; maximize-count mode now orders DFS children by descending count of qualifications for *reachable* specializations, surfacing generalist courses (e.g., Climate Finance with 6 qualifications) before specialists.
- **Mode-aware comparator** — top-K and per-cell ceiling rankings now match the active mode: priority-order ranks by `(priorityScore, count)` so the top-priority spec surfaces; maximize-count ranks by `(count, priorityScore)` so the highest count wins. Recommended badges follow the same rule.
- **"Recommended" badge per set** — each elective set now highlights the choice with the best ceiling outcome under the current mode. Rendered inline next to the course name to keep button height stable.
- **Color-coded spec tags** — the per-cell outcome list and the Top Plans badges now use a fixed per-spec color palette so each specialization is visually identifiable at a glance.
- **"Top outcome if picked ↓" caption** — added a small column header on each open elective set so the spec tags are clearly identified as decision-tree outcomes (not the course's own qualifications).
- **Visual progress bar** — Top Plans header now shows a progress bar with `iterations / total · NN%` while the search runs, replacing the earlier text-only count.
- **Per-cell streaming indicators** — courses that haven't been evaluated yet show a "searching" pulse instead of misleading "0 specs"; cells transition to their final value as the search completes.
- **Per-set spinner** — each elective set heading shows a spinner while at least one of its choices is still unevaluated.
## v1.3.0 — 2026-05-09
### Changes
- **Top Plans panel** — new ranked list of up to 10 complete course plans, each showing the achieved specializations and the courses to pin. An "Adopt plan" button pins all of a plan's courses in one click. Updates progressively as the search finds better outcomes.
- **Priority-aware decision tree** — fixes the bug where a specialization could show "Achievable" without any per-set ceiling cell surfacing it. The decision-tree search now compares enumerated combinations by `(count desc, priority score desc)` and reorders DFS children so courses qualifying for the user's first reachable ranked spec are tried first, surfacing high-priority outcomes early.
- **Bounded search with saturation termination** — search stops when the top-K stabilizes (default 500 stable iterations) or when the iteration cap (10000) is hit; partial results are flagged in the UI.
- **Per-cell streaming** — the worker now emits per-cell ceiling updates instead of per-set rollups, so the per-set table refines progressively rather than appearing in coarse chunks.
## v1.2.2 — 2026-05-09
### Changes
- **Healthcare specialization (HCR)** — added 15th specialization, Healthcare; qualifies via The Business of Health & Medical Care (Spring Set 2), Analytics & Machine Learning for Managers (Spring Set 3), Digital Marketing Strategy in Practice (Summer Set 2), and Managing Change (Fall Set 1); 10 total credits available, no required course gate
- **Course rename** — "Social Media and Mobile Technology" renamed to "Digital Marketing Strategy in Practice"; description replaced with new MSKCC-anchored content covering digital strategy and agentic AI; instructor cleared pending confirmation
- **Cancellations (Approach B)** — switched from delete-and-replace to flagging cancelled courses with `cancelled: true`. "Customer Insights" (Spring Set 5) is now marked cancelled. "Managing Growing Companies" reappears in Summer Set 2 as a cancelled placeholder per the J27 sheet
- **Reachability test** — `data.test.ts` now excludes cancelled courses when counting per-spec set reachability, so future cancellations are caught by an obvious assertion failure
## v1.2.1 — 2026-03-27
### Bug Fixes
- **Achievable status accuracy** — specializations marked "Achievable" are now verified via LP feasibility check against already-achieved specs; previously, a specialization could show "Achievable" based on raw credit potential while actually being infeasible due to credit sharing with higher-priority achieved specializations
## v1.2.0 — 2026-03-27
### Changes
- **Course info popovers** — each course now has an info icon that opens a popover showing the course description, instructor(s), and specialization tags, extracted from the J27 Electives PDF; opens on hover (desktop) or tap (mobile), with smart positioning that flips above when near the bottom of the viewport
- **Page title and favicon** — updated browser tab from "app" with Vite icon to "EMBA Specialization Solver" with a graduation cap favicon in NYU Stern purple
- **Viewport-fitted layout** — desktop layout now fits within the viewable area without page-level scrolling; each pane scrolls independently
## v1.1.1 — 2026-03-27
### Changes
- **Course replacement** — replaced cancelled "Managing Growing Companies" with new course "Innovation and Design" in Summer Elective Set 2; qualifies for Brand Management, Entrepreneurship and Innovation, Marketing, and Strategy (S2)
## v1.1.0 — 2026-03-13
### Changes
- **Cancelled course support** — "Managing Growing Companies" (Summer Elective Set 2) is marked as cancelled and rendered with strikethrough, greyed-out styling, and a "(Cancelled)" label; it is excluded from solver computations and decision tree enumeration
- **Duplicate course prevention** — courses that appear in multiple elective sets (e.g., "Global Immersion Experience II" in Spring Set 1 and Summer Set 1, "The Financial Services Industry" in Spring Set 2 and Fall Set 4) are now linked; selecting one automatically disables and excludes its duplicate from selection and solver calculations, shown with an "(Already selected)" label
- **Credit bar tick marks** — specialization progress bars now display light vertical tick marks at 2.5-credit intervals for visual scale reference, layered above bar fills with the 9.0 threshold marker remaining visually distinct
## v1.0.0 — 2026-02-28
Initial release of the EMBA Specialization Solver.
### Features
- **Optimization engine** — LP-based credit allocation solver with two modes:
- **Maximize Count** — finds the largest feasible set of specializations, using ranking as tiebreaker
- **Priority Order** — greedily adds specializations in user-ranked order
- **Course selection UI** — select one course per elective set across 12 sets (Spring, Summer, Fall terms)
- **Drag-and-drop specialization ranking** — reorder the 14 specializations by priority with touch and keyboard support
- **Decision tree analysis** — Web Worker enumerates remaining course combinations to compute ceiling outcomes per choice
- **Status tracking** — each specialization classified as achieved, achievable, missing required course, or unreachable
- **Mode comparison** — displays what the alternative optimization mode would produce
- **Credit bars and allocation breakdowns** — visual progress toward the 9-credit threshold with expandable per-course detail
- **Credit legend** — collapsible explainer for bars, badges, and limits
- **Required course labels** — courses that are prerequisites for a specialization show "Required for ..." labels
- **Algorithm explanations** — clear descriptions of how each optimization mode works
- **Skeleton loading** — placeholder UI while decision tree analysis runs
- **Auto-expand achieved specializations** — achieved specs show their credit breakdown by default
- **Responsive layout** — two-panel grid on desktop/tablet, single-column on mobile
- **Mobile floating banners** — top banner summarizes specialization statuses, bottom banner shows selection progress (N/12); both appear via IntersectionObserver and scroll to their sections on tap
- **CSS transitions and animations** — cross-fade course pin/unpin, credit bar width changes, status badge color transitions, expand/collapse panels, mode toggle switching, flash on status changes; all respect `prefers-reduced-motion`
- **State persistence** — rankings and selections saved to localStorage
- **Docker deployment** — multi-stage Dockerfile (Node 22 build → Nginx Alpine serve) with Docker Compose, gzip compression, SPA fallback routing, immutable cache headers for hashed assets, configurable port (default 8080)
- **Full test suite** — data integrity, feasibility solver, optimizer, and decision tree tests via Vitest
### Constraints Modeled
- Credit non-duplication (2.5 credits per course shared across specializations)
- Maximum 3 specializations (30 total credits, 9 required each)
- Required course prerequisites for 4 specializations
- Strategy S1/S2 tier system (at most 1 S2 course contributes to Strategy)
- Mutual exclusion from same-set conflicts