# Design: Course-Conclusion Workflow (FW-4)

**Status:** Draft for review
**Author:** Benjamin Booth (with Claude Code)
**Date:** 2026-04-20
**Parent:** [prd-classroom.md](../prds/prd-classroom.md)
**Mirrors:** [design-instructor-prep-workflow.md](design-instructor-prep-workflow.md) (FW-1)

---

## 1) Goal & non-goals

**Goal.** Deliver a complete post-completion experience: the instructor
archives the classroom, harvests a research-ready dataset, reviews
per-cohort analytics, finalizes grades in Canvas, and proposes an
updated course template for the next offering — all without touching
raw artifact storage.

**Non-goals.** Multi-course longitudinal analysis, cross-institution
federation of harvested findings, automatic grade computation (grading
rubrics stay instructor-authored). Those come later.

---

## 2) Reuse map

| Layer | Status | Module |
|---|---|---|
| Classroom publish/unpublish | Built today (P5) | `classroom/publish.py` |
| Canvas grade-push provider | Built | `classroom/lms/canvas.py` |
| Course lifecycle (draft→review→published→deprecated) | Built | `classroom/course_lifecycle.py` |
| Grade ledger | Built | `classroom/grade_ledger.py` *(verify)* |
| MemoryFragment composition | Built | `memory/composition.py` |
| Classroom tracing | Built (auto-wired via T1a) | `classroom/tracing.py` |

Gap: a dedicated `conclusion.py` module that reads from the above,
produces summaries, and drives Canvas + archival writes.

---

## 3) Phased delivery

| Phase | Ships | Instructor can… |
|---|---|---|
| P1 | `classroom archive` command + ARCHIVED state | Freeze a classroom; downstream writes refuse |
| P2 | `classroom wrap analytics` report | See cohort trajectories + score distributions |
| P3 | `classroom wrap harvest` — research-export bundle | Export dataset for paper writeup |
| P4 | `classroom wrap grades` — Canvas grade finalization | Push final grades to Canvas in bulk |
| P5 | `classroom wrap template` — propose updated manifest | Roll lessons learned into next offering |

P1 ships the lifecycle invariant (ARCHIVED prevents writes), which
all subsequent phases depend on. P2/P3/P4/P5 then compose freely.

---

## 4) Lifecycle states (updated)

```
UNPUBLISHED  →  PUBLISHED  →  ARCHIVED
       ↑            ↓
       └── unpublish ┘  (ARCHIVED is terminal; no transition out)
```

ARCHIVED is deliberately terminal in v0. A classroom that needs to
be re-opened should be cloned (`from-demo`-style) into a new
classroom, preserving the archived cohort's data intact.

---

## 5) Commands

### 5.1 `axi classroom archive <id> [--reason]`

- Checks current state is PUBLISHED
- Refuses writes thereafter (grade push, enrollment change,
  manifest edit, publish) — callers see "classroom archived" error
- Writes an `archive_event` episodic fragment

### 5.2 `axi classroom wrap analytics <id> [--format json|markdown]`

- Roster size, completion rate, per-checkpoint score distribution,
  rails completion rate, total turns, median retrieval-context
  relevance (if traced)
- Pure read — works at any state (PUBLISHED or ARCHIVED)

### 5.3 `axi classroom wrap harvest <id> --out <path>`

- Bundles the classroom's MemoryFragments + grade ledger + manifest
  into an `.axiompack` archive
- Anonymizes principals per the write-policy transform (already in
  composition stack for SHARED tier)
- Writable format: JSONL + manifest YAML for paper-writeup tooling

### 5.4 `axi classroom wrap grades <id> [--dry-run]`

- Computes final grades from checkpoint scores + rubric weights
- Pushes each via the configured LMS provider's `push_grade`
- Dry-run returns the compute + target grades without hitting Canvas

### 5.5 `axi classroom wrap template <id> --out <manifest.yaml>`

- Reads the classroom's harvested signals (what retrievals worked,
  which rails got high consent rates, which prompts led to grounded
  answers vs hallucinations)
- Proposes an updated CourseManifest — new system prompt tweaks,
  rail ordering changes, missing corpus gaps
- Writes a YAML that the instructor can review + load into a new
  course via `prep init --manifest`

---

## 6) Chat tools (FW-4 mirror of FW-1)

Added to `classroom/chat_tools/prep_tools.py` or a sibling
`conclusion_tools.py`:

- `classroom_archive` (WRITE)
- `classroom_wrap_analytics` (READ)
- `classroom_wrap_harvest` (WRITE)
- `classroom_wrap_grades` (WRITE)
- `classroom_wrap_template` (READ)

Same discovery + dispatch flow as FW-1 tools.

---

## 7) Out-of-scope (explicit)

- Multi-cohort longitudinal comparison (P6+ when we have ≥2
  cohorts' worth of data)
- Automatic course-template promotion across instructors (federation
  track, ADR-030 adjacent)
- Rubric evolution (separate PRD)
- Cross-institution benchmarking (Prague outcome vs Austin outcome
  for the same course template)

---

## 8) Open questions

1. **Harvest anonymization strength** — pseudonymize student
   principals (`@alice:demo` → `@student-1:archived`)? Or hash?
   Lean pseudonymize for readability; hash available via flag.
2. **Template update — how much automation?** v0 produces a
   "proposed diff" the instructor reviews; v1 could auto-apply with
   an approval gate (mirrors LearnedSwitch governance pattern).
3. **Grade compute formula** — weight by checkpoint rubric weights?
   Hardcode an average? Instructor-configurable? Lean
   instructor-configurable with a default = rubric-weighted mean.

---

## 9) Immediate next step

Implement **P1** (`classroom archive` + ARCHIVED state) as a single
commit. Under 250 LOC plus tests. Ships standalone invariant
(post-archival writes refuse) and unblocks P2–P5.
_Copyright (c) 2026 The University of Texas at Austin and B-Tree Labs. Apache-2.0 licensed._
