Inspiration

A learner gets a hint, submits more work, and the dashboard turns green. Everybody high-fives the rectangle. But what did the learner actually learn?

That question became the whole project.

AI makes explanations and polished answers cheap. Teacher attention is still expensive. Once a model is helping, task completion becomes joint performance: learner plus system. That can be useful. It can also become a very clean way to lie to yourself if the product quietly relabels assisted completion as understanding.

  • I did not want to build another tutor chat with a nicer haircut.
  • I wanted a system that could answer a narrower, harder question:

Given the evidence we actually have, what help is allowed, what thinking still belongs to the learner, and does a teacher need to step in?

That became LURNR.

What it does

LURNR is a synthetic-only AI-assisted algebra intervention system. The hosted demonstration follows one learner through a real evidence sequence:

  • The learner submits an incorrect equivalent equation.
  • They request a bounded, non-answer-giving hint.
  • They produce additional reasoning while that support is present.

Then LURNR removes the instructional support and changes the representation of the problem.

The transfer check fails.

LURNR keeps the useful part of the record. Substantive reasoning was observed under support. Missing concept coverage can be settled by that evidence. But the system refuses to claim independent transfer. It does not call the learner “mastered.” It does not close the episode. It routes RepairRequired to the teacher. That failure is the point of the demo. We did not relabel it as a win because it looked nicer on a submission page.

The teacher dashboard turns the result into one plain reason for attention:

The learner made progress with support. The reasoning did not survive the changed-representation check. Review is needed before the episode can close. Underneath that experience, LURNR separates several things most AI education products blend together:

  • GPT-5.6 may propose a bounded instructional question through strict structured output.
  • A pure Haskell engine decides whether that proposal is legal under the compiled course policy.
  • The TypeScript daemon performs external effects and returns typed receipts.

Before anything can become canonical state, Haskell checks the exact program, learner snapshot, privacy taint, capabilities, timing, effect binding, freshness, and protected cognitive work again.

The same algebra unit supports three genuinely different delegation policies:

  • Granular support removes interface friction while leaving the algebra strategy with the learner.
  • Assisted support may expose a bounded partial step, but operation choice, equality reasoning, and independent validation remain protected.
  • Orchestration treats imported AI work as something the learner must specify, compare, challenge, repair, and defend.

LURNR also makes concurrency part of educational correctness.

In the stale-response demo, proposal A is evaluated for learner snapshot A. Before it returns, new evidence creates snapshot B. A may still be grammatically perfect and pedagogically plausible. It is also late.

The Haskell gate rejects A. Only the separately evaluated decision for current snapshot B reaches committed storage.

Plausible is not the same as current.

How I built it

The trusted core is written in Haskell.

  • It compiles the synthetic course source, evaluates policy, tracks protected obligations, evaluates structured transfer evidence, reproduces decisions before commit, and authorizes claims, expiry, approval, repair, and final episode state.
  • The network and effect layer is TypeScript running on Node.js 24. It supervises the Haskell process, calls the OpenAI Responses API, coordinates deterministic and live proposal paths, records typed receipts, serves the MCP and HTTP boundaries, and manages durable PostgreSQL state.
  • The teacher experience uses Astro and LiteShip. LiteShip is my pre-existing, MIT-licensed open-source UI framework. In LURNR, it provides host-owned component catalogs and validates generated interface trees rather than accepting model HTML.

LURNR itself, including the Haskell engine, evidence model, daemon, protocol, product surfaces, tests, and deployment integration, was built in the hackathon repository. The repo includes a full provenance ledger distinguishing prior work, research influences, the clean-room baseline, and Build Week implementation.

  • The hosted version runs on Railway:
  • The teacher and learner surfaces are public.
  • The Haskell/Node daemon stays on Railway’s private network.
  • PostgreSQL has no public TCP proxy.

The exercised learner-to-teacher state survived an actual daemon restart, including the current teacher projection and review receipt.

No local PostgreSQL install, Docker setup, account, or credentials are needed to see the public demo.

How I used Codex

Codex was involved throughout the build, not dropped in at the end to generate a README and collect a participation trophy.

I used the main Codex session as a principal implementation and integration partner. It inspected the repository, broke the architecture into bounded slices, implemented across Haskell and TypeScript, ran tests, diagnosed CI failures, red-teamed authority boundaries, reviewed privacy and concurrency behavior, built the Railway packaging, and maintained evidence receipts and collaboration history.

I kept the product and authority decisions explicit:

