Store what a school charges to apply, and whether it waives it - #57
Merged
Merged
Conversation
The grid the deadline ingest already reads carries three more columns beside the round dates — the US fee, the international fee, and whether a waiver is taken. We were parsing past them. Worth having because the answer is so often "nothing". Of the 1,128 schools the grid lists a fee for, 611 charge $0. The median is $0, the 90th percentile $70, the highest $150. A student looking at ten schools and bracing for $700 is usually wrong, and has no way to find out short of opening ten admissions pages. So the school page says it: "Free to apply", or "$85 · Fee waivers accepted". Where no figure is on file it says that too rather than guessing — an invented $0 is a promise, and an invented fee is a reason not to apply. The waiver task now follows the real figure instead of the school's type. A known $0 removes it; a known fee adds it, including at the two-year schools that do charge. Only where nothing is on file does it fall back to the type, which is most of the 6,273 colleges, since the grid covers Common App members and little else. Fees refresh on every ingest rather than only filling blanks. Nobody curates them by hand, the grid is their only source, and a school that drops its fee should stop being shown as charging one. Deadlines keep the opposite rule, for the same reason in reverse. One parser bug worth naming: "Not Accepted" contains "Accepted", so matching the short label first turned all 29 schools that refuse waivers into schools that take them. Longest label first now, and a test pins it.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Deploying timeline-prototype with
|
| Latest commit: |
25e7639
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://727f3903.timeline-prototype.pages.dev |
| Branch Preview URL: | https://feat-application-fees.timeline-prototype.pages.dev |
This was referenced Oct 1, 2026
ZubairQazi
added a commit
that referenced
this pull request
Oct 2, 2026
Merges #56, #57 and #59. tasksForEntry keeps main's version (any missing default task, in its own phase, only while the application is still being worked on) and adds the aid task's label refresh and the per-entry cache. From review on #58: the aid-application task now goes to any school with a guarantee, not just four-year ones, so Ohio State's regional campuses and Emory's Oxford College get the task that wins theirs. The guarantee copy is compared field by field rather than as JSON, which depended on key order.
This branch was successfully deployed
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.
Follows #56 — based on that branch, not
main, because the waiver task this gates was added there. Merge #56 first and this retargets cleanly.The data was already in the file
The Common App grid the deadline ingest reads carries three columns beside the round dates — US fee, international fee, and whether a waiver is taken. We were parsing straight past them.
A student looking at ten schools and bracing for several hundred dollars of fees is usually wrong, and has no way to find out short of opening ten admissions pages.
What's here
A migration adding
application_fee_cents,fee_waiver_policyandfees_verified_at, with a check constraint on the three policy values the grid actually publishes — anything else is a parse bug, not a new policy, and should fail loudly.Cents, matching every other money column. Null and 0 stay distinguishable: "we don't know" and "it costs nothing" are very different things to tell someone.
The parser reads both columns, per page, off the same calibrated header as the dates.
The school page says it plainly — "Free to apply", or "$85 · Fee waivers accepted". Where no figure is on file it says that, rather than guessing: an invented $0 is a promise, an invented fee is a reason not to apply.
The waiver task follows the real figure instead of the school's type. A known $0 removes it; a known fee adds it, including at the two-year schools that do charge. Only where nothing is on file does it fall back to type — which is most of the 6,273 colleges, since the grid covers Common App members and little else.
One rule that differs from deadlines
Fees refresh on every ingest; deadlines only ever fill a blank. Nobody curates fees by hand, the grid is their only source, and a school that drops its fee should stop being shown as charging one. Deadlines keep the opposite rule for the same reason in reverse — 46 of them are hand-checked.
A bug worth naming
"Not Accepted"contains"Accepted". Matching the short label first turned all 29 schools that refuse waivers into schools that take them — exactly backwards, and the kind of thing that reads fine in review. Longest label first now, with a test pinning it.Applying it
supabase db push, or paste it in the SQL editor)node scripts/ingest-deadlines/emit-sql.mjs fees.sqlStep 2 fails until step 1 lands, since it selects the new columns. The ingest writes with the anon key, which
collegesis read-only to, so it goes through the SQL editor as owner — same path as the September deadline run.Also noticed
The grid was republished 2026-09-25, the day after the last ingest, and Yale is no longer in it. Everything else checks out (Princeton, Columbia, Brown, Cornell, Dartmouth all present, 1,127 rows parsed against 1,127 before). The source is live and this is what re-runnable is for — but worth knowing a school can leave it mid-cycle, and worth a look before the next apply.
Testing
527 passing plus 11 parser tests, lint and typecheck clean, build succeeds. New coverage: free vs unknown vs priced, each waiver policy, that the real figure overrides the type guess in both directions, and the
Not Acceptedsubstring case.The ingest itself is unrun — the migration has to land first, so nothing here has touched real data.