# Axiom Manual Test Walkthrough

Hands-on verification of the classroom extension and the core memory
primitives it rests on. Everything here works **standalone** — no
LMS, no external LLM, no federation peers needed.

## Prerequisites

```bash
cd ~/Projects/UT_Computational_NE/axiom
source ../.venv/bin/activate
```

## A. Automated test suite (baseline)

First confirm the code base is green end-to-end:

```bash
pytest tests/memory/ tests/rag/ tests/agents/ tests/medallion/ \
       tests/classroom_unit/ tests/infra/ tests/artifacts/ -q
```

Expected: **800+ passed, ~10 skipped** (pyarrow-only tests).

## B. CLI flow — offline instructor prep (standalone)

Standalone-first path: no LMS, no prior course, manual authoring.

```bash
export AXIOM_RUNTIME_ROOT=/tmp/axiom-manual-test

# 1. Bootstrap a fresh classroom (auto-generates course + classroom ids + slugs)
python -m axiom.extensions.builtins.classroom.cli \
  prep init --instructor "ben@ut.edu" --title "NE Prague 2026"

# Grab the classroom id from the output (pattern: "Classroom: <slug>  (id: <uuid>)")
CR=<paste-classroom-uuid-from-above>

# 2. Upload a corpus doc + preview retrieval
echo "Fission is the splitting of heavy nuclei into lighter fragments." > /tmp/chap1.txt
python -m axiom.extensions.builtins.classroom.cli \
  prep corpus $CR --upload /tmp/chap1.txt --preview "fission"

# 3. Set system prompt + approve it
python -m axiom.extensions.builtins.classroom.cli \
  prep prompt $CR --set "You are a patient nuclear engineering tutor." \
                  --test "What is fission?"

python -m axiom.extensions.builtins.classroom.cli \
  prep prompt $CR --approve

# 4. Pick RAG policy
python -m axiom.extensions.builtins.classroom.cli \
  prep rag $CR --mode course_only

# 5. Connect LMS (fake for offline)
python -m axiom.extensions.builtins.classroom.cli \
  prep lms $CR --canvas-course 101 --fake --fake-roster 3

# 6. Unified status — should report READY
python -m axiom.extensions.builtins.classroom.cli prep status $CR
```

## C. CLI flow — LMS-assisted init (zero friction)

Same outcome as B, but Canvas-like import pre-fills corpus,
assessments, and roster in one shot:

```bash
export AXIOM_RUNTIME_ROOT=/tmp/axiom-manual-lms

python -m axiom.extensions.builtins.classroom.cli \
  prep init --instructor "ben@ut.edu" --title "NE Prague 2026" --lms-assist-fake

# Status should show course corpus, assessments, and LMS step already green
python -m axiom.extensions.builtins.classroom.cli prep status <classroom-uuid>
```

Remaining manual steps: `prep prompt --set ... --test ... && prep prompt --approve`.
Everything else came from the simulated LMS import.

## D. CLI flow — reuse prior course

```bash
# First classroom was CR above. Spin up a new classroom that reuses its course:
python -m axiom.extensions.builtins.classroom.cli \
  prep init --instructor "ben@ut.edu" --title "NE Prague 2027" --from $CR

# Course-selected step already completes; skip straight to rag + lms + dry-run.
```

## E. MCP server — make Axiom memory visible inside Claude Code

Add to `~/.claude/config.json` (or your Claude Code config):

```json
{
  "mcpServers": {
    "axiom-classroom": {
      "command": "python",
      "args": ["-m", "axiom.extensions.builtins.classroom.mcp_server"]
    }
  }
}
```

Restart Claude Code. You should now have four tools available —
ask Claude: *"List my Axiom classroom sessions"* and it will call
`axiom_classroom_list_sessions` under the hood.

## F. Python-level flows (not yet CLI-wrapped)

These exercise the memory primitives + classroom modules directly.

### F.1 Help ticket

```python
from axiom.extensions.builtins.classroom.help_tickets import (
    create_help_ticket, acknowledge_ticket, resolve_ticket,
)

t = create_help_ticket(
    student_id="s1", classroom_id="cr-1",
    issue="Stuck on critical mass calculations",
    context_turns=[
        {"role": "user", "content": "How do I find critical mass?"},
        {"role": "assistant", "content": "..."},
    ],
)
t = acknowledge_ticket(t, instructor_id="ben@ut.edu")
t = resolve_ticket(t, resolver="ben@ut.edu",
                   resolution="Worked through it 1:1.")
print(t.status, t.resolution)
```

### F.2 Quiz scoring + local grade ledger

