# RULES (template for ASUCB users)

What Claude should always do / never do when operating on ASU Canvas Course Builder work.

Copy to `~/.claude/projects/<project-hash>/memory/RULES.md` to adopt.

Companion: `MEMORY.md` (identity/role/knowledge), `BACKLOG.md` (pending/deferred).

---

## NEVER

### Canvas writes — absolute staging rule
- NEVER push anything to Canvas without the user reviewing it in the staging server and giving explicit approval first. No exceptions. Applies to every change — new objects, edits, fixes, one-line tweaks, anything.
- A request phrased as "fix please" / "fix it" / "update X" is NOT push approval. It is a request to *prepare* the change. Always: stage, ensure staging server, show the preview URL, STOP and wait, user reviews in staging, user gives an explicit push verb (`push` / `approved` / `deploy`), only then push.
- Do not collapse the gate when a push gets blocked (e.g. by audience-lint) and then fixed. After any re-stage, return to the preview step and wait for the explicit verb again. Never go fix-then-push directly.
- `yes` / `ok` / `sure` / "fix please" / "do it" are not push verbs. If the reply is ambiguous, re-ask: "Confirming you want me to push to Canvas, reply `push`." This is an absolute strengthening of the Write Safety Guard: no staged review + explicit approval = no push.
- **Never infer push approval from analogy.** "Do the same as M1" / "do that for M2" / "repeat for X" describes the *edit pattern* to apply, not the *push action*. Even if the prior workflow ended in a push, the new workflow restarts at stage, preview, wait. Each push requires its own explicit verb on its own turn.

### Canvas write-access tests: sandbox shells only
- NEVER run write tests against a real production course (`canvas.asu.edu`) without explicit per-course approval. Real production courses are read-only for testing unless the user names them and authorizes the write.
- When testing requires Canvas write access (creating pages, quizzes, assignments, rubrics, modules, etc.), use a sandbox shell on `asu-dev.instructure.com`. Confirm the active sandbox ID with the user before any write.
- Switching to a sandbox requires temporarily changing `CANVAS_DOMAIN` in `.env` (or in-memory) to `asu-dev.instructure.com`. Restore the production domain after the test.
- Read-only tests against production courses are fine: `--include-canvas` graph builds, audit scrapes, alignment scrapes, navigator browses, etc.

### Alignment reports: opt-in only
- NEVER auto-create an alignment report artifact (HTML, `graph.json`, `alignment-preview.html`, or equivalent persistent file) as a side effect of audit, mockup, build, or content-edit work.
- Generate one ONLY when (a) the user explicitly asks ("run alignment", "generate alignment report", "check CLO coverage"), or (b) you offer and the user says yes.
- Discussing alignment in chat, surfacing gaps inline, sketching coverage in a response: all fine. Writing a persistent artifact requires opt-in. Mirrored in repo `skills/alignment/SKILL.md` "Invocation gate" callout for team-facing enforcement.

### Never clobber the user's staged edits — the staged file is their working copy
- Once content is staged and the user is editing it (in the staging Source panel or by direct request), `courses/{id}/staging/.../` is THE working copy. The user's editor saves write to it. Apply surgical edits to that file; never rebuild it from a cached source (a `/tmp` pull, a from-scratch regenerate). Rebuilding wipes their edits.
- If a re-stage from clean source is genuinely needed (e.g. the editor mangled the markup), FIRST diff the current staged file against your source and preserve every user change, or ask. Do not assume your last-generated version is current.
- Edit richly-styled pages (syllabus, design-system/template pages) surgically or via the staging Source (raw-HTML) panel; never through a lossy WYSIWYG round-trip that flattens unmodeled inline styles. Recovery when styling is lost: live Canvas (if never pushed) or `courses/{id}/backups/` holds the unflattened original.

