---
name: course-mockup
description: "Create, edit, and present faculty-facing course mockups; promote approved mockups into course builds."
---

# Course Mockup

Create presentable course prototypes for faculty buy-in. IDs use mockups to show faculty "your course could look like this" before committing to a full build. Faculty never touches this tool — IDs present the generated `{Course Name} Mockup.html` via email or screen-share.

> **Mockup object-type parity (mandatory)**: a mockup must mirror the real Canvas object type it represents — homework → Assignment, quiz → Quiz, overview/lesson/syllabus → Page, peer-rating/teamwork → Assignment. Identify the target type before creating; if ambiguous, ask. Never default to Page.

## Faculty-Facing Framing

All faculty-facing mockups are editable suggestions by default. Frame the work as draft options the instructor can keep, modify, or replace, not as imposed final course design.

- Address faculty directly as "you" when the mockup text is meant to be shared with them.
- Open substantive mockup pages or review notes with language such as: "For your review: this is a draft option you can keep, modify, or replace."
- Avoid overclaiming subject-matter authority. For revised prompts, homework, quiz questions, rubrics, or examples, make clear that the instructor's disciplinary judgment decides the final version.
- When keeping an existing faculty-authored item because it aligns well, briefly and lightly acknowledge that alignment before explaining why it was kept.
- When replacing or revising an item, show the current item, name the evidence used for the change, and provide the revised item as an example option. For quiz and assessment revisions, use MLO alignment as the primary evidence when available.
- Separate suggestions from standards. Content and design choices should read as editable suggestions; accessibility, broken links, missing instructions, or clear alignment gaps should be stated more firmly with evidence and one possible fix.
- Keep Canvas-native styling in mind. If the instructor may replicate the pattern later without HTML expertise, prefer native Canvas structures, simple headings, lists, tables, and code blocks over complex custom blocks.

### Quiz Mockup Item Structure

When creating faculty-facing quiz mockups or quiz-revision suggestions, use this structure for each item:

1. Add a status badge at the top: **KEEP**, **ADD**, or **REPLACE**. Use consistent Canvas-native inline styling; recommended colors are green for KEEP, ASU gold for ADD, and ASU maroon for REPLACE. If the suggestion is optional, add an **OPTIONAL** badge beside the status.
2. **Current question:** Quote or summarize the current quiz question being kept, revised, or replaced. If the suggestion fills a missing assessment gap, state that there is no current question for that target.
3. **Why keep it?**, **Why add this question?**, or **Why replace it?** Combine the rationale with the MLO alignment evidence. For kept questions, lightly acknowledge what the existing item already does well. For added questions, explain the missing evidence target without implying the instructor must add the example. For replaced questions, explain the alignment gap or evidence gap without presenting the replacement as mandatory. If the current question is still defensible, say the replacement is optional and that faculty may keep the original, revise it, or use the example.
4. **Suggested Alternative Wording**, **Suggested Question**, or **Suggested Replacement:** Provide the editable example question text, using the label that fits the situation. Use "alternative wording" when the core question is kept; use "suggested question" when filling a missing target; use "replacement" when the current question is being swapped.
5. **Why revise the wording?** Use this only when the current question is being kept and the suggestion mainly changes wording, formatting, or answer-choice clarity. Explain why the wording change is clearer, easier to scan, or more directly assessable. Skip this section when **Why add this question?** or **Why replace it?** already explains the reason for a new or replacement item.

Keep answer choices, correct answers, and feedback in the mockup when available, but do not bury the four-part review structure inside answer feedback.

## Alignment Evidence Integration

When generating a faculty-facing quiz, assignment, or discussion mockup, check `courses/{id}/alignment/mockup_decisions.json` before writing the per-item Why-keep / Why-add / Why-replace rationale.

1. **Read-before-generate.** If `mockup_decisions.json` exists, read it and use per-item `decision_label` (`KEEP` / `ADD` / `REPLACE` / `OPTIONAL`) plus `rationale` to populate the Quiz Mockup Item Structure ("Why keep it?" / "Why add this question?" / "Why replace it?" + MLO evidence). Per-MLO entries (`decisions_by_mlo`) cover coverage gaps that need new items.
2. **Offer-then-generate.** If `mockup_decisions.json` does not exist, offer once: "I can build an alignment graph + mockup decisions file first so the mockup can cite MLO evidence directly. Run alignment Mode B first? [yes/no]". Do not auto-run — the `alignment` skill's Invocation Gate still applies. Only proceed when the user explicitly says yes.
3. **Decision-label → mockup-badge mapping.** The decision labels in `mockup_decisions.json` map 1:1 to the `KEEP` / `ADD` / `REPLACE` / `OPTIONAL` badges in the Quiz Mockup Item Structure above. Faculty-safe rationale text is precomputed in the artifact — surface it verbatim or lightly rephrase, but do not invent a different decision label than the one in the file.
4. **Confidence display.** Show the underlying confidence (`EXTRACTED` / `INFERRED` / `AMBIGUOUS`) as the faculty-safe label ("Explicit evidence" / "Likely alignment" / "Needs review") next to the badge, matching the `alignment_preview_html.py` convention.
5. **Read-only.** This integration never writes Canvas content directly. Mockup pushes still go through the staging gate.

## Delivery Surface

The standalone mockup UI remains the default for shareable full-course previews. When the ID wants faculty to review proposed changes directly in Canvas, use a Canvas-native mockup delivery path instead:

1. Create mockup pages, assignments, discussions, or quiz drafts as staged Canvas items.
2. Preview through the staging skill before any Canvas write.
3. After explicit approval, push them into a visible "Mockups" or "Suggestions" module near the top of the Canvas course.
4. Title the items clearly with `Mockup:` or `Suggestion:` so faculty and IDs do not confuse them with final live course content.
5. Keep the items unpublished unless the user explicitly asks to publish them.
6. Preserve structured briefs or local mockup files when the content may later be promoted, reused, or audited.

