# Classroom Memory — End-to-End Review (2026-04-25)

Working notes that produced ADR-033. This doc captures the diagnosis; the ADR captures the commitment.

## Where classroom memory works today

After this session's work, four things hold up that any redesign must preserve:

- **Append-only event log with deterministic IDs and tombstones.** `ClassroomInteractionStore` is already DPM-shaped: hash-based `interaction_id`, append-only JSONL, tombstone-based retraction with audit trail intact on disk.
- **Per-student transparency view.** `axi classroom me --memory` makes the coordinator's memory legible; `--forget` makes it controllable. Cross-student forget is rejected at the store and at the HTTP layer (404).
- **Cohort-scoped reads.** Every memory endpoint enforces `student_id` scope; no cross-student leakage observed in smoke or test coverage.
- **Verifiable provenance on materials.** Ed25519-signed manifest, content-addressed file storage, sync-on-join with verification.

That's a working DPM-shaped substrate at the coordinator. The question is what's missing on top.

## The seams that hurt the experience

Ranked by how often a real user would feel them.

### S1 — The brief is the wrong abstraction

Today briefs are *the* output: generated, frozen, aging. They should be *projections* — pure functions over `(event log, graph, task)` computed on demand. "Brief for tomorrow's class" and "brief for end-of-semester" are different projections of the same log; we currently re-implement instead of re-parameterizing.

This is the DPM signature move and we are not doing it. Fixing this generalizes immediately to `summary_for_student`, hot topics, instructor brief, study plans, peer signals — every derived view.

### S2 — Student-side retrieval is FTS-only

`ClassroomLocalIndex` is SQLite + FTS5. Embeddings live elsewhere in the codebase but aren't wired in. "What's similar to my confusion about criticality?" needs the literal word; concept-adjacent retrieval doesn't work.

Cheapest fix is a localized patch (vector embeddings into the existing SQLite — sqlite-vec is already in the box). The bigger payoff is when this combines with the concept graph (S3): vector for semantic adjacency + graph traversal for conceptual adjacency.

### S3 — There is no concept graph

A student asking "How is criticality related to thermal margin?" can't be answered from the index. Two materials about neutron absorption are unrelated as far as the system is concerned. Cohorts have no shared concept space; cross-cohort federation cannot project concepts because no concepts exist.

This is the Cognee NodeSet shape. Adopting Cognee behind a sovereign protocol (per `project_cognee_assessment`) is the recommended path; the protocol stays Axiom-owned so the KG layer is swappable post-Prague.

### S4 — Hot topics is a stopword-filtered token histogram

Counting token occurrences across question text gives `["control", "rod", "absorbs"]` instead of `[concept: reactivity]`. CHALKE's "what's the cohort confused about?" signal is wired to the wrong primitive — token statistics, not concept statistics. Fixing S3 fixes this for free.

### S5 — No episodic memory in the LLM context

When a student asks a question, the LLM context contains the retrieved passages and the system prompt — nothing about what the student asked yesterday. The interaction log exists; it just isn't projected into the prompt. The model has amnesia by default.

This is the cheapest user-visible win available. ~1 day of work: project the last N interactions for this student into the system prompt before the LLM call. Validates the projection pattern without touching the graph or federation.

### S6 — No task awareness

A student studying for a quiz wants different memory than a student writing a paper. We have no notion of "task" so every retrieval looks identical. DPM's task-conditioned projection assumes a `TaskSpec`; we don't have one.

### S7 — Federation policy is unwritten

The user's three concerns:
- (a) "wrong things in my memory" — addressed locally this session by `--memory` and `--forget`.
- (b) "federation oversharing" — entirely TODO.
- (c) "federation under-sharing" — entirely TODO.

Cohorts and signed manifests exist. There is no declared trust profile saying "this cohort shares aggregate concept stats with peers, but no question text and no student IDs." Per-fragment sharing tags do not exist. Materials are the dominant federation risk, not interactions; the design has to address materials first.