### The staging WYSIWYG (Tiptap) editor flattens richly-styled HTML: do not round-trip styled pages through it
- The staging-server rich-text editor models only what its schema knows (StarterKit plus our table/image/link/align extensions). Opening a heavily inline-styled page (e.g. the syllabus with centered maroon title spans, bordered tables, th backgrounds, hr) in the WYSIWYG and saving STRIPS every style it doesn't model.
- **Rule:** edit styled pages via the editor's **Source mode (`<>` button)** which preserves raw HTML, OR edit the staged file surgically yourself. Never round-trip a styled page through the WYSIWYG. The WYSIWYG is acceptable for plain-prose content pages where faculty drop a single image into mostly-unstyled text; it is lossy for the syllabus and any page built from the design system / canvas templates.
- Recovery when it happens: live Canvas (if never pushed) or `courses/{id}/backups/` holds the unflattened original. Rebuild the staged file from that plus re-apply known scripted edits; do not trust the flattened staged copy.

### Sycophancy patterns
- "Great idea" / "Perfect plan" / "You're absolutely right" / "Happy to help" / "Of course"
- Agreeing because the user seems sure
- Skipping the counter-argument step
- Pretending an earlier recommendation matches a current reversal
- Closing turns with "Want me to draft X next?" — let the user direct expansion
- Bloating revisions without subtracting weakened content

### Course-design anti-patterns to refuse
- Forward design from a content list ("we'll cover topic A, then B, then C…")
- Coverage-as-curriculum ("but we need to teach all the cases")
- Bimodal assessment (attendance + final exam, nothing graduated between)
- Recognition-only quizzes weighted as if they measure application
- Pass/fail formative writing weighted at 0% of grade
- Rubric shells without populated criteria shipped to production
- "Sync session will fill the gaps" framing
- "Students should know this from another course" assumed without sequencing verification
- "Faculty preference" as override on structural design defects
- Compliance instruments treated as learning instruments (attendance check-ins, syllabus quizzes)

### ASUCB HTML styling system (opt-in design layer, locked 2026-05-29)
- **Default surface = ASU Online QM template.** Plain native-Canvas content unless the user explicitly asks for HTML styling. The system below is the OPT-IN polish layer, not the auto-default. Faculty mockups, generated Canvas pages, and HTML reports follow it WHEN styling is invoked.
- **Color roles (locked):**
  - **Gold `#FFC627` = ONLY interactive elements.** Buttons (dataset downloads, CTAs), action links if styled as buttons. Header separator rules also gold (the one structural exception, explicit user override). NEVER on non-interactive pills, badges, bullets, dots, backgrounds, fills.
  - **Maroon `#8C1D40` = structural / brand chrome.** Headings, body links, pills/badges/MLO anchors, bullet dots, problem-block stripes, code-block stripes, attribution badges, any non-interactive accent.
  - **Neutrals:** white `#ffffff`, off-white `#fafafa`, border `#e5e7eb`, body text `#1f2937`, secondary text `#4b5563`, code `#f6f8fa` background.
  - No third accent. No purple. No warm-beige+brass.
- **Component philosophy:**
  - **Cards** = optional polish for organizing simple sibling information. NOT every section needs a card. Use when grouping multiple related items reads better as a visual unit than as flat prose.
  - **Callouts** = mandatory attention only. Compliance notices (academic integrity, late policy, accessibility, plagiarism), single high-priority items the reader cannot miss. Cap: ~1 per page. Maroon left-border + light tint, not a card.
  - **Interactive buttons** = ASU Gold. Inline pill style (`background:#FFC627;color:#191919;border-radius:8px;padding:6px 14px;font-weight:600`).
  - **Accordions / dropdowns** = `<details>/<summary>` for organizing lots of related information (reduce cognitive load). Native HTML works in Canvas. Summary text styled maroon.
  - **Pills / badges** = MAROON, white text. Highlight secondary structural elements (MLO anchors, number tags, status badges).
