zirvDocs

DocsSupervision (zirv ctx)

Context Management (zirv ctx)

README.md, lines 3217–3365View source

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.

SurfaceTreatment once obfuscate.mode is enabled
Native direct-provider and official-harness requestsThe 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 resultsPostToolUse masks all JSON string values before they return to model context.
Claude Code Bash, Write, Edit, MultiEdit and NotebookEdit actionsPreToolUse 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 promptsMasked before the helper model subprocess is launched.
Memory, mail, worker briefs, task text, workflow artifacts and configured system promptsMasked before persistence or prompt composition; placeholders remain stable across relays.
zirv report issue bodiesMasked before the GitHub API request and never rehydrated remotely.
Operator text entered through UserPromptSubmitFindings 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.

toml
[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_context stay empty).
  • No usage source: a codex session's usage reads openai: no usage source rather than a real reading.
  • Lifecycle hooks are available and zirv setup registers 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_instructions override. 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.