Use for building and syncing Canvas course CONTENT — mirroring a course to/from local files (canvas_sync pull/push/build), master→blueprint→section propagation (blueprint_sync), module prerequisites/completion settings (module_settings_sync), multi-course orchestration, and offline (.imscc) editing. Load this when the task is "make Canvas match the local source" or "propagate content across sections" — NOT grading and NOT read-only auditing.
Scanned 10/2/2026
npx -y skills add chaz-clark/canvas-toolbox --skill course-build --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Course Build?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/chaz-clark-course-build-1488ed80)More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.
---
name: course-build
description: Use for building and syncing Canvas course CONTENT — mirroring a course to/from local files (canvas_sync pull/push/build), master→blueprint→section propagation (blueprint_sync), module prerequisites/completion settings (module_settings_sync), multi-course orchestration, and offline (.imscc) editing. Load this when the task is "make Canvas match the local source" or "propagate content across sections" — NOT grading and NOT read-only auditing.
---
# Course Build (CI / sync)
Manage Canvas course content as code: mirror to local files, edit locally, and push
approved changes back through the Canvas REST API.
> The **constitution** (AGENTS.md) binds you: confirm scope before any write, and
> Canvas writes go through the toolkit (never a hand-rolled API call). This skill is
> the content-sync *how*.
## Core principle — local is the source of truth
The local working folder is authoritative; **Canvas is the sync target.** Pull to
mirror, edit locally, push to apply. Canvas has no version history — the local
files + git are your history.
## The multi-course model
| Term | What | `.env` id | Folder |
|---|---|---|---|
| **Master** | Template course where authoring happens | `MASTER_COURSE_ID` | `master/` (or `course/` legacy) |
| **Blueprint** | Canvas Blueprint sections clone from (online programs) | `BLUEPRINT_COURSE_ID` | `blueprint/` |
| **Section** | Live per-semester course (S1, S2…) | `S1_COURSE_ID`, … | `s1/`, `s2/` |
**Confirm scope before every write** — master vs blueprint vs section. A stale
`.env` can silently target the wrong course (Canvas API lesson L12); `canvas_course_guard`
defends against it, but naming the target explicitly is on you.
## Tools
| Task | Tool |
|---|---|
| Mirror one course (pull/push/build) | `canvas_sync.py --pull` / `--push` / `--build` |
| Master → Blueprint propagation | `blueprint_sync.py` |
| Validate a blueprint sync | `validate_blueprint_sync.py` + `blueprint_exception_report.py` |
| Module prerequisites / completion | `module_settings_sync.py` |
| Create a custom grading scheme, optionally attach it to assignments | `grading_scheme_setup.py --preset performance-tiers [--assignment-ids 1,2] --apply` (or `--title ... --tiers ...`) |
| Create a peer-review assignment + peer rubric (unpublished) | `peer_review_setup.py --title ... --criteria "Prepared?:yesno;Effort:1-5" --apply` — enrolled course needs `--allow-enrolled`, only on the instructor's explicit say-so |
| Pair group members as peer reviewers of each other (counts-only output) | `peer_review_assign.py --title ... --group-set-id N --apply` — same `--allow-enrolled` rule; re-run after late submissions or group changes |
| Report per-student peer rating averages (read-only, user_id-keyed, no names) | `peer_review_summary.py --title ... [--csv out.csv]` |
| Multi-course wrapper | `sync_context.sh` |
| Post-push structural audit | `course_quality_check.py` *(then hand off to the `audit` skill)* |
**Always run `course_quality_check.py` after a push** to catch duplicates, floating
items, and date-window problems the write may have introduced.
## Canvas API write lessons that bite content sync
The full catalog is [`lib/agents/knowledge/canvas_api_lessons_learned.md`](../../lib/agents/knowledge/canvas_api_lessons_learned.md).
The ones that matter here:
- **Module prerequisites & published state are FORM-ENCODED, not JSON** (L1, L2) —
a JSON payload silently no-ops.
- **Date writes need the `due_at`/`lock_at`/`unlock_at` trio** (L3); discussions use
`todo_date`, not `due_at` (L7).
- **`late_policy` PATCH is admin-only** — 403 for teacher tokens (L4).
- **Classic quizzes have two IDs** (`quiz_id` + `assignment_id`); `points_possible`
reads 0 until questions are pushed (L5, L6).
- **NewQuiz / ExternalTool items use a separate REST surface** (L8) — the toolkit
currently pulls sidecars and has sandbox-proven CRUD coverage, but production
sync writes are still a separate roadmap item; do not assume `canvas_sync.py`
can push them yet.
- **Blueprint resync can spawn `-N` slug orphan Pages and silently revert section
page bodies** (L13, L14) — `blueprint_orphan_pages.py` detects both.
- **Page title collisions auto-suffix** `-2`/`-4` (L15).
Adding content is a **two-step** operation (course item + module item) — a page/
assignment isn't visible to students until it's also a module item.
## Offline mode (v1.7)
Tools read a local `course/` (from `canvas_sync --pull` or `offline_import` of a
`.imscc`) and run identically online/offline. The `.imscc` is the source of truth;
`course/` is the working folder. `imscc_record` patches your `course/` edits back
into the sidecar `.imscc` faithfully (quiz questions / files / LTI preserved
byte-for-byte) for re-import via the Canvas UI.
## Quick command map
| Ask | Command |
|---|---|
| "Pull the course from Canvas" | `canvas_sync.py --pull` |
| "Push changes to Canvas" | `canvas_sync.py --push` |
| "Sync master → blueprint" | `blueprint_sync.py` |
| "Fix module prerequisites" | `module_settings_sync.py` |
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!