Working system · Approved for use · Public demo · school deployment not established

Classroom Narrative AI

Privacy-first K–12 narrative infrastructure: teachers turn classroom evidence and standards performance into report-card narratives — built on the assumption that data about minors is never ordinary application data.

EXTERNAL VALIDATION
Approved for use in a Mountain View school district

The district is not named here because public attribution has not been granted — withholding it is the same identity-minimization rule the product itself is built on. The approval evidence is held privately; the design rules that came out of the district review are dated 2026-08-30 and are documented verbatim in the product source (lib/privacy/alias.ts). Approval means approved for use. It does not mean piloted, deployed, or in production — those stages have their own rows below, and they are honest.

BUILD · working system REVIEW · completed APPROVAL · approved for use PILOT · not started SCHOOL DEPLOYMENT · not deployed

The problem

Narrative report cards take teachers hours per student, three times a year: gather the evidence, map it to standards, find words that are accurate and kind. Generic AI tools make the writing faster and the privacy worse — they want the one thing a teacher must not hand over: who the student is.

Identity architecture

The roster-to-alias mapping is designed to stay on the teacher’s device. Off-device records use randomized aliases. Free-text observations can still identify a student and require teacher review.

Teacher device · roster→ Student name / ID (local only)→ Local mapping→ Randomized alias · S-K7MPQ2XW→ Application→ Evidence & history→ Narrative→ Teacher review→ Approved export
/** lib/privacy/alias.ts — verbatim from the product source */
 * Every student gets one cryptographically random code (e.g. "S-K7MPQ2XW")
 * that stands in for them everywhere off this device. Design rules, from the
 * Mountain View district review (2026-08-30):
 *
 *  - Random, never derived. The alias must carry zero information about the
 *    student: not a name fragment, not a hash of the SIS ID, not a counter.
 *  - Stable for the school year, so T1 -> T2 -> T3 records connect on the
 *    server without a name or SIS ID.
 *  - The mapping (alias <-> real name) lives ONLY where the roster lives —
 *    on this device (or in memory in No-Save). It is never synced.
 *
 * Honesty note: aliased records are pseudonymous, not "de-identified" — a
 * comment's content can still describe a recognizable student. Nothing in
 * this file or the marketing may claim otherwise. */
const ALPHABET = "23456789BCDFGHJKMNPQRSTVWXZ";  // no vowels, no 0/O/1/I/L

The words are chosen carefully on purpose: pseudonymous, randomized alias, identity minimization. The source forbids claiming “de-identified” because narrative content can still describe a recognizable student. A PII guard (lib/privacy/piiGuard.ts) additionally flags emails, honorific+surname patterns, and name-shaped words before anything is shared — tuned to over-flag rather than miss.

Persistence: three honest modes

NO SAVE · default

Student-bearing data lives in memory for the session only. Nothing touches localStorage, sessionStorage, or IndexedDB; refresh clears it.

DEVICE ONLY

Explicit opt-in for browser persistence. This storage choice does not by itself mean narrative generation runs on-device; review the product’s data flow before entering student information.

SAVED HISTORY

Signed-in school account: server storage scoped by row-level security, using aliases in structured identity fields, with retention and deletion controls.

Standards & evidence

Narratives are composed from structure, not vibes: the California standards tree, per-skill performance levels (growth / developing / proficient / advanced), fluency WPM where relevant, and per-period ratings that connect T1 → T2 → T3 through the stable alias — so growth-over-time can connect records by alias. Narrative text still requires identity review.

Tenant security & audit

Row-level security, forced

Every student and assessment row is keyed to the teacher who created it. RLS is enabled and forced with a separate policy per operation, and blanket role grants are revoked — Postgres is the only door, not app code that could be bypassed. School workspaces add an organization boundary on top.

Append-only audit history

School-workspace actions write to an audit log carrying tenant, actor, action, and entity. The isolation script in the repo proves the properties as executable assertions:

-- db/tenant-isolation.sql — the runnable proof script (excerpt)
-- ==== As USER A (owner tenant): audit immutability + lifecycle ====
update public.audit_log set action = 'tampered'
  where org_id = '...aaaa';
-- asserts: 'audit UPDATE affects 0 rows (immutable)' -> true

API & agents

A real, live, scoped API — not a diagram. The OpenAPI 3.1 contract is served at /api/v1/openapi and matches the deployed routes. Requests without a key get 401; scopes are least-privilege (standards:read, comments:generate, exports:create…), and delegated agent calls carry masked on-behalf-of attribution into the audit trail.

The identity contract holds at the API boundary too — calls use the alias, and the generated text uses it as a placeholder the teacher re-substitutes locally:

$ curl -H "Authorization: Bearer $KEY" -d '{
    "mode":"standards",
    "student":{"alias":"S-K7MPQ2XW","pronoun":"she"},
    "scores":{"rf_phonics":"proficient"},"languages":["en"]}' \
  https://www.papalotledu.com/api/v1/comments

"S-K7MPQ2XW demonstrates strong, dependable skills in Phonics
 and Word Recognition. It is a genuine pleasure to teach
 S-K7MPQ2XW, and I look forward to seeing her continue to grow."

Testing

The product reports 336 checks across 26 files covering unit, end-to-end, and accessibility suites. A current run tied to a source revision is not published here. That includes the new API alias-contract tests: a valid alias generates, a name-shaped or lookalike-character alias is rejected at validation.

What broke, and what the design does about it

The contract said live; production said 404

The marketing page advertised a live OpenAPI document while the production deployment — correctly failing closed with no keys configured — hid it. Caught in this review. Fixed by provisioning scoped API keys, so the contract is now genuinely live and every route still authenticates. The fail-closed behaviour itself was right: a cleared env var must never silently open an endpoint that accepts student rosters.

Over-flagging as a choice

The PII guard matches any name-shaped capitalized word, which flags some false positives. That is deliberate — documented in the source — because the cost of missing a student name is not symmetric with the cost of a spurious warning.

The word “de-identified” is banned

The alias module’s honesty note forbids the product and its marketing from claiming de-identification, because narrative text can describe a recognizable student. Precision beats the stronger word.

Maturity, exactly

Build stageworking_system
External reviewcompleted
Approvalapproved_for_use — “Approved for use in a Mountain View school district”, district review completed 2026-08-30
Pilotnot_started
School deploymentnot established; public Guest demo available

These rows only ever move on explicit evidence. Approval does not imply a pilot; a pilot would not imply production. When those stages actually happen, they get their own dated entries — the history is append-only.

Daniel’s contribution

Product discovery with working teachers, the privacy architecture above, the deterministic narrative engine, the standards catalog, the scoped API and OpenAPI contract, the RLS and audit model, the test suites, and the deployment. Built end to end; the district review shaped the identity rules, and those rules are now enforced in code rather than promised in copy.

See it yourself

Open the working system at papalotledu.com Live OpenAPI 3.1 Book a walkthrough

The demo opens in No Save mode — the same default a district reviewer saw: nothing student-bearing persists unless a teacher explicitly chooses otherwise.