## Prerequisites
> **Edit discipline**: When editing or revising a mockup, preserve all existing sections and cards. Change only what the user explicitly named. Do not delete placeholder cards or scaffolding.

- **Course shell must exist.** The ID must have a Canvas course shell (blank or from ASU Online template) before creating a mockup. Mockups are anchored to a course ID.
- If the course ID is not yet in `courses/registry.json`, auto-add it by verifying via the Canvas API and updating the registry.

## Clone Layout from a Reference Course

When the user wants to build a new course that mirrors the structure of an existing Canvas course, extract the reference course's layout first, then feed the template to mockup/build.

1. **Extract:** `python3 scripts/extract_course_layout.py --course-id {SOURCE_COURSE_ID} --save`
   - Optionally add `--include-syllabus` to also capture syllabus section structure.
   - Output is saved to `templates/canvas-templates/extracted/{SOURCE_COURSE_ID}-layout.json`.
2. **Review:** Inspect the JSON or the human-readable outline (`--json` flag omitted) to confirm module order, SubHeader placement, item-role sequence, and page signatures.
3. **Feed to mockup/build:** Reference the extracted layout JSON when generating module scaffolding. Use the `name_pattern`, `items[*].role`, and `items[*].title_pattern` fields to pre-populate module names and item titles for the target course.

The extracted template captures genericized structure only (no course-specific text). `Module 1 Overview` becomes `Module N Overview`; a title like `Module 3: Categorical Predictors` becomes `Module N: [Title]`. Peer-review previous-module references (`Peer Review of Module 2 Homework`) become `Peer Review of Module N-1 Homework`.

## Canvas Templates
When these trigger phrases are active, load the spec doc instead of duplicating template content:
- **SDO JD Online** — triggers: "Use SDO JD template", "SDO-JDO template", "JD Online template", "Sandra Day template".
  Spec: `standards/canvas-templates/sdo-jdo-online-template.md`
- **ASU Online QM** — triggers: "Use ASU Online QM template", "ASU Online template", "QM template", "Quality Matters template".
  Spec: `standards/canvas-templates/asu-online-qm-template.md`
- This skill must mirror the active template's module sequence, SubHeader structure, and visual signatures per the spec doc.
Do not paste template tables or HTML bodies here; reference the spec doc.

## Storage

All mockup data lives under `courses/{courseId}/mockup/`:

```
courses/{courseId}/mockup/
  manifest.json           — metadata (course name, version, faculty, status)
  pages/                  — mockup page HTML files
    m1-overview.html      — real design-system HTML (promotable)
    m1-lesson.html        — real design-system HTML (promotable)
    m1-knowledge-check.html  — placeholder card HTML
    m1-discussion.html    — placeholder card HTML
  briefs/                 — structured JSON for non-page content (saved alongside placeholders at creation time)
    knowledge-check-m1.json
    discussion-m1.json
    assignment-m1.json
  feedback.md             — Claude-written faculty feedback log
  {Course Name} Mockup.html     — current self-contained shareable file
  {Course Name} Mockup-v1.html  — previous versions preserved
```

## Commands

### 1. Create

Generate mockup pages for one or more modules.

**Workflow:**
1. Confirm the course via the setup gate. Read `course-config.json` if it exists for module structure and objectives.
2. Ask the user which modules to mock up, and what level of detail:
   - **Pages only** — just the content pages (overview, lesson, etc.)
   - **Full module** — pages + placeholder cards for quiz, discussion, assignment, and any external tools
3. Choose the delivery surface:
   - **Standalone mockup preview** — default for full-course or email/screen-share review.
   - **Canvas-native mockup module** — use when the ID wants faculty to review mockups directly in Canvas through a visible Mockups/Suggestions module. This path still requires staging preview and explicit push approval.
4. Create the manifest: `${ASUCB_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}}/bin/python3 scripts/mockup_manager.py --course-id {ID} --create-manifest --name "{COURSE_NAME}" --term "{TERM}" --faculty "{FACULTY}"`
5. For each page, generate HTML using the same design system and templates as `course-build`:
   - Use templates from `templates/page-html/` as starting points
   - Follow `standards/page-design.md` for component library, inline styling, craft quality, and UX quality (unified standard)
   - Save via mockup_manager: `mockup_manager.save_page(course_id, filename, html, page_type="page")`
6. For non-page content (quizzes, discussions, assignments), generate placeholder cards **with structured brief data**:
   - Use the placeholder card template from `templates/mockup-html/placeholder-card.html`
   - Include the actual content in the HTML: quiz questions, discussion prompt, rubric criteria, grading info
   - **CRITICAL**: Also pass the structured content as `brief_data` so it's preserved for later promotion to course-build. The brief is saved automatically alongside the placeholder HTML. This is the **only time** the structured data is available — do not skip this step.
   - Save with `page_type="placeholder"` and `brief_data`:
     ```python
     mockup_manager.save_page(
         course_id, "m1-knowledge-check.html", html,
         page_type="placeholder",
         brief_data={
             "questions": [
                 {"question_text": "...", "question_type": "multiple_choice_question",
                  "answers": [{"answer_text": "...", "answer_weight": 100}, ...],
                  "points_possible": 3, "correct_comments": "...", "incorrect_comments": "..."}
             ],
             "settings": {"time_limit": None, "allowed_attempts": -1, "show_correct_answers": True}
         }
     )
     ```
   - Save Canvas module dividers with `page_type="subheader"`:
     ```python
     mockup_manager.save_page(
         course_id, "m1-00a-overview-section.html", "<h3>Overview</h3>",
         page_type="subheader"
     )
     ```
   - Brief format for **discussion**:
     ```python
     brief_data={"prompt": "Full discussion prompt text...", "discussion_type": "threaded",
                  "grading": {"points_possible": 25}, "replies_required": 2,
                  "require_initial_post": True}
     ```
   - Brief format for **assignment**:
     ```python
     brief_data={"description": "Full assignment instructions...",
                  "rubric": [{"criterion": "...", "ratings": [{"description": "...", "points": N}, ...]}, ...],
                  "submission_types": ["online_upload"], "points": 30}
     ```
