Its recipient is a human glance at a lock screen. The Reminders design: a derived-output discipline that turns confirmed Together agreements and composed life items into deliveries on channels the owner picks, with payload minimization enforced by channel trust class. It is deliberately not a third rail. Specified July 18, 2026 (pna/REMINDERS-SPEC-v0.1.md): ten privacy invariants (R1–R10), ten build-verifiable acceptance criteria (C1–C10). Status: BUILT, the same day: pna/cli/pna_remind.py, stdlib-only; 36 builder tests carrying C1–C10 by name, plus 23 independent adversarial tests that found 5 real defect classes, all fixed; 678 tests green across the whole Personal Neutral Archive (PNA) suite at that day's build, nothing regressed. Current whole-suite counts live on the build log. This page says exactly what a test enforces today and what's future. The plain-words version is for everyone else.
The house doctrine gives a PNA exactly two ways of touching another person: the gate is how others read you; message transport is how you speak. A reminder touches no one: no envelope, no verb, no pairing, no roster. So we name it a derived output, the same family as derived/event.ics (the archive compiling what it already holds into a form that reaches its own owner at the right moment).
| Grant rail — the gate | Sharing rail — message transport | Reminders — derived output | |
|---|---|---|---|
| What moves | Access: another identity reads your archive | A consented utterance, to a paired human | A note from the archive to its own owner. Nothing moves to anyone else |
| Direction | Inbound | Outbound | Inward |
| Trust object | A grant in policy.yaml | A contact card, by pairing | A channel in your registry — judged by who operates the road |
| Example | Your caregiver reads Medications | You invite a friend for a walk | "Sat 10:00 — walk with Marta," on your own lock screen |
Sovereignty is symmetric. When three archives confirm a Saturday walk, each derives its own reminders, under its own rules, on its own channels. Nothing about reminding crosses the wire: not the ladder you chose, not a snooze, not whether your archive had to nag you three times. How much reminding a person needs is health data: memory, vigilance, executive function. The rail already refuses to share completion (I9, enforced); this design extends the refusal to the nag itself (R1).
Text-message transport could demand the e2e rung because a machine receives the envelope and decrypts before any human looks. A reminder has no decrypt step to hide behind. The whole point is that a person can read it at arm's length, on whatever surface she actually looks at. It cannot be sealed, ever, by construction.
Therefore the only privacy controls that exist for a reminder are what it says and which road carries it. The whole design is those two controls made explicit and enforced:
Every delivery road is declared once in Reminders/channels/<id>.yaml and judged by the message transport catalog's one question: who operates this road, and what do they see? Two rungs only: device (renders on hardware you hold) and operator(<who>) (a named company's servers carry and can read the text).
| Channel | Who reads the reminder | Rung | v0.1 path |
|---|---|---|---|
| System notifications | Your device only | device | Default. Local agent posts a user notification |
| Apple Calendar | Device — if the target calendar is On-My-Mac/iPhone. An iCloud calendar syncs through Apple's servers, and Calendar is excluded from Advanced Data Protection's end-to-end coverage by Apple's own design (CalDAV interop) | device or operator(apple) — recorded per calendar. The app is not the unit | Derived .ics with VALARM; the person's calendar app carries and fires it |
| Google Calendar | Google, always — hosted by definition; schedule-knowledge is the business model (message transport catalog §2.3) | operator(google) | Import the same .ics; API export is future |
| Your mail provider (for Gmail: Google again) | operator(provider) | Local agent sends-to-self via SMTP; credentials C0 | |
| SMS / Messages | Carrier in plaintext for SMS; Apple for iMessage — the text-message transport uniform-distrust verdict applies unchanged | operator(carrier|apple) | Local agent stages a Messages-to-self send; postcard payload only |
Composed fields only (R3) — the utterance-only rule, turned inward: a reminder's outbound text contains only what was composed or derived for it at set-time. The archive may know the medication's dose; the SMS says what you wrote for exposure. Nothing is ever auto-attached.
Together-derived (automatic). When CONFIRM lands and event.yaml shows agreed, replay derives reminder occurrences from the agreed UTC instant plus standing rules. These are never originals. Delete every derived reminder and pna remind rebuild reconstructs them from the signed exchange plus rules.yaml. CANCEL, WITHDRAW, and DONE quiesce pending occurrences on replay (R9): a tombstoned walk never nags.
Composed life items (originals). Medication at 8:00 daily, the car inspection August 1, call mamá on Sundays. One YAML per item in Reminders/items/: title, note, when, channels, urgency, and optionally a postcard line composed in the knowledge it travels exposed.
Current limit: a reminder must be machine-readable to fire, so the floor is C1. A C0-sealed item cannot have reminders because the scheduler cannot read it. Blind reminders for sealed items ("something at 10:00") are outside v0.1.
Doctrine lands intact: originals are canonical; the queue is an index. rebuild reconstructs derived/ byte-identically from items + rules + the Together exchange replay + the delivery log (fired marks are history, and history is original). A Together-derived reminder never appears under items/, because its original is the signed agreement.
The design's engineering split, stated before anyone asks why SMS didn't fire while the laptop was closed:
| Artifact-fed (Apple Calendar, Google Calendar) | Sender-fed (notifications, email, SMS) | |
|---|---|---|
| Who fires it | The person's own calendar tool, from a compiled artifact (VEVENT/VTODO + VALARM) | A local agent on the owner's machine: the OS scheduler runs pna remind fire every few minutes: due-scan → render per rung → deliver → log |
| Awake when? | Always-on for free: the firing machinery is already in the person's pocket | As awake as the owner's machine. Asleep past due ⇒ fires late on wake, labeled ("was due 09:00"); past the staleness bound ⇒ expires with a log line, and nothing fires (R7) |
| New servers | None | None: stdlib/OS adapters only. The always-on sender is the future relay (the drop-box the sharing spec already names), a checkpoint item, v0.2 |
Semantics: at-least-once attempts, at-most-once effect. The delivery log is the dedupe truth: the agent consults it before every send; a double-run no-ops; a rebuild consults it so a fired occurrence can never re-arm (R6). Logging is fail-closed: no log line, no delivery, and no artifact compile either (R5). Channel credentials live under the archive's secrets discipline at C0. They are read at fire time and at no other moment, and they appear in no queue, artifact, or log line (R10). No delivery receipt of any form leaves the archive: nobody learns whether your archive had to remind you (R1).
The Together rail's existing derived/event.ics (built, tested at A2) gains VALARM blocks inside the same build_ics boundary, inheriting its sanitization: the adversarial pass's bare-CR ICS-injection fix and _sanitize_display apply to alarm DESCRIPTION fields exactly as to SUMMARY (I10).
The status labels follow the builders page: ENFORCED means a current test refuses the violation. The build ran the same day as the specification, every acceptance criterion passed, and an independent adversarial pass reviewed the result. FUTURE means designed for a later version. The first four rows were already enforced by the rail; the C-rows belong to this build.
| Claim | Standing | Where |
|---|---|---|
| The agreement a reminder derives from is one UTC instant across zones; each side's .ics parses | ENFORCED | rail test A2 — the boundary VALARM extends |
| ICS derivation sanitizes hostile titles (bare-CR injection, ANSI escapes) | ENFORCED | rail adversarial defects #1–2, fixed + tested |
| Tombstone replay: cancelled events rebuild as cancelled, history intact | ENFORCED | rail tests A4/A5/A9 — what R9's quiesce rides on |
| Completion never crosses the wire (the rule R1 extends to nags) | ENFORCED | rail I9, test A3 |
| Cross-zone alarm correctness, VALARM triggers well-formed | ENFORCED | C1 — builder tests, by name |
| Postcard serialization limits: no archive paths or contents, and no full fields on an unescalated operator channel | ENFORCED | C2 + adversarial pins (incl. kind/rung coherence, defect #4) |
| Quiet-hours deferral + compose-time-urgent crossing | ENFORCED | C3 + adversarial pins (edges, defer-then-expire interplay) |
| CANCEL/WITHDRAW/DONE quiesce pending occurrences; nothing fires after a tombstone | ENFORCED | C4 + adversarial pin (exactly one cancel line) |
| Rebuild determinism: byte-identical queue + .ics; fired stays fired | ENFORCED | C5 |
| Agent double-run no-ops; a rebuilt queue cannot re-fire a logged occurrence | ENFORCED | C6 + adversarial pins (torn log line, ctrl-C fake-delivery — defect #5) |
| Fail-closed: unwritable log refuses delivery and artifact compile | ENFORCED | C7 |
| Late labeling and staleness expiry | ENFORCED | C8 |
| DAILY 08:00 stays 08:00 local across both DST edges; occurrence keys unique across horizon rolls | ENFORCED | C9 + adversarial pins (fall-back, horizon roll, WEEKLY default — defect #1) |
| Credential hygiene: zero secret material in queue, artifacts, logs; fire-time-only reads | ENFORCED | C10 + adversarial pins (hostile error text, failed-send paths) |
| The always-on sender (the relay, same box as the future envelope drop-box) | FUTURE | v0.2, checkpoint item |
| Google Calendar API export (import-the-.ics is v0.1) | FUTURE | message transport catalog §2.3 scoped it |
| EventKit direct hand-off; Apple Reminders (VTODO's sibling) as a first-class adapter | FUTURE | message transport catalog §2.4 |
| Monthly/ordinal recurrence; blind reminders for C0-sealed sources | FUTURE | spec §12 |
These targets were published before any code existed; the build's independent adversarial pass then ran them. Outcome: the VALARM-injection target yielded two real injections (anchor durations reaching TRIGGER; RRULE BYDAY unvalidated), the postcard target yielded the kind/rung coherence hole (an operator-by-nature channel claiming device), the dedupe target yielded the ctrl-C fake-delivery, plus one class the list missed: WEEKLY-without-BYDAY over-firing seven days a week. All five fixed and pinned; details in pna/PNA-REMINDERS-BUILD-REPORT.md. The list stands for the next attacker. Good-faith security research is welcome through the main site's Write to us → security door — a safe harbor written into our Terms, §15.
This is the Together rail's first inward derivation — the plan-making is built (the sharing rail, 126 tests of its own); the reminding is designed against it. For the delivery channels we inherit the message transport catalog's verdicts — calendars as derived-output targets, uniform distrust of messaging operators. The product tether is the PRD's Care Calendar (P3), which promised "recurring rhythms… reminders"; this is the mechanism underneath the promise. Working documents, in the repository: pna/REMINDERS-SPEC-v0.1.md · pna/SHARING-SPEC-v0.1.md · pna/COURIER-SMS-SPEC-v0.1.md · pna/COURIER-CONCEPT-v0.1.md.
Prototype built July 18, 2026, on synthetic data only. An Opus builder made C1–C10 pass in 36 named tests. An independent adversarial pass, conducted without reading the builder's tests, found five defect classes; all were fixed and re-verified in the same session. The full suite then had 678 passing tests with no regressions. The founder's real archive was not touched: there was no Reminders/ directory and the access log was unchanged. The assigned adversary account reached a session limit before writing, so the main thread completed the pass while preserving the rule against reading builder tests.
Open design calls (the founder's, recorded; work continues while they wait): the partition name (Reminders/ vs Recordatorios/) · the default ladder · the quiet-hours window · postcard defaults and the plain sentence's wording, EN/ES, riding the terms-pack seam · the agent-now/relay-later sequencing, joint with the infrastructure lane.
Not yet: the three real adapters (osascript notification, SMTP email, Messages-to-self) are seam-tested through fakes alone. No test sends anything real, so their wire correctness awaits a first live run on actual hardware, which is a human act. The launchd agent ships as an uninstalled template. The always-on sender (the relay), Google Calendar API export, EventKit hand-off, and VTODO remain future, as specified. No new servers were added, and no new trust surface touches the rail.