DocsSupervision (zirv ctx)
Memory
zirv memory#
zirv memory manages this repository's memory bank without starting an AI
session:
zirv memory init --dry-run
zirv memory init
zirv memory init --source /path/to/docs --merge
zirv memory status
zirv memory list
zirv memory recall staging-db
zirv memory remember staging-db-creds "the staging DB creds live in 1Password under staging-db"
zirv memory remember deploy-cmd "cargo build --release" --importance high --confidence high --tag deploy --tag release
zirv memory forget staging-db-creds
zirv memory verify staging-db-credsremember also takes --importance <low|normal|high>, --confidence <low|normal|high>, and a repeatable --tag <t> — all optional, unset by
default. They land in the stored entry and feed zirv memory recall's
ranking (retrieval::score_one); zirv ctx remember has no equivalent
flags. --importance/--confidence reject any value outside the three
listed.
memory init proposes a bounded set of durable shared entries from repository
validation/toolchain surfaces and high-signal Markdown sections. --dry-run
changes nothing, --source accepts a Markdown file or directory (including an
Obsidian vault), and a non-empty shared bank is refused unless --merge is
passed; merge mode adds missing keys and never silently overwrites curated
entries. --max-entries and --max-bytes cap initialization.
Every verb defaults to the private (machine-local) bank; pass --shared
to act on the shared, repository-owned bank instead — see
Memory bank below for what the two scopes mean. list and
recall respect each scope's own gate (memory.enabled /
memory.shared_enabled): a disabled scope lists or recalls empty rather
than showing what it holds. status never hides a disabled scope's counts
— it marks the scope disabled but still reports its entry count and
stored bytes, since a byte count is not the entry content the gate exists
to withhold. forget and verify work even while a scope is disabled —
disabling a scope must never trap data behind it. status never prints an
entry's key or body, only scope availability, entry counts, stored bytes,
and the configured injection budget. zirv ctx remember --key <k> --text <t> / zirv ctx recall / zirv ctx forget <k> (flag-based, private-scope
only) are untouched and keep working exactly as before — zirv memory is a
newer, scope-aware surface alongside them, not a replacement. forget on a
missing key exits 0 (it is idempotent — "already gone" is success);
verify on a missing key exits 1 (it is stamping a claim about an entry
that does not exist, which is a real failure) — the asymmetry is
deliberate, not a bug.
Memory bank#
Besides mail (session-to-session notes) and handoffs (task continuity across a restart), zirv keeps a third, longer-lived store per repository: a small bank of durable facts about the repository itself, independent of any one task or session.
zirv ctx remember --key staging-db-creds --text "the staging DB creds live in 1Password under staging-db"
zirv ctx recall
zirv ctx forget staging-db-credsSee zirv memory above for a newer, scope-aware surface over
this same bank (status/list/recall <query>/remember <key> <text>/
forget <key>/verify <key>, --shared for the repository-owned scope
below) that works without starting an AI session — the two verbs above keep
working unchanged alongside it.
The handoff-vs-memory boundary matters. A handoff is task continuity: what this specific task was doing, what remains, what to try next — written once per restart, and only ever useful to the session that picks that particular task back up. Memory is repository facts: things true regardless of which task is in progress — a build command, where a credential lives, a convention, a gotcha about how a dependency behaves. Task state does not belong in memory, and a repository fact does not belong in a handoff.
By default nothing is added to the bank automatically — zirv ctx remember
is a deliberate act, by a session or a human. Set [memory] harvest = true
(or ZIRV_CTX_MEMORY_HARVEST=true) to let zirv also try to extract durable
facts on its own: right after a rot restart distills a handoff (exec or
wrap, and only from a genuinely distilled handoff, never the mechanical
fallback), one extra cheap-model call looks at the handoff's Gotchas learned and Files touched sections and proposes zero or more key: value
facts. Harvesting stays off by default because a cheap model can be
confidently wrong: an unreviewed guess landing in a bank every future session
reads is a worse failure mode than simply not harvesting. A harvested entry
replaces any existing entry with the same key rather than duplicating it, the
same as an explicit remember does, and a distiller failure or timeout
leaves the bank untouched.
Every entry has a Verified stamp alongside its Written one; an entry
older than 30 days without being re-verified is flagged as stale wherever the
bank is summarized (zirv ctx status, zirv ctx optimize). zirv ctx optimize's report includes a memory-bank summary block — count, total
bytes, oldest/newest age, how many are stale, a duplicate-key check — but
never quotes an entry's key or body: that content is repository-scoped,
cross-session data with nothing to do with what optimize is reviewing, and
it is read separately from (never folded into) the surfaces sent to the
judgment model.
[memory]
enabled = true
harvest = false # off by default; see above
max_entries = 50
max_entry_bytes = 512
max_injected_bytes = 2048 # superseded by core_max_bytes; kept only so an old config does not error
shared_enabled = true
core_max_bytes = 2048 # cap on the merged private+shared core layer folded into every session
retrieval_max_bytes = 2048 # cap on `zirv memory recall`; reserved for future context-aware session retrieval
retrieval_max_entries = 6 # max number of recalled entries, independent of bytesThere are two memory scopes, gated separately but not independently:
enabled is a master switch that disables both scopes, and shared_enabled
is a second, shared-only toggle underneath it -- enabled = false always
wins, so an operator who turned memory off before the shared scope existed
does not silently start receiving repo-controlled prompt content on
upgrade. The private bank still lives
under the state dir, never in the repo (the same "a checkout is not the
operator" reasoning as handoffs and mail). The shared bank is the
opposite by design: <repo>/.zirv/memory/ is untrusted repository content,
one key-addressed file per entry, meant to be committed, reviewed, and
hand-edited like any other file in the checkout -- that is the whole point of
a Git-friendly shared memory bank. What stays forbidden either way is the
configuration, not the content: every [memory] key, including
shared_enabled, is repo-forbidden outright -- a checkout may not set any of
them to any value at all, so it can neither switch either scope's own gate on
or off for itself, raise its own caps, nor turn on automatic harvesting. Only
~/.zirv/ctx.toml, ZIRV_CTX_*, or flags (the operator) may. See
Trust boundary above.
Consistent sessions#
When zirv starts an agent through wrap, exec, loop or resume it injects a
small system prompt so sessions behave the same way every time. Three layers
concatenate, in order:
- A shipped default baked into the binary: respect repo conventions, use tools deterministically, report failures honestly.
- Your own additions, by session role:
~/.zirv/system-prompt.mdfor an interactive session you drive yourself (chat,wrap,resume), or~/.zirv/system-prompt.worker.mdfor a delegated headless worker (agent,exec,loop). Both are optional, and neither role reads the other's file — if you had worker-facing instructions insystem-prompt.md, copy them intosystem-prompt.worker.md. <repo>/.zirv/system-prompt.md, the repository's additions (one file, both roles).
The repo layer is untrusted input, treated the same way ctx.toml's repo
layer is: it is capped in size, labeled in the composed prompt as coming from the
checkout, and stated not to override anything above it. A repository cannot turn
its own layer on or raise its own cap: prompt.enabled, prompt.repo_layer and
prompt.max_repo_bytes are all rejected from a repo config. Set them in
~/.zirv/ctx.toml, or with ZIRV_CTX_PROMPT, ZIRV_CTX_PROMPT_REPO and
ZIRV_CTX_PROMPT_MAX_REPO_BYTES.
[prompt]
enabled = true
repo_layer = true
max_repo_bytes = 4096prompt.verbosity (issue #427; "minimal" | "standard" | "verbose",
default "verbose") picks how much of the Orchestrator-only meta-harness
orientation layer (zirv's own explanation of itself, the checkpoint/mail
habit, delegation mechanics, lifecycle sizing, the design-approval gate,
the review policy) is injected. "verbose" is today's full text, byte for
byte -- the default changes nothing for an operator who never touches this
key. "standard" drops only the purely descriptive framing and
self-discovery bullets; every bullet that changes what the session does is
kept verbatim. "minimal" drops those two plus the roster pointer (the
harness roster itself, if enabled, is still appended regardless of tier),
and compresses everything else to its shortest behaviour-changing form: it
still teaches delegation mechanics, that an undirected zirv ctx send is
claimed by exactly one session while --all fans out, that durable facts
persist via zirv ctx remember/recall, that substantial work starts a
zirv workflow, the design-approval gate, and the review policy's hard
stop after 2 fix rounds. It is REPO_FORBIDDEN: set it in
~/.zirv/ctx.toml or with ZIRV_CTX_PROMPT_VERBOSITY.
[prompt]
verbosity = "verbose"Issue #772 also tiers the shipped engineering-standard floor itself
(DEFAULT_PROMPT/DEFAULT_PROMPT_WORKER, not a [prompt] key -- there is
nothing to configure): an Orchestrator/SubOrchestrator session gets the full
standard, while a delegated Worker/Single session -- already handed a
bounded, pre-sized task -- gets a compact variant that keeps every rule that
changes behaviour (verify with evidence, one focused test per behaviour
change, no slop, deliver exactly what was asked, honest report) and drops the
full sizing taxonomy's own long-form explanation and the UI/design-thinking
bullet, the same role split the standing skill index already draws (prompt. skill_index) for the identical reason: a delegated worker re-reads this
layer on every headless turn, so its cost is not amortised the way an
interactive session's is.
Pass --simple to any of the four verbs to start the agent with no zirv text at
all, shipped default included. Supervision, pacing and hooks are unaffected.
Whether a prompt was injected, and from which layers, is recorded in the decision
log at every session start.