Agent skill

Canvas Reading Annotation

by X-isdoingreat in X-isdoingreat/canvas-pilot

Generic reading-annotation handler for academic-writing courses — annotates reading PDFs with color-coded highlights + margin notes + filled answer blanks per the instructor's rubric.

AGPL-3.0Auto-check: notesEducation

Install Canvas Reading Annotation

skills CLI
$ npx skills add X-isdoingreat/canvas-pilot --skill canvas-reading-annotation -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install X-isdoingreat/canvas-pilot canvas-reading-annotation --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/X-isdoingreat/canvas-pilot.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/canvas-reading-annotation .claude/skills/canvas-reading-annotation && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
canvas-reading-annotation
GitHub stars
125
Token cost
~8.4k tokens
SKILL.md length
3,898 words
Files
1
Skills in repo
32
Repo updated
First seen
Licence
AGPL-3.0

At a glance

Generic reading-annotation handler for academic-writing courses — annotates reading PDFs with color-coded highlights + margin notes + filled answer blanks per the instructor's rubric.

  • Works in 8 steps: classify → 5 — research-before-improvise (run when… → locate_reading (reading_annotation flow) → …
  • Tasks that involve Educational content
  • SKILL.md covers §1 — Identity & contract, §2 — Stage 0: load overlay, §2.5 — Stage 0.5:… and §3 — Pipeline stages, plus 5 more sections
  • Reaches eapfoundation.com

What it does

Canvas Reading Annotation is an agent skill from X-isdoingreat/canvas-pilot. Generic reading-annotation handler for academic-writing courses — annotates reading PDFs with color-coded highlights + margin notes + filled answer blanks per the instructor's rubric. Invoked by canvas-execute when an assignment's routing skill is acenglish AND src/acengrouter.py returns "short" (long essays go to canvas-essay). Before doing anything, this skill loads private/canvas-reading-annotation-app.md which encodes your school/instructor-specific behavior (homework module ID, reading-PDF file mapping…

Its SKILL.md is about 8.4k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Education, covering Educational content, Quizzes and assessments and PDF. The repository describes itself as: Local-first Canvas LMS AI agent that learns each course's recurring assignment workflow and reuses it through scan - approval - execute with student review. The licence is AGPL-3.0.

When your agent uses it

  • Tasks that involve Educational content
  • Tasks that involve Quizzes and assessments
  • Tasks that involve PDF

Example prompts

  • “s rubric. Invoked by canvas-execute when an assignment”
  • “/canvas-reading-annotation”

Requirements

  • Pre-approved tools (allowed-tools): Bash, Read, Write, Edit, Glob, Grep, WebFetch, Skill

Workflow steps

8 steps, taken from the step headings in SKILL.md.

  1. classify
  2. 5 — research-before-improvise (run when HW page doesn't fit the 4-kind table)
  3. locate_reading (reading_annotation flow)
  4. extract_text_and_blanks
  5. annotate_pdf
  6. fill_answer_blanks
  7. verify (6-check gate)
  8. 5 — post-delivery self-audit (MANDATORY, never skip)

What it can do on your machine

Read from SKILL.md and the folder at commit 6b79d5b. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves these tools, so the agent can use them without asking each time:

    • Bash
    • Read
    • Write
    • Edit
    • Glob
    • Grep
    • WebFetch
    • Skill

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are python, json and markdown).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Hosts in commands or code, which the agent is likely to contact:

    • eapfoundation.com

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Canvas Reading Annotation loads about 8.4k tokens when it runs. Until then it costs about 168 tokens; SKILL.md has 3,898 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~168
When it runs · the whole SKILL.md, loaded when a task matches
~8.4k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check: notes

The automated check noted patterns worth knowing about, such as sudo or a known installer.

  • NotePre-approves every shell command (allowed-tools: Bash)SKILL.md
    allowed-tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, Skill

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from X-isdoingreat/canvas-pilot at commit 6b79d5b, republished under its AGPL-3.0 licence (© X-isdoingreat). 3,898 words, ~8,400 tokens.

Download SKILL.mdSave it as .claude/skills/canvas-reading-annotation/SKILL.md (or your agent's skills folder).
name
canvas-reading-annotation
description
Generic reading-annotation handler for academic-writing courses — annotates reading PDFs with color-coded highlights + margin notes + filled answer blanks per the instructor's rubric. Invoked by canvas-execute when an assignment's routing skill is `ac_english` AND src/ac_eng_router.py returns "short" (long essays go to canvas-essay). Before doing anything, this skill loads `_private/canvas-reading-annotation-app.md` which encodes your school/instructor-specific behavior (homework module ID, reading-PDF file mapping, color rubric, voice register, video→worksheet pairings). Without the overlay the skill stops and asks the user to author one.
allowed-tools
Bash, Read, Write, Edit, Glob, Grep, WebFetch, Skill

canvas-reading-annotation

§1 — Identity & contract

What this skill handles: assignments where the deliverable is a PDF marked up in place — reading-annotation HWs (color highlights + margin notes + answer blanks filled to ≥90% line width). Common in ESL / EAP / freshman composition / humanities-source-annotation courses. Output is one PDF with the same page count as the original reading, no appended pages.

Trigger: invoked by canvas-execute when an assignment's routing skill in courses.yaml is ac_english AND src/ac_eng_router.py classifies the assignment as "short" (the 6-layer cascade — long-essay assignments go to canvas-essay instead).

Success criterion: writes a result.json with status: "draft_ready" and draft_path pointing at the annotated PDF. The 6-check verification gate (§4) must pass before the file is moved to final_drafts/ or submitted. Auto-submission is overlay-gated; default is draft_ready → human review → human upload.

Failure statuses:

  • status: "skipped" if the assignment is in-class / pen-on-paper (submission_types: ['on_paper'] or name contains "(In Class)" / "Practice Summary").
  • status: "error" if overlay missing, reading PDF can't be located, HW page classifier hits an unsupported kind (e.g. response_paper / external_tool), or any verification check fails after 3 retries.

Explicitly does NOT handle:

  • Long essays / response papers → canvas-essay
  • Discussion posts, online_quiz, in-class assignments → routed elsewhere or skipped
  • Producing the worksheet content itself for a video-exercises HW — for those the skill searches the web for the public source worksheet first; if no public worksheet is found, the skill returns error rather than fabricating exercises (see §3 stage classify)

Agent stops at can't-do, not shouldn't-do — this skill serves the student. Behavioral policies set by course staff (prohibitions on AI tools, on cooperation, on outside tutors, on paraphrasing) are between the student and the staff; the skill does not gate on such rules. The skill does the work regardless; whether to use the output is the student's call. The skill only stops on intrinsic can't-do:

  1. Physically impossible / the agent genuinely can't do it — in-person attendance, printing paper artifacts, in-person signing, Lockdown-Browser-locked quizzes, Respondus monitor proctoring.
  2. Must be the student personally — academic honesty contract signature, ID proof, in-person peer review, interview-style.
  3. Input missing and unobtainable — spec cannot be located anywhere; a required material file cannot be fetched and the student cannot supply it.
  4. Verification fails after retries — sub-agent-designed checklist still fails after 3 retry rounds.

YuJa-style soft-stop — for resources the agent physically can't reach but where a fetched substitute may exist (linked videos, password-protected pages, third-party site logins), the skill does not hard-stop. It offers the student a chance to supply the link:

"This week's HW references a YuJa video. I can't log into YuJa myself. If you send me the video URL, I'll try transcript / scraping; otherwise I'll skip this step and do the rest of the work I can do."

Student declines → skip that step and continue with the work that can be done. Student supplies → agent attempts fetch (transcript / OCR / scraping, whichever the platform supports).


§2 — Stage 0: load overlay

First action every run is to read _private/canvas-reading-annotation-app.md. It is one flat file per generic skill, multi-course inside — find the ## Course {course_id} block for the current assignment, then within that block find the #### {kind} sub-block whose naming_regex matches assignment.name.

Three fallbacks if anything is missing:

  • Whole overlay file absent → dispatch canvas-bootstrap to run the detective on this course, write the overlay, then resume.
  • Course block absent (overlay exists, other courses are configured, this course isn't) → same: route to bootstrap for this course only, then resume.
  • #### {kind} sub-block absent (course is configured but this assignment's naming pattern was never seen at bootstrap time) → ask the user the minimal subset of fields needed for this single assignment (which reading, what kind of HW, color choice if instructor varies), append a new kind block to the overlay, continue.

If the overlay exists but Stage 0 still cannot resolve a kind for this assignment (e.g. user declines to author the new kind block), write status: "error" and stop.


§2.5 — Stage 0.5: required-sources receipt (enforced gate)

The overlay's §0 declares, in a machine-readable required_sources: yaml block, the materials that MUST be loaded before any draft is produced for this assignment's kind. The check-source-manifest hook refuses to let a draft_ready/submitted result.json through unless every mandatory source for this kind is recorded as loaded with a real file on disk — a loaded flag with nothing in <work>/sources/ does not pass. This is the machine-checkable form of §0's "load BEFORE writing" rule; it is what would have caught the 2026-05-21 transcript-improvisation incident automatically.

Once Stage 1 classify has resolved the kind and the material-loading stages have run (transcript via §0 / Stage 1.5b; reading PDF via Stage 2 locate_reading), copy each loaded material into <work>/sources/ and write runs/<today>/<work>/sources.json:

json
{
  "course_id": "<id>",
  "skill_name": "reading-annotation",
  "kind": "<resolved kind>",
  "sources": {
    "yuja_transcript": {"enforcement": "mandatory", "status": "loaded", "path": "sources/<Slug>.txt"},
    "reading_pdf":     {"enforcement": "mandatory", "status": "loaded", "path": "sources/Reading_N.pdf"}
  }
}

Rules:

  • Include only the sources whose applies_to_kinds covers this kind (a reflection HW lists yuja_transcript; an annotation HW lists reading_pdf).
  • path must point at a real non-empty file under <work>/sources/ — the hook verifies existence and size.
  • If a mandatory source can't be loaded (cookie dead, video not in the library, reading PDF not locatable), DO NOT fabricate. Soft-stop and ask the user with the three-part template: (1) show the evidence of the gap, (2) offer options — point me at the real source / confirm a substitute is fine / tell me it isn't needed (skip), (3) state you will NOT improvise from general knowledge. If the user authorizes skipping, append a line to <work>/.sources_ack: {"source_id": "<id>", "action": "skip", "quote": "<user's verbatim words>"} and set that source's status to soft_stop_acked in sources.json.

§3 — Pipeline stages

Six stages run sequentially per assignment. Each writes its output under runs/<today>/<assignment>/<stage>/. Stage outputs are traceable artifacts; if a downstream stage fails, the upstream artifacts let the user (or a retry) see exactly where things broke.

Stage 1 — classify

The same Canvas course can mix several HW kinds in one week's module page (reading annotation, video exercises, in-class summary, response paper). The Canvas assignment description is usually empty. The real instructions live in the Homework module page body (overlay-specified homework_module_id).

Pull the matching week's HW page, find the paragraph in the page body that links back to assignment.id, then keyword-classify:

Signals in the paragraphKindContinue to
"read Reading N", "answer the pre-/post-reading questions", reading PDF referencereading_annotationStage 2
"watch video on", "as you work through the video", "copy-paste the exercises"video_exercisesStage 2-alt (web search the public worksheet, see below)
"Practice Summary", "(In Class)", submission_types: ['on_paper']in_class_skipwrite status: "skipped", exit
"Response Paper", submission_types: ['external_tool']response_papernot in scope — write status: "error", route to manual

Write kind.txt with the detected kind. Don't default to reading_annotation — that is the easy-to-make mistake (real incident on 2026-04-13: an unbranched skill defaulted to reading_annotation for a video-exercises HW and fabricated worksheet content; the student lost points because their submission didn't match what every classmate produced).

For video_exercises: instructors' Yuja-style videos for standard EAP topics are almost always based on free public worksheets. The skill searches the web first, not the LLM. Queries like "<topic>" EAP Foundation worksheet PDF or "<topic>" academic English exercises fill in blank. Fetch the worksheet PDF, reproduce its tasks verbatim, fill in B1-B2 student answers. Never invent exercises — if no public worksheet matches after 3 searches, return status: "error" with a note asking the user to dictate the exercise structure. An invented worksheet is worse than no submission.

Stage 1.5 — research-before-improvise (run when HW page doesn't fit the 4-kind table)

Trigger (any of these in the HW page body Stage 1 fetched):

  • "write N sentences" / "Five Takeaways" / "Five Insights" / "your reflections on" — the reflection_bullets shape that Wk6 hit and the kind table doesn't cover
  • "Use the title 'X'" with a specific title string — the HW page mandates a verbatim title
  • "one sentence per number" / "no more than X words" — hard numeric content constraints that Stage 6's format gate doesn't check
  • Past 3 same-course HW Scans had grader comments mentioning issues this HW page's body also touches (e.g. "be specific", "avoid generalization", "this is the wrong assignment")
  • A new section / question type appears that prior weeks didn't have

When triggered, do NOT improvise. Spawn 2-3 agents IN PARALLEL — single message with multiple Agent tool calls (same idiom as canvas-inside §7c):

  • Agent A — spec-verifier (subagent_type=general-purpose):

    Re-read the HW page body from <work>/hw_page.txt from scratch. List literal requirements: input source (Yuja video / Canvas Files PDF / textbook chapter), deliverable shape (annotated PDF / new write-up / scan upload), hard numeric constraints (grep "no more than" / "at most" / "exactly" / "one ... per"), required title verbatim if any, required sections, forbidden items, 1-3 ambiguities. Output under 400 words, plain markdown bullets.

  • Agent B — quality-inferrer (subagent_type=general-purpose):

    Read this HW page body AND past N (=5) writing-course HW Scan grader comments (pull via cv.get('/courses/<cid>/assignments/<aid>/submissions/self?include[]=submission_comments&include[]=rubric_assessment') for the 5 most recent same-course assignments under runs/). Infer what "doing this well" requires beyond literal asks: quality criteria (specificity, cliché-avoidance, depth, accurate causal language), common failure modes with verbatim grader quotes from the Wk6 incident class ("too obvious / weak / vague" / "be specific + add a noun" / "there is no way to prove overgeneralization" / "one sentence per number"), recommended mechanically-checkable gates. Output under 400 words.

  • (Optional) Agent C — template-fit checker (subagent_type=general-purpose):

    Given the four kinds in the §3.5 table (reading_annotation / video_exercises / in_class_skip / response_paper) and this HW page body, list places where none of the 4 templates cover the assignment. Recommend whether to: (a) treat as one of the 4 with adjustments, or (b) treat as a wholly new shape needing custom flow. Output under 300 words.

Save the reports to <work>/research_findings.md. Use the findings to:

  • Pick the closest-fit kind from the §3.5 table (or proceed with a custom flow noted in kind.txt: ad_hoc__<short_descriptor>)
  • Augment the Stage 6 verification gate set with content-quality gates derived from Agent B's grader-history analysis (e.g. "exactly 5 items, each ≤1 sentence", "no pronoun without noun", "no overgeneralization claims")
  • Customize Stage 4 (annotate_pdf) or Stage 5 (fill_answer_blanks) for THIS HW's specific content shape

Then continue to Stage 2 (or whichever stage is appropriate for the chosen kind).

Stage 1.5b — video_transcript_library_path overlay hook (added 2026-05-21)

If the HW page body references an instructor-recorded video (YuJa, Panopto, Echo360, Zoom recording, etc.) AND the deliverable is a free-form reflection (N sentences / takeaways / insights / "what you learned"), the agent must NOT improvise content from general topic knowledge. Instead:

  1. Check the overlay for a video_transcript_library_path declaration (whatever path the school's overlay declares for its video platform — YuJa, Panopto, Echo360, Zoom cloud recording, etc.). The overlay should also expose a mapping from HW-page video title to transcript filename.
  2. If the library exists and the matching transcript is available: load it. Use the transcript's distinctive moments (named anecdotes, instructor-coined terms, concrete contrast pairs, memorable analogies, specific warned-against examples) as the source for ≥2 of the N takeaways. This step runs BEFORE Stage 2-alt's web-search-first rule fires.
  3. If the library exists but the matching transcript is missing (catalog drift): attempt refresh via the overlay's documented procedure. If refresh fails (cookie expired, video missing from channel, network), soft-stop and ask the student whether to (a) supply transcript text manually, (b) skip this week. Do not fall back to general-knowledge improvisation — every instructor video has details only that instructor uses, and a generic-knowledge reflection is recognizable to the grader at a glance.
  4. If the overlay does NOT declare a video_transcript_library_path at all, fall through to §1's YuJa-style soft-stop — offer the student to supply the link / transcript, otherwise skip that HW gracefully.

The 2026-05-21 Thu Wk8 HW Scan incident (overlay had a 9-file cached video transcript library but neither this SKILL.md nor the overlay's §0 referenced it; the skill improvised 5 generic EAP takeaways and audit flagged item 2 as "too obvious / vague") was the failure that motivated this hook. The fix lives jointly here (generic skill) and in the overlay's §0 (school-specific paths).

Stage 2 — locate_reading (reading_annotation flow)

For reading_annotation only. Map the HW page's reference ("Reading 3", "this week's reading", etc.) to a Canvas file_id through the overlay's reading_files table. Download to <work>/attachments/<filename>.pdf.

If the overlay's mapping doesn't cover the referenced reading (e.g. instructor added a Reading 7 mid-quarter): ask the user once for the file_id or filename, append to the overlay's reading_files, continue.

Stage 3 — extract_text_and_blanks

Open the reading PDF with PyMuPDF:

python
import fitz
doc = fitz.open(reading_pdf)
full_text = "\n\n".join(p.get_text() for p in doc)

Identify three structures:

  1. Pre-reading questions (usually on page 0 — "Before You Read").
  2. Article body with numbered paragraphs (regex (?m)^\s*(\d{1,2})\s*$ against full text finds them).
  3. Post-reading questions (usually on the last page — "Comprehension and Analysis" / "Reading and Analysis").

Each question is followed by underscore-glyph runs forming answer blanks. Group underscores by y-coordinate — never search_for("____...") with a fixed-length string, that silently returns a partial rect when the real line is wider:

python
from collections import defaultdict

def find_answer_blanks(page):
    """Return list of (y_top, x_min, x_max) per blank line on the page.

    Grouping every underscore glyph by its y-coordinate yields the FULL line
    extent. A fixed-length search_for trick silently truncates to ~322pt when
    the real line is 454-466pt wide.
    """
    by_y = defaultdict(list)
    for r in page.search_for("_"):
        by_y[round(r.y0)].append(r)
    return sorted(
        (y, min(r.x0 for r in rs), max(r.x1 for r in rs))
        for y, rs in by_y.items()
    )
Stage 4 — annotate_pdf

Clone the original PDF in memory and add annotations in place on the original pages. Do NOT append new pages — the deliverable page count must equal the original. Two annotation categories driven by the overlay's color_rubric:

Vocab highlights + definitions (overlay's color_rubric.vocab family):

  • Pick a small number of words per reading (default 5, overlay can override). Prefer uncommon-but-not-rare vocabulary; avoid technical jargon.
  • Highlight the single word (not surrounding phrase) with the vocab highlight color.
  • Write a LDOCE-style definition in the page margin, aligned vertically with the word. Margin x ≈ 5 (left) or 546 (right) for a standard US Letter; alternate sides if two vocab words on the same y-band.
  • Font color of the definition matches the highlight color family (instructor's stated rule: "if you highlight vocab in green, make the definition font green"). Headword line bold (fontname="hebo"), body plain (fontname="helv"), ~6.5pt, 4-5 lines, ~18 chars each.

Content margin notes (overlay's color_rubric.content family):

  • One margin note per numbered paragraph. If the article has 17 paragraphs, the draft has 17 notes. Skipping paragraphs is a verification failure.
  • Each note: ≤110 chars on one line, summarizing the paragraph's main idea.
  • Place in the gap before the next paragraph's number, at x ≈ 76, baseline = next_paragraph_y - 6, 7.5pt Helvetica, in the content color family.
  • For the highlight itself: pick 1-2 phrases per paragraph as the note's anchor, highlight with the content highlight color.
  • NEVER use page.add_text_annot for the note (renders as a clickable yellow sticky icon at the page edge — that's the wrong format).

Color family rules (enforced by the verify stage):

  • Vocab highlight color and vocab definition text color must be the same family (HSV hue delta ≤30°).
  • Content highlight color and content margin note text color must be the same family.
  • Vocab family and content family must be different families so the reader can distinguish.
  • No yellow highlights — renders too faint on mobile Canvas to distinguish from white.

Overlap-avoidance rule: pick vocab words FIRST. Before adding any content highlight, check its text does not contain any vocab word's exact span; if there's a conflict, drop the content phrase and pick a different one from the same paragraph. Otherwise the later-drawn highlight covers the earlier one and the reader only sees one color.

Show full SKILL.md (1,450 more words)Show less
Stage 5 — fill_answer_blanks

For each (y, x0, x1) blank found in Stage 3, compute line_width = x1 - x0, generate an answer in the overlay's target voice_register, then measure before writing:

python
text_width = fitz.get_text_length(answer, fontname="helv", fontsize=10)
# Must be >= 0.90 * line_width AND <= line_width

If the generated answer is too short, pad with a concrete specific detail in the same voice register, not with filler words. If too long, regenerate with a tighter word budget. Insert with the overlay's answer_text_color (typically blue for "typed answer" contrast against black body text):

python
page.insert_text((x0 + 1, y - 1), answer,
                 fontsize=10, color=ANSWER_TEXT_COLOR, fontname="helv")

Voice register: free-form text from the overlay (e.g. "B1-B2 international student English with Chinese L1 hint"). The skill uses this verbatim as system-prompt context when generating each answer. The voice register reflects the student's identity (typically a non-native learner at B1-B2 level for this course type) — the output should read like the student wrote it, not like a polished editor or an automated tool. The skill targets identity fidelity, not third-party detection avoidance. Voice rules typical for non-native learner voices:

  • Short sentences (10-18 words)
  • First-person specific examples
  • 1-2 small grammar slips per page (subject-verb disagreement, missing article, wrong preposition, tense slip) — overlay can set the slip-density knob
  • Avoid native-speaker discourse markers ("Furthermore", "Moreover", "It is important to note")
  • No em dashes, no perfect parallel structure, no rhetorical questions
  • Never mention AI / ChatGPT / Claude / "as a student"
Stage 6 — verify (6-check gate)

Before declaring draft_ready, run all six checks. If any fail, fix the draft (max 3 retry rounds total across all stages); if still failing after 3 rounds, return status: "error" with the failure list.

CheckMeasurementPass
line_filleach answer span width ÷ its blank's underscore-line widthevery line ≥85%, avg ≥92%
margin_note_densitycount of content margin notes ÷ numbered paragraph count in originalratio ≥1.0 (one note per paragraph)
color_family_consistencyvocab highlight HSV vs vocab definition HSV; content highlight vs content noteboth pairs within ΔH ≤30°
page_count_unchangeddraft doc.page_count vs original doc.page_countequal — no appended pages
no_vocab_content_overlapvocab-highlight rect ∩ content-highlight rectzero intersections
no_sticky_iconscount of PyMuPDF Text (type 0) annotationszero

Write the full report to <work>/verification.log whether it passes or fails. The submit hook reads this log; without all-pass it refuses to upload.

Stage 6.5 — post-delivery self-audit (MANDATORY, never skip)

Runs AFTER Stage 6's 6-check format/structure gate and BEFORE writing result.json status=draft_ready/submitted. The 6-check gate is purely structural (colors, line fill, no overlap); this stage adds the content-vs-rubric semantic check that Wk6 Tue/Thu lost points on.

Spawn 1 audit agent (subagent_type=general-purpose), inputs:

  • The HW page body <work>/hw_page.txt (verbatim spec from §3.5 Stage 1)
  • The chosen kind.txt value (e.g. reading_annotation, reflection_bullets, etc.)
  • <work>/research_findings.md if Stage 1.5 ran
  • Every file in <work>/draft/ (the annotated PDF or the new write-up)
  • Past N (=3) writing-course HW Scan grader comments (pulled via cv.get('/courses/<cid>/assignments/<aid>/submissions/self?include[]=submission_comments&include[]=rubric_assessment') for the 3 most recent)

Agent prompt:

Compare the HW page body + research findings + past grader comments against the produced deliverable. Return a JSON array of gaps. For each gap:

json
{
  "severity": "HIGH" | "MED" | "LOW",
  "kind": "spec-violation" | "historical-risk" | "ambiguity-unresolved" | "format-mismatch" | "voice-register-drift" | "wrong-reading-file",
  "gap": "<one-line description>",
  "spec_anchor": "<verbatim quote from HW page or grader comment showing the requirement>",
  "deliverable_anchor": "<verbatim quote from deliverable showing the violation, or 'MISSING' if a requirement is unaddressed>",
  "fix_suggestion": "<one-line concrete fix referencing the deliverable file + page/section>"
}

Honesty rules:

  • Quote VERBATIM from both spec and deliverable — do not paraphrase
  • If no gaps, return [] exactly
  • For each severity: "HIGH", the issue must be one a grader would dock points for (not stylistic preference)

Writing-course-specific checks beyond the generic spec-vs-deliverable diff:

  • Voice register: count lowercase i / intentional grammar slips / sentence length distribution. Compare to the overlay's voice_register spec. Flag drift (too AI-clean, OR too sloppy beyond B1-B2).
  • Correct reading file: if the HW page says "Reading N" but the deliverable annotates a different PDF, this is the 2026-04-13 incident class — HIGH severity, kind=wrong-reading-file.
  • Per-item constraints from research_findings (e.g. "one sentence per number" — Wk6 Thu) — count and flag.
  • Cliché check — for "Five Takeaways" / "Five Insights" deliverables, flag any item that reads like a textbook platitude a grader would mark "too obvious / vague".

Save the response to <work>/audit/round_1.json (atomic write).

Read returned JSON. If ANY severity == "HIGH" gap:

  1. Apply each fix_suggestion to the deliverable under <work>/draft/.
  2. Re-run Stage 6 6-check gate (in case fix broke format) AND re-run Stage 6.5 audit (round_2.json).
  3. Loop max 3 rounds total.

After 3 rounds:

  • 0 HIGH gaps → proceed to submit (or write status=draft_ready for manual upload)
  • HIGH gaps remain → write result.json status: error, notes: "Stage 6.5 self-audit failed after 3 revision rounds; see <work>/audit/round_*.json"

Wk6 catch verification: if Tue Wk6 Q2 had said "thesis shouldn't be 'in this paper I will discuss'", the cliché check would flag HIGH ("too obvious — match grader comment pattern: 'too obvious? weak? --> vague'"). Fix would replace with a specific video takeaway. Same for Thu Wk6 multi-sentence violation: the "one sentence per number" gate would flag 4 of 5 items, fixes would compress them.


§3.5 — Stage-by-stage mode (first-run calibration only)

When invoked with a context line containing STAGE-BY-STAGE MODE AND the control file <work>/.first_run_stage_by_stage exists, run only the single stage named in the directive instead of the full pipeline. Set by canvas-bootstrap §8 during first-run calibration so the student reviews each stage before the next runs.

Behavior:

  1. Read <work>/.first_run_stage_by_stage to confirm. If absent, run full pipeline as usual.
  2. Parse the directive for the stage name (classify, locate-reading, extract-text-and-blanks, annotate-pdf, fill-answer-blanks, verify, self-audit).
  3. Run only that stage's substeps from §3 above. Prior stages' artifacts must already be in <work>/.
  4. Write a 1-3 sentence summary to <work>/stages/{stage_name}.done and STOP.

Daily dispatch via canvas-execute does not set the marker; runs full-pipeline as usual.

§3.6 — Stage-by-stage time bands

StageBandOne-line description
1 classifyshortDecide reading_annotation / video_exercises / in_class_skip / response_paper based on HW page body
1.5 research-before-improvisemedium(Conditional) deeper module/wiki investigation when HW page doesn't fit the 4-kind table
2 locate-readingshortFind the source PDF in Files/Readings/ via overlay's reading_files mapping
3 extract-text-and-blanksshortPyMuPDF underscore-group find for typed answer blanks
4 annotate-pdfmediumColor-coded highlights + left-margin notes on the source PDF
5 fill-answer-blanksmediumLLM generation of typed answers at ≥90% line width in target voice
6 verifyshort6-check gate (line fill, note density, color family, page count, no overlap, no sticky icons)
6.5 self-auditmediumMandatory final pass; catches voice / cliché / instructor-rubric mismatches

Band: short ~1 min, medium ~3-5 min, long ~10+ min.


§4 — Personal course design schema

The overlay (_private/canvas-reading-annotation-app.md) is one flat markdown file containing one ## Course {id} block per course routing to this skill. Each course block holds one or more #### {kind} sub-blocks per recurring assignment kind. Recognized fields per sub-block:

FieldRequiredExample
naming_regexyes^(Tue|Thu) Wk\d+ HW Scan$
homework_module_idyes67890
homework_page_patternyesWk(?P<week>\d+) (regex over module item titles)
readings_folder_pathyesFiles/Readings/
reading_filesyes{ "Reading 1": <file_id>, "Reading 2": <file_id>, ... }
color_rubric.vocabyes{ highlight: "(0.55, 0.95, 0.55)", text: "(0.0, 0.45, 0.0)" }
color_rubric.contentyes{ highlight: "(1.0, 0.72, 0.82)", text: "(0.75, 0.10, 0.45)" }
answer_text_coloroptional, default blue(0.0, 0.15, 0.80)
voice_registeryes"B1-B2 international student English with Chinese L1"
vocab_count_per_readingoptional, default 55
instructor_rubric_verbatimyesfull quoted rubric from the instructor — used as a final sanity-check prompt
video_to_worksheet_mapoptional{ "Academic Verbs": "https://www.eapfoundation.com/.../questions.php" }
auto_submit_scopeoptional, default ask-each-scan"weekly HW Scan: confirmed"

Course-level fields (one set per ## Course block, applied to all kinds in that course):

FieldRequiredExample
course_idyes12345
course_nameyes"Writing Course B — Spring 2026"
instructoroptional"Dr. Example"
lab_course_idoptional12346 (if Canvas separates lecture vs. lab into two course IDs but the same skill handles both)

§5 — Worked demo overlay

A complete overlay for a fictional course. Fork users copy-paste-modify this rather than writing from scratch.

markdown
# canvas-reading-annotation — Personal Course Design

This file holds per-course overlays for the canvas-reading-annotation skill.

## Course 99999 — Writing Course B (Spring 2026)

- course_name: Writing Course B
- instructor: Dr. Example
- lab_course_id: 99998

### Tue Wk N HW Scan

- naming_regex: `^Tue Wk\d+ HW Scan$`
- homework_module_id: 67890
- homework_page_pattern: `Wk(?P<week>\d+)`
- readings_folder_path: Files/Readings/
- reading_files:
    Reading 1: 11111111
    Reading 2: 11111112
    Reading 3: 11111113
    Reading 4: 11111114
    Reading 5: 11111115
    Reading 6: 11111116
- color_rubric:
    vocab:
      highlight: (0.55, 0.95, 0.55)   # light green
      text:      (0.00, 0.45, 0.00)   # dark green
    content:
      highlight: (1.00, 0.72, 0.82)   # light pink
      text:      (0.75, 0.10, 0.45)   # dark pink / magenta
- answer_text_color: (0.00, 0.15, 0.80)   # blue
- voice_register: |
    B1-B2 international student English with Chinese L1 hint.
    Short sentences (10-18 words). First-person concrete examples.
    Allow 1-2 small grammar slips per page (subject-verb / article /
    preposition / tense). No native discourse markers, no em dashes,
    no perfect parallel structure.
- vocab_count_per_reading: 5
- instructor_rubric_verbatim: |
    Read and annotate Reading N, with highlighting & margin notes in
    at least two colors, one for vocab (5+ English dictionary
    definitions) and another color for content. Color-code: if you
    highlight vocab in green, make the definition font green. Margin
    notes for content are expected for each paragraph. Answer the
    "Before you Read" and "Comprehension and Analysis" questions,
    with both lines completely filled for each answer. Use full
    sentences.
- video_to_worksheet_map:
    Academic Verbs: https://www.eapfoundation.com/download/worksheets/reporting/questions.php
- auto_submit_scope: ask-each-scan

### Thu Wk N Video Worksheet

- naming_regex: `^Thu Wk\d+ Video Worksheet$`
- homework_module_id: 67890
- homework_page_pattern: `Wk(?P<week>\d+)`
- (no reading_files — uses video_to_worksheet_map from sibling kind)
- color_rubric: (inherits from Tue Wk N HW Scan)
- answer_text_color: (inherits)
- voice_register: (inherits)
- auto_submit_scope: ask-each-scan

Multiple courses routing to this skill all live in the same _private/canvas-reading-annotation-app.md file, each as its own ## Course block. Bootstrap appends new course blocks rather than rewriting the whole file.


What you MUST NOT do

  • Do NOT append pages to the original reading PDF. Output page count must equal input.
  • Do NOT default to reading_annotation for a HW page that signals video_exercises or in_class_skip. Classify first.
  • Do NOT invent worksheet content for video_exercises HWs. Search the web for the public source first; return error if not found.
  • Do NOT use yellow highlights. Renders too faint on mobile Canvas to distinguish from white background.
  • Do NOT mix vocab and content highlights on the same span. Apply the overlap-avoidance rule from Stage 4.
  • Do NOT use page.add_text_annot for margin notes — renders as a clickable sticky icon. Use page.insert_text with the content color.
  • Do NOT mention AI / ChatGPT / Claude / "as a student" / "as an AI" anywhere in the produced answers. The overlay's voice_register is the only voice the generator follows.
  • Do NOT submit to Canvas without explicit auto_submit_scope authorization in the overlay AND all six verification checks passing. Default behavior is draft_ready → human review → human upload.

© X-isdoingreat, AGPL-3.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .claude/skills/canvas-reading-annotation of X-isdoingreat/canvas-pilot.

Open the folder on GitHubat commit 6b79d5b

Compare with similar skills

Canvas Reading Annotation next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Canvas Reading Annotation compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Canvas Reading Annotation this skillX-isdoingreat/canvas-pilot125—~8.4kAutomated safety check: NotesAGPL-3.0
Ccar F Examprep Coachsarveshtalele/claude-architect-exam-guide175—~5.8kAutomated safety check: PassNone
Codebase to Coursezarazhangrui/codebase-to-course5.7k—~4.4kAutomated safety check: PassNone
Claude Code Self-Assessment Advisorlhfer/claude-howto-zh-cn2.3k—~1.7kAutomated safety check: PassMIT
Document Generationbionic-gpt/bionic-gpt2.4k—~1.1kAutomated safety check: PassApache-2.0
Lesson Generatordair-ai/dair-academy-plugins6142 repos~1.1kAutomated safety check: PassMIT

Similar skills

  • Ccar F Examprep Coach

    sarveshtalele/claude-architect-exam-guide

    A personalized study-coach skill for the Claude Certified Architect – Foundations (CCAR-F) certification.

    175 GitHub stars~5.8k tokensUpdated 28 days ago
    EducationAuto-check passed
  • Codebase to Course

    zarazhangrui/codebase-to-course

    Turns a codebase into an interactive single-page HTML course for non-technical learners, with scroll modules, animated diagrams, quizzes and plain-English code translations.

    5.7k GitHub stars~4.4k tokensUpdated 6 mo ago
    EducationAuto-check passed
  • Claude Code Self-Assessment Advisor

    lhfer/claude-howto-zh-cn

    Quizzes you on Claude Code in a quick or deep mode, scores your level across 10 topics and recommends what to learn next, in Chinese.

    2.3k GitHub stars~1.7k tokensUpdated 2 mo ago
    EducationAuto-check passed
  • Document Generation

    bionic-gpt/bionic-gpt

    Create polished printable documents and PDFs, including forms, checklists, reports, briefs, comparisons, worksheets, task lists, and other operational documents.

    2.4k GitHub stars~1.1k tokensUpdated 4 days ago
    Documents & OfficeAuto-check passed
  • Lesson Generator

    dair-ai/dair-academy-plugins

    Builds a self-contained multi-lesson course page with lesson navigation, objectives, flashcards, quizzes and source links, as plain HTML, CSS and JavaScript.

    614 GitHub starsUsed in 2 repos~1.1k tokens
    EducationAuto-check passed
  • Content Evaluation Framework

    aiskillstore/marketplace

    This skill should be used when evaluating the quality of book chapters, lessons, or educational content.

    433 GitHub stars~3k tokensUpdated yesterday
    EducationAuto-check passed

More from X-isdoingreat/canvas-pilot

All 32 skills in this repo
  • Daily Work Tweet

    X-isdoingreat/canvas-pilot

    A skill your agent uses when verified work from today or another day should become an X/Twitter post, build-in-public update, ship log, or bilingual draft.

    125 GitHub stars~1.6k tokensUpdated 2 mo ago
    Auto-check passed
  • Canvas Awkward Syntax

    X-isdoingreat/canvas-pilot

    A skill your agent uses when a short local academic draft needs role-aware syntax diversification while preserving meaning, locks, source grounding, rubric-critical openings, and document structure.

    125 GitHub stars~2k tokensUpdated 2 mo ago
    Auto-check passed
  • Canvas Cron

    X-isdoingreat/canvas-pilot

    A skill your agent uses when managing Canvas Pilot schedules: install, inspect, pause, change, delete, or safely test scheduled scans and runs.

    125 GitHub stars~2.1k tokensUpdated 2 mo ago
    Auto-check: notes
  • Canvas Essay

    X-isdoingreat/canvas-pilot

    A skill your agent uses for an approved long academic-writing assignment routed by canvas-execute after the deterministic writing router selects essay.

    125 GitHub stars~2.2k tokensUpdated 2 mo ago
    Auto-check passed
  • Canvas Generic

    X-isdoingreat/canvas-pilot

    A skill your agent uses for an approved Canvas assignment that no specialized course skill can handle.

    125 GitHub stars~2.4k tokensUpdated 2 mo ago
    Auto-check passed
  • Canvas Humanizer

    X-isdoingreat/canvas-pilot

    A skill your agent uses when a local academic draft needs a meaning-preserving humanizing pass with less uniform syntax while retaining rubric, source, lock, voice, and length constraints.

    125 GitHub stars~1.8k tokensUpdated 2 mo ago
    Auto-check passed

Questions about Canvas Reading Annotation

What does Canvas Reading Annotation do?

Generic reading-annotation handler for academic-writing courses — annotates reading PDFs with color-coded highlights + margin notes + filled answer blanks per the instructor's rubric. Canvas Reading Annotation is an agent skill from X-isdoingreat/canvas-pilot. Generic reading-annotation handler for academic-writing courses — annotates reading PDFs with color-coded highlights + margin notes + filled answer blanks per the instructor's rubric.

When should I use Canvas Reading Annotation?

Canvas Reading Annotation fits situations like: tasks that involve Educational content; tasks that involve Quizzes and assessments; tasks that involve PDF.

How do I install Canvas Reading Annotation in Claude Code?

Run `npx skills add X-isdoingreat/canvas-pilot --skill canvas-reading-annotation -a claude-code`. Or copy the skill folder (.claude/skills/canvas-reading-annotation in X-isdoingreat/canvas-pilot) into .claude/skills/canvas-reading-annotation in your project. Claude Code loads it when a task matches its description.

How do I install Canvas Reading Annotation in Codex?

Run `npx skills add X-isdoingreat/canvas-pilot --skill canvas-reading-annotation -a codex`. Or copy the skill folder (.claude/skills/canvas-reading-annotation in X-isdoingreat/canvas-pilot) into .agents/skills/canvas-reading-annotation in your project. Codex loads it when a task matches its description.

Can I use Canvas Reading Annotation in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add X-isdoingreat/canvas-pilot --skill canvas-reading-annotation -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/canvas-reading-annotation, .gemini/skills/canvas-reading-annotation, .github/skills/canvas-reading-annotation and .opencode/skills/canvas-reading-annotation in your project.

What does Canvas Reading Annotation need to run?

SKILL.md names no scripts, command-line tools or credentials: Canvas Reading Annotation is instructions for the agent only. Its frontmatter pre-approves these tools: Bash, Read, Write, Edit, Glob, Grep, WebFetch, Skill.

Does Canvas Reading Annotation access the network?

SKILL.md names 1 domain. In commands or code: eapfoundation.com; the agent is likely to contact it when it follows the instructions. This is read from the text; nothing was executed.

Is Canvas Reading Annotation safe to install?

Our automated static check of SKILL.md found notes only (pre-approves every shell command (allowed-tools: bash)), nothing it rates as a warning. It is not a guarantee. Review the folder before installing.

What licence does Canvas Reading Annotation use?

Canvas Reading Annotation is published under the AGPL-3.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Canvas Reading Annotation use?

About 8.4k tokens (SKILL.md is roughly 34k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Canvas Reading Annotation?

Skills that share tags, products or a category with Canvas Reading Annotation: Ccar F Examprep Coach (sarveshtalele/claude-architect-exam-guide, 175 stars), Codebase to Course (zarazhangrui/codebase-to-course, 5.7k stars), Claude Code Self-Assessment Advisor (lhfer/claude-howto-zh-cn, 2.3k stars) and Document Generation (bionic-gpt/bionic-gpt, 2.4k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Canvas Reading Annotation?

X-isdoingreat (a GitHub user) maintains it in X-isdoingreat/canvas-pilot, which has 125 GitHub stars. The repository holds 32 skills in this directory. The repository was last updated on July 31, 2026.

Source: X-isdoingreat/canvas-pilot on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.