- **Header separator (locked).** Every `<h3>` (top-level section in body, since body has no `<h1>` and rarely needs `<h2>`) gets a thin gold rule underneath: `<div style="height:2px;width:100%;background:#FFC627;margin:0 0 14px 0;"></div>`. Topic heading gets a thicker version (`height:3px`). Edge-to-edge inside the Canvas content column. Visual signature for the system.
- **Shape lock.** 8px corner-radius on all containers and pills. No mixed radius scales.
- **RTE-safety pattern (faculty edit through Canvas Rich Text Editor without breaking layout).** Canvas RTE wraps Enter keystrokes in `<p>` and does not understand `display:flex`/`display:grid` layouts. Faculty edits = text typing inside existing blocks. Design must survive that.
  - ALL editable content sits in plain block elements: `<p>`, `<ul>`/`<li>`, `<ol>`/`<li>`, `<h3>`/`<h4>`, `<blockquote>`. Never wrap editable text in `<div style="display:flex">` or `<div style="display:grid">`.
  - Chrome (badges, pill labels, button styling) lives on inline `<strong>`/`<span>`/`<a>` INSIDE the editable `<p>` or `<li>`. Faculty editing the paragraph text leaves the inline chrome intact; pressing Enter creates a clean sibling `<p>`.
  - Structural wrappers (border-left stripes, callout containers, code-block padding) use plain `<div style="border-left:...padding:...">` with editable text inside as `<p>`/`<ul>`. No flex/grid on the wrapper.
  - Concrete rewrite: "MLO pill + verbatim text" goes from `<div display:flex><span>MLO</span><span>text</span></div>` to `<p><strong style="pill-styles">MLO 1.1</strong> text</p>`. Pill stays as inline `<strong>`, text is a normal paragraph child.
  - Concrete rewrite: numbered "step 1/2/3" list goes from flex-column to 3 `<p>` siblings, each `<p>` starts with an inline `<strong style="pill-styles">1</strong>` followed by the body sentence.
  - DO NOT use `<div style="display:flex">` / `<div style="display:grid">` as a layout wrapper for any region faculty may edit.
- **3-up card row implementation = `<table>` with 3 `<td>` cells**, NOT 3 floated/inline-block `<div>`s. Canvas RTE handles tables natively (faculty has Tables menu); cells survive editing. Each `<td>` carries inline card styling (`width:33%; vertical-align:top; background:#fafafa; border:1px solid #e5e7eb; border-top:3px solid #8C1D40; border-radius:8px; padding:14px;`). Use `border-collapse:separate; border-spacing:8px 0` on the parent `<table>` for the gap between cards. No `<th>`, no `<tr>` background (avoids the table-header-contrast trap).
- **Numbered lists = plain `<ol>`, NEVER styled pill-numbers.** Faculty edits steps freely (add, delete, reorder) and the numbers auto-renumber. Styled `<strong>` pill-numbers per `<p>` create maintenance debt: deleting one breaks sequence, adding one requires HTML editing. Apply the same rule to checklists, step-by-step instructions, ordered procedures. Plain `<ol>` or `<ul>` wins.

### Editing anti-patterns
- Deleting placeholders, instructor notes, scaffolding, or optional sections during edits unless explicitly told to remove
- Regenerating an artifact from scratch when a targeted edit was requested
- Single-data-point escalation to "Critical" severity without multi-source verification
- Same-ecosystem data treated as independent confirmation (stakeholder notes echoing my own prior framing back are not independent)
- Tool-driven design (building feature rosters before evidence the users want them)

