DocsSupervision (zirv ctx)
Context Management (zirv ctx)
What leaves this device#
Zirv can mask credential and personal-data values before text it controls is
sent to a model or another remote service. This is opt-in and off by default;
the operator turns it on with [obfuscate] mode = "obfuscate" in
~/.zirv/ctx.toml (never a repository checkout -- see REPO_FORBIDDEN
below). Once enabled, masking is deterministic and on-device: repeated values
become the same typed placeholder (for example ZIRV_SECRET_GITHUB_PAT_1 or
ZIRV_PII_EMAIL_2@example.org) in every session and worker for that
repository. The plaintext mapping remains in an operator-owned, mode-0600
vault under the Zirv state directory. It is not encrypted at rest.
| Surface | Treatment once obfuscate.mode is enabled |
|---|---|
| Native direct-provider and official-harness requests | The final provider request boundary masks system text, messages, tool inputs/results, tool descriptions and schemas. Signed thinking with a finding fails closed. |
| Claude Code tool results | PostToolUse masks all JSON string values before they return to model context. |
| Claude Code Bash, Write, Edit, MultiEdit and NotebookEdit actions | PreToolUse rehydrates placeholders immediately before the local action. Writes to .zirv/memory/ and .zirv/work/ deliberately retain placeholders. Unknown placeholders fail closed. |
| Handoff and memory-harvest helper prompts | Masked before the helper model subprocess is launched. |
| Memory, mail, worker briefs, task text, workflow artifacts and configured system prompts | Masked before persistence or prompt composition; placeholders remain stable across relays. |
zirv report issue bodies | Masked before the GitHub API request and never rehydrated remotely. |
Operator text entered through UserPromptSubmit | Findings are flagged and logged by default, or the prompt is blocked when prompt = "block"; the hook protocol cannot rewrite this event. |
The guarantee is limited to Zirv-controlled boundaries. Zirv cannot inspect a harness's private API implementation, model responses, traffic from plugins or MCP servers that bypass Zirv, or content transformed into an encoding the detectors do not recognise. Names and street addresses require an operator literal or pattern. For Claude Code, typed prompts can be flagged or blocked but cannot be rewritten. These boundaries are why the native harness applies the transformation to the complete provider request, while the meta harness uses both prompt composition and lifecycle hooks.
[obfuscate]
mode = "obfuscate" # off (default) | flag | obfuscate
entropy = "flag" # flag | obfuscate
prompt = "flag" # flag | block
email_domain = "keep" # keep | mask
allow = ["fixture@example.com"]
literals_file = "~/.zirv/obfuscate-literals.txt"
patterns = [{ kind = "customer_id", regex = "CUST-[0-9]{8}" }]zirv ctx obfuscate list reports kinds, counts and first-seen surfaces without
values; reveal <placeholder> prints one value locally and records the audit
action; scan [transcript] detects without storing; and purge deletes the
repository vault. zirv ctx status reports vault counts and rehydration misses.
Repository configuration may only narrow this posture by masking email domains
or adding patterns; it cannot disable masking, replace the operator's literals
file, or add allow-list entries.
zirv ctx watches Claude Code sessions for context rot and intervenes before
quality drops: it advises, compacts early, or restarts the session with a
distilled handoff. Scoring is deterministic, and every decision is logged.
Agent support. Claude Code and Codex are both supported for supervised
sessions, but not to the same depth. Claude Code gets the full feature set:
event parsing, a rot score, turn signals and an injected system prompt. Codex
launches and supervises fine -- --agent codex succeeds both when codex
resolves to a real binary and when nothing named codex is installed at all
(that case is left to fail at spawn time with the OS's own "not found"), the
same contract --agent claude gives claude -- but with an honestly degraded
surface, because the pieces below were never verified against an
authenticated CLI:
- No event parsing, so no rot score and no structural context for codex
sessions (
parse_events/structural_contextstay empty). - No usage source: a codex session's usage reads
openai: no usage sourcerather than a real reading. - Lifecycle hooks are available and
zirv setupregisters them, but event parsing is still absent, so Codex cannot yet produce a meaningful rot score or structural context from its rollout. - Direct launches receive Zirv's composed prompt through Codex's official
per-run
developer_instructionsoverride. Windows command/PowerShell shims stay fail-closed because inline repository text would be reparsed by a shell.
That direct path covers interactive orchestrators and headless workers. Shell-shim launches retain the task-prompt fallback for mail and worker instructions but intentionally withhold repository-authored system layers.
Full event support is tracked in issue #11.
Model discovery and pricing#
zirv ctx models combines the compiled catalogue with account-local evidence:
Codex's ~/.codex/models_cache.json and model ids already present in Claude
transcripts. It never reads Codex authentication data. Known families follow
their newest available observed version; an unknown family is listed but is
never placed on the ladder. Run-time tier and rung resolution (handover and
proxy tiers, review-below, dispatch tiering, pricing equivalence) follows the
discovered ladder; Claude dispatch still passes aliases. [models].pin and
explicit model configuration hold a family on the operator's selected id, and an
empty registry leaves every resolver on the compiled ladder. Dated ids, provider prefixes and
[1m] variants normalize to the same ladder and price entry.
zirv ctx models refresh persists discovery in <state>/models.json and
conditionally fetches models.dev plus LiteLLM into <state>/prices.json with
short timeouts and ETags. When the registry is older than 24 hours or the price
cache older than 7 days, zirv ctx status and dashboard startup spawn one
detached zirv ctx models refresh --quiet (single-flight lock, at least one
hour between attempts, gated by [models].discovery/price_fetch); hooks and
launch never fetch or wait. Effective pricing is per model: ~/.zirv/prices.toml (or
price.table_path) wins, then the fetched models.dev cache, then the compiled
snapshot. A LiteLLM disagreement marks the value approximate (~$); an
unknown model remains unpriced, never free. zirv ctx status warns when a
selected model retires within 14 days and names its advertised upgrade target.
Scorecard and avoid list. zirv ctx models (and --json, now an object with
models, scorecard and avoid) appends a per-model scorecard folded from logs
zirv already writes: logs/delegations.jsonl and the workflow outcome rows. It
makes no model call and no request. Failures are classified first and only task
failures count: a delegation that exits 1 after producing output, or a workflow
that ends failed (failed verification or review rework) or closed after the
maximum review rounds. Provider/auth/quota errors, timeouts, signals, rot
exhaustion, an exit before any output, abandoned workflows and anything
uncertain are infrastructure and are excluded. Metrics per model and per stratum
(task_class, complexity, effort, where recorded): task success, first-attempt
verification pass, mean review rounds, rework rate (two or more review rounds),
cost per completed task (the effective price table) and median wall time.
Rates carry a Wilson 95% interval, and anything under 20 samples reads
"insufficient data". Review findings are not joined to a reviewer model
(evidence records the adapter only) and rot scores are keyed by transcript, so
neither is scored. zirv ctx models refresh writes the result to
<state>/scorecard.json.
[models] avoid = ["gpt-6.1-sol"] (operator-only) stops a tier or rung from
resolving to that id. The replacement is, in order, a newer or older
non-avoided version of the same family, then another non-avoided rung of the same
tier; never a cheaper tier, and the last model of a tier is kept (zirv ctx models prints a warning). An explicit pin ([worker], [review], [handover],
[model_tiers], [models] pin) wins over avoid and also prints a warning. Claude
dispatch keeps passing aliases, so an avoided Claude id behind an alias is kept
with a warning unless another alias of that tier exists. [models] auto_avoid = true (default false) additionally avoids a model whose task-success upper bound
is below the lower bound of its best same-tier peer in a stratum of at least 20
samples on both sides; the refresher records each decision (model, stratum, both
intervals, peer) in the scorecard cache and logs/model-auto-avoid.jsonl.
Callers with no config (rung lookups such as review-below) read only
ZIRV_CTX_MODELS_AVOID/ZIRV_CTX_MODELS_AUTO_AVOID. The agent tree marks an
avoided model [avoid].
Platform support. Supervision is unix only. wrap and exec need unix
domain sockets for turn signals, and wrap additionally needs raw terminal
mode and the terminal's window size. On Windows those degrade rather than
fail: exec falls back to polling the transcript, and wrap runs as pure
passthrough with the inner terminal pinned at 80x24. Everything else,
including score, handoff and status, works on all three platforms.