The model could propose, but never authorize. Task completion could not become mastery.

  • A stale result could not commit.
  • Teacher confirmation could matter without becoming a policy bypass.
  • The public build would use synthetic data and structurally exclude grading, placement, discipline, disability inference, and other high-impact decisions.
  • The commit history and collaboration log show where Codex accelerated the work and where I made or ratified the engineering and product calls.

How I used GPT-5.6

GPT-5.6 is used as a bounded proposal engine. It receives minimized synthetic context and responds through a strict JSON Schema. The output vocabulary is closed. The request uses store: false, an opaque privacy-preserving safety identifier, an explicit timeout, and no silent fallback from a failed live request to a fixture pretending to be live.

A valid-shaped response still has zero educational authority by itself.

The Haskell engine evaluates whether the exact proposal preserves the learner’s protected work and remains legal under the current snapshot before a consequence can commit.

The repository contains a separate real GPT-5.6 Responses API exchange. That call passed structured-output validation, was accepted by the Haskell evaluator, and reached an authorized synthetic commit. Its sanitized receipt records the model, schema, request and response digests, observed latency, token use, engine decision, and commit linkage without storing the raw provider output or credentials.

The canonical Railway classroom case uses deterministic proposals so every judge can inspect the same durable evidence trace. It is labelled honestly and never presented as live GPT output.

Judges can also trigger one isolated live GPT-5.6 proposal from the teacher experience. That route uses fixed synthetic context, strict rate limits, ephemeral process-local storage, Haskell authorization, and a host-owned LiteShip component catalog. It has no capability to mutate the canonical learner record.

Challenges

The hardest part was refusing the easy architecture. The obvious version was a prompt, a chat box, a database, and some vibes around “personalized learning.” That version collapses policy, evidence, authority, timing, and educational claims into one plausible response.

LURNR keeps those concerns separate.

  • A well-formed model response can still be illegal.
  • A useful hint can still weaken what later performance proves.
  • A learner can complete supported work and still fail transfer.
  • A teacher click can be required without becoming sovereign.
  • An immediate success can remain provisional and later expire.

Concurrency made the problem impossible to hand-wave. A response can be correct for the world that existed when it was requested and wrong for the world that exists when it returns.

  • The interface created another useful failure.
  • An earlier teacher page led with source digests, authority revisions, snapshot IDs, and receipt chains. Technically honest, but a terrible way to explain anything to a teacher.
  • The final product pass moved the classroom situation up front and put the machine room behind “Technical proof.”

The proof still exists. It just waits until somebody asks the question it answers.

Accomplishments

LURNR makes stale model output unable to reach committed learner state. It preserves different cognitive obligations across granular, assisted, and orchestration modes instead of putting three labels around identical behavior.

It evaluates structured transfer work rather than accepting a caller-supplied pass or fail flag.

  • It prevents failed transfer from acquiring terminal closure.
  • It prevents one immediate success from quietly minting mastery.
  • It allows provisional evidence to expire without rewriting the historical observation.
  • It requires teacher intent and fresh Haskell authorization before a full explanation can commit.
  • It converts missing evidence, transfer failure, expired claims, and uncertainty into a small teacher attention queue instead of another class-sized analytics landfill.

It runs publicly with synthetic data, persisted PostgreSQL state, a private Haskell and Node backend, and no local setup for judges.

What I learned

Correctness has a clock.

A response can be shaped correctly, semantically reasonable, and still wrong to apply because the evidence changed before it arrived.

Assistance also has an evidentiary cost. A learner doing better with a system does not automatically tell us what they can do without the system.

And product design matters here more than I expected. The interface has to preserve uncertainty without dumping the entire proof apparatus on the human using it.

The model should be useful.

The system around it should know exactly what usefulness does and does not prove.

What's next

  • The next step is not opening this to real students and hoping the receipts make it responsible. I need to run a teacher-controlled shadow evaluation.
  • I would measure whether teachers agree with the routing, whether the queue saves attention rather than generating more work, whether quiet learners remain visible, and whether the transfer checks tell us anything useful beyond the original task.

Real learner data, institutional identity, retention policy, accessibility review, and school deployment controls remain separate gates.

LURNR will not make final grades, placement, discipline, disability, or other high-impact decisions.

Built With

  • astro
  • axe
  • cabal
  • codex
  • ghc
  • gpt-5.6
  • haskell
  • json-schema
  • liteship
  • liteship-and-genui
  • mcp
  • node.js-24
  • open-knowledge-format
  • openai-responses-api
  • playwright
  • pnpm
  • postgresql-17
  • railway
  • typescript
Share this project:

Updates