### Provenance labels: AI-Generated vs ID-Owned (locked 2026-06-03)
- Two states for every content block in mockups, generated Canvas pages, assignments, discussions, quiz descriptions, syllabus body, and faculty mockups: **ID-Owned (default, no label)** vs **AI-Generated (visible badge)**.
- **ID-Owned** = the ID accepts accountability. Includes: faculty-verbatim content (MLOs, problem references, faculty's code, syllabus body); ID-authored prose (alignment commentary, evidence-type bullets, "Possible Misalignments" notes, rubric structure, scoping questions); ID + AI collaborative work where the ID curated, edited, or approved the result through staging review. No label needed.
- **AI-Generated** = AI improvised content the ID has NOT directly curated as their own. Includes: sample replacement questions in REPLACE/ADD blocks; AI-drafted starter code (when faculty did not author it); AI-drafted rubric descriptor wording; AI-drafted example interpretations or misconceptions; generated images / diagrams; any "Example:" or "Sample:" content the AI invented. **Visible badge required.**
- **Badge form (locked):** `<strong style="color:#8C1D40;">AI-Generated by [Model Name + Version]. Validate before using.</strong>` Placed inline BEFORE the AI-drafted block. Maroon per the design system (structural, non-interactive).
- **Labels persist on mockup AND live student-facing content.** Honest disclosure is a feature, not a flaw. Students seeing "AI-Generated by Claude..." adds trust through transparency.
- **Faculty-verbatim content is NOT ID-Owned, NOT AI-Generated.** It is faculty-authored and must be pulled verbatim from the canonical source (Canvas Module Overview, live assignment description, syllabus body). Covered by the separate verbatim-source rule, not by this provenance convention.
- **Enforcement is doc + behavioral, not preflight.** Same level as the verbatim-MLO rule. Trusts ID + Claude discipline. No new lint, no Canvas fetch infra. If recurrence (a second fabrication slips past discipline, especially undetected by faculty), revisit and ship a preflight.
- **Trichotomy collapsed to dichotomy:** "ID + AI collaborative" falls under ID-Owned. Rationale: the ID accepts accountability by curating the output through staging review. The act of approval transfers ownership.

### No em dashes in generated output (standing rule)
- NEVER use em dashes (`—`, `&mdash;`) or en dashes (`–`, `&ndash;`) in ANYTHING generated: Canvas content, mockups, page/quiz/assignment HTML, docs, commit messages, chat prose, code comments.
- Replacements: label separator ("Term — desc") use a colon ("Term: desc"); parenthetical/clause break use a comma or restructure; numeric range ("10–15") use "to" ("10 to 15"); compound modifier use a hyphen.
- Applies to all future output across every surface. When editing existing artifacts that contain em/en dashes, strip them as part of the edit.

### Code anti-patterns
- `--no-verify` on commits
- `--no-gpg-sign`
- Squashing phase commits — each phase commits its own logical batch so rollback is surgical
- Updating test assertions to hide bugs (updating for intentional behavior change IS allowed; relaxing them to mask regressions is NOT)
- Skipping `python3 -m pytest tests/ -x` after a commit

---

## ALWAYS

### Quiz mockup feedback: pattern + principles
- Question-level correct/incorrect feedback (`correct_comments_html` + `incorrect_comments_html`) is the typical shape for mockup quizzes and works for the formative loop without the bloat of per-answer feedback.
- Per-answer feedback (`answer_comments_html` / `comments_html`) is the anti-pattern the preflight flags: too granular, low signal. The ID can override on insistence; Claude flags the trade-off once and complies.
- The mockup description conventionally flags feedback as an ADD against the live baseline so faculty see the addition.
- Feedback prose stays in plain language. The Bloom-jargon principle holds (no "Apply level", "cognitive demand", etc.). Concept-anchoring to the MLO is the working pattern; alternate framings are fine.

### Mockup REPLACE/ADD: principles vs presentation
- The ID is the assessment-design expert; faculty is the SME. Mockup call-outs respect both lanes: the ID flags alignment, faculty owns content correctness.

**Principles** (Claude pushes back; the ID can override after one trade-off statement):
- **Verbatim MLO quotes** when citing alignment. Faculty doesn't memorize MLOs and neither does the ID; paraphrased MLOs lose the alignment-correctness contract the mockup exists to surface.
- **Verbatim live-question excerpts** in REPLACE blocks. The mockup's value depends on 1:1 mapping to the live item.
- **LLM attribution** on any generated draft visible to faculty (academic-honesty floor).
- **No Bloom-taxonomy jargon** in faculty-facing prose ("Apply level", "cognitive demand", framework citations beyond bare MLO id).
- **Content correctness is out of the ID lane.** When a discipline-specific claim surfaces, separate it visually from the ID observation and label as content-review work.

**Presentation conventions** (suggestions; IDs adapt freely):
- Whether to include a generated question draft inline by default, on explicit faculty request only, or never. The "format-shape only, no draft" pattern works when faculty owns drafting; the "draft inline with attribution" pattern works when faculty wants concrete starting material. Either is fine.
- Which of the 5 evidence types to cite by name in bold vs paraphrase vs skip the label entirely.
- The 4-part REPLACE/ADD block structure (chip+excerpt / evidence-type+MLO+format / attribution / example block) is one workable pattern. Alternatives that preserve the principles are fine.
- Whether to render examples in `<blockquote>` or `<pre>` (both work; `<pre>` requires `white-space:pre-wrap` to avoid Canvas overflow).
- Whether to drop a faculty name into the header. "For Your Review" is the conventional generic phrasing because it re-uses across courses without per-course substitution.

**Shared evidence-type vocabulary** names common alignment gaps. Some IDs cite by name in bold, others paraphrase. Canonical labels in `scripts/alignment_validator.LANE_EVIDENCE_TYPES`:
1. **Verb mismatch**: MLO verb vs what the instrument measures.
2. **Evidence absence**: MLO has 0 instruments measuring it.
3. **Format mismatch**: item format can't carry the MLO verb.
4. **Weight inversion**: lower-cognitive-demand instrument outweighs higher-cognitive-demand instrument in points.
5. **Practice/feedback loop missing**: terminal performance lacks formative checkpoint.

**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.

### HW/exam/discussion/quiz mockups deploy as the same Canvas object type as the live artifact they preview
- A mockup for a homework assignment must be a Canvas **Assignment** (not a Page). A mockup for a quiz must be a **Quiz**. A mockup for a discussion must be a **Discussion**. Faculty plus Canvas analytics expect type-parity with the live item.
- Pages are appropriate ONLY for mockup-of-pages (task lists, module overviews, rubrics, scaffolding docs that are themselves Pages in production).
- When pushing a HW mockup: use `assignment` type with `submission_types`, `points_possible`, `grading_type` matching the eventual live version, but leave `published: false` until faculty approves.

### HW/assignment mockup misalignments: "Possible Misalignments" subsection
- HW/assignment mockups have no per-question REPLACE/ADD block surface (that's quiz-only). Alignment observations go in a **Possible Misalignments** `<h3>` subsection placed directly AFTER the "Aligned MLOs" list.
- Format each as a bulleted list item: **MLO#** (ASU maroon `<strong style="color:#8C1D40;">MLO2.5</strong>`), **evidence type** (one of the 5: verb mismatch, evidence absence, format mismatch, weight inversion, practice/feedback loop missing), then the verbatim MLO quote in `<em>"…"</em>` plus plain-language reason plus "Out of my expertise…" disclaimer where content-correctness is involved.
- Cross-cutting observations not tied to one MLO (e.g., weight inversion across quiz vs HW) use a combined anchor like `MLO3.2 / MLO3.3` or a topic label.
- Do NOT use the gold `<mark>` highlight for these (reads as an eyesore at scale). Do NOT use a black `NOTE:` prefix. The maroon MLO# is the visual anchor.
- Presentation pattern, not a principle. IDs adapt freely. Mirrors the quiz-mockup lane discipline on the assignment surface.

### Unified design standard for new pages and mockups
- When generating design mockups or new full Canvas pages, apply `standards/page-design.md` (the unified design standard). It contains the component library, craft rules (§Design Craft Standards), and UX quality rules (§UX Quality Layer) in one place.
- ASU brand is the fixed baseline — maroon `#8C1D40` structural, gold `#FFC627` interactive-only, the ASU font stack, the `Mockup: Homework HTML Design` page as the canonical reference look. The UX Quality Layer elevates typography, spacing, accessibility, interaction, and media execution within those constraints; it never overrides ASU color, logo, or font.
- When a general UX rule and an established ASU-specific convention conflict, ASU wins. Flag the conflict; do not silently change the brand convention.
- Scope: mockups and new full pages. Not required for small assignment/quiz/discussion description edits (minimal-diff applies there).

### Title Case for HTML titles
- Title Case is the convention for HTML headings (h1 to h6) and bold callout labels in Canvas pages, assignments, discussions, and quiz scaffolding ("Use the Weekly Homework Standards", "Before You Submit", "Aligned MLOs", "For Your Review").
- Sentence case reads as off-register for headings. Body text and bullet items use sentence case.
- Article words ("a", "an", "the") and short prepositions ("of", "in", "for") mid-heading stay lowercase; the first word is always capitalized.
- Casing is a presentation convention. Claude does not push back on ID overrides. The preflight does not lint this.

### Body content does not repeat the Canvas object title
- **Principle (accessibility floor).** Canvas chrome renders the object title as the page H1. Body HTML carrying its own H1 stacks the title twice: visually duplicated for students and breaks screen-reader heading hierarchy. Body content typically starts at H2 (top-level sections) or H3 (subsections). The `check_body_no_title_repeat` preflight errors on body `<h1>` and warns on `<h2>` that restates the Canvas title.
- The H1 case carries a real a11y cost; Claude pushes back on overrides and states the trade-off before complying.
- The H2-restating-title case is a presentation drift pattern; the warning is override-friendly and IDs can ship the warning if it fits the context.
- Applies to pages, assignments, discussions, quiz descriptions, syllabus body, and mockup HTML staged for any of those surfaces. Pattern: lead with the first content heading (e.g. `<h3>Module 3 Homework: Categorical Predictors</h3>`).

### Auto-clear staged files after a verified Canvas push
- After every successful Canvas write (page/assignment/discussion/quiz/syllabus), **delete the staged file immediately** as the final step of the push sequence, including the `.meta.json` sidecar and any `.issues.json` / `.vision.json` sidecars. Sequence is stage, ensure preview server, user approval, push, verify, then **clear**. Atomic.
- Applies to both `push_to_canvas.py`-driven pushes and direct-API creations (new pages/objects not yet supported by the script).
- Do not "hold staging in case of follow-up edits." If a follow-up edit comes, re-stage from Canvas, edit, repeat the gate. Lingering staged files cause drift and re-push of stale content.

### Challenge your own verdict before claiming anything
Before asserting a fact, a recommendation, or "done," run this check:
- What evidence actually backs this claim? Did I verify it, or am I reconstructing from memory?
- What would falsify it? If I can't name a falsifier, the claim isn't grounded.
- Have I looked at the real artifact (the live page, file, diff, or Canvas state) or am I working from a plausible reconstruction?

Open the real source, compare, then claim. Applies to design fidelity, recommendation support, alignment claims, and status reports ("pushed successfully," "audit passed," "matches the template"). Confident-but-wrong claims cost trust and rework; the check costs seconds.

### Counter-position discipline (mandatory before any architectural recommendation)
Before recommending structural changes, write down:
- Strongest opposite view to my recommendation
- What would have to be true for the opposite view to win
- What evidence I have that the opposite view is wrong
- If I can't answer all three, the recommendation is not ready

### When the user is right and I previously was wrong, use this template:
1. "You're right because [strongest reason]"
2. "Implication you may not have stated: [downstream consequence]"
3. "What I missed earlier: [the argument I underweighted]"
4. "Cost / risk to acknowledge: [real downside of the new path]"

### Severity discipline
- **Critical** requires multi-source evidence
- **High** requires verified single-source OR triangulated lower-confidence sources
- **Medium** = uncorroborated observation worth flagging
- Single anecdote in a transcript = Low/Medium until verified
- Never elevate severity to drive Sprint 0 unless evidence is multi-source

### Recommendation discipline
Every recommendation includes:
1. The performance gap it closes
2. The data backing the diagnosis (LIVE evidence, transcript evidence, behavior data)
3. The principle / framework that predicts the intervention will work
4. The mechanism by which it closes the gap

No recommendations as taste preferences. Every recommendation is testable.

### Edit discipline (minimal-diff default)
When the user asks to edit, update, fill in, revise, or modify existing content (Canvas syllabi, pages, configs, HTML templates, MLO text, rubric criteria, anything):
- Fetch original verbatim
- Locate the exact field for each change
- Swap only that token
- Leave untouched placeholders untouched
- Diff against original before reporting done; only cited fields should differ
- When in doubt about whether something is "deletable scaffolding" or "important structure," treat as important. Ask before removing.

### Testing discipline
- Routine work: "Compile, test, check for drift"
- Canvas writes: Stage, push, verify Canvas state, roll back
- UI changes: Start server, open in browser, test the feature, screenshot
- New skills/routing: Run routing coverage tests
- For any change: describe what the correct result looks like BEFORE verifying

### Codex co-implementation
- Always use the `codex-rescue` subagent for new code. If the Codex runtime is unavailable, fall back to direct with thorough self-verification, but retry Codex first.
- For multi-step phases: commit in batches (don't accumulate uncommitted work; Codex may stop mid-batch).
- Read the actual diff, not Codex's summary, before merging.
- Codex sandbox false-fails the staging/socket-bind tests; its "tests failed" verdict on those is an environment artifact. Verify the suite locally before trusting a reject.

### PR-only workflow + role separation (standing rule)
- NEVER push directly to `main`. Every change goes through a feature branch + pull request, merged with `--squash --delete-branch`.
- Roles, coder is never the reviewer: **Planner** = the parent session (writes the phased plan). **Coder** = a Sonnet sub-agent (implements each phase). **Reviewer** = Codex; if Codex is unavailable or hangs, the parent (Opus) reviews as fallback. Read the actual diff, re-run the suite, verify. A `node --check` on any generated JS is mandatory for editor/script changes.
- **Auto-advance:** when a phase's full test suite passes AND the reviewer verifies, automatically start the next phase without waiting for the user. Stop only on test failure, review rejection, or a Canvas-write gate.
- **Auto-merge gate:** the reviewer agent runs `scripts/agent_review_and_merge.py --pr <n> --reviewer {codex|opus-extra-high}` which enforces the four-gate auto-merge: mergeable + CI green + full pytest passes + reviewer sign-off. On all-green it merges with `gh pr merge --squash --delete-branch`. Coder is never the reviewer; the same agent cannot do both roles.
- Override `git push --no-verify` only for emergency hotfixes the user has explicitly approved in chat; record the reason in the commit message.

### Tag-version alignment (release discipline)
- Every release moves four things together and they must always show the same number: `.claude-plugin/plugin.json`, `.codex-plugin/plugin.json`, the `VERSION` file, and the `vX.Y.Z` git tag, plus a matching `## X.Y.Z` CHANGELOG section.
- After any merge, check `git describe --tags`. If `main` is ahead of the latest tag and any commit is a `feat`, cut the next release immediately. Never leave feature commits unreleased/untagged.
- Semver: `feat` triggers a minor bump; `fix`/`docs` trigger a patch.
- Both plugin manifests must be bumped together; `tests/test_plugin_manifest_sync.py` enforces the two manifests.
- Release distribution: after tagging and the Coda sync, an engineer attaches the release `.zip` to the Coda "Download & Versions" page (the install/update channel for non-repo users). Attach AFTER the sync run for that release.

### Mockup output
- After every `mockup_manager.py --generate-preview` run, copy the resulting `{Course Name} Mockup.html` from `courses/{courseId}/mockup/` to the user's Desktop. The Desktop copy is in addition to the canonical location, not a replacement. Applies to all courses: the user presents and refers to mockups from the Desktop for faculty meetings and screen-shares. Do not hardcode a Desktop path into plugin code (Desktop is user-specific); this is a per-session behavior.

### Mockup artifacts are stakeholder-facing — show only the final product
- Mockup pages, sample pages, and sample Canvas objects are what faculty/stakeholders see. The body must read as the **finished published product the end user (student) sees**: no ID explanations, no "this is a sample," no "why this is a discussion vs. solo," no meta-commentary, no instructor/ID notes, no yellow `#fbeeb8` placeholder commentary. A published QM-template page carries zero yellow highlight; mockups should match that.
- Stakeholders need to see how the course will look as a final product, not the reasoning behind it. Explanations belong to the ID, not the artifact.
- ID-facing reasoning (rationale, framework citations, format-choice justification, open questions) goes in **separate ID-facing reference pages**, e.g. an "Other" section holding redesign-rationale, open-questions, and recommendation pages. Keep those clearly internal ("Remove before faculty-facing publish"). The module pages and sample artifacts themselves stay clean. Test: would a student see this sentence in the live course? If no, it does not belong in the mockup body.
- Genuine pending-asset placeholders (banner image, video embed, transcript, slide deck, a faculty-supplied value still TBD) are legitimate in an in-progress mockup; keep those. The rule targets design-reasoning notes, not honest "to be filled" markers.

---

## Operational patterns

### Codex dispatch — common failure modes
1. **Stale task ID** — `Task task-xxx is still running in the background` but no actual job. New dispatch blocked.
2. **Sandbox file-write claim** — Codex reports inability to acquire `.git/worktrees/{name}/index.lock` even when no lock exists.
3. **Background job phantom** — Codex says "running in background" but never commits anything.
4. **Stale worktree view** — Codex CDs into a different worktree than parent, sees divergent state, refuses to proceed.
5. **Mid-flight stop** — Codex makes uncommitted changes then halts at a "pre-existing test failure" that's actually a side effect of its own incomplete work.

### Codex dispatch — mitigation
1. First dispatch: foreground `codex-rescue` subagent with explicit working dir + verified baseline (HEAD SHA + pytest count + clean working tree).
2. If blocked: verify state independently — `git status`, `git log --oneline -1`, `python3 -m pytest tests/ -x`. If clean and matches expected, re-dispatch with the literal current state in the prompt.
3. If still blocked: solo per project root `CLAUDE.md` exception (`Emergency hotfixes when Codex runtime unavailable`). Self-verify with full test gate after each commit.

### Codex reliability profile
- **Reliable:** single-file additions or extractions, pure file renames + import updates, test-only commits
- **Flaky:** multi-file refactors spanning skills + scripts + tests + `CLAUDE.md`; anything requiring CD between worktree and main repo paths; long-running sessions (>=10 min) — sandbox state seems to decay

### Worktree pitfalls
- ASUCB uses `.claude/worktrees/{slug}/` for isolated sessions. Main repo at `/Users/<you>/<path>/ASU Canvas Builder/`.
- Codex CDs into worktree by default. Parent agent's `cd` targets main — separate state.
- Cannot `git checkout main` from inside a worktree. Use `git reset --hard <main-sha>` to align.
- `courses/` is symlinked from worktree to main. `.env` must be symlinked separately for the staging server's Canvas pulls.
- In Codex dispatch prompts, specify the exact working directory on the first line.

### Cherry-pick-on-drift pattern (Codex co-implementation)
When dispatching Codex to a worktree that has drifted from main, DO NOT merge worktree branch. Instead:
1. Before dispatch: `cd <worktree> && git fetch origin && git reset --hard origin/main` to align worktree with current main HEAD.
2. Dispatch Codex with explicit baseline (commit SHA + test count) in prompt.
3. After Codex commits in worktree: `cd <main repo> && git cherry-pick <first>^..<last>` to bring commits onto main.
4. Run full pytest on main post-cherry-pick. Push origin only if green.
5. If cherry-pick adds new skills/families, also update CLAUDE.md skill table, course-concierge SKILL.md, and test count assertions in test_routing_coverage.py + test_concierge_routing.py.

### Phase-gated refactor pattern
- **Phase 0** — Inventory file `docs/cleanup-plan.md` listing every reference to be touched, grouped by category, with `path:line — context` per hit. Extract shared helpers to neutral modules FIRST.
- **Phase 1** — Security / hard fixes + acceptance tests. Land security fixes early with their own test gate.
- **Phase 2** — Deletions. Only AFTER callers updated. Strip references in `CLAUDE.md`, `README.md`, `SKILL.md`. Add CHANGELOG entry for breaking changes.
- **Phase 3** — Renames + bug fixes. `git mv` to preserve history. Fix integration bugs the rename exposes. Add tests that lock in the new signature.
- **Phase 4** — Routing / skill consolidation. Snapshot baseline routing tests BEFORE changes. Convert aliases first (keep old names working). For each alias: assert `route("/<alias>") == route("/<primary>")`.
- **Phase 5** — Broader test coverage. Cover the surfaces the refactor touched but didn't already have tests.

### Universal commit rules
- Every commit: `python3 -m pytest tests/ -x` green
- Every refactor commit: read the diff yourself
- No squashing of phase commits
- Final step: doc bump + version bump + push origin + update Coda doc per release checklist

---

## Failure modes to never repeat (catalogued cross-project)

1. **Silent pivot on user critique** — when reversing prior recommendation, state the methodological error explicitly: "I flipped — prior rec was wrong because I didn't verify X. Implication: discount my Y claims until verified directly."
2. **Single-data-point Critical escalation** — never elevate single anecdote to Critical without multi-source verification.
3. **Same-ecosystem data treated as independent confirmation** — stakeholder notes echoing my prior framing are not independent. Real independent confirmation = different data source.
4. **Tool-driven design** — never build a feature roster before evidence that users want it.
5. **"Underutilization" framing accepted uncritically** — more features != better outcomes for the target cohort. Test counter-position before activating.
6. **Faculty resistance framed as architecture-blocker** — counter-test whether stated faculty preference is expertise vs. resistance.
7. **"Want me to draft X next?" closers** — soft sycophancy. Stop when work is done.
8. **Bloating revisions without subtraction** — revisions should subtract weakened claims, not only add new ones.
