## ADDED Requirements ### Requirement: Streamed ranked top-K plan outcomes The decision-tree analysis SHALL maintain a bounded ranked list of up to K complete plan outcomes (default K=10) and emit a stream update each time the visible list changes. Each `PlanOutcome` SHALL include the full course assignment for open sets (`Record`), the achieved specializations, and the priority score used for ranking. The list SHALL be ordered by `(achievedSpecs.length descending, priorityScore descending, deterministic tiebreaker on courseAssignments)`. #### Scenario: Top-K converges on user's top-priority spec - **WHEN** the user pins courses such that their first reachable ranked spec (e.g., HCR) is achievable somewhere in the remaining decision tree - **THEN** the final `topK[0].achievedSpecs` includes that spec #### Scenario: Streaming is monotonically improving - **WHEN** the worker emits a sequence of `topKUpdate` events during a single analysis - **THEN** each emitted topK is greater than or equal to the previous one entry-for-entry under the comparator #### Scenario: Tied outcomes with different course plans appear as separate entries - **WHEN** two distinct course assignments produce the same `achievedSpecs` and `priorityScore` - **THEN** both appear as separate ranked entries in the top-K (deterministic tiebreaker resolves their order) ### Requirement: Heuristic enumeration ordering The decision-tree search SHALL identify a `priorityTarget` (the first specialization in the user's ranking whose upper-bound credit potential meets the threshold) and SHALL reorder the children at each level of its DFS so that courses qualifying for the `priorityTarget` are tried before courses that do not. Reordering SHALL be a stable sort that preserves declaration order on ties. #### Scenario: Priority target derived from first reachable ranked spec - **WHEN** the user's ranking is `[HCR, BNK, ...]` and HCR's upper bound is ≥ 9 - **THEN** `priorityTarget = 'HCR'` and DFS children at every level are reordered HCR-first #### Scenario: No reachable spec disables the heuristic - **WHEN** no specialization in the ranking has upper bound ≥ 9 - **THEN** `priorityTarget = null` and DFS children are not reordered ### Requirement: Bounded search with saturation termination The decision-tree search SHALL terminate when EITHER (a) the top-K ranked list has not changed for the last `SATURATION_LIMIT` iterations (default 500), OR (b) the iteration count exceeds `MAX_TREE_ITERATIONS` (default 10000). When (b) terminates the search before (a), the result SHALL include `partial: true`. #### Scenario: Saturation stops a converged search early - **WHEN** the top-K becomes stable well before the iteration cap - **THEN** the search stops within `SATURATION_LIMIT` iterations of the last top-K change #### Scenario: Iteration cap stops an unconverged search - **WHEN** the search would otherwise enumerate beyond `MAX_TREE_ITERATIONS` combinations - **THEN** the search returns its best-found top-K with `partial: true` ### Requirement: Per-cell choice updates from streaming search For each combination evaluated in the search, for each `(setId, courseId)` in that combination's assignments, the per-set per-choice ceiling SHALL be updated if the combination's outcome is better under the comparison rule than the current ceiling for that choice. Each ceiling change SHALL emit a `choiceUpdate` event identifying the affected `setId` and the updated `SetAnalysis`. #### Scenario: Per-set ceiling reflects streamed improvements - **WHEN** an HCR-feasible combination is evaluated mid-search - **THEN** the per-set ceiling cell for `spr3-analytics-ml` (the HCR-qualifying course in spr3) is updated to include HCR ## MODIFIED Requirements ### Requirement: Decision-tree per-set ceiling comparison For each open elective set and each course choice within that set, the system SHALL compute a ceiling outcome representing the best achievable specialization result if that course is pinned. The "best" outcome SHALL be determined by `(achievedSpecs.length descending, priorityScore descending, deterministic tiebreaker)`, where `priorityScore` matches the optimizer's existing definition (`sum over specs of (15 - rankIndex(spec))`). When two outcomes have the same count, the higher priority score wins. #### Scenario: Equal-count outcomes resolved by priority score - **WHEN** the search finds two combinations both achieving 2 specializations, one with `[FIN, MTO]` and another with `[HCR, BNK]`, and the user's ranking places HCR first - **THEN** the per-set ceiling reflects `[HCR, BNK]` (higher priority score) #### Scenario: Higher count beats higher priority - **WHEN** one combination achieves 3 specializations not including the top-priority spec, and another achieves 2 specializations including it - **THEN** the 3-specialization outcome wins ### Requirement: Decision-tree worker protocol The decision-tree worker SHALL accept a `WorkerRequest` that includes optional `topK` (default 10) and `saturationLimit` (default 500) parameters. It SHALL emit a tagged-union `WorkerResponse` stream with three event types: `topKUpdate` (when the ranked top-K list changes), `choiceUpdate` (when a per-set ceiling cell changes), and `allComplete` (when the search terminates, carrying both final top-K and final per-set analyses, plus a `partial` flag). #### Scenario: Worker accepts K parameter - **WHEN** the request specifies `topK: 5` - **THEN** the worker maintains a bounded list of at most 5 entries and emits updates accordingly #### Scenario: Worker emits final allComplete event - **WHEN** the search terminates (saturation or cap) - **THEN** the worker emits `{ type: 'allComplete', topK, setAnalyses, partial }` #### Scenario: Worker emits per-cell choice updates rather than per-set rollups - **WHEN** a single combination causes a ceiling change for one course in one set - **THEN** the worker emits one `choiceUpdate` event identifying that set, not a coarse `setComplete` rollup