## What "make it shine" requires

Reviewing S1–S7, the failures partition into structural gaps that no amount of polish closes:

- **Missing layer above the event log.** Briefs, summaries, hot topics, study plans, retrieval contexts — all want to be projections. Today they're each a one-off.
- **Missing concept layer.** Vector + graph RAG, "how is X related to Y", cohort federation that doesn't leak text — all want concepts. Today they want them silently.
- **Missing federation membrane.** (b)/(c) want a primitive. Today they want a primitive.

A four-layer architecture (event log → concept graph → projections → federation policy) addresses all three structurally. Implemented correctly, the layers also generalize beyond classroom to research loops, chat agent working memory, and downstream domain-specific extensions — which matters because re-doing the analysis for each extension would multiply the cost.

ADR-033 commits to this architecture and stages the migration so Prague is not at risk.

## Materials specifically

Materials do six jobs at once: corpus, semantic substrate, MIRIX `resource` memory, versioned-over-time signal, provenance carrier, dominant federation surface. They don't reduce neatly to "events" — they need both an event surface (`MaterialAdded`, `MaterialRemoved`, `ManifestSigned`) and a blob surface (content-addressed bytes).

The DPM insight applied to materials: **the manifest is a projection, not stored state.** Computing manifest at request time gives time travel, peer-specific projection (federation gateway re-projects through trust profile before signing), and no stored/in-memory drift.

The federation insight: per-fragment sharing tag is the right grain. `cohort-private` for student-submitted, `cohort-shared` for instructor-uploaded, `public-curated` for explicit promotion. Trust profile composes per-cohort defaults with per-fragment overrides.

## Build order — the cheapest first move

If we ship one thing tomorrow:

> Wire episodic memory into the student LLM context via a `RecentActivityProjection`.

That's a one-day change that exercises L1 + L3 without needing the graph, blob refactor, or federation policy. The student gets continuity (LLM stops repeating itself across sessions); the instructor's brief shows real arcs; we validate the projection pattern before refactoring more.

Stages 1–4 in ADR-033 are the rest of the runway, in order. Stage 5 (federation gateway) is post-Prague. Stage 6 (downstream extensions consuming the same primitives) is the proof point that the design generalizes.

## Notes for whoever writes the implementation tickets

- `ClassroomInteractionStore` is already DPM-shaped at the file level — the L1 migration is a *protocol* refactor, not a data-format refactor. JSONL stays as a backend behind the `EventStore` protocol.
- `ClassroomMaterialsStore.add_text` / `add_file` already content-address. The L1 blob extraction is mostly renaming + adding the `MaterialAdded` event.
- `summary_for_student` is the natural first projection to refactor — small, well-tested, currently bespoke. Refactoring it first proves the projection pattern on a small surface.
- Cognee adoption decision is already made (per `project_cognee_assessment`); ADR-033 just locks in the protocol boundary.
- The chat extension is the second-best place to validate the design (Profile C in the ADR) — its working memory is currently ad-hoc and would benefit immediately from the projection layer.

## What the user gets at the end

When all four layers are in place:

- **Student side:** "what haven't I touched in this material?" — projection over student's concept-coverage vs material's. "Show me material as of last week" — manifest-as-projection at past timestamp. "Find me a peer cohort with material on this topic" — cross-cohort concept federation.
- **Instructor side:** "brief for tomorrow's class" vs "brief for end-of-semester" — same log, different projection. "Show me the cohort's confusion clusters" — graph traversal over concept nodes weighted by `unanswered_count`. "Promote this material to peer cohorts" — sharing-tag flip + federation gateway re-projection.
- **CHALKE (and future agents):** task-aware memory access. The agent declares its task; gets the relevant projection; doesn't see anything outside that scope. DPM's stateless property gives the audit trail for free.

That's the experience worth building toward.
