Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/cornell-dti/course-plan/llms.txt

Use this file to discover all available pages before exploring further.

Courseplan’s requirement data lives in TypeScript files rather than raw JSON, which gives contributors full type safety and the ability to express complex logic with plain functions. When a requirement definition changes, a code-generation step compiles those TypeScript files into a JSON artifact that the runtime loads at startup.

Where requirement files live

src/data/
├── majors/        # One file per major  (cs.ts, ece.ts, info.ts, …)
├── colleges/      # One file per college (en.ts, as.ts, hu.ts, …)
├── minors/        # One file per minor
├── grad/          # Graduate programs
├── university/    # University-wide requirements (PE, swim test, …)
└── index.ts       # Aggregates all data into sourceRequirements
Each file exports an object that conforms to Major<CollegeOrMajorRequirement> or the equivalent college type.

The CollegeOrMajorRequirement type

Every single requirement in the system is described by CollegeOrMajorRequirement, defined in src/requirements/types.ts:
// A checker function receives a Course and returns true if that course satisfies the requirement.
export type RequirementChecker = (course: Course) => boolean;

export type CollegeOrMajorRequirement = RequirementCommon &
  RequirementFulfillmentInformation<{
    readonly checker: readonly RequirementChecker[];
  }>;
RequirementCommon contributes fields such as name, description, source, and optional allowCourseDoubleCounting. RequirementFulfillmentInformation adds fulfilledBy, perSlotMinCount, and slotNames.

How checker functions work

A RequirementChecker is a plain TypeScript predicate:
export type RequirementChecker = (course: Course) => boolean;
During the req-gen build step, every checker is run against the full Cornell course roster to produce a pre-computed list of eligible course IDs. At runtime, the graph builder uses those lists — not the functions themselves — for performance. The src/requirements/checkers.ts module provides composable helpers:
  • includesWithSingleRequirement(...courseCodes) — course must match one of the given codes
  • includesWithSubRequirements(...slotCodes[]) — multi-slot requirement; each inner array is one slot
  • courseMatchesCodeOptions(course, codes) — boolean test against a list of subject+number strings
  • ifCodeMatch(value, pattern) — glob-style match ('CS', '4***', etc.)

A real example: CS Introductory Programming

From src/data/majors/cs.ts:
{
  name: 'Introductory Programming',
  description: 'CS 111x (CS 1110, 1112, 1114, or 1115) and CS 2110 (or CS 2112) or equivalent.',
  source: 'https://www.cs.cornell.edu/undergrad/csmajor',
  allowCourseDoubleCounting: true,
  checker: includesWithSubRequirements(
    ['CS 1110', 'CS 1112', 'CS 1114', 'CS 1115'],
    ['CS 2110', 'CS 2112']
  ),
  fulfilledBy: 'courses',
  perSlotMinCount: [1, 1],
  slotNames: ['CS 111x', 'CS 2110 or CS 2112'],
},
includesWithSubRequirements creates two slots. The student must place at least one course in each slot (perSlotMinCount: [1, 1]). allowCourseDoubleCounting: true lets these courses also count toward Engineering college requirements.

Handling year-based requirement changes with RequirementMigration

Cornell sometimes changes requirements for students who entered before a specific year. Model this with RequirementMigration:
export type RequirementMigration = {
  /** Applies to students whose entryYear is equal to or EARLIER than this value */
  entryYear: number;
  /** 'Modify' updates an existing requirement, 'Delete' removes it, 'Add' inserts a new one */
  type: typeOfMigration;
  /** The `name` field of the requirement being modified or deleted */
  fieldName: string;
  /** Required for 'Modify' and 'Add' — the replacement or new requirement object */
  newValue?: CollegeOrMajorRequirement;
};
Attach a migrations array to the major or college object:
const csMajor: Major<CollegeOrMajorRequirement> = {
  name: 'Computer Science',
  schools: ['Engineering'],
  requirements: csRequirements,
  migrations: [
    {
      entryYear: 2022,
      type: 'Modify',
      fieldName: 'CS Electives',
      newValue: legacyCSElectivesRequirement,
    },
  ],
};
The store applies migrations at runtime based on the user’s stored entranceYear.

Regenerating the JSON artifact

After any change to a requirement file, regenerate decorated-requirements.json:
npm run req-gen
This runs src/requirements/requirement-json-generator.ts via ts-node. It evaluates every RequirementChecker against the full course roster and writes the resulting eligible-course-ID lists into the JSON file.
Committing changes to requirement TypeScript files without running npm run req-gen will leave the JSON artifact out of sync. The UI will silently show stale eligibility data until the artifact is regenerated and the app is rebuilt.

Build docs developers (and LLMs) love