7. For external tools (Harmonize, Turnitin, PlayPosit, etc.), generate a generic placeholder card:
   - Tool name, description of what it does
   - Any configuration details the ID provides (prompt text, grading settings)
   - Save with `page_type="placeholder"`
8. For standalone mockup preview, generate the course-named preview: `${ASUCB_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}}/bin/python3 scripts/mockup_manager.py --course-id {ID} --generate-preview`
9. For Canvas-native mockup delivery, stage the proposed Canvas items and use the staging review before asking for push approval.
10. Report the preview path or staging review URL so the ID can inspect it.

### 2. Edit

Modify individual mockup pages after faculty feedback.

**Workflow:**
1. Read `courses/{courseId}/mockup/feedback.md` if it exists for context on what needs changing.
2. List current pages: `${ASUCB_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}}/bin/python3 scripts/mockup_manager.py --course-id {ID} --list-pages`
3. The user says what to change (e.g., "update the Module 1 overview to emphasize case studies").
4. Read the current page, modify it, save it back.
5. Regenerate preview: `${ASUCB_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}}/bin/python3 scripts/mockup_manager.py --course-id {ID} --generate-preview`

### 3. Preview

Regenerate `{Course Name} Mockup.html` (e.g., after manual edits or adding new pages).

```bash
${ASUCB_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}}/bin/python3 scripts/mockup_manager.py --course-id {ID} --generate-preview
```

This writes `{Course Name} Mockup.html`, with the course name from `manifest.json` sanitized for filesystem safety, and versions the previous preview as `{Course Name} Mockup-v{N}.html` while bumping the version in the manifest.

### 4. Feedback

Capture faculty feedback after a presentation.

**Workflow:**
1. The ID relays what faculty said (e.g., "they loved Module 1 but want the color palette warmer").
2. Claude writes the feedback to `courses/{courseId}/mockup/feedback.md`, organized by date and version.
3. Also update `courses/{courseId}/course-memory.md` with relevant approvals and preferences under the appropriate sections (Faculty Preferences, Design Decisions).
4. Update manifest status: `mockup_manager.update_manifest(course_id, status="revision-requested")` or `status="approved"`.

### 5. Promote

Move approved mockup content into the course's staging area for actual build.

**Workflow:**
1. Confirm scope with the user: one page, one module's pages, or the entire mockup.
2. For **page-type** files:
   - Promote to staging: `${ASUCB_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}}/bin/python3 scripts/mockup_manager.py --course-id {ID} --promote {FILENAME}` or `--promote-all`
   - This copies HTML to `courses/{courseId}/staging/pages/{slug}.html`
3. For **placeholder-type** files (quizzes, discussions, assignments):
   - Briefs were saved at creation time (via `save_page(brief_data=...)`). Promote verifies they exist.
   - If a brief is missing (placeholder was created without `brief_data`), report it as needing attention. The ID should re-run the Create command for that placeholder with the structured data.
   - `promote_all` reports `briefs_available` (briefs ready for course-build) and `needs_attention` (placeholders missing briefs).
4. For **subheader-type** files, promotion skips them and reports `subheaders_skipped`; Canvas SubHeaders are module dividers, not page content.
5. Update build progress: `${ASUCB_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}}/bin/python3 scripts/progress_tracker.py --course-id {ID} --update {MOD_NUM} {PAGE_TYPE} complete` for each promoted page.
6. Log the promotion: `${ASUCB_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT}}}/bin/python3 scripts/progress_tracker.py --course-id {ID} --history "promoted" "mockup v{N}"`
7. Update manifest: `mockup_manager.update_manifest(course_id, status="promoted")`
8. Tell the user: "Mockup promoted. Page content is staged — run `/course-build` or `/staging` to push. Assessment briefs are saved — course-build will use them when generating quizzes, discussions, and assignments."

## Placeholder Card Design

Non-page content (quizzes, discussions, assignments, external tools) uses styled placeholder cards that visually show the content but are clearly marked as previews.

**Placeholder card structure:**
```html
<div style="border: 2px dashed #8C1D40; border-radius: 8px; padding: 24px; margin: 20px 0; background: #faf5f7; position: relative;">
  <div style="position: absolute; top: 8px; right: 12px; font-size: 10px; color: #8C1D40; text-transform: uppercase; letter-spacing: 1px;"><strong>Preview Only</strong></div>
  <div style="display: flex; align-items: center; gap: 12px; margin-bottom: 16px;">
    <span style="font-size: 24px;">{ICON}</span>
    <div>
      <p style="font-size: 11px; color: #8C1D40; text-transform: uppercase; letter-spacing: 0.5px; margin: 0;"><strong>{TYPE_LABEL}</strong></p>
      <h4 style="margin: 0; color: #333; font-size: 18px;">{TITLE}</h4>
    </div>
  </div>
  <div style="color: #555; font-size: 14px; line-height: 1.6;">
    {CONTENT_PREVIEW}
  </div>
</div>
```

**Icons and labels by type:**

| Type | Icon | Label |
|------|------|-------|
| Knowledge Check (Quiz) | `?` | `Knowledge Check` |
| Discussion | `💬` | `Discussion` |
| Assignment | `📝` | `Assignment` |
| External Tool | `🔗` | `External Tool: {Name}` |

