Open loops
Task and open-question memories that stay in the task buffer until they are closed, with a soft briefing TTL.
An open loop is a memory of kind task or open-question: a piece of
unfinished work or an unanswered question. Unlike ordinary memories, which
compete for relevance in retrieval, recent open loops surface in a dedicated
section of every briefing of their scope. One precondition: a briefing shows
a project's loops only once the session is attached to that project (a
project hint, an inherited thread, or explicit scopes). An unattached
briefing narrows to the personal layer, so the hand-off lands when the
receiving session names the same project. Closed loops disappear from
briefings but stay in history.
Transport is not a hand-off
Because one server backs all your machines, projects, and agents, memory already works as a transport: a fact written on machine A is visible in the next briefing on machine B. What ordinary memories lack is the semantics of a hand-off: "surface at the recipient, persist until handled, disappear once done." Open loops add exactly that lifecycle and nothing else. The same mechanics covers two everyday cases. One is the inter-machine hand-off, "check log X on machine Y": record it on one machine, and the agent on the other machine sees it at session start. The other is the unfinished thread, "we never settled question Z": the question keeps resurfacing until a session answers it.
Open loops are a lifecycle dimension, not a topic. Kinds in zero-memory describe how the system should behave toward a memory, and open loops behave differently: short-lived, priority-surfaced, and excluded from long-term knowledge machinery.
Surfacing, ageing out, and closing
build_context returns active loops in a dedicated open_loops field,
separate from (and in addition to) the normal top-k memories, with a total
count. Session hooks render them as a distinct "Open loops" section, newest
first, with an "N more" counter; old loops get a staleness badge by age. The
server caps the list at 10 loops. In the hook briefing the section holds its
own share of the channel budget, so a long block of standing rules cannot
crowd it out, and a loop too long to fit whole arrives as a one-line stub with
its id instead of ending the section. A loop attached to a card that the
briefing's board lines name, or promoted into
such a card, is shown through that card instead of on its own. The section is worded as recorded open
work — data, not instructions.
Depth follows the topic, selection does not. All ten arrive whatever the briefing is about, but the loops nearest the topic are inlined in full while the rest carry a headline. Nearest is a rank rather than a bar, so a scope holding a single open loop always gets that one in full. A loop's relevance is judged against its whole text, so one whose live part sits past its opening is still recognized. Nothing can be pushed out of sight by being off-topic; being off-topic only makes a loop shorter on that particular briefing.
The briefing TTL is soft. By default, loops older than 14 days stop appearing
in open_loops and its total, and the instance knob zm.open_loop_ttl_days
changes that window. Aging out does not close or invalidate the memory: it
remains recallable, visible in the dashboard, and closable by id. It does not
vanish from the briefing without a trace either. build_context also returns
open_loops_aged, the number of open loops past the window that sit on no
card, and the hook briefing ends the section with one line saying how many
there are and how to list them. The line appears even when no loop is inside
the window. A loop promoted into a live card or attached to one is not
counted, because the board already reaches it.
The close_loop tool closes a loop the moment it is done or answered. The
memory is invalidated (add-only, so it is kept in history and audited) and
stops surfacing. When the closure itself is a fact worth keeping ("done:
deployed the fix, root cause was …"), remember with a supersedes link to
the loop closes it too, organically. The agent on the receiving machine can
close a loop after completing it.
Explicit closing depends on the finishing agent's discipline, so the hygiene
cycle carries a safety net. It rolls up evidence that a loop's work was
completed, and a conservative judge (high confidence gate) may close it:
reversibly, with a successor link and an audit trail, and with a guard against
re-judging the same loop. Reopening a loop with restore_memory holds it open
against the evidence that exists at that moment: the judge takes it up again
only once newer evidence arrives, so a person's "not done" is never overruled
by the very evidence it answered. A loop promoted to a
card is never judged: its work was handed
over, and the card now carries it.
Two exclusions keep loops out of the knowledge machinery. They never become rules incubator candidates, and they are kept out of long-term knowledge metrics, because they are work items rather than durable knowledge.
A hand-off, not a status report
A loop is for a one-off hand-off or a question: written once, closed once.
The running status of ongoing work (the branch, what is done, what comes
next) is different. It changes every session, so a status kept in a loop gets
re-cut again and again, each version retiring the one before. That status
belongs on the work's card as a note
(card_log note). There the next session is offered it together with the
card, and the notes accumulate as the card's history instead of replacing
each other.
The remember response points this out at the moment it happens. When a
task write retires an earlier task loop, whether through a supersedes
link or the same-session collapse, the response carries status_hint. It
says which cut of the loop this is and where the next update goes: the card
the conversation is bound to, by its label, or, when there is none, an
instruction to run card promote_loop on the loop. A first cut gets no hint.
Neither does a re-cut open-question, which adds evidence to the question
rather than reporting progress.
A loop is a pointer, not a payload. Logs and dumps do not belong in memory. A task carries a path or URL to the artifact plus a distilled summary of what to look at; the content guard enforces this on the write path like any other memory.
Creating and closing a loop
A loop is created and closed through MCP tools. An agent calls them; there is
nothing to type into a terminal. Arguments to remember:
{
"content": "Check the watcher warnings in /var/log/... on the build machine — summary: repeated reconnects after resume",
"kind": "task"
}Outside a session there IS a command, the quick-capture CLI:
zm --task "verify the migration applied cleanly on the staging database"Close it when done. Arguments to close_loop:
{ "memory_id": "mem_..." }Or store the outcome with remember plus a supersedes link, which closes
the loop as a side effect. close_loop refuses non-loop kinds; use forget
for a wrong or outdated regular memory.
In the dashboard, the memories feed filters by kind, so task and
open-question give you a live list of open and historical loops.

Deliberate limits of the loop model
Closing is invalidation, never deletion. The store is add-only; a closed loop remains queryable history with its audit trail. Hard deletion is reserved for its own purpose and is not part of the loop lifecycle.
There is no automatic creation. The transcript extractor deliberately cannot
produce task or open-question memories, because its schema is narrowed to
exclude them. Auto-extracted loops that nobody closes would be noise, so loops
are created only intentionally.
Addressing is conventional. A loop targets its audience by scope plus naming the machine or agent in the text; strict per-agent addressing is a later concern (see Agent principals).
See Briefing for where open loops appear at session start, and Session receipt for the end-of-session counter of loops created and closed.