Inspiration

Linda cares for her mother, Margaret, who has Alzheimer's.

Every appointment opens with the same question — is she worse than last month? — and Linda has no way to answer it. "She seems about the same" is not a clinical observation. So Linda started keeping a tally on a notepad: how many times today did Mum ask what day it is. She keeps it there because there is nowhere else to put it.

6.9 million Americans live with Alzheimer's and 11 million family members care for them. Almost all of that observation lives on notepads, in group chats, and in memory. The decline is measurable. Nobody is measuring it.

Memory Book is Linda's notepad, turned into a graph that can be traversed, measured, and handed to a doctor.

What it does

Two surfaces, one graph, escalating tiers of data quality.

The companion — an old phone on a lanyard. One button: long-press to start, long-press to stop; the patient always holds the off switch. The browser's speech engine transcribes and the text batches into the graph every few seconds. The system never records or stores audio — only the recognized text is kept.

The caregiver console — people, facts and events grow into a living graph, and four columns escalate the same data from raw to clinical:

  • What was heard — raw entries, each tagged with where it came from
  • Daily notes — auto-written, auto-filed
  • Handoff — the AI drafts the shift report; the caregiver reviews and confirms. This is the only human step in the whole loop.
  • Doctor — one click before an appointment produces a printable clinical timeline: per-day signal trends, memory-confidence decay, key events

Above it all, DriftWalker compares time windows and raises decline alerts (repeat_question: 9.5/day now vs 3.0/day baseline — 3.2×), and CritiqueWalker adversarially re-checks every alert against graph evidence — an alert keeps its severity only if its signals span at least two distinct days and at least two independent entries, otherwise it is downgraded. Every surviving alert carries its own proof.

Ask the graph anything — "Does she remember Emma is visiting?" — and RecallWalker answers by deterministic traversal, with a spotlight replay of the exact path it walked and the raw entries behind it.

Every node carries its provenance — the wearable contributes 0.7 confidence, a human caregiver 0.9 — so data quality is part of the graph model rather than a disclaimer.

Nothing in the system is Alzheimer's-specific. The graph measures whether recall is degrading, not why. The same walkers apply to post-stroke and TBI recovery, post-surgical delirium monitoring, and the much larger group of families who simply suspect a parent is slipping and have no way to check.

How we built it

Everything that thinks is Jac. The graph schema, all fourteen walkers, every line of decline logic, and even the demo corpus live in the root *.jac files. The frontend is dependency-free vanilla JS whose only job is to render what walkers return. This repo contains no ORM, no database code, no API routing layer and no server framework — Jac provides all four. That is not a stylistic choice; it is why two people shipped this in one day.

The patient's world is the graph: Patient, Person, Fact, Event, Entry, Signal and Report nodes, with typed remembers edges carrying confidence and provenance that decays 0.05/day unless the memory is re-mentioned. "How well does she remember Emma" is an edge attribute we read, decay and refresh — not a column somewhere.

The LLM never makes the medical judgement. Decline detection is arithmetic on the graph — count signals in the recent window, compare against the baseline window, decay stale confidence. Deterministic, reproducible, and able to name the exact signals and entries behind every alert. byLLM is used at exactly two boundaries: text in (transcript → a typed result object) and words out (facts → a warm sentence, items → a plain-language report). It labels; the graph decides.

That is also our answer to "what if the AI is wrong about my mother?" — we do not diagnose. We make what the caregiver already observed countable, and every alert opens to show the records that produced it.

What the demo shows

A simulated 7-day corpus, 22 entries across both sources:

Baseline (prior 5 days) Recent (last 2 days)
Repeated questions 3.0 / day 9.5 / day — a 3.2× rise
Peak — 11 in a single day

Crucially, not every signal rises. Social moments stay flat across the same period. So the finding is not "she talked more this week" — it is specifically orientation and person-recall that degraded, while positive recall still marks what is intact. That flat control is the difference between a metric and a finding.

Then the part a single confidence score can never tell you: the graph localizes which memories are going. Name confusion and disorientation land on specific people and specific days, and every alert is one click from the raw record that triggered it.

All demo data is simulated. No real patient data is used anywhere.

Challenges we ran into

  • Walkers pulled in via include silently lose their :pub tag under jac start and return 401; type and def includes are unaffected. We found this empirically and restructured so every walker is declared in the served module.
  • The persistent root graph is persistent across runs — which is the feature, but during development every re-run stacked another copy of the corpus into the graph. We spent real time chasing a de-duplication bug that did not exist before realising the data was simply loading twice.
  • The hosted runtime and local jaclang disagree about byLLM packaging — the hosted runtime bundles byLLM inside jaclang core and cannot parse the legacy pip package. Model loading is now runtime-adaptive.
  • Deploy packaging drops loose data files. Our seed corpus kept vanishing in the sandbox, so we moved it into a Jac module — a module always ships, and the corpus is part of the graph program anyway.
  • Static file serving differs across runtimes. We read the runtime source, found the gateway path that works on both, and monkey-patched the older server's unimplemented send_static_file.
  • Chrome's SpeechRecognition self-stops on silence, and it requires a secure context — a phone hitting a laptop over plain HTTP silently gets no microphone. The companion page runs an auto-restart loop; the secure-context requirement nearly cost us the live demo.
  • Our first alert wording compared raw counts across unequal windows ("19 in the last 2 days vs 15 in the prior 5"), which made a 3.2× rise read like a rounding error. It reports rates now.

Accomplishments that we're proud of

CritiqueWalker. Most projects stop at "the agent found something." We added an adversarial walker whose only job is to attack the first walker's conclusions and downgrade the ones that cannot survive — a spike packed into a single afternoon is not a trend. Every alert that survives carries its own proof. When you are telling a family their mother is getting worse, an alert you can argue with is worth more than an alert that is merely confident.

A flat control in our own data. It would have been easier to make every signal climb. Holding one flat is what lets us say the finding is specific rather than "she talked more this week."

No database, no backend framework, no build step. Fourteen REST endpoints, a persistent graph, two working surfaces and a Jac test suite — in one day, by two people.

What we learned

Object-spatial programming changed what we modelled. Our first instinct was rows of observations with a decline score on top. Putting the relationships first — a memory as an edge with confidence, a signal as a node with provenance — meant decline detection stopped being a statistics problem and became a traversal. And because it is a traversal, it can always show its work, which for this particular product is not a nice-to-have; it is the entire basis for trusting it.

We also learned to keep the LLM at the boundary on purpose. Every time we were tempted to let it judge, the feature got less explainable and no more accurate.

What's next for Memory Book

On-device transcription so recognition never leaves the phone; guardian-managed consent profiles; longitudinal per-person baselines; clinician input as a further data tier.

Ethics and consent

Simulated data only — no real patient data anywhere, and every page carries a "Demo data — simulated" footer. No audio is stored, only text. Guardian (POA) authorization, plus a visible indicator that the companion is listening — the long-press off switch is a dignity affordance, not our consent mechanism; for someone with cognitive impairment, guardian authorization is. AI drafts, humans confirm; alerts are advisory, never a diagnosis, and never change who signs the record.

Built With

Share this project:

Updates

Submission history