# Alignment Extraction Manifest Schema

`scripts/alignment_skill.py --phase scrape` writes this manifest to:

`courses/{id}/staging/alignment/extract_manifest.json`

Claude fills the extracted fields, then `--phase report` reads the same file.

## Fields

| Field | Type | Required | Notes |
|---|---:|---:|---|
| `course_id` | string | yes | Canvas course ID. |
| `course_name` | string | yes | Course display name when available, otherwise `Course {id}`. |
| `syllabus_html` | string | yes | Raw Canvas `syllabus_body` HTML. |
| `syllabus_url` | string | yes | Canvas syllabus page URL. |
| `modules` | list | yes | Scraped Canvas modules with `id`, `name`, `position`, `overview_page_url`, and regex `classification` (`mlo_section`, `topics_only`, `prose_only`, or `empty`). |
| `module_overviews_html` | object | yes | Dict keyed by Canvas `module_id`; each value contains the overview page HTML and metadata such as `page_url`, `page_slug`, `module_id`, `module_name`, and `module_position`. |
| `assessments` | list | yes | Scraped assignments plus quiz/discussion module items. |
| `materials` | list | yes | Scraped non-assessment module items such as pages, files, external URLs, and external tools. |
| `declared_clos` | list | optional | CLOs declared in `courses/{id}/course-config.json`, if present. |
| `declared_mlos` | object | optional | MLOs declared in `course-config.json`, keyed by module number, if present. |
| `extracted_clos_regex_pass` | list | yes | Regex pre-pass CLOs from syllabus headers such as `Course Learning Outcomes`, `Learning Outcomes`, `Course Outcomes`, `Course Objectives`, `Student Learning Outcomes`, or `CLO`. May be empty. |
| `extracted_clos_regex_header_used` | string | yes | Header text matched by the regex CLO pass, or `none`. |
| `extracted_mlos_by_module_regex_pass` | object | yes | Regex pre-pass MLOs keyed by Canvas `module_id`. `mlo_section` modules are extracted here. |
| `extracted_clos` | list | yes | Claude AI fallback fills with verbatim CLO objects only when the regex pass misses CLOs. Empty after scrape. |
| `extracted_mlos_by_module` | object | yes | Claude AI fallback fills with extracted MLO objects, keyed by Canvas `module_id`, only for modules classified `topics_only`, `prose_only`, or `empty`. |

## Claude-Filled Extraction Shape

`extracted_clos` is a list of:

```json
{
  "text": "Verbatim objective text from the source HTML.",
  "page_url": "https://canvas.asu.edu/courses/123/assignments/syllabus",
  "confidence": "high"
}
```

`extracted_mlos_by_module` is a dict keyed by Canvas module ID:

```json
{
  "456": [
    {
      "text": "Verbatim module objective text from the overview page.",
      "page_url": "https://canvas.asu.edu/courses/123/pages/m1-overview",
      "confidence": "low"
    }
  ]
}
```

Confidence values:

- `high` when the objective anchor is clean and explicit.
- `low` when interpretation is needed.

Regex pre-pass entries also include:

- `index`: 1-based objective position within the matched list.
- `extraction_method`: `regex`.
- MLO entries additionally include `module_id`.