**Content preview by type:**
- **Quiz**: Show 2-3 sample questions with answer choices, points per question, total points, time limit
- **Discussion**: Show the full prompt, discussion type, reply requirements, grading info
- **Assignment**: Show instructions, submission type, rubric table (criteria x levels), total points
- **External tool**: Show tool name, what it does, the prompt/config that will go into it, note that "ID will configure this in {tool name}"

## SubHeader Items

Canvas module SubHeaders are plain-text section dividers between module items, saved with `page_type="subheader"` and a sidecar `subheader_text` value. Use the same `m{N}-{position}-{slug}.html` filename pattern as pages, with an optional alpha suffix to slot dividers without renumbering, such as `m1-00a-overview-section.html` or `m1-04a-learning-materials.html`. SubHeaders render as quiet italic dividers in the mockup TOC and body; they are not page cards, not placeholders, and do not promote to staging.

## Integration with Course Build

When `/course-build` runs, it checks for promoted content:
1. **Staged pages**: If `courses/{courseId}/staging/pages/{slug}.html` exists, use it instead of generating fresh. Confirm with user.
2. **Assessment briefs**: If `courses/{courseId}/mockup/briefs/{type}-m{N}.json` exists, use the brief as generation input for the quiz/discussion/assignment. Confirm with user.

## Output

The primary deliverable is `{Course Name} Mockup.html` — a self-contained HTML file that:
- Opens in any browser
- Can be emailed as an attachment
- Can be screen-shared during faculty meetings
- Has sidebar navigation for clicking between module pages
- Clearly marks placeholder content as "Preview Only"
- Includes course name, term, version, and generation date
- Is print-friendly (sidebar hides in print view)

## Mockup Quiz Layout — Pattern + Principles

The section that follows describes one workable pattern for mockup quiz HTML,
iterated during the STP530 M1 / M2 / M3 mockup cycle (2026-05-27). IDs adapt
this pattern to their faculty, course, and review style. Skill templates live
at `templates/mockup-html/quiz-mockup-template.html` and
`templates/mockup-html/quiz-mockup-block-snippets.html`. Preflight checks live
at `scripts/preflight_checks.check_mockup_lane_discipline` and
`check_body_no_title_repeat`.

### Principles vs Presentation

A small set of items are principles — accessibility and pedagogical floors
where Claude pushes back if an ID insists on violating them, then complies on
explicit override after stating the trade-off once. Everything else is
presentation preference and IDs adapt freely.

**Principles** (Claude pushes back; ID can override on insistence):

1. **No `<h1>` in mockup body HTML.** Canvas chrome renders the object title
   as the page H1. Body H1 stacks the title twice — visually duplicated for
   students and breaks heading hierarchy for screen readers. The
   `check_body_no_title_repeat` preflight raises an error on body H1.
2. **No Bloom-taxonomy jargon in faculty-facing prose.** Faculty are subject-
   matter experts, not instructional designers — terms like "Apply level" or
   "cognitive demand" communicate nothing useful. The `bloom_jargon`
   preflight warns; the principle holds even on override-friendly cases.
3. **Verbatim MLO quotes when citing alignment.** Paraphrased MLOs break the
   alignment-correctness contract that the mockup exists to surface. When
   a block names an MLO, the MLO text appears verbatim in `<em>"…"</em>`.
4. **LLM attribution on any generated content visible to faculty.** Academic
   honesty floor. Drafts produced by Claude (or any model) ship with a
   visible attribution badge so faculty cannot mistake a draft for an ID
   expert recommendation.
5. **`questions` array stays verbatim from the live baseline.** ID
   commentary never bakes into `question_text`. The mockup item maps 1:1
   to the live item; all commentary lives in the `description` field.

**Presentation conventions** (suggestions; IDs adapt freely):

- Header wording. "For Your Review" is common. "Alignment Notes" / "Quick
  Review" / any phrasing that fits the faculty relationship is fine.
- Block order — commentary first vs MLOs first; KEEP-then-REPLACE vs
  interleaved.
- Whether to include a generated example draft inline by default, on
  faculty request, or never.
- Styling specifics — chip colors, NOTE format, the ASU brand palette
  documented in `page-design.md` is one option among several.
- The 4-part REPLACE / ADD block structure described below — one pattern
  that works; alternatives are fine if the principles still hold.
- Code-block styling on examples.

**Anti-patterns** (preflight lints warn, never block except H1):

