The implementation-level design for §4 of Live personas. "Should a persona grow?" splits into three things with different answers: knowledge of the world (Nolan should know The Odyssey is out), the relationship with each human (it should deepen), and the personality (it should not move). Assembled from how the assistants handle dates and cutoffs (Anthropic's and ChatGPT's actual system-prompt lines), the FreshPrompt and TimeChara results, Delphi's source-sync, Inworld's relationship dims and the game meters they descend from, and the drift and personality-stability literature. Status: building — slices land one by one (§4 carries the per-slice state). Written 2026-08-18.
recent.md refreshed weekly by a job — outside the profile, outside the repo. The relationship lives in the memory store and shows up as one summary line, not a meter. The profile stays the anchor; the only exam we add for growth is one that checks the persona has not changed.Nolan's profile says "present ≈ now (2024–26) … in production on The Odyssey for 2026 … a living figure with no knowledge horizon — he knows the present and may speak to it", and lists "17 Jul 2026 — The Odyssey due (upcoming/announced)". Nothing in the panel call, the producer, the prop master or the act call says what day it is — only the search dispatch injects a date. So asked in August, "preparing it" is the only answer the record supports. Two things are missing: the date, and anything after the record.
| System | The actual lines | What to copy |
|---|---|---|
| Claude system-prompt release notes, 2026 | "Claude's reliable knowledge cutoff, past which it can't answer reliably, is the end of Jan 2026. It answers the way a highly informed individual in Jan 2026 would if talking to someone from {{currentDateTime}}, and can say so when relevant. For events or news that may post-date the cutoff, Claude often can't know either way and says so … gives its most recent pre-cutoff information, notes it may be outdated, and points to web search … neither confirms nor denies post-Jan 2026 claims it can't verify without search, and only mentions the cutoff when relevant." | the horizon clause: know your horizon; answer as an informed person from then; for anything after it, say you can't know either way; never confirm or deny; mention it only when relevant. That is a persona rule, almost word for word. |
| ChatGPT leaked, gpt-5.5, 2026-05 | "Knowledge cutoff: 2025-08 · Current date: 2026-05-23 … you MUST search the web for any queries that require information around or after your knowledge cutoff. If you remotely think it is possible a fact might have changed … you MUST search." | the date line as two fields, cutoff + today; and the reflex "if it might have changed, look" — for us that reflex is the dispatch, not the persona. |
| FreshPrompt Vu et al. 2023 | Questions in four kinds — never-changing / slow-changing / fast-changing / false-premise. Retrieved evidence formatted as source · date · title · snippet, sorted oldest → newest (newest nearest the question), then reason to "the most relevant and up-to-date answer". GPT-4 strict accuracy 28.6% → 75.6%. Saying "as of my cutoff…" scores only when the fact truly hasn't moved. | the shape of recent.md: dated, sourced bullets in date order, newest last; and the honesty rule — hedging is not a substitute for knowing. |
| TimeChara ACL Findings 2024 | "Point-in-time character hallucination": a character shows knowledge that contradicts its position in time. Four data types (future / past-absence / past-presence / past-only). Fix = a temporal expert (which chapter are we in? future or past?) + a spatial expert (was the character there?) whose hints are prepended; GPT-4-Turbo 62.7% → 83.3%, +RAG-cutoff 85.3%. | the mirror-image rule for fixed-horizon personas (the deceased, the fictional): the profile's Anchor names the horizon and the persona is told, per turn, that today is after it. |
| Delphi docs.delphi.ai | A digital clone's knowledge syncs from the person's own RSS / YouTube / podcast feeds + Notion/Drive; "automatically keeps that content up to date"; new items only, no edit capture; a Feeds page shows "last synced"; answers cite the source, to the second of a video. Cadence undocumented. | the refresh job pattern: poll the person's own feeds and the record, append new dated items, show when it last synced. Nobody publishes a per-character news refresh — this is the one place the design is ours. |
| Piece | What we build | Borrowed from · why |
|---|---|---|
| ① the date line | One sentence in the user turn's standing notes (beside the roster and clock notes — _clock_note already rides there), so the cached prefix never changes: [Today is Tue 18 Aug 2026 (UTC+8).] The producer and the prop master read their own views of the room (the transcript tail; the staging summary), and neither needs the day for its job — the note is the panel's; the dispatch already has its own. Timezone: the last human line's client clock (_human_tz — the same clock the memory harvest dates by), else the room's language (日本語 → UTC+9, other CJK → UTC+8), else UTC. (As built: Room._date_note(); the per-line time tag [Thu … 14:32, GMT+8] already marked when a message was sent, but only when the client sent a tz — this is the standing "today", unconditional.) | ChatGPT "Current date:" · our §8 caching contract (never in the prefix) |
| ② the horizon clause | The system prompt's Speaking beyond the record section gains the Claude rule, addressed to the host: "Your record runs to its horizon (living figures: the present as far as the notes below say; fixed figures: the date named in your Anchor). For anything after it you often cannot know either way — say so in your own voice, neither confirm nor deny, and only mention it when it matters." A prompt change → baselined against its BEFORE per the harness rule. (As built 2026-08-20: the bullet sits between "Within the record" and "Beyond the record" in both variants, verbatim per this row.) | Anthropic system prompt · TimeChara (a horizon the persona is told about, per turn) |
| ③ recent.md | For living real figures only (the card gains horizon: living; today the Anchor prose says it): a small dated file, refreshed weekly by a job, loaded after the profile as "Since the record — as of 2026-08-16". Details below. (As built 2026-08-20: 56 cards tagged after a full-library Anchor audit — every real figure whose Anchor pins a living present; deliberate earlier pins — jack-dorsey ~2021, rick-kurnit ~2021, the period and final-year figures — stay fixed, and xiao-pang, a real private person, is never swept; the block carries a knows-not-lived gloss, prohibition last, outranking the profile's own improv licence for post-record items. The field is the app's to know — the owner's rule: a newly added persona must not silently miss the sweep. Four gates: the template requires horizon: living|fixed on every real figure and says how the Anchor decides it; the builder's mechanical checks refuse a real build without it (the patch loop makes the author write it) and strip it from invented builds; the smoketest lints the repo — a real persona without the field fails the build; and the console's card lists any unclassified real card that still slips through, in red. And the record's edge is data (owner, 2026-08-21): every living card carries anchor: YYYY[-MM] — the Anchor prose's own pin, machine-readable. The FIRST sweep asks "what changed since {anchor}" (the file's as_of takes over from week one); the builder refuses a living build without it; the audit compares it to the prose. Deriving it library-wide exposed two round-one mis-tags — zhang-xiaolong is pinned to the 2021 keynote night and zhang-yiming to late 2021, both now fixed — so the living set is 55. And since 2026-08-28 the file is also OWN NEWS (agenda ⑤): the gloss licenses volunteering ONE recent piece at a moment with room —「the way a friend mentions their week」, never a bulletin, cite-don't-inhabit intact — and a bullet dated within RECENT_FRESH_DAYS=10 opens the FP's OWN GROUND row (recent_fresh_count), so the floor may stage「room for a move of your own」; freshness expiring closes the ground by itself, no state.) | FreshPrompt shape · Delphi sync · Zep dated facts |
| ④ knowledge ≠ lived experience | The extrapolation contract gains one clause: a recent.md item is public record the persona knows, not a memory it lived. "The record says The Odyssey opened in July" is Track A; "when I was on set last spring" is a fabrication and Track E. Distance-0 stays quote-and-cite; recent items are cite-only. | our contract · FreshPrompt's strict scoring (no hallucinated claims) |
exam/runs/growth-nolan-*.md.| System | Mechanism, exactly | Read |
|---|---|---|
| Inworld Dynamic Relationships | Five attributes — trust · respect · familiar · flirtatious · attraction. After each exchange a "relation graph" emits a delta per attribute in {−2, −1, 0, +1, +2}; totals accumulate and persist with the Player Profile; thresholds label a stage, e.g. Friend = trust ≥ 10 & respect ≥ 5 & familiar ≥ 10 & flirtatious ≤ 5 & attraction ≥ 5; ladder Archenemy ↔ Enemy ↔ Acquaintance ↔ Friend ↔ Close Friend (romance: Date ↔ Relationship ↔ Life Partner); the label feeds the character's prompt; a slider scales the step size. | The cleanest published state machine. Its cost: an extra scoring call per turn and a fixed vocabulary of five axes. |
| Games (proven UX) | Stardew: 250 pts/heart; talk +20/day; silence −2/day; gifts +80 (loved) … −40 (hated); birthdays ×8. Sims 4: two tracks −100..+100, decay after a day without contact, no decay beyond ±20, cull at 0. Animal Crossing: 25 → 255, six levels at 30/60/100/150/200. | Decay-when-ignored and reinforce-on-contact — the same curve as memory recency. Visible meters work in games because the game is the meter; a chat with a real-feeling person is not. |
| Companions | Replika XP → levels (community: ~L30 "knows you better"); 星野 亲密度 levels unlocking features; Character.ai: none. | Our proven bar (WhatsApp · Character.ai) shows no meter. Under Proven-first, neither do we. |
| Xiaoice | Optimised for expected conversation-turns-per-session (CPS 23 vs human 9); a user profile + emotion state feed replies; framed as "long-term relationships". | Relationship as an optimisation target is a different product; ours is a room, not a retention loop. |
| Decision | Our pick | Borrowed from · why |
|---|---|---|
| no meter shown | the proven bar has none; a number on a friendship reads as a game | Character.ai · Proven-first |
| a summary line | cheap, honest, and it changes the persona's register on its own ("someone I've talked with six times" vs "a stranger") without a rule | Zep user summary in memory.context |
| decay = memory recency | we don't add a second decay curve; the store's ranking already ages what isn't recalled | Stardew/Sims decay · MemoryBank |
| dims, if ever | Inworld's five is too many for a room; three (trust · familiarity · warmth), ±1 per reflection, thresholds → a label; a slider like Inworld's to scale the step | Inworld |
| Decision | Our pick | Borrowed from · why |
|---|---|---|
| profile.md is immutable at runtime | the only writers are the pipeline (a CC session, audited) and the Studio builder; every change is a dated diff. No interaction, no reflection, no job touches Background · Values · Voice · Tells. | drift studies · Roberts & DelVecchio |
| the persona-side state that may change | exactly two files/rows: recent.md (world) and the memory store (relationship). Both are additive, dated, and reviewable. | ours |
| a drift scenario in the exam | a 40-turn room; a judge scores CharacterEval's five character-consistency metrics at turns 5 / 20 / 40 against the profile; the delta is the number. Also run once with recent.md loaded and once without, to confirm knowledge refresh doesn't move voice. | CharacterEval · PersonaGym · our exam battery |
| protective questions | each real-figure Anchor already lists "known traps"; add the horizon question ("what did you think of <event after the horizon>?") to the audit's cold-subagent probe set. | Character-LLM protective experiences · TimeChara |
| # | Slice | What lands | Lens | Needs |
|---|---|---|---|---|
| 1 | the date line shipped 2026-08-20 | a persona knows the day; baselined (a prompt change) — Live personas ①. As built: Room._date_note() beside the clock note, six smoketest pins | proven | — |
| 2 | the horizon clause shipped 2026-08-20 | the SP's beyond-the-record section speaks Claude's rule in the host's voice; baselined. As built: one bullet in BOTH SP variants + the contract's since-the-record clause (knows, not lived — cite-only) + the horizon question in the audit's layer ② | proven | 1 |
| 3 | recent.md loader shipped 2026-08-20 | a hand-written file for one persona (Nolan) loads after the profile — proves the seam before any job exists. As built: read_recent() gated on the card's horizon: living (56 living figures tagged (full-library audit — every Anchor pinned to a living present; deliberate pins like Dorsey ~2021 and Kurnit ~2021 stay fixed)), root personas-recent/ (env MAD_PERSONAS_RECENT, gitignored), loaded at BOTH profile doors (room open + a joiner's injection), 9 smoketest pins. The bar, measured, in the box below | better | 2 |
| 4 | the refresh job shipped 2026-08-20 | weekly, living figures, poll → read → route → write → admin diff; horizon-changing items gated to the owner. As built: module-level recent_refresh_run() on the brew heartbeat (auto OFF-PEAK only; the file's own as_of is the weekly clock), ONE SERP round + ONE flash read per persona, recent_route() pure (exact bullet shape or NONE; HORIZON → the admins' rail via ckey horizon:<slug>, never the file), console ▸ Personas ▸ "Since the record" card with Refresh-now + the diff log. Mock-SERP full path pinned in the smoketest; first real single-persona run: +2 true bullets, $0.006, 3.5s. v1 polls the SERP engines only — the RSS/Wikidata feeds in the block above remain open. And no single mind writes to users (owner, 2026-08-21): a VERIFIER — a second flash call with the opposite job — judges every candidate against the same evidence before it is written: reject-on-doubt (a wrongly dropped line returns next week; a wrongly kept line lies to every reader), FAIL-CLOSED on an unreadable or dead verdict, every rejection logged with its reason to the console. Plus the split-name identity block, the date floor, and honest-empty weeks | better | 3 |
| 5 | the summary line | at the top of each memory block, no model call | proven shape | the memory store |
| 6 | the drift scenario shipped 2026-08-20 | a 40-turn exam with consistency scored at 5/20/40. As built: exam/growth_drift.py — a flash guest brain wanders eight themes (the last ones press the personal), a DeepSeek judge scores CharacterEval's five character-consistency metrics per snapshot against the whole profile (+ the since-the-record block as ground truth, so a current fact is never graded as hallucination); arms with/without recent.md; incognito room (no memory writes). First full run's numbers below | harness | — |
exam/runs/growth-drift-20260820T1822.md.recent.md the owner writes by hand (Xiao Pang's "this month") — cheap, and it is exactly the "wants" state the initiative page needs. Probably yes, later.
Special:EntityData/Q<id>.json (P39 · P108 · P166 · P570)