A base prompt starts clean and then slowly becomes a junk drawer. One rule is needed only when someone asks for a CSV. Another needs a fresh memory index every 5 turns. Put both in the permanent prompt and they consume context even when they do no work.
Limerence handles that pressure with runtime reminders: code-declared context fragments that can evaluate against a turn and add guidance when they match. The current product uses them on the user turn, before ordinary fragment, artifact, and sandbox setup.
◆Key Takeaway
Runtime reminders keep selected guidance fresh by folding it into the saved user turn. They can be conditional when configured with a predicate, not a larger permanent prompt or a new memory store.
A Base Prompt Cannot Refresh Every Fifth Turn
The useful cases are concrete. Every fifth user turn, auto-memory evaluates its resolver and reads up to 200 lines of the memory index when it exists. In the work profile's data-insights style, export guidance appears when someone asks for a report, export, CSV, JSON, PDF, download, or artifact. Those are different rules with different clocks.
A reminder is a ContextFragment declared in capability or output-style code. It records text, a when predicate, asPart, and a delivery target. In the audited implementation, that makes it deployable code rather than a per-agent database record.
The predicate can be a turn counter, a content match, or a one-time latch. The memory fragment uses everyNTurns(5); the export tip uses a content pattern. If either condition does not match, the model receives nothing extra on that turn.
reminder() Defaults Limerence Guidance to the User Turn
The default matters because delivery changes the persistence story. A user-target reminder is folded into the pending real user message when that message is saved. The underlying engine requires a when predicate for non-user targets, and it allows asPart only for user delivery.
Here is the shape of the content-gated export guidance. The target is absent on purpose, so this is current deployed user-turn behavior.
context.reminder(
'When the user asks for a report, export, CSV, JSON, PDF, or downloadable artifact, create it with the workspace file system.',
{
when: context.contentPattern(
/\b(?:report|export|csv|json|pdf|download|downloadable|artifact)\b/i,
),
},
);context.reminder(
'When the user asks for a report, export, CSV, JSON, PDF, or downloadable artifact, create it with the workspace file system.',
{
when: context.contentPattern(
/\b(?:report|export|csv|json|pdf|download|downloadable|artifact)\b/i,
),
},
);The engine has more delivery machinery than the app currently uses. Calling it a deployed mid-loop feature would be wrong. The distinction saves readers from building product assumptions around a package capability with no audited current Limerence declaration; the audited local tests cover only save-time user folding.
engine.continue(message) Is the Save Boundary
The turn path is ordered. Limerence collects profile reminders with output-style reminders first and capability reminders afterward, installs them on the context engine, then calls engine.continue(message).
- 1
Collect the profile's style reminders, then capability reminders in their declared order.
- 2Optionally add the explicit-skill hint or the skills reminder.
- 3Register the resulting fragments with
engine.set(...reminders). - 4
Save the incoming user message through
engine.continue(message). - 5
Only then assemble ordinary fragments and begin artifact and sandbox work.
That order is an operational guarantee with a deliberately unglamorous mechanism: the decorated question is stored before later setup can fail. The focused profile test makes ordinary fragment assembly throw after the save and still finds both the style and capability reminder text on the stored user message.
It also creates a partial-success state. If skill listing, fragment assembly, artifact setup, or sandbox startup fails later, the question can exist without an assistant answer. Recovery UI must recognize a saved turn that never reached the model loop.
Inline Ranges Keep the Chat Bubble Human
For an inline user reminder, the engine appends a <system-reminder> block to a text part and records where it inserted that text. The range starts at the JavaScript string length of the existing text and ends after the reminder text, so those values are JavaScript string indices used by .length and .slice, not byte offsets.
The stored turn therefore has two intentional views. The model sees the tagged addition. The chat UI strips the tagged material from the human bubble and separately uses valid reminder metadata for its disclosure.
That split depends on valid metadata. If the metadata is malformed, the UI omits it from the disclosure even while it still strips tagged reminder text from the raw message, so the model-facing content and human-facing disclosure can disagree.
Stored model input
Please export Q2 revenue. followed by a tagged system-reminder telling
the model to create the requested artifact with the workspace file system.
- one persisted user message
- reminder ranges point into its text part
- the model receives the appended guidance
Rendered user bubble
Please export Q2 revenue.
- the same persisted message supplies the bubble
- tagged reminder text is removed for the human-facing rendering
- valid metadata can appear in the reminder disclosure
asPart takes the other user-target form: it creates a new text part whose recorded range starts at 0. Neither form needs a separate reminder table. Ranges and reminder state travel with the stored message payload.
once(id) Latches on the Persisted Real User Message
Some guidance should happen once per conversation. Dashboard discovery uses a once('dashboard-index-discovery') predicate, and the engine collects that id only when the reminder fires rather than when it is merely declared.
For current user-target reminders, that latch is persisted on the real carrier message as metadata.onceIds. When the engine loads the chain later, it reads normal-message onceIds back into its fired set, so the same reminder is suppressed on the next matching turn.
The namespace is easy to get wrong. metadata.synthetic.onceIds belongs to synthetic messages from the engine's steer and tool-output path. Those targets are installed engine capability, unused by current Limerence declarations. A present-day user reminder stores its latch on the real message instead.
The Context Store Owns Durability
Production stores context messages through Postgres, while desktop uses SQLite. In both cases, the message payload carries reminder ranges and onceIds; the application does not maintain a Prisma reminder model beside the conversation.
That choice has a cost. Changing a reminder needs a code deployment, and the audited implementation contains no per-agent reminder editor or kill switch. It also has a useful property: message lifecycle and reminder lifecycle remain coupled, rather than requiring a second record to be joined, cleaned up, and recovered.
Async reminder text gets the same practical treatment as any fallible context read. The memory reminder reads the user's memory file; a failed read is skipped rather than aborting the turn. The workflow index goes even further by converting a failed listing to an empty result, which becomes no reminder.
Mid-Loop Delivery Needs Product Coverage First
The engine can evaluate steer reminders after a safe assistant-step boundary and tool-output reminders from terminal tool outcomes. It combines fired texts into one synthetic role: 'user' carrier, which keeps its latch data under metadata.synthetic.onceIds.
This boundary should stay explicit. Before Limerence could describe mid-loop failure and persistence behavior as a product guarantee, it would need product-level declarations, observability, and integration tests.
The current diagnostic gap is smaller but real. Rejected predicates and resolvers produce package warnings, while the application does not record a fired-or-not-fired trail around engine.continue. When a reminder is absent, distinguishing a false predicate from an async read failure may require inspecting those warnings and the turn execution.
A worthwhile next step is not another prompt paragraph. It is a per-turn reminder trail that says which declarations matched, which were skipped, and why, then a deliberate product decision about whether mid-loop targets should remain engine capability or become an exposed feature.