- Compound nouns ("interpretation-item", "confidence-interval-interpretation
  item").
- Body headings that restate the Canvas object title (warning except H1 =
  error).
- Unwrapped `<pre>` blocks (Canvas overflows without `white-space:pre-wrap`).
- Per-answer feedback bloat (question-level feedback is typical; ID can
  override).

### Where commentary lives

- **`questions` array** = verbatim live baseline (principle 5 above).
- **`description` field (Quiz Instructions)** is the conventional surface
  for all ID commentary — KEEP / REPLACE / ADD blocks, evidence-type
  call-outs, open scoping questions, generated example drafts. IDs who
  prefer a separate scratchpad surface can split the commentary out, as
  long as the verbatim-`questions` principle holds.

### Description structure (reference pattern)

A typical mockup description runs in this order. The header strings and the
"Optional" Open Scoping block are presentation; IDs reorder or rename as
fits.

1. `<h3>For Your Review</h3>` + a short intro paragraph stating the ID-lane
   scope of the review (alignment only; content correctness flagged
   separately). The header is generic by convention — appending a faculty
   name forces per-course substitution and adds no signal.
2. `<p><strong style="color:#8C1D40;">NOTE:</strong> Per-question
   correct/incorrect feedback added…</p>` — declares the feedback ADD
   against the live baseline so faculty can flag it.
3. Optional Open Scoping Questions block — `<h3>` + `<ol>` of questions
   faculty input is needed on (course-level scope decisions, not item-level).
4. `<h3>MLO Alignment Evidence</h3>` + `<ul>` of verbatim MLO quotes for
   every MLO referenced in the per-question commentary (principle 3).
5. `<h3>Per-Question Commentary</h3>` + KEEP blocks followed by REPLACE /
   ADD blocks.

### KEEP block (reference shape)

One workable shape — adapt the prose for the faculty's review style:

```html
<p><span style="…KEEP chip…">KEEP</span> <strong>Question N</strong></p>
<p><em>{verbatim live Q excerpt}</em></p>
<p>Aligns with MLOX.Y. {short alignment why}</p>
```

### REPLACE / ADD block (reference pattern)

One pattern that works in practice — adapt as needed. REPLACE flags a
misaligned live item; ADD flags an evidence target with no live item.
Structures are identical apart from chip color.

1. **Chip + question heading + verbatim Q excerpt.**
   `<p>[chip] <strong>Question N</strong></p><p><em>{verbatim live Q excerpt}</em></p>`
   The Q excerpt is verbatim (principle 5 carries over — paraphrasing the
   live question loses 1:1 mapping).
2. **Evidence type + verbatim MLO quote + response-format suggestion.**
   `<p><strong>{Evidence Type}:</strong> {gap statement}. MLOX.Y says: <em>"{verbatim MLO}"</em> {response-format suggestion using verbatim MLO verb}</p>`
   The 5 evidence types below are a shared vocabulary; some IDs cite the
   type name in bold, others paraphrase or skip the label. MLO quote is
   verbatim (principle 3). Response format = `multiple-choice`,
   `short-response`, `true/false`, `ordering`, `matching`.
3. **LLM attribution badge** (principle 4, required when an example draft
   follows).
   `<p><strong style="color:#8C1D40;">Example Generated by Claude Opus 4.7. Please validate content before using:</strong></p>`
4. **Example code block with correct-answer highlight.**
   `<pre style="white-space:pre-wrap;word-wrap:break-word;background:#f6f8fa;padding:12px;border-radius:4px;font-size:13px;"><code>{plain-text example with green-highlight span on correct line}</code></pre>`
   `white-space:pre-wrap` prevents Canvas overflow (anti-pattern above).
   Highlight the correct-answer line via inline
   `<span style="background:#d8f5c2;">…    ← correct</span>`.

### The five evidence types — shared vocabulary

A vocabulary that names common alignment gaps. Some IDs cite types by name
in bold; others paraphrase or skip the label. Canonical labels live in
`scripts/alignment_validator.LANE_EVIDENCE_TYPES`. Call-outs typically run
≤3 sentences and avoid Bloom verbs and "cognitive demand" framing
(principle 2).

1. **Verb mismatch** — MLO verb vs. what the instrument measures. *"Q2
   measures counting. MLO3.2 calls for interpretation. An interpretation-
   format item would close the gap."*
2. **Evidence absence** — MLO has 0 instruments measuring it. *"MLO3.3 has
   no items in M3. Worth one item that uses a mixed predictor model."*
3. **Format mismatch** — item format can't carry the MLO verb.
   *"Interpretation is hard to measure with multiple-choice. A short
   constructed-response item fits MLO3.2 better."*
4. **Weight inversion** — lower-cognitive-demand instrument outweighs
   higher-cognitive-demand instrument. *"Quiz = 6pt, HW = 3pt. Points
   signal what counts — consider flipping."*
5. **Practice loop missing** — terminal performance lacks formative
   checkpoint. *"R skill is summative-only (HW). One formative R item on
   the quiz would catch errors before they cost HW points."*

### ID-lane conventions (preflight warnings)

The `check_mockup_lane_discipline` preflight warns (non-blocking) on the
items below. IDs can override any warning; the principles section above
flags which ones Claude pushes back on first.

- **Verbatim MLO quotes — no paraphrase.** Principle 3. Wrap in
  `<em>"…"</em>`. Use the exact verb from the MLO text in the response-
  format suggestion.
- **Plain-English response format.** No compound nouns like
  "interpretation-item" or "confidence-interval-interpretation item". No
  unexplained CI / MR / df abbreviations in faculty-facing prose; spell
  out first use or scope to a `<pre>` block / `<em>` MLO quote.
- **Tools and languages are in lane.** R, Python, SQL, Excel — fine.
  `summary(lm())` — fine. Tools are not subject-matter content.
- **Subject-matter content is NOT in lane unless named in the MLO.** "A
  fitted equation with categorical dummies" is a content setup. If the MLO
  doesn't name a stats concept, the call-out doesn't either.
- **LLM attribution on every generated draft.** Principle 4.

**Discipline test for the lane conventions:** can the format be described
without naming subject-matter content concepts that aren't already in the
MLO? If yes, in lane. If no, redraft.

Examples of common drift patterns the preflight lints catch:

- BAD: "a confidence-interval-interpretation item (a stated CI + four
  interpretation options)" — compound noun + abbreviation.
- GOOD: "A multiple-choice item that asks students to 'obtain and
  interpret the confidence interval'" — verb verbatim from MLO2.2, plain
  response format.

- BAD: "a multiple-choice item that shows a fitted equation with
  categorical dummies and asks the student to interpret what one
  coefficient means compared to the baseline category" — names content
  setup + content concepts (dummies, baseline).
- GOOD: "A multiple-choice item that asks students to 'interpret' a
  regression model with a categorical predictor" — verb verbatim, MLO
  content paraphrased ONLY to the extent the MLO already names it.

### Title pattern

