Repository navigation
Curriculum map: skills, outcomes and ABET coverage across the program - #1
snow-jallen wants to merge 3 commits into
Conversation
A new /curriculum page shows, for an academic program's plan, where each skill is introduced, reinforced and mastered; the course outcomes from the approved master syllabi; and which courses cover each ABET student outcome and program criterion. It replaces the 2019 snow-jallen/ProgramPlanning desktop tool. - Skill threads, ABET coverage, a prerequisite map that highlights a skill or ABET outcome, the skill catalog, and a page per course. - "Re-read & suggest" reads every course's master syllabus from snow.edu (outcomes are mirrored; added and removed outcomes are flagged), then asks the AI which skills each course teaches, using the master syllabus, the latest three terms of SnowSE/faculty materials and the section syllabi, and finally which proposed skills duplicate each other. Nothing changes until an editor accepts a suggestion; rejected ones are not asked again. - The ABET matrix workbook is uploaded on the page, never committed. - Every approved user can read the map; curriculum_admin edits it. - Seeds the skills the department named (command line, git, GitHub, the dotnet CLI, VS Code, Visual Studio, TDD, BDD, DDD, mocks & fakes, onion architecture, ...) and ABET SLOs 1-7. Tables bootstrap themselves on start like the other domain managers; schema.sql carries them for tests. The faculty repository is private, so reading it needs CURRICULUM_GITHUB_TOKEN; without it the review uses the other sources and says so.
There was a problem hiding this comment.
🔵 Needs a closer look
It is a very large, multi-module feature whose central orchestration files (curriculum_domain_manager.ex, curriculum_live.ex, course_view.ex) and an external AI/network-driven review flow were not fully reviewable from the provided diff, warranting human review.
1 open finding
What changed in this PR
This PR adds a new Curriculum feature to the Phoenix app: a /curriculum LiveView that visualizes an academic program's skills, outcomes, and ABET coverage. It introduces skill threads (Introduced/Reinforced/Mastered), an ABET SLO/criteria coverage matrix, a prerequisite/corequisite graph, a skill catalog with "builds on" links, per-course pages, and an AI-powered "Re-read & suggest" flow that mirrors master syllabi from snow.edu, consults SnowSE/faculty materials plus section syllabi, and proposes skills and merges for an editor to accept. It adds a new open area plus a curriculum_admin group to the access model, creates its tables on domain-manager start (mirrored in schema.sql), and wires the pages into smoke/snapshot tests.
Changes:
- New curriculum domain (DB modules, domain manager, parsing for master syllabi + ABET xlsx, suggestion builder/decisions) and web layer (LiveView + threads/skills/suggestions/graph/ABET views, server-side
CurriculumAccessgate). - Access-control changes in
access.exto support an open-to-all-approved-users area alongside thecurriculum_adminedit tier. - Supporting wiring:
application.exsupervision,schema.sqltables,runtime.exs/mix.exs(:xmerl,CURRICULUM_GITHUB_TOKEN), and comprehensive new test suites.
| File | Description |
|---|---|
lib/snow_se_tools/data/access.ex |
Adds the open curriculum area + curriculum_admin; adjusts can?, accessible_areas, role_position. |
lib/snow_se_tools/curriculum/curriculum_db.ex |
Bootstraps tables and seeds skills/dependencies/ABET SLOs idempotently. |
lib/snow_se_tools/curriculum/{skill_db,outcome_db,abet_db,suggestion_db}.ex |
Query modules for skills, outcomes, ABET matrix, and suggestions. |
lib/snow_se_tools/curriculum/{suggestion_builder,suggestion_decisions}.ex |
Builds AI suggestions/fingerprints; transactional accept/merge/rename logic. |
lib/snow_se_tools/curriculum/{master_syllabus,abet_matrix,xlsx_reader,course_code,term_label,faculty_materials}.ex |
Parsing/selection helpers for syllabi, xlsx, course codes, terms, faculty files. |
lib/snow_se_tools/curriculum/curriculum_domain_manager.ex |
Orchestrates fetching, AI runs, DB writes, PubSub (large; central). |
lib/snow_se_tools_web/curriculum/*.ex |
LiveView, course view, threads/skills/suggestions/graph/ABET views, shared components, CurriculumAccess gate. |
lib/snow_se_tools/application.ex |
Supervises CurriculumDomainManager under the DB-domain gating. |
schema.sql |
Adds curriculum_* tables for tests/first-start. |
config/runtime.exs, mix.exs |
CURRICULUM_GITHUB_TOKEN config; adds :xmerl to extra_applications. |
test/snow_se_tools/curriculum/*, test/snow_se_tools_web/curriculum/*, smoke/snapshot tests |
New unit, DB, and LiveView coverage. |
One concrete, low-severity issue was flagged inline: the tentative legend swatch in graph_view.ex uses a non-existent ring-dashed Tailwind class, so it renders inconsistently with the dashed graph node. The access-control, parsing, suggestion, and DB logic I was able to inspect are internally consistent with their (thorough) tests.
🧠 Review effort: Balanced
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
| defp swatch("reinforced"), do: "bg-violet-700 ring-1 ring-violet-400" | ||
| defp swatch("mastered"), do: "bg-emerald-700 ring-1 ring-emerald-400" | ||
| defp swatch("covered"), do: "bg-indigo-700 ring-1 ring-indigo-400" | ||
| defp swatch("tentative"), do: "ring-1 ring-dashed ring-indigo-300" |
The endpoint, model and key were read from application env at the call site, so pointing the app at a different model server meant editing prod.env and restarting. A new box on /admin edits all three, and SnowSeTools.AI.Settings resolves them per completion instead. A row in ai_settings overrides the environment field by field: NULL means "use what prod.env set", so repointing the model does not also mean restating the endpoint. Every read falls back to the environment if the database cannot answer -- a database outage should not take the AI down too, and during boot the table may not exist yet. The API key is write-only. It is stored but never sent to a browser, so a blank key box means "keep the current key"; clearing an override is the explicit revert button rather than an empty field. That also means a live credential now lives in Postgres, and so in backups and database dumps. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A course with no faculty materials and no section syllabi has nothing the prompt can be answered from: it asks for skills the sources show students practising, and there are no sources. Asking anyway invited the model to invent skills from the master syllabus blurb, and in practice it kept generating until it ran the server's context window out -- MATH 1210, whose whole input is a 571-character stub, failed with "Context size has been exceeded". Such a course is now reported as not reviewed instead. It still has to report in: the run finishes when it has heard once about every course, so a course that is never sent would otherwise leave the run waiting for an answer nobody asked for. The test asserts the count, not just the note. Course reviews also now send max_tokens, so a runaway generation fails as a completion the run can report rather than as an opaque engine 500. The review stub in the LiveView test had no clause for the faculty repository, so fetch_tree/0 crashed the task that enqueues the reviews whenever CURRICULUM_GITHUB_TOKEN was set -- the suite passed only on a machine without a token, and hung on one with it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

What this adds
A Curriculum page (
/curriculum) that follows an academic program's plan (from Scheduling → Academic Programs) and shows:Re-read & suggest
course.html→syllabus.html?q=…) and mirrors its outcomes. Outcomes no longer listed are retired rather than deleted, so changes show up.AsyncCompletionsqueue, which skills the course teaches and at what level. Its evidence is the master syllabus, the latest three terms ofSnowSE/facultymaterials (every instructor's folder for those terms), and the section syllabi already synced from Simple Syllabus.Nothing changes until an editor accepts a suggestion. Accepting a merge renames the merged skill in the other waiting suggestions. Rejected suggestions are not asked again. "Re-read master syllabi only" skips the AI.
Access
Every approved user can read the map; this is a new open area in
Access. Editing, running the review, accepting suggestions and importing the ABET matrix need the newcurriculum_admingroup.CurriculumAccessrefuses everycurriculum-edit:*event from viewers on the server, and refuses any event it doesn't recognise.ABET matrix
Upload the workbook on the ABET tab. It defaults to the newest SLO sheet (
Fall2025_Catalog_Matrix) and takes the program criteria fromMain table, with a preview before importing. The file is never committed, since this repo is public.?marks are kept as tentative.To deploy
CurriculumDomainManagercreates its tables on start, like the other managers.schema.sqlhas them for tests.CURRICULUM_GITHUB_TOKENto a fine-grained, read-only token forSnowSE/faculty. Without it the review skips faculty materials and says so in the run notes.curriculum_adminin Admin.Notes
mochiweb_html(already present viahtml_sanitize_ex) and the xlsx with OTP:zip+:xmerl;:xmerlwas added toextra_applications.page_smoke_test.exsand to the snapshot dump /screenshots.sh.Checks
mix compile --warnings-as-errors,mix format --check-formatted(on the files in this PR),mix deps.unlock --check-unused, and the full suite (291 tests, run several times) all pass.discord_domain_manager.exandtelemetry/events.ex.mix hex.auditreports an existingmintadvisory;mix.lockis unchanged here.