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.
140 lines
16 KiB
Markdown
140 lines
16 KiB
Markdown
# 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 (3–5 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
|