# Course Desk > Paste the course brief or outline you already have — a title, an audience, a week list, some > objectives, whatever subset exists — and get one teachable document back: measurable learning > outcomes, a syllabus whose time budget adds up, a minute-by-minute lesson plan, an assessment > plan with a rubric, or a quiz item bank with an answer key. URL: https://course-desk.skillsafe.ai/ API: https://course-desk.skillsafe.ai/api.html Tokens: https://course-desk.skillsafe.ai/tokens.html Model: gpt-terra (resolves to gpt-5.6-terra) · metered, priced per run, free to estimate Course Desk is a design tool for the person writing the course: an instructor, an instructional designer, a training lead, a bootcamp or workshop author, an academic assembling a module for the next term. It works on the brief you paste, in one sitting. It is a drafting aid, not an authority on your institution's regulations, and not an accreditation or quality-assurance review. ## What it takes as input One work object: the course brief or outline the user pastes. It may be a one-paragraph pitch, a week-by-week table, a syllabus draft, an existing outline being rewritten, or all of them in one paste. The exact fields, all of which are sent on every run: - `task` — the lane, one of `outcomes`, `syllabus`, `lesson`, `assess`, `quiz`. If it is missing or unrecognised the model picks the closest lane and names the lane it chose in its first sentence. - `brief` — the pasted course brief. Clipped to 24,000 characters from the MIDDLE, head and tail kept, on line boundaries, with the cut announced in-band so the model is told not to infer anything about the removed section. `brief_clipped` reports how many characters were removed. - `objectives` — optional extra learning objectives, one per line, from a separate box. - `focus` — which module the `lesson` and `quiz` lanes should target (`"m3"`, or a module title). Left empty, the model picks one and says which it picked. - `level` — `intro`, `working`, `advanced` or `mixed`. - `delivery` — `live-online`, `in-person`, `async` or `hybrid`. - `rigor` — `light`, `standard` or `strict`: how hard the model pushes on measurability. - `item_count` — how many quiz items the `quiz` lane writes, 4 to 40. - `notes` — free context. - `upstream` — the previous lane's artifact when the user pressed a handoff button, else empty. - `prescan` — the browser's own measurements, described below. ## The five lanes The lanes run in the order a course actually gets built: inspect the outcomes, plan the syllabus, teach a session, assess it, then test it. Every lane takes the same work object and the same prescan facts and routes on `task`. One lane hands to the next with a button, which fills `upstream` with the artifact just produced. | task | name | what it produces | verdicts | artifact file | | --- | --- | --- | --- | --- | | `outcomes` | Learning outcomes | measurable Bloom-mapped objectives, rewritten where they were not assessable | `measurable`, `mostly-measurable`, `needs-rewrite`, `not-assessable` | `OUTCOMES.md` | | `syllabus` | Syllabus | a session-by-session syllabus with a time budget that adds up | `teachable`, `overloaded`, `underfilled`, `unsequenced` | `SYLLABUS.md` | | `lesson` | Lesson plan | one module expanded into a minute-by-minute lesson plan | `ready-to-teach`, `needs-materials`, `over-scheduled`, `thin` | `LESSON-PLAN.md` | | `assess` | Assessment plan | an assessment plan plus an analytic rubric aligned to the objectives | `aligned`, `partly-aligned`, `misaligned`, `no-evidence` | `ASSESSMENT-PLAN.md` | | `quiz` | Item bank | quiz items (MCQ, true/false, fill-in-blank, matching) with distractors and an answer key | `ready-to-use`, `needs-review`, `too-easy`, `ungradeable` | `QUIZ.md` | Each lane also returns a structured `artifact_json` of a declared kind: `learning_outcomes`, `syllabus`, `lesson_plan`, `assessment_plan`, `item_bank`. ## What runs for free, in your browser, before anything is sent `coursescan.js` is a reader, not a keyword search. It resolves the brief into a structure and then lints that structure, entirely client-side, with no token and no network call. Specifically: - **The course metadata**: title, subject, audience, level, delivery mode, prerequisites, goal, constraints, rubric, and the raw duration / session-count / session-length expressions. - **The module list**, read from headed sections, numbered lines, roman numerals, markdown tables, pipe, tab and comma tables — each module with its title, its topics and its duration in minutes. - **Every learning objective classified against Bloom's revised taxonomy** — remember, understand, apply, analyze, evaluate, create — from a verb lexicon of roughly 130 verbs, recording the verb found, the level it implies, whether a condition and a success criterion are present, whether the objective is compound, and whether its verb is outside the taxonomy altogether. - **The non-measurable check**, which is the single most useful lint here: objectives anchored on a private mental state ("understand", "know", "appreciate", "be aware of", "gain an understanding of", "have a working knowledge") name something nobody can observe, so they cannot be assessed and are flagged for rewrite. - **The assessment plan with its weights**: each assessment's name, its type, its weight, whether it is graded or formative, and whether the declared weights sum to 100. - **The alignment between all three**, computed and reported as four explicit lists: objectives with no assessment, objectives attached to no module, modules with no objective, and assessments linked to no objective. - **The contact-time budget**: contact minutes resolved from the duration, the session count, the session length, or a per-week cadence ("6 weeks, 2 sessions per week"), with the route recorded in `schedule.source`, compared against the minutes the modules themselves add up to. A stated duration this reader cannot resolve sets `declared_unreadable` and raises a flag, rather than silently switching the budget check off. - **Ten negation-aware signals**: rubric, prerequisites, exam, project, grading, accessibility, asynchronous, online, in-person and feedback, each reported as `on` plus `negated`. - **About twenty lints**, each carrying `flag_id`, `rule`, `severity`, `confidence`, `scope`, `target`, `message` and `evidence`: non-measurable verb, verb outside the taxonomy, compound objective, no success criterion, objective with no module, objective with no assessment, duplicate objective, module with no objective, objective overload on one module, module minutes missing, session overflow, assessment with no objective, unclear assessment type, an assessment citing an objective that does not exist, missing assessment weight, weights that do not sum, no assessment declared, no rubric declared, audience not declared, prerequisites not declared, duration unreadable, duration not declared, a time-budget mismatch over 10 percent, a Bloom ceiling stuck at levels 1-2, all objectives at a single Bloom level, no objectives at all, no modules at all, accessibility not addressed, and the two sampling notices. ### Three parsing failures it was designed against Each of these produces a confidently wrong answer rather than a missing one, which is why the reader handles them explicitly: 1. **Negation.** "no prior programming experience", "there is no final exam", "no rubric yet" must never switch the corresponding signal on. Negation binds inside the comma-or-semicolon segment the phrase lives in, with a bounded four-word look-back, plus a trailing "none / n/a / tbd" form. A stated "no prerequisites" is recorded as an explicit none, not as a gap. 2. **Multi-entity lines.** "Modules 3 and 4 cover recursion and dynamic programming" refers to two modules; "quiz (not graded) and final project (graded)" is one ungraded and one graded assessment. Splitting is parenthesis-depth-aware, so a qualifier can never migrate to its neighbour. 3. **Prose quantities.** "in about four weeks", "two ninety-minute sessions", "half a day" are durations. "90 minutes" is one session of ninety; "two 45-minute sessions" is two of forty-five — the unit decides which reading to take. A prose duration the reader cannot resolve is flagged, never treated as absent. ### Tri-state confidence, and sampling Every flag carries a confidence of `high` or `unknown`, and the difference is a claim about different things. A lint with evidence in the paste is definite: an assessment citing objective o7 when the brief lists six is true of the text. A lint asserting absence across the whole brief is `unknown`: "no rubric declared" is a claim about the paste, not about the course, because the user may have pasted an extract. The prompt requires the model to answer every `flag_id` exactly once in `coverage_check`, and forbids it from upgrading an `unknown` into a claim about what the course actually has. At most 26 modules and 60 objectives are sent. When there are more, rows are drawn with a golden-ratio Kronecker sequence rather than every nth row — a fixed stride can share a factor with a periodic column (every third module is a lab, an assessment every fourth week) and hand the model one repeated phase. The draw is reported to the model and to the user, and the first and last rows are always kept. ## Output contract One JSON object per run, the same envelope in every lane: `lane`, `title`, `verdict`, `headline`, `summary`, `checks[]`, `findings[]`, `rows[]`, `artifact`, `artifact_json`, `coverage_check[]`, `questions[]`, `confidence`. Every key is always present; an empty section is `[]`, `""` or `{}`, never null and never omitted. `rows` is one six-column table whose columns mean something different per lane — objective/Bloom/verb/evidence in `outcomes`, module/minutes/objectives/activities in `syllabus`, segment/minutes/teacher/learner in `lesson`, criterion/weight/objectives/evidence in `assess`, item/stem/type/answer/distractors in `quiz`. ## What it costs Metered, on the `gpt-terra` model (which resolves to `gpt-5.6-terra`). The prescan is free and needs no account. `POST /estimate` is free, creates no job, and returns the model, the markup in basis points, the credit hold and the minimum balance — so you can price a lane before running it. A run places a **hold** for the estimated cost and is then **charged** the actual cost when the job settles; the difference between the hold and the charge is released. A guest token can call `/me` and `/estimate`; running a lane needs a personal token from signing in. Errors come back as `UNAUTHORIZED`, `FORBIDDEN`, `VALIDATION_ERROR`, `INSUFFICIENT_CREDITS`, `RATE_LIMITED`, `NOT_FOUND` or `INTERNAL`. ## What it does not do - It never runs code. There is no sandbox, no interpreter, no execution of anything in your brief. - It never reaches an LMS. It does not connect to Canvas, Moodle, Blackboard, Google Classroom or any other system, does not read a course from one and does not publish anything to one. Copying the artifact out is a manual step you take. - It never grades a real student. It writes rubrics and answer keys as course materials; it does not score submissions, and no student work is an input to it. - It does not know your institution's rules. Credit-hour arithmetic, accreditation criteria and academic-integrity policy are yours to check against the draft. - It does not verify subject-matter facts in the brief, and quiz items it writes still need a subject expert to read them before they meet a learner. - Nothing in the free prescan leaves your browser, and files dropped on the page are read locally. ## Sources and credit Course Desk is a derived work built on two agent skills, and is **not affiliated with, endorsed by or maintained by either author**: - `@teachingai/course-designer` — the primary source: the outcomes, syllabus, lesson-plan and assessment-plan lanes, and the Bloom's-taxonomy framing of the free prescan. - `@onewave-ai/quiz-maker` — the item-bank lane: item types, distractor construction and the answer key. All trademarks and skill names belong to their respective owners. This app reimplements the ideas in its own code and its own prompts; no code from either skill is redistributed here.