v1.3.2: Leaf cache for instant pin/unpin + TopPlans block UX
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 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. Cache survives pin,
unpin, and adopt-plan; only ranking or mode changes invalidate it.
Solver / worker:
- searchDecisionTree accepts skipKeys (Set<string>) and pinnedAssignments
(Record<setId,courseId>). Leaves are emitted with their full 12-set
assignment so cache keys are stable across pin/unpin operations.
- evaluateLeaf short-circuits when the leaf's assignmentKey is in
skipKeys: increments iterations + emits progress, but skips the
optimizer call and all callbacks. Keeps progress percentage honest
(counts whole tree, not just delta).
- New deriveFromLeaves pure helper produces {topK, setAnalyses} from a
leaf collection; used by the main-thread cache filter and gives a
reusable derivation primitive for tests.
- Worker request gains skipKeys and pinnedAssignments fields. Worker
response gains a leafEvaluated event so the main thread can populate
its cache as the search streams.
App state:
- leafCacheRef holds Map<assignmentKey, PlanOutcome> scoped to the
current (ranking, mode) pair. The search effect now: invalidates on
ranking/mode change; computes the orderedCourses + expectedTotal;
filters the cache against the current pinned/excluded state; calls
deriveFromLeaves to render immediately; spawns the worker only when
filtered.length < expectedTotal, passing skipKeys.
- Cache cap of 500,000 leaves with full clear on overflow. Bounds
worst-case memory at ~150 MB.
UI (TopPlans):
- Course blocks in the per-plan row are now interactive buttons. Click
pins (or unpins, if the course is currently pinned) the course in
that set. Pinned blocks render in a selected blue color.
- Each plan row now shows the FULL 12-set sequence including pinned
courses (interleaved with the search's recommended choices for the
remaining open sets) so the displayed plan is always complete.
- Spec qualification tags removed from per-block display (kept the
set-label + course-name treatment for clarity).
Tests:
- New app/src/solver/__tests__/leafCache.test.ts with 4 tests:
skipKeys parity (second-pass run with skipKeys evaluates zero
leaves), deriveFromLeaves parity (matches a fresh search), cache
filter on pinned assignments, cache filter on excluded courses.
- All 78 prior tests continue to pass; 82 total.
Browser-verified: pin click on a Top Plans block from the cached
8-open-set scenario completes instantly with no spinner; unpin restores
the original cached subset (also instant when the prior space was
already cached); mode toggle correctly invalidates and re-runs the
search.
This commit is contained in:
@@ -1,5 +1,16 @@
|
||||
# Changelog
|
||||
|
||||
## 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
|
||||
|
||||
Reference in New Issue
Block a user