The conventional mockup quiz title is **"Mockup: Module N Quiz Alignment
Review"**. The form "Revised Module N Quiz Questions Review" reads as
content imposition (the questions are verbatim live baseline; nothing in
the questions array is *revised*). The convention is about framing more
than the literal string — IDs adapt to their faculty's preferred phrasing
as long as the framing stays ID-observation, not content-imposition.

Body HTML carries the H1 principle: `check_body_no_title_repeat` errors on
body `<h1>` (principle 1) and warns on `<h2>` that restates the Canvas
title. The H2 warning is an override-friendly anti-pattern; the H1 error
carries the real a11y cost.

### Mockup HW / Assignment Layout

Quiz mockups have a per-question surface (KEEP / REPLACE / ADD blocks mapped
1:1 to live items). HW and assignment mockups do not — an assignment is a
single prompt, not an item array. Alignment observations therefore aggregate
in one place rather than attaching to individual questions. The conventions
below are presentation preferences (IDs adapt freely), consistent with the
quiz section's principles-vs-presentation framing above; the five evidence
types are the shared vocabulary already documented for quizzes
(`scripts/alignment_validator.LANE_EVIDENCE_TYPES`).

**Misalignments → a "Possible Misalignments" subsection.** Because there is
no per-question REPLACE / ADD surface, alignment observations collect in a
`<h3>Possible Misalignments</h3>` subsection placed directly after the
"Aligned MLOs" list. Each observation is a `<li>` formatted in this order:

1. `<strong style="color:#8C1D40;">MLO#</strong>` — the MLO number in ASU
   maroon, which is the visual anchor for the observation.
2. The evidence type — one of the five canonical types (verb mismatch,
   evidence absence, format mismatch, weight inversion, practice/feedback
   loop missing).
3. The verbatim MLO quote in `<em>"…"</em>` (principle 3 — no paraphrase).
4. A plain-language reason the alignment is in question.
5. An "Out of my expertise…" disclaimer wherever content-correctness is
   involved — the ID lane is alignment, not subject-matter accuracy.

Cross-cutting observations not tied to a single MLO use a combined anchor
(`<strong style="color:#8C1D40;">MLO3.2 / MLO3.3</strong>`) or a topic label
in place of a single MLO number.

**No gold `<mark>` highlight on misalignment notes.** At scale (many notes on
one assignment), gold highlight reads as an eyesore. The maroon MLO# carries
the emphasis on its own.

**No black `NOTE:` prefix.** The maroon MLO# is the visual anchor — a leading
`NOTE:` adds a second competing anchor in default black. (This differs from
the quiz description structure, where a single `NOTE:` line declares a
feedback ADD against the baseline.)

**LLM attribution badge uses ASU maroon.** Any Claude-generated example or
draft content visible to faculty (e.g., R starter code, a sample rubric row)
ships with the attribution badge in ASU maroon, not default black:

`<strong style="color:#8C1D40;">Example Generated by Claude Opus 4.7. Please validate content before using:</strong>`

This is principle 4 (LLM attribution) carried over from the quiz layout. Black
badges read as plain body text and faculty can miss the AI-generated flag —
gen-time misses shipped black badges on the M1 / M2 HW mockups during the
STP530 cycle, so the `check_mockup_lane_discipline` preflight now warns
(`mockup.lane.attribution_not_maroon`, non-blocking) when an
"Example Generated by Claude" `<strong>` lacks `color:#8C1D40`.


## Mockup Quiz Tagging Convention

Use these tags when turning alignment decisions into faculty-facing quiz mockups:

- **KEEP**: The current item already aligns with the named MLO(s) at an acceptable Bloom level. Keep the current wording and answers unless the instructor requests edits.
- **REPLACE**: The current item is misaligned, under-targeted for the MLO Bloom level, or assesses the wrong evidence. Show the current item and provide an editable example replacement.
- **ADD**: No current item assesses the named MLO or evidence target. Provide an editable example question and answer set.
- **OPTIONAL**: The suggestion is useful but not required. In YAML, set `optional: true` beside a KEEP, REPLACE, or ADD decision so the OPTIONAL badge appears with the primary tag.

Rationale templates:

- KEEP: `Aligns with MLO X[, Y] because...`
- REPLACE: `Current item misaligns with MLO X because [Bloom mismatch / wrong cognitive level]; replacement targets [Apply/Analyze/etc.]`
- ADD: `Closes a gap on MLO X because no current item assesses [Y]`

Decision YAML schema read by `scripts/build_mockup_quiz.py`:

```yaml
title: Optional faculty-facing quiz title
quiz_canvas_id: 123456
decisions:
  1:
    tag: KEEP        # KEEP, REPLACE, or ADD
    mlos: [1, 2]     # empty list means CLO-only
    why: rationale text
    optional: false  # true adds the OPTIONAL badge
  2:
    tag: REPLACE
    mlos: [3]
    bloom_current: Remember
    bloom_target: Apply
    why: Bloom mismatch / wrong cognitive level
  3:
    tag: ADD
    mlos: [4]
    why: no current item assesses applied interpretation
```

### Quiz Details vs Questions

The staging editor surfaces quiz settings in two places, with different write semantics:

