A Markdown-first system for durable learning notes, selective LLM retrieval, review queues, and study workflows. Markdown files under courses/ are the source of truth; the manifest and review queue are generated views.
One repository, one working copy. Tracked content (
courses/plus the framework) is the public-safe surface pushed to the GitHub remote; raw or source-risky material lives under a Git-ignoredprivate/directory and is never committed.License: MIT. See LICENSE. The scripts, templates, prompts, and documentation are MIT licensed. Real course-derived notes should not be published through this repository unless separately cleared.
For a human overview, open INDEX.md. For an LLM session, provide LLM_GUIDE.md and manifest.json first. For today's study priorities, open REVIEW_QUEUE.md.
After adding or changing notes, run the normal workflow:
make allThis validates notes, rebuilds generated files, and runs tests. For the complete one-folder workflow (including private-study commands and the pre-commit safety gate), see docs/operating-checklist.md. If Make is unavailable, use the direct commands:
python3 validate_notes.py
python3 build_manifest.py
python3 build_review_queue.py
python3 -m unittest discover -s testsIndividual Make targets are also available:
make validate
make manifest
make review
make test
make allMake is only a convenience; the scripts require Python 3, use only the standard library, and remain directly runnable.
The publishable work in this repository is original study material, not reproduced lecture material. Both public and private material live in this single repository, separated by directory and Git tracking rather than by repository:
| Public Course Notes material (tracked, pushed) | Private by default (private/, Git-ignored) |
|---|---|
| Templates, scripts, prompts, docs, metadata schemas, validation logic, generated-file conventions, and synthetic examples | Real lecture-derived notes, problem sheets, exam maps/questions, lecturer hints, LMS material, assessment material, personal study evidence, and course-specific examples |
courses/holds only public-safe, polished notes. It is tracked by Git and must passmake validate-public. This is the only course tree the GitHub remote ever sees.private/holds everything source-risky or not yet cleared: private course notes underprivate/courses/, raw source material underprivate/raw/, drafts underprivate/drafts/, andprivate/framework-feedback.md. The wholeprivate/tree is ignored by Git (see .gitignore) and is never committed or pushed. It stays local to your working copy.
AI summarizing or paraphrasing alone does not make protected course material safe to publish. Keeping a file under private/ is not sanitization; promoting a private note into courses/ requires a separate sanitization and review step.
Read docs/publication-policy.md before changing visibility or preparing a release, and use docs/public-release-checklist.md before promoting any private note into courses/.
make validate-public applies the same conservative gate as before: it scans only courses/ and fails on visibility: private, course-derived or unknown source-risk, missing publication classifications, and local paths. The gate is not weakened by the private/ directory — private/ is simply outside its scan scope.
This repository is both the study workspace and the public Course Notes release. Tooling, templates, prompts, documentation, validation logic, and synthetic/demo content are tracked; real course notes and raw source material are kept under the Git-ignored private/ directory.
A note is private by default (visibility: private). A private note may live under private/courses/ and intentionally fail make validate-public, because that gate only ever runs against courses/. A note may move into courses/ only after it has been sanitized to public-framework / public-original / public-open-licensed with an allowed source-risk and has passed make pre-release.
course-notes/
├── courses/ tracked, public-safe polished notes only
│ └── <course-code>/
│ ├── course.md course metadata, resources, and priorities
│ ├── syllabus.md official outcomes and confirmed coverage
│ ├── concepts/ durable, one-concept-per-file notes
│ ├── lectures/ chronological lecture records
│ ├── problem-sheets/ attempts, corrections, and reflections
│ ├── exam/ exam maps and revision summaries
│ └── glossary.md course terminology
├── private/ Git-ignored; never committed or pushed
│ ├── courses/ private/source-risky notes (mirror the courses/ layout)
│ ├── raw/ raw source material
│ ├── drafts/ work in progress
│ └── framework-feedback.md scratch notes about the framework itself
├── docs/ onboarding and friction-test checklists
├── prompts/ reusable LLM study workflows
├── templates/ course, syllabus, and raw lecture templates
├── Makefile optional command shortcuts
├── TEMPLATE.md canonical concept-note template
├── LLM_GUIDE.md LLM navigation and editing protocol
├── INDEX.md maintained human navigation
├── manifest.json generated machine-readable index
└── REVIEW_QUEUE.md generated ranked review list
The included courses/demo-course/ tree is fictional and exists only to demonstrate the system; replace it with other synthetic material—not real course notes—when preparing a public variant. The private/ directory is ignored by Git: it exists for your local working material and is not part of the public release.
- Markdown under
courses/is the source of truth for course knowledge, evidence, and review metadata. manifest.jsonis a generated retrieval index. Its top-level_generatedfield says how to rebuild it.REVIEW_QUEUE.mdis a generated study-priority view and begins with a generated-file warning.INDEX.mdand courseREADME.mdfiles are maintained human navigation.- Files under
prompts/are reusable instructions, not course facts.
Never edit manifest.json or REVIEW_QUEUE.md manually. Update source Markdown and run make manifest, make review, or make all.
Determinism: Generated files use a date-only generated-at field (no time). Running make all multiple times on the same day produces no diff when notes are unchanged. For fully reproducible builds, set the SOURCE_DATE_EPOCH environment variable (Unix timestamp) or pass --today to build_review_queue.py.
| Command | Purpose |
|---|---|---|
| make validate | Check metadata, IDs, dates, links, required sections, and mistake structure |
| make validate-public | Apply the stricter public-release gate to courses/ only; private notes under private/ are outside its scope. Loads .public-release-blocklist if present (see below) |
| make manifest | Validate and rebuild manifest.json |
| make review | Validate and rebuild REVIEW_QUEUE.md for today; set DATE=YYYY-MM-DD for a reproducible date |
| make test | Run the dependency-free regression suite |
| make pre-release | Run the public-release gate, regenerate all files, and run tests (for release readiness) |
| make all | Run normal validation, both generators, and all tests |
| make reviewed NOTE=layered-recall | Update last-reviewed and review-after for a note by ID (see below) |
| make reviewed NOTE=layered-recall DATE=2026-07-15 | As above, using a specific review date |
| python3 build_review_queue.py --today YYYY-MM-DD | Generate the review queue for a specific date |
| make study-validate | Validate notes under private/courses/ |
| make study-manifest | Build private/manifest.json from study notes |
| make study-review | Build private/REVIEW_QUEUE.md from study notes |
| make study-all | Run study-validate, study-manifest, and study-review |
| make study-reviewed NOTE=<id> | Mark a study note under private/courses/ as reviewed |
| make public-safety | Validate the public tree and run all tests |
The equivalent direct Python commands are shown in Quickstart. Append --study to any tool to target private/courses/ instead of courses/.
CI runs on every push and pull request: make all, make validate-public, make pre-release, and a generated-file staleness check. See ci.yml.
Create .public-release-blocklist in the repository root with one term per line to extend make validate-public. Lines starting with # and blank lines are ignored. Matching is case-insensitive.
# Lines starting with # are comments.
Example University
PROFESSOR NAME
canvas.example.edu
MATH123
When present, make validate-public scans all Markdown files for these terms and reports each match as an error. This is a safety smoke alarm — it does not replace manual review. Remove or replace any blocked terms before publishing.
After studying a note, update its review metadata with:
make reviewed NOTE=layered-recallOr with a specific date:
make reviewed NOTE=layered-recall DATE=2026-07-15The script updates only last-reviewed and review-after. It does not change status, visibility, source-risk, or any other frontmatter. The next review date is chosen from the current status:
| Status | Next review |
|---|---|
new |
+1 day |
shaky |
+2 days |
learning |
+7 days |
solid |
+30 days |
mastered |
+60 days |
reference / archived |
blank |
Run make all after the update to regenerate the manifest and review queue.
The full copy-and-check workflow is in docs/course-onboarding.md. In short:
- Create
courses/<course-code>/withconcepts/,lectures/,problem-sheets/, andexam/. - Copy
templates/course.mdtocourses/<course-code>/course.mdand replace every placeholder. - Copy
templates/syllabus.mdtocourses/<course-code>/syllabus.md; add only official or explicitly sourced material. - Copy
templates/lecture.mdintolectures/for each raw chronological lecture record. - Use
prompts/import-lecture.mdto propose durable concept extraction, then create approved concepts fromTEMPLATE.md. - Put attempts, corrections, and evidenced mistakes under
problem-sheets/. - Put confirmed exam information and outcome maps under
exam/. - Run
make validate. - Run
make manifest,make review, andmake test(or simplymake all).
Set every document's course field to the exact directory name. Keep raw lecture notes intact, preserve source paths, and keep missing official information visibly unknown.
After the first import, run the ten-minute course import friction test.
- Copy
TEMPLATE.mdtocourses/<course-code>/concepts/readable-filename.md. - Give it a repository-unique, permanent
id. For new notes,<course-code>-<concept-slug>is a useful convention. - Fill in every metadata field and write the note in your own words.
- Use note IDs, not filenames, in
prerequisitesandrelated. - Validate, then regenerate the manifest and review queue.
Keep filenames readable and stable. A concept note should grow as understanding improves instead of being replaced by dated copies.
- Run
make review(orpython3 build_review_queue.py). - Open
REVIEW_QUEUE.md, or giveprompts/daily-study.mdto an LLM with repository access. - Study the highest-priority item through active recall before rereading it.
- Update review dates and Mistake Log items only after checking your performance. Use
make reviewed NOTE=<id>to update dates quickly. - Run
make allafter changing source notes.
For a weekly planning session, run make all, then use prompts/weekly-review.md with the refreshed manifest and queue.
Every study document under courses/ (except navigational README.md files) uses the same frontmatter keys:
| Field | Meaning |
|---|---|
id |
Permanent, repository-unique identifier; lowercase letters, numbers, dots, and hyphens |
title |
Human title, matching the document's # heading |
course |
Course directory name |
type |
concept, lecture, problem-sheet, exam-map, glossary, or reference |
topic |
Stable topic slug within the course |
aliases |
Alternative search terms as an inline list |
prerequisites |
Note IDs needed first |
related |
Other relevant note IDs |
exam-weight |
none, low, medium, or high |
status |
new, learning, shaky, solid, mastered, reference, or archived |
last-reviewed |
Last active-recall date as YYYY-MM-DD, or blank |
review-after |
Next review date as YYYY-MM-DD, or blank |
source |
Lecture, text, problem sheet, URL, or an honest note that the source is unknown |
visibility |
Optional publication classification: private, public-framework, public-original, or public-open-licensed |
source-risk |
Optional provenance risk: lecture-derived, problem-sheet-derived, exam-derived, lms-derived, open-licensed, original, or unknown |
Lists use the simple inline form [first-id, second-id]. This constrained frontmatter format keeps the scripts dependency-free and the files easy to inspect.
Use status as a judgment about recall, not note polish:
new: captured but not learnedlearning: partly understood; cannot yet reproduce reliablyshaky: previously learned but errors or gaps remainsolid: can explain and solve representative problemsmastered: reliable after repeated delayed reviewreference: useful navigation material, not a review itemarchived: retained but excluded from active review
After an active review:
- Update
last-reviewed. - Set
review-afterto the next useful date. A practical starting cadence is 1–3 days fornew/shaky, 7–14 days forlearning, 30 days forsolid, and 60–90 days formastered. - Propose a
statuschange separately from making it. Upgrade only after repeated recall or application evidence; downgrade when repeated mistakes demonstrate a real gap. - Record any evidenced error with the structured Mistake Log convention below.
- Regenerate
manifest.jsonandREVIEW_QUEUE.md.
Use prompts/update-status.md after a quiz, problem sheet, or timed recall session. One good answer, confidence alone, or improved prose is not enough to upgrade status. A useful upgrade normally needs successful evidence from at least two attempts separated by time or using different task types.
Mistakes are immutable evidence, not a to-do list. Add one five-field block under ## Mistake Log only when an actual quiz, problem sheet, exam, or recall attempt exposed the error:
- date: 2026-06-30
source: problem-sheet-01
mistake: Describe what went wrong.
correction: Record the action or idea to apply next time.
tags: [exam-trap, procedure]Use one line per field and inline slug-style tags. date: unknown is allowed only when migrating a genuine older mistake whose date was never recorded. Do not delete old entries when they improve; their recent-priority effect expires automatically after 30 days, while the history remains useful.
Every concept note has ## Practice Questions with ### Easy, ### Medium, and ### Exam-style groups. Questions are ordinary Markdown bullets. Empty groups use _No practice questions recorded yet._; never create filler questions merely to satisfy validation.
The manifest records counts by difficulty plus has-practice-questions. It also records whether the note has a substantive worked example, allowing weekly review to expose thin notes.
The generated queue documents the same additive formula beside its results:
- status: shaky +40, new +32, learning +26, solid +8, mastered +0;
- exam weight: high +25, medium +14, low +5, none +0;
- review timing: +30 when due, plus up to +30 for overdue days; missing
review-after+10; - review age: never reviewed +20, otherwise up to +20 after the first seven days;
- mistakes: +5 each up to +20, plus +12 for each dated mistake in the last 30 days up to +36;
- missing practice questions on a concept: +10;
- high exam weight combined with shaky status: +15.
The score ranks attention; it is not a grade or automatic status decision. Use python3 build_review_queue.py --today YYYY-MM-DD for a reproducible queue on a specified date.
manifest.json contains metadata, publication classifications, short summaries, headings, mistake counts/dates/tags, worked-example coverage, and practice-question counts for every study document. An LLM should:
- Read the manifest first.
- Filter to relevant course, topic, type, status, aliases, or exam weight.
- Open only the selected files listed in
file. - Follow prerequisite and related IDs only when needed.
- Treat the Markdown notes as authoritative and generated files as disposable indexes.
See LLM_GUIDE.md for the full protocol and prompts/README.md for ready-to-use study workflows.
| Workflow | Prompt |
|---|---|
| Import a rough lecture safely | prompts/import-lecture.md |
| Run today's study session | prompts/daily-study.md |
| Quiz one topic interactively | prompts/quiz-me.md |
| Explain a selected concept | prompts/explain-a-concept.md |
| Improve or extract a durable note | prompts/improve-this-note.md |
| Generate source-backed flashcards | prompts/generate-flashcards.md |
| Prepare for an exam | prompts/prepare-for-an-exam.md |
| Plan the next week | prompts/weekly-review.md |
| Evaluate a status change from evidence | prompts/update-status.md |
Every workflow must distinguish repository evidence from external explanation, preserve uncertainty, and avoid inventing course content.
validate_notes.py catches missing fields, malformed dates/lists, malformed structured mistakes, invalid status/weight/visibility/source-risk values, duplicate IDs, broken ID and Markdown links, absolute machine-specific links anywhere in repository Markdown, type/course-directory mismatches, title mismatches, and missing concept sections such as Practice Questions. Errors name the affected file and field or section, show the bad value when useful, and suggest a repair. The command exits non-zero on errors.
python3 validate_notes.py --public-release (or make validate-public) adds a conservative publication gate. It fails on private visibility, course-derived or unknown source risk, missing publication classifications, and local paths. Do not weaken this check to make a real private repository pass.
build_manifest.py and build_review_queue.py validate before writing. They will not replace their outputs when notes are malformed. manifest.json carries a top-level _generated warning; REVIEW_QUEUE.md begins with a generated-file HTML comment. Never edit either file manually: change the source Markdown and rebuild it.
For documentation navigation and repository-maintenance checks, see docs/README.md.