```python
from pathlib import Path
from axiom.extensions.builtins.classroom.quiz_scoring import auto_score, override_score
from axiom.extensions.builtins.classroom.grade_push import push_grades

# Objective auto-score
mcq = auto_score(
    {"student_id": "s1", "assessment_id": "pre", "question_id": "Q1",
     "question_type": "mcq", "answer": "B"},
    answer_key={"Q1": "B"},
)

# Free-text manual override after review
ft = auto_score(
    {"student_id": "s1", "assessment_id": "pre", "question_id": "Q2",
     "question_type": "free_text", "answer": "Fission splits nuclei."},
    answer_key={},
)
ft = override_score(ft, final=0.9, reviewer="ben@ut.edu")

# Push to local ledger (no Canvas needed)
result = push_grades(
    scored_responses=[mcq, ft],
    assessment_id="pre",
    classroom_id="cr-1",
    canvas_course_id=None,  # offline
    canvas_assignment_id=None,
    lms=None,
    local_dir=Path("/tmp/axiom-grades"),
)
print(result.local_logged_count, "grades written to", result.ledger_path)
```

### F.3 Memory fragment + bipartite access check

```python
from axiom.memory.fragment import create_fragment
from axiom.memory.access import (
    AccessGraphs, add_user_agent_edge, add_agent_resource_edge, is_visible,
)

frag = create_fragment(
    content={"fact": "Critical mass depends on geometry."},
    cognitive_type="semantic",
    principal_id="ben@ut.edu",
    agents={"axi"},
    resources={"rag-org"},
)

# Empty graphs → fragment invisible
g = AccessGraphs()
print(is_visible(g, "alice@ut.edu", "axi", frag))  # False

# Grant access
g = add_user_agent_edge(g, "alice@ut.edu", "axi")
g = add_agent_resource_edge(g, "axi", "rag-org")
print(is_visible(g, "alice@ut.edu", "axi", frag))  # True

# Revoke → instantly invisible (fragment unchanged)
from axiom.memory.access import remove_user_agent_edge
g = remove_user_agent_edge(g, "alice@ut.edu", "axi")
print(is_visible(g, "alice@ut.edu", "axi", frag))  # False
```

### F.4 Signed provenance

```python
from axiom.vega.identity.keypair import generate_keypair
from axiom.memory.fragment import create_fragment
from axiom.memory.attest import sign_fragment, verify_fragment_signature

kp = generate_keypair()
frag = create_fragment(
    content={"fact": "x"}, cognitive_type="semantic",
    principal_id="ben@ut.edu", agents={"axi"}, resources=set(),
)
signed = sign_fragment(frag, kp)
print(verify_fragment_signature(signed, kp.public_bytes))  # True

# Tamper with content → verification fails
import dataclasses
tampered = dataclasses.replace(signed, content={"fact": "TAMPERED"})
print(verify_fragment_signature(tampered, kp.public_bytes))  # False
```

### F.5 Post-filter breach detection

```python
from axiom.memory.post_filter import check_llm_output
from axiom.memory.fragment import create_fragment

visible = create_fragment(
    content={"fact": "public info"}, cognitive_type="semantic",
    principal_id="u1", agents=set(), resources=set(),
)
# Pretend this is a fragment the current user can't see
secret = create_fragment(
    content={"fact": "the launch code is golf tango seven seven alpha bravo"},
    cognitive_type="semantic",
    principal_id="u2", agents=set(), resources=set(),
)

# LLM emitted a verbatim quote from the secret fragment
llm_output = (
    "The rumor is that the launch code is golf tango seven seven alpha bravo."
)
result = check_llm_output(
    output=llm_output,
    visible_fragments=[visible],
    all_fragments=[visible, secret],
    min_quote_words=5,
)
print(result.is_clean, result.breaches)
```

## G. Reset / cleanup

```bash
rm -rf /tmp/axiom-manual-test /tmp/axiom-manual-lms /tmp/axiom-grades
```

---

## Known gaps (not tested manually yet)

- **Federation A2A transport**: primitives built, wire-up deferred.
- **Live LLM + RAG**: stubs only; real Bonsai/LangFuse wiring pending.
- **Open WebUI provisioning**: infrastructure side, not in this repo.
- **Canvas API**: only `--fake` path. Real `--lms canvas:<token>` is
  scaffolded but not wired to a live Canvas tenant.

See `docs/specs/spec-classroom.md` §2.10a for the remaining
integration surface and `project_memory_architecture_unified.md`
in instructor memory for the federated-memory design follow-ups.
_Copyright (c) 2026 The University of Texas at Austin and B-Tree Labs. Apache-2.0 licensed._
