feat(prereq): model program-type-gated prerequisites#4437
Open
TheMythologist wants to merge 4 commits into
Open
feat(prereq): model program-type-gated prerequisites#4437TheMythologist wants to merge 4 commits into
TheMythologist wants to merge 4 commits into
Conversation
A prerequisite branching by program type — e.g.
`(Graduate THEN X) OR (Undergraduate THEN Y)` — failed to parse, since
`program_types THEN compound` existed only at the top level; any
parenthesised or combined program-type conditional hit "no viable
alternative". This affected ~65 modules (ESE5608, MA5202, IT5003, the
honours *HM modules, etc.).
Model program type as a first-class, display-only prereq node
({ programType, then }), mirroring cohort — the planner has no
program-type input to evaluate against.
Scraper:
- Grammar: add `program_types_conditional`, lift into `compound`, and
simplify `overall` to `compound EOF`; regenerate parser.
- Visitor: build { programType, then }; drop bare program types; remove
the old "too many program types" bail. Normalise by flattening
redundant same-type repeats (greedy `pt THEN A OR pt THEN B` collapses
to a symmetric `A OR B`) and dropping the outermost
undergraduate-inclusive wrapper. A graduate/CPE-only wrapper is kept
and labelled (e.g. ESE5608, whose undergraduate branch is entirely
unrepresentable PROGRAMS/SPECIAL constraints).
- flattenTree: walk only the gated requirement.
Website:
- Mirror the types; planner skips program-type nodes (never unmet) and
conflictToText handles them defensively.
- ModuleTree renders "For <program type>". A disjunction whose branches
are all program-type gates is a per-degree split (not a choice), so it
drops the "one of" label and collapses the empty junction so the
connector line runs straight through.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Prerequisites gated on a disjunction of program types — e.g.
`(PROGRAM_TYPES IF_IN Undergraduate Degree OR PROGRAM_TYPES IF_IN Graduate
Degree Coursework) THEN X` (EE5113, EE6438, EE6733, BL5232D) — still failed
to parse, since the gate accepted only a single program type before THEN.
- Grammar: `program_types_conditional` now takes a `program_types_gate`
(`'(' program_types_gate ')' | program_types (OR program_types)*`),
handling both parenthesised and unparenthesised disjunctions; parser
regenerated.
- Visitor: `programTypeGateCondition` collapses the disjunction into one
condition over the union of types, since `IF_IN X OR IF_IN Y` is just
`IF_IN X, Y`.
- Narrow the outer-wrapper drop: only the lone "Undergraduate Degree" gate
is the assumed wrapper and dropped; any gate naming more (a graduate/CPE
type, or undergraduate alongside others) is a real restriction and is kept
and labelled. So EE5113 keeps "For Undergraduate Degree or Graduate Degree
Coursework" and EE6438/BL5232D keep "For Graduate Degree Coursework or
Graduate Degree Research".
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…etting Program-type gates were carried for display only. Now that the planner has an Undergraduate/Graduate setting (settings.modRegNotification.scheduleType), evaluate them in the prerequisite conflict check, like cohort gates against the matriculation year. - programScheduleType maps a PROGRAM_TYPES value to the coarse setting (Undergraduate Degree -> Undergraduate; Graduate Degree * -> Graduate; CPE (Certificate) -> neither). - programTypeConditionApplies: a gate applies when the student's schedule type matches one of its types; conservatively applies when the type is unknown. - resolveProgramTypes prunes non-applicable program-type branches before the tree is checked, so a non-matching branch is removed rather than vacuously satisfying an enclosing OR (e.g. the differing-degree MA5202 shape). - checkPrerequisite takes scheduleType; selectors/planner threads it from settings. Display is unchanged: ModuleTree still shows every program-type branch; only conflict-checking is schedule-type-aware. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #4437 +/- ##
==========================================
+ Coverage 54.52% 57.58% +3.05%
==========================================
Files 274 317 +43
Lines 6076 7228 +1152
Branches 1455 1774 +319
==========================================
+ Hits 3313 4162 +849
- Misses 2763 3066 +303 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Contributor
Both Greptile comments were on the program-type evaluation logic: - nOf count not reduced when program-type gates are pruned: clamp the count to the surviving option pool in resolveProgramTypes, so pruning a gated option can never leave an unsatisfiable "n of fewer-than-n". A no-op on real data (nOf options are course-code leaves and are never pruned); defensive only. - Misleading comment in defensive fallback: conflictToText's program-type branch is unreachable because resolveProgramTypes strips every gate before checkPrerequisite returns. Reword from "gates are not evaluated" to note it is an exhaustiveness safety net. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Prerequisites that branch by program type — e.g.
(PROGRAM_TYPES IF_IN Graduate Degree Coursework THEN X) OR (PROGRAM_TYPES IF_IN Undergraduate Degree THEN Y)— previously failed to parse and produced no prerequisite tree at all. The grammar only allowedprogram_types THEN compoundat the top level, so any program-type conditional that was parenthesised, combined withAND/OR, or gated on a disjunction of program types raised a "no viable alternative" error. This silently dropped the tree for ~65 modules (ESE5608, MA5202, IT5003, EE5113, the honours*HMmodules, and others).This PR parses those prerequisites and carries the program type as a first-class node in the tree (
{ programType, then }), mirroring the existing cohort modelling. It is display-only: the planner has no program-type input to evaluate against, so these gates are shown in the tree but are never treated as unmet requirements.Scraper
program_types_conditionalnow takes aprogram_types_gate—'(' program_types_gate ')' | program_types (OR program_types)*— so the gate may be a single program type, a disjunction of them, or that disjunction parenthesised.compoundgainedprogram_types_conditional, andoverallwas simplified tocompound EOF. The parser was regenerated withpnpm antlr4ts.{ programType, then }; a gate that is a disjunction ofIF_INprogram types is collapsed into one condition over the union of types (sinceIF_IN X OR IF_IN Yis justIF_IN X, Y); bare program types are dropped; and the old "too many program types" bail-out is removed. The result is then normalised — redundant same-type repeats are flattened (the greedyTHENmakespt THEN A OR pt THEN Bparse aspt THEN (A OR (pt THEN B)), and flattening recovers the symmetricA OR B), and the single outermost wrapper is dropped only when it is the lone "Undergraduate Degree" gate (the ubiquitous assumed audience). Any gate naming more is a real restriction and is kept.flattenTreewalks only the gated requirement.Website
ProgramTypeRule/ProgramTypeConditiontypes and the newPrereqTreevariant.checkPrerequisiteskips program-type nodes (never unmet) andconflictToTexthandles them defensively.ModuleTreerenders a program-type gate as "For " (joining a disjunction's types with " or "). A disjunction whose branches are all program-type gates is a per-degree split rather than a choice, so it drops the misleading "one of" label and collapses the now-empty junction so the connector line runs straight through.Behaviour by case
Undergraduate Degree THEN …wrapper is dropped, so every existing tree is unchanged.A OR B.IT5001under both "For Graduate Degree Coursework" and "For CPE (Certificate)", and MA5202 shows three branches.Undergraduate OR Graduate Coursework) shows "For Undergraduate Degree or Graduate Degree Coursework → EE5934", and EE6438 / BL5232D show "For Graduate Degree Coursework or Graduate Degree Research".PROGRAMS/SPECIALconstraints the tree cannot represent — shows "For Graduate Degree Coursework → ESE5003" instead of a bare, unexplainedESE5003.Testing
parseString: added cases for differing-type modelling, same-type flattening, the ESE5608 degenerate shape, parenthesised and unparenthesised OR-of-program-types gates, and multi-type gates; updated the two previously-null"cannot parse" tests. Full scraper suite passes (151).conflictToText) and aModuleTreesnapshot for the per-degree split. Planner +ModuleTreesuites pass.Before:

with error
["no viable alternative at input '(PROGRAM_TYPESIF_INGraduate Degree CourseworkTHEN'"]After:

with no errors