- **Unified review** (`/courses/{id}/review`, read-only): each staged quiz block shows a Details summary card immediately under the section header — one row of inline pill chips for quiz type, points, attempts, time limit, scoring policy, shuffle, and (when applicable) an `Unpublished` flag. Chips are anchors that link to the per-quiz edit page with the `#details` hash so a click jumps straight to the Details tab. The card is summary only; no edits happen here.
- **Per-quiz edit page** (`/courses/{id}/preview/quizzes/{key}`): a tab bar (`Details` / `Questions`) sits above the existing question render. The Questions tab is the default; `#details` in the URL activates Details. The Details tab is a form (title, quiz_type, points_possible with auto-calc + override, allowed_attempts, time_limit, scoring_policy, shuffle_answers, published) that saves via `PUT /api/staging/{course_id}/quizzes/{key}/details` — additive to the existing full-quiz `PUT`. The Details payload is validated against `QuizDetailsSchema` (`scripts/staging_schema.py`) which rejects unknown keys to catch typos before they reach Canvas. Non-blocking preflight warnings (`check_quiz_details_consistency` in `scripts/preflight_checks.py`) flag redundant scoring policies, published-but-zero-points, and time limits on practice quizzes.

CLO-only flag pattern: wrap the flag text in a yellow mark block:

```html
<mark style="background:#fff3bf;color:#191919;padding:2px 4px;">CLO-only flag: this item is not mapped to a module learning objective.</mark>
```

### CLO-only decision pathway

When a quiz item aligns with a course-level outcome (CLO) but no Module Learning Outcome (MLO), use the CLO-only decision pathway. Three resolution paths exist:

- **KEEP** — keep the item in this module, flagged as foundational CLO support (optionally naming the MLO it supports indirectly via `clo_supports_mlo`).
- **MOVE** — recommend moving the item to the module where the relevant MLO is taught (`target_module`).
- **DROP** — recommend removing the item from this module entirely.

`MOVE` and `DROP` tags **require** `clo_only: true`. Omitting it raises a `ValueError` at build time.

Decision YAML examples:

```yaml
# Pattern A — KEEP with CLO-only flag; supports MLO 4 indirectly
7:
  tag: KEEP
  clo_only: true
  clo_supports_mlo: 4
  mlos: []
  why: "Aligns with the course-level outcome for developing basic R code."

# Pattern B — MOVE to module where the relevant MLO lives
8:
  tag: MOVE
  clo_only: true
  target_module: 1
  mlos: []
  why: "This item tests foundational syntax covered in Module 1."

# Pattern C — DROP from this module entirely
9:
  tag: DROP
  clo_only: true
  mlos: []
  why: "No Module 2 MLO requires this knowledge."
```

Optional top-level key `module_number` on the decisions file sets the module label used in faculty-facing closing sentences.

**Faculty review pattern**: each CLO-only item renders:
1. The primary tag badge (`KEEP` / `MOVE` / `DROP`) followed immediately by the yellow `CLO ONLY` chip.
2. A yellow `<mark style="background-color: #fff2a8; color: inherit;">` block prefixed with **"Flag for faculty: this item does not align with any Module N MLO."**, the user's `why` text, and one of the three closing faculty-decision sentences.

The closing sentences are:
- KEEP + `clo_supports_mlo: M` → "Faculty decision: keep this item as foundational support for MLO M, move it to a module where this concept is taught directly, or drop it from Module N."
- MOVE + `target_module: M` → "Faculty decision: move this item to Module M where the supporting MLOs are taught directly, keep it here as foundational support, or drop it."
- DROP → "Faculty decision: drop this item from Module N (recommended), move it to another module, or keep it here as foundational support."

The preflight check `check_clo_only_consistency` (warning, never blocking) detects chip/callout sync issues and unresolved items older than 14 days.

### Plain-language rule

Rationale text shown to faculty must never use Bloom taxonomy jargon. Faculty are subject-matter experts, not instructional designers — "Remember level" or "Apply level" communicates nothing useful to them.

**Banned terms** (regex, case-insensitive): `\bRemember level\b`, `\bApply level\b`, `\bUnderstand level\b`, `\bAnalyze level\b`, `\bEvaluate level\b`, `\bCreate level\b`, `\bBloom\b`, `\bcognitive level\b`, `\bcognitive demand\b`, `\bcognitive load level\b`.

The preflight check `bloom_jargon` (warning, never blocking) flags any of these in staged content.

**Before / after examples:**

| Before (jargon) | After (plain language) |
|---|---|
| "Replacement targets the Apply level per MLO 2." | "MLO 2 calls for students to use the formula in a new context — this item asks them to do that." |
| "Current item is at the Remember level; MLO asks for Analyze." | "MLO 3 calls for comparing competing interpretations; the current item only asks students to recall the definition." |
| "Bloom mismatch — cognitive demand is too low." | "MLO 2 calls for building and interpreting a confidence interval; this item asks students to pick a definition instead." |

### Collaborative tone for REPLACE rationales

REPLACE rationales address faculty as partners, not as passive recipients of ID judgments. The preflight check `replace_tone_directive` (warning, never blocking) flags directive phrasings in REPLACE-tagged content.

**Denylist phrases** (trigger the warning):
- `MLO \d+ requires`
- `the current item is wrong`
- `you should` / `you must`
- `the item fails to`
- `we need to replace`
- `students fail to`
- `this is incorrect because`

**Allow-list phrases** (preferred register — pass silently):
- `MLO X calls for`
- `if you'd like an item that`
- `open to your preferred direction`
- `this could be strengthened by`
- `happy to revise either direction`
- `one possibility is`

Full denylist and allow-list live in `skills/course-mockup/tone_denylist.yaml`. IDs may extend either list there without editing Python code.

---

## Proposal Mode

### When to use

Use Proposal Mode when the ID is presenting a **build plan or conversion strategy to faculty or program leadership**, not when sharing a draft course for faculty feedback. The output is a neutral business-proposal deck — the ID's own strategy document — not a faculty-review mockup.

Proposal Mode is appropriate when:
- The ID is pitching a course conversion (in-person → online) and needs a shareable rationale deck.
- Presenting scope decisions, assessment architecture, module map, and timeline to a decision-maker.
- The deliverable reads as a professional proposal authored by the ID, not as draft content the faculty approves.

Proposal Mode is **not** appropriate for:
- Sharing draft Canvas pages or quiz items for faculty review — use the standard mockup flow above.
- Any surface where "for your review" / "keep, modify, or replace" framing is expected.

### Three non-negotiable constraints

1. **Warm-neutral editorial chrome — NOT an ASU maroon/gold re-skin.**
   The proposal shell uses a warm-neutral palette (creams, warm grays, restrained sienna accent):
   `--cream:#F7F2EA; --paper:#FFFEFB; --ink:#2A2521; --body:#514A42; --muted:#938A7E; --hair:#E6DDCE; --sienna:#9A5B3F; --sage:#5E6B4F`.
   Do NOT use `#8C1D40` (ASU maroon) or `#FFC627` (ASU gold) as chrome or background. This palette governs proposal *chrome*; `page-design.md`'s maroon/gold palette governs Canvas *page content* — different surfaces. A past attempt re-skinned the proposal shell in ASU maroon and was rejected — do not repeat it.

2. **Neutral business-proposal tone.**
   The proposal reads as the ID's plan to a decision-maker. Do NOT emit faculty-review deferential phrases: `for your review and approval`, `a draft you can keep, modify, or replace`, `your calls`. These belong in the standard faculty-review mockup flow, not here.

3. **No AI-attribution badge.**
   The proposal deck is presenter-authored. Do NOT emit `Drafted by Claude`, `Generated by Claude`, or any AI badge. This is a **deliberate, documented exception** to principle 4 in the Quiz Mockup Layout section above ("LLM attribution on any generated content visible to faculty"). That principle covers generated *course content* that faculty might mistake for an ID recommendation — a proposal deck is the ID's own strategy document, not course content, so the attribution principle does not apply here.

### Generator

`scripts/proposal_builder.py` — `build_proposal(data: dict) -> str`

Reads `templates/mockup-html/proposal-base.html`, renders only the sections present in `data`, omits absent ones cleanly, builds a sidebar nav listing only the rendered sections, and returns self-contained HTML. Pure (no network). HTML-escapes all user-supplied text via `html.escape`.

**CLI:**
```bash
python scripts/proposal_builder.py --in data.json --out proposal.html
python scripts/proposal_builder.py --in data.json --course-id 261470
```

When `--course-id` is provided and `--out` is omitted, output goes to `courses/{id}/mockup/{course_title}-proposal.html`.

### Data schema

All sections are optional — absent sections are omitted cleanly with no empty headers.

```json
{
  "course": {
    "title": "Applied Multivariate Analysis",
    "code": "STP 533",
    "term": "Fall A 2026",
    "version": "v1",
    "date": "2026-05-28",
    "about": "A plan to convert STP 533 to a 7.5-week ASU Online course…"
  },
  "parameters": [
    {"label": "Session", "value": "Fall A 2026 — 7.5 weeks"},
    {"label": "Proctoring", "value": "None. Take-home assessment throughout."}
  ],
  "at_a_glance": {
    "now": ["17 modules of reference material", "No objectives anywhere"],
    "next": ["Measurable course + module outcomes driving every page"]
  },
  "outcomes": [
    "Select and justify an appropriate multivariate method…",
    "Verify the assumptions a chosen method depends on…"
  ],
  "assessment": {
    "summary": "Project-centered assessment with applied deliverables.",
    "weighting": [
      {"item": "Applied Project", "pct": 40},
      {"item": "Homework / R labs", "pct": 25}
    ],
    "note": "No proctoring. Take-home applied assessment throughout."
  },
  "module_map": {
    "columns": ["Wk", "Module", "Outcomes", "Checkpoint"],
    "rows": [["1", "M1 — Multivariate foundations", "CLO1, CLO2", "HW 1"]]
  },
  "decision_points": [
    {
      "title": "Scope cuts",
      "detail": "Repeated measures MANOVA is a cut candidate.",
      "recommendation": "Drop repeated measures; retain canonical correlation as optional."
    }
  ],
  "timeline": [
    {"when": "Now → sign-off", "what": "Scope decisions per Section 5"},
    {"when": "Week 1–2", "what": "Objectives + module outlines"}
  ]
}
```

The ID (and Claude) author the content in the data dict; `proposal_builder.py` renders it into the consistent neutral shell. This is the correct division: the generator handles layout, palette, and tone discipline; the ID handles substance.

---

### Canonical badge HTML

Use these exact strings for KEEP / REPLACE / ADD / OPTIONAL badges in mockup quiz HTML. IDs who hand-edit mockups should copy from this section. The preflight check `badge_style_mismatch` (warning, never blocking) flags badges with non-canonical colors.

```html
<!-- KEEP — ASU green -->
<span style="display:inline-block; background:#78BE20; color:#191919; padding:3px 9px; border-radius:4px; font-size:12px;">KEEP</span>

<!-- REPLACE — ASU maroon -->
<span style="display:inline-block; background:#8C1D40; color:#FFFFFF; padding:3px 9px; border-radius:4px; font-size:12px;">REPLACE</span>

<!-- ADD — ASU gold -->
<span style="display:inline-block; background:#FFC627; color:#191919; padding:3px 9px; border-radius:4px; font-size:12px;">ADD</span>

<!-- OPTIONAL — outlined maroon chip -->
<span style="display:inline-block; border:1px solid #8C1D40; color:#8C1D40; padding:2px 8px; border-radius:4px; font-size:12px;">OPTIONAL</span>
```

These constants are defined in `scripts/build_mockup_quiz.py` (`KEEP_BADGE`, `REPLACE_BADGE`, `ADD_BADGE`, `OPTIONAL_CHIP`) and mirrored in `scripts/preflight_checks.py` (`CANONICAL_KEEP_BADGE`, etc.).
