Command reference
This is the full, technical list of every command. If you’re just starting, you only
need a handful (create-vivary init, doctor, tropo check); the getting started
guide walks through those. Come back here for the details.
The four atmospheric layer CLIs use Python 3.11+ and no third-party runtime
dependency; the optional governed paths compose the first-party vivary-core
contract seam. Command names are tropo / strato / ozone / exo; the scaffolder
remains create-vivary. The separate, optional vivary-mcp package pins its reviewed
MCP SDK dependency.
- Install (PyPI):
pip install vivary - Run without installing (uv):
uvx --from vivary-tropo tropo check,uvx --from vivary-ozone ozone review, … - Scaffold (npm):
npm create @vivary@latest my-workspace/npx @vivary/create@latest my-workspace - From a repo checkout:
python packages/tropo/tropo.py check, etc.
Every command on this page is available from the published PyPI and npm packages
listed in the root release status, except the front-door
task verbs, which ship in vivary 0.2.0 from source until this train publishes.
Governed paths stay behind an explicit --governed flag and are opt-in, not default
behavior.
For the four layer CLIs and the scaffolder, exit codes are uniform: 0 success ·
1 findings/errors · 2 usage/config error. Gate CI on the exit code; don’t
parse text. Those commands take --json for machine-readable output.
Local run receipts are not telemetry
Section titled “Local run receipts are not telemetry”Every core CLI also accepts --receipt PATH, or the equivalent
VIVARY_RECEIPT_LOG=PATH, to append one local JSONL run receipt after the command
finishes. This is not telemetry: Vivary does not send receipts anywhere, does not
start a background process, and does not record stdout, stderr, environment variables,
file contents, raw query text, target ids, or local paths. Receipts record only a small
debug envelope: schema version, tool/version, command, flag names, argument count,
exit code, duration, Python version, and platform. Receipt targets must be regular
files; symlink targets, symlink/junction directory ancestors, directory targets, and
Windows device names are refused.
For governed Exo control, the receipt target must be provably distinct from the request
document. Exact-path and hard-link aliases are refused. A stdin request cannot be
combined with a receipt because file identity cannot be established.
Install the vivary meta package when you want a human-readable pull surface over
those receipts:
tropo check --root . --receipt .vivary/receipts.jsonlvivary logs .vivary/receipts.jsonlvivary logs .vivary/receipts.jsonl --failed --tail 10 --jsonvivary logs email .vivary/receipts.jsonl --to support@example.com --out .vivary/support.emlvivary logs email writes a local .eml draft or prints a mailto: URL. It does not
connect to SMTP, call an API, upload logs, or send mail by itself.
The CLI remains the baseline agent API. Every baseline operation an agent needs is
available without MCP or another special protocol. Commands that interact or install
also accept --yes (auto-confirm all prompts), --auto (agent selects from explicit
storage/privacy/size hints), and --dry-run (inspect without side effects). The
optional MCP adapter exposes only four read-only context operations. See
SPEC-data-layer.md for the agent CLI contract and
MCP.md for that adapter’s separate boundary.
Governed machine-readable envelopes
Section titled “Governed machine-readable envelopes”This is the schema router; the command sections below own fields, limits, and exit behavior. A named request schema is a closed object: missing or unknown fields refuse rather than being ignored. Core artifacts carried through the envelopes use the public vocabulary.
| Surface | Request | Success | Refusal / incomplete result | Contract evidence |
|---|---|---|---|---|
tropo find --governed --json |
CLI arguments plus an operator-selected root | vivary.task-capsule/v0 |
Structured CLI error; no partial capsule is presented as complete | Tropo regressions |
strato decide --governed --json |
vivary.strato-decision-request/v0 |
vivary.strato-decision/v0 |
vivary.strato-decision-refusal/v0 |
Strato regressions |
ozone verify --governed --json |
vivary.ozone-verification-request/v0 |
vivary.ozone-verification/v0, carrying Core receipt/gate verdicts and an optional repair proposal |
vivary.ozone-verification-refusal/v0 |
Ozone regressions |
exo control --governed --json |
vivary.exo-control-request/v0 |
vivary.exo-control-result/v0 |
vivary.exo-control-refusal/v0; a Core refusal remains a typed result |
Exo regressions |
vivary-mcp tools |
Closed per-tool Draft 2020-12 arguments | vivary.mcp-tool-result/v0 with status: "known" |
The same envelope with unknown or refused; oversized results refuse whole |
MCP regressions |
An Execution Receipt is vivary.execution-receipt/v0; Ozone’s nested Core outputs are
vivary.receipt-verdict/v0, vivary.gate-verdict/v0, and
vivary.context-repair-proposal/v0. Provider-neutral learning returns
vivary.recall-transition/v0 through the Core API, not a baseline CLI. Exact MCP
argument and work bounds live only in MCP.md.
vivary — the front door and local visibility helpers
Section titled “vivary — the front door and local visibility helpers”vivary <verb> [ARGS...]vivary logs [PATH] [--json] [--tail N] [--failed]vivary logs email [PATH] --to EMAIL [--subject TEXT] [--out FILE] [--json] [--tail N] [--failed]The vivary meta package installs the four core CLIs and the scaffolder, routes ten
task verbs to them, and adds a local helper for the receipt files they emit.
Task verbs
Section titled “Task verbs”A verb runs the component operation in the same process. Arguments pass through
unchanged and the operation’s output is the component’s, while the program name, the
usage line, and the hidden command list are the front door’s. vivary --help groups
the verbs the same way this table does. These verbs ship in vivary 0.2.0 from source.
The published 0.1.10 on PyPI does not have them until the next release publishes.
| Group | Verb | Standalone equivalent | Job |
|---|---|---|---|
| Workspace | vivary create |
create-vivary init |
Create a Vivary workspace scaffold. |
| Workspace | vivary adopt |
create-vivary adopt |
Plan governed context for an existing workspace. |
| Workspace | vivary doctor |
create-vivary doctor |
Validate a Vivary workspace scaffold. |
| Workspace | vivary capabilities |
create-vivary capabilities |
List the optional preset capabilities. |
| Graph and retrieval | vivary check |
tropo check |
Validate the context graph and report errors and warnings. |
| Graph and retrieval | vivary find |
tropo find |
Retrieve a token-budgeted context set for a query. |
| Policy | vivary decide |
strato decide |
Evaluate one governed decision request. |
| Review | vivary review |
ozone review |
Run a review rule pack over the context graph. |
| Review | vivary impact |
ozone impact |
Show what one node affects. |
| Coordination | vivary control |
exo control |
Dispatch one governed Core control request. |
Each route declares the component version floor that shipped the verb:
create-vivary>=0.4.3 for the workspace verbs, vivary-tropo>=0.5.4 for check and
find, vivary-strato>=0.1.3 for decide, vivary-ozone>=0.3.2 for review and
impact, and vivary-exo>=0.3.1 for control. A component below its floor is refused
with exit code 2 and a message naming the required version. The floors match the
meta-package manifest.
A missing component, or one below its floor, is refused with exit code 2 and a pip install hint naming the distribution.
The front door covers the ten approved task intents and nothing else. The standalone
create-vivary, tropo, strato, ozone, and exo commands remain the advanced
surface with every operation, and vivary --help lists them. They are not deprecated,
and a new component operation stays reachable there without a meta-package release.
Promoting an operation to a verb is a policy decision, not a code detail. It takes a
meta-package minor release, a matching floor bump, and approval of the verb name.
Routed help names the front door. vivary <verb> --help prints usage: vivary <verb> ..., and a usage error from any of the ten verbs reads vivary <verb>: error: ... above the operation’s own usage line, never the component’s command
list. vivary check --help and vivary review --help carry the component’s full
option set under the verb, and the component’s other commands are hidden because
the verb already named the operation. Those two components share one flat option
set, so a flag their routed help names for another operation, such as tropo’s
--budget for find, applies only to those standalone operations.
The routed name covers the program, not the reporting layer. A status or error line
a component prints itself still carries the component’s own name, so vivary check
in a folder with no configuration reports tropo: no tropo.toml found walking up from ....
vivary --version reports the front door. vivary <verb> --version answers
exactly as the standalone operation does, so check, find, review, and
impact report the component version that serves them, and create, adopt,
doctor, capabilities, decide, and control refuse the flag with the routed
usage because it belongs to their component’s own top level.
The floor is checked on the imported module. __version__ is read from the module
the router just imported, which is the code the verb will run, and the distribution
metadata is the fallback only for a component that declares none. In an installed
environment the floors guarantee the seam, and the signature check covers a source
checkout whose imported module is newer than its distribution metadata. Standalone
help is unchanged: tropo --help still prints usage: tropo ....
Receipt commands
Section titled “Receipt commands”vivary logs summarizes a JSONL receipt file as text or JSON. vivary logs email,
also reachable as vivary email, creates a redacted support email draft from the same
whitelisted receipt fields.
Unknown fields, malformed lines, stdout/stderr-like fields, file contents, raw query
text, target ids, and local paths are not copied into the summary.
| Command | Job |
|---|---|
logs [PATH] |
Read local JSONL receipts from PATH, VIVARY_RECEIPT_LOG, or .vivary/receipts.jsonl and print a summary. |
logs --failed --tail 10 |
Show only recent failed receipts. |
logs --json |
Return {summary, records} for agents and bug-report tooling. |
logs email ... --out FILE |
Write a local .eml draft; directory targets, symlink targets, symlink/junction ancestors, and Windows device names are refused. |
logs email ... |
Without --out, print a mailto: URL for the user’s mail client. |
vivary-mcp — optional local read-only bridge
Section titled “vivary-mcp — optional local read-only bridge”vivary-mcp --workspace ALIAS PATH [--workspace ALIAS PATH ...] [--observability off|errors|json]This optional package serves exactly vivary_find, vivary_query,
vivary_check, and vivary_capsule over local standard input/output. Workspace
roots are operator-bound at startup; tool callers cannot provide roots, executables,
commands, transports, or endpoints. The adapter has no write, repair, network,
provider, publication, deployment, or gate-approval path.
Standard output is reserved for MCP JSON-RPC. --observability writes bounded,
sanitized diagnostics to standard error: none in off, refusal/cancellation/timeout
events in errors, or all bounded lifecycle events in json.
The package pins protocol 2026-07-28 and official SDK mcp==2.0.0. External
conformance remains unproven. MCP.md owns the tool schemas, privacy and
process boundaries, passive Doctor report, and verification procedure.
tropo — the typed knowledge graph
Section titled “tropo — the typed knowledge graph”tropo [command] [paths...] [--lenient | --strict] [--json] [--quiet] [--depth N] [--max-entries N] [--out FILE] [--packs a,b] [--root DIR] [--config PATH] [--receipt PATH] [--type TYPE] [--path GLOB] [--edge FIELD[:TARGET]] [--snippet N] [--explain] [--mode text|vector|semantic] [--budget N]A document’s type is the folder it lives in (decisions/0001.md → type
decision). Metadata is only what can’t be derived from where a file sits and what it
says. tropo.toml declares the types.
| Command | What it does |
|---|---|
check [paths] |
Validate frontmatter + the graph. Opinionated: warnings fail by default. Default command. |
signal [paths] |
Print only the irreducible metadata per doc — the literal signal, noise stripped. |
types |
Print the resolved, merged type registry. |
stats |
Document counts per type + a health summary. |
graph [--json] |
Emit the typed graph: nodes (id,type,path) + edges (from,field,to,broken). |
blast <id> [--depth N] |
The blast radius of <id>: everything that (transitively) refs it — what a change could touch. |
view [graph | blast <id>] [--out FILE] |
Render the graph (or one radius) as a single self-contained HTML file. --out must stay inside the tropo root, refuse symlink targets, and rewrite the workspace output path without mutating hard-linked files outside the workspace. |
plan <change.toml> |
Simulate a change (remove/retype/break/add) and show the graph delta. |
fix [--dry-run] |
Strip redundant frontmatter (W210 — a field equal to its derived value). The only mechanical edit tropo makes. |
init [DIR] [--packs a,b] |
Scaffold a tropo.toml (optionally composing reusable type packs). |
find <text> [--budget N] [--k N] [--json] or find <text> --governed [--max-claims N] [--json] |
Human-friendly retrieval. Plain mode returns typed nodes/files with reasons and snippets under an approximate token budget. Experimental governed mode runs Tropo’s read-only workspace scan through vivary-core and returns a bounded, fingerprinted Task Capsule with evidence, conflicts, unknowns, omissions, and required checks. |
query <text> [--k N] [--mode text|vector|semantic] [--type TYPE] [--path GLOB] [--edge FIELD[:TARGET]] [--snippet N] [--explain] [--json] |
Filtered graph search over typed nodes. Default text searches id/title, frontmatter, path, body, and outbound edge context. vector uses dependency-free local typed vectors when .vivary/storage.toml enables them, prefers stored embedded vectors when current rows exist, and otherwise falls back to text search. semantic calls an explicitly configured optional semantic-memory provider and returns typed node ids. |
migrate --from file --to embedded [--dry-run] [--json] |
Move file-backed graph data into the configured embedded backend. When local vector policy is explicitly enabled, migrated rows also include typed-node vectors and provenance metadata. Cloud migration, non-file sources, backend installation, and migrated_at tracking are future 0.3.x work. |
map [--root PATH] [--depth N] [--max-entries N] [--json] |
Read-only filesystem inventory of a repo/vault/docs tree — no tropo.toml required. See Filesystem map below. |
tropo find is the default “what should I read first?” command for humans and agents.
tropo find --governed is the first opt-in vivary-core adapter. It scans only the
resolved Tropo root, passes that same normalized path as the explicit allowlist and
capsule scope, performs no fetch, write, index mutation, provider call, or memory
operation, and reports anything unproved as an unknown or omission. --max-claims
sets the capsule’s non-negative claim bound (default 24). Governed mode rejects every
plain/query retrieval modifier it does not consume: --budget, --k, --mode,
--type, --path, --edge, --snippet, and --explain. Conversely,
--max-claims requires --governed, and both governed flags are valid only with
find; invalid combinations exit 2 rather than being ignored. Plain tropo find
remains unchanged when the flag is absent.
Derived required checks use checkout-scoped names and carry both the normalized cwd
where the command must run and the exact observation that justified it. A standalone
tropo.toml derives tropo check; create-vivary doctor is derived only when the
observed root also carries the scaffold identity markers AGENTS.md and STRATO.md.
Governed search drops one-letter ASCII contraction fragments, uses NUL-framed Git
output, treats every path passed to git check-ignore as literal, and excludes tracked
paths covered by repository ignore policy or an explicit readable global/system
core.excludesFile. Effective ignore decisions over the named commit tree are checked
in one bounded NUL-framed stdin operation and committed as a nondisclosing privacy
fingerprint shared by the workspace graph and content source. Content reads name the
resolved commit with replacement objects disabled. Revision or privacy-policy drift
therefore invalidates old content even when dirty path/state facts are unchanged.
Governed search also refuses a Tropo root nested inside a larger Git worktree rather
than leaking sibling checkout facts. Every default Git command used by checkout
observation or content retrieval disables repository-configured filesystem monitors.
Workspace markers and package scripts pass through the same fail-closed ignore-policy
filter as content and dirty paths.
Reparse-point and multiply linked markers are rejected; a bounded package manifest is
read only through a descriptor whose identity is verified around the open. Ignored or
externally linked manifests cannot leak facts or derive commands. Core brackets content
with checkout observations; dirty or privacy-filtered checkouts also require two
identical content scans inside a stable bracket. A changed bracket retries once.
Persistent mutation reports content unavailable, while an unobservable dirty state
reports dirty_state_unknown; neither case compiles mixed-state facts and content.
Unicode terms and content remain supported; unrankable non-content facts become
explicit omissions instead of aborting the capsule. Question extraction preserves
order, deduplicates terms, and searches at most the first 16; core then caps matched
bytes, lines per file, claims, and omission detail.
tropo query is the lower-level filtered search primitive. By default both are
graph/text retrieval, not the CocoIndex active-context sidecar.
tropo query --mode vector is a dependency-free typed-vector mode:
it preserves type/path/edge filters and returns typed Vivary node ids without
installing an embedding provider. Enable it explicitly in .vivary/storage.toml:
[storage.embedding]enabled = trueprovider = "local-hash"dimensions = 128When the workspace is configured for embedded storage and current migrated vectors
exist, JSON output reports vector.source: "stored" and vector.index: "embedded".
Stored-vector query validates compact metadata first and asks the backend for a
bounded candidate set. If the embedded index is empty, stale, missing vectors,
dimension mismatched, too large for conservative validation, or unavailable, --mode vector reports status: fallback, fallback: "text", and a detail string, then
returns deterministic typed text results.
Workspaces that enable local vectors without embedded storage still use computed
graph-node vectors and report vector.source: "computed". Without local vector
config, --mode vector falls back to the normal typed text search.
tropo query --mode semantic is an optional-provider bridge: it requires
.vivary/memory.toml to enable a supported semantic-memory provider, and today that
means the separate vivary-memory-cognee package must be installed and indexed by
the user. It does not add Cognee or network calls to vivary-tropo core. Use
create-vivary init ... --active-context cocoindex-code when a coding workspace
needs semantic code candidates.
tropo migrate --from file --to embedded --json reports an embedding object.
Without [storage.embedding], the status is disabled and rows stay plain typed
nodes. With enabled = true and provider = "local-hash", each migrated row gets a
vector plus embedding_provider, embedding_dimensions, embedding_version,
embedding_scope, embedding_text_fingerprint, and source_fingerprint. Bad
embedding config fails before backend writes. Root and nested exclude rules,
symlink/junction pruning, and out-of-root path checks run before any text is
embedded. Embedded storage paths must stay inside the workspace and avoid symlink or
junction-backed directories. Real file-to-embedded migration replaces the embedded
node snapshot, so deleted, renamed, or newly excluded nodes do not leave stale
embedded rows.
Simple rule: start with plain tropo find or tropo query. Reach for the other
modes only when the plain graph search is not enough.
| Mode | Use it when | What changes |
|---|---|---|
text (default) |
You want deterministic local search over the typed graph. | No setup, no index, no provider, no network. |
vector |
You want local “close wording” ranking over graph nodes, but still no provider. | Requires explicit [storage.embedding] provider = "local-hash"; embedded workspaces use stored vectors when current, otherwise deterministic text fallback. |
semantic |
You already chose and indexed an optional semantic-memory provider. | Calls that provider, then filters hits back to known typed Vivary node ids. |
Useful retrieval flags:
| Flag | Effect |
|---|---|
--type TYPE |
Restrict to a document type; repeat for multiple allowed types. |
--path GLOB |
Restrict to path globs such as decisions/*; repeatable and slash-normalized for Windows paths. |
--edge FIELD[:TARGET] |
Require an outbound graph edge field, optionally pointing at a target id. |
--snippet N |
Include up to N snippet characters per result; 0 disables snippets. |
--explain |
Include stable match reasons such as title/id, frontmatter, path, body, or edge context. |
--mode text|vector|semantic |
query only: use dependency-free graph/text search, dependency-free local typed-vector search, or call the configured optional semantic-memory provider. |
--budget N |
find only: approximate token budget for the returned context packet. |
--governed |
find only: opt into the experimental Tropo scan → vivary-core evidence graph → bounded Task Capsule path. |
--max-claims N |
find --governed only: maximum capsule claims; must be a non-negative integer (default 24). |
tropo find "where is release truth owned" --root . --budget 800 --jsontropo query "release truth" --type decision --path "decisions/*" --explain --jsontropo query "agent workspace" --edge affects:agent-workspace# Opt-in governed and vector paths:tropo find "where is release truth owned" --root . --governed --max-claims 12 --jsontropo query "release truth" --mode vector --jsontropo query "release truth" --mode semantic --jsonStrictness (the check gate)
Section titled “Strictness (the check gate)”check is strict by default — unknown fields on typed documents, broken refs, and
redundant frontmatter fail it. Untyped documents are allowed only when
[base] allow_untyped = true; that setting omits W201, validates declared base
fields, and ignores undeclared fields because no type owns them. A matching derived
field on such a permitted untyped document is also retained without W210 because an
external host schema may require it. Relax emitted warnings when you need to:
tropo check # strict: any warning fails (exit 1)tropo check --lenient # warnings shown, exit 0tropo check --quiet # hide warnings, errors onlyOr persistently per vault, in tropo.toml: [base] strict = false. --strict forces
it back on (overrides a lenient config). strict is tighten-only across nested
configs — a sub-folder may turn it on, never off.
allow_untyped = false emits W201 as an error. Typed documents retain strict
W202 and W210 handling regardless of that setting. Disallowed-untyped documents
also retain W210 for redundant derived fields.
Behavioral evidence; verified: 2026-08-13.
Finding codes
Section titled “Finding codes”| Code | Level | Meaning |
|---|---|---|
E000 |
error | file can’t be read |
E001 |
error | frontmatter isn’t valid YAML / not a mapping |
E101 |
error | required field missing for the type |
E102 |
error | required field is empty |
E103 |
error | field value violates its type spec |
W201 |
error | untyped document when base.allow_untyped = false; omitted when permission is true |
W202 |
warn | unknown field on a typed document (typo? add it to the schema) |
W210 |
warn | field equals its derived value on a typed or disallowed-untyped document (noise — run tropo fix) |
W220 |
warn | ref points at no document id (broken edge) |
(Under the default strict mode, every emitted warning fails the check.)
Filesystem map (tropo map)
Section titled “Filesystem map (tropo map)”tropo map [PATH | --root PATH] [--depth N] [--max-entries N] [--json]Read-only inventory of a large repo, vault, docs tree, or file system — no
tropo.toml required, and nothing is ever written. Meant to let an agent
understand the shape of a tree without opening hundreds of files: a directory
table, extension and size summary, existing index/routing files, and folders
that look like modules but have no index.md/README.md.
| Flag | Effect |
|---|---|
PATH / --root PATH |
Tree to inventory (default: current directory) — give one or the other, not both; extra positional paths are an error. Does not need a tropo.toml. |
--depth N |
Directory-table depth, root = depth 0 (default: 3). Counts (totals, extensions, largest files, missing-index detection) always cover the whole tree regardless of --depth — only the table rows are limited. |
--max-entries N |
Cap the number of directory rows — the markdown table and the JSON directories array alike (default: unlimited). Summary sections are never capped. |
--json |
Emit a single JSON object with sorted keys and deterministic ordering (stable to diff and safe to cite). |
The output is safe to share: the root field (and the markdown heading) is the
mapped directory’s basename only — the absolute local path never appears.
Every other path is root-relative with forward slashes.
Skipped: .git, node_modules, __pycache__, .venv, venv, dist,
build, .astro, .next, target, plus any exclude patterns from a
tropo.toml found by walking up from the map root (the same is_excluded
mechanism check/graph use, applied to directories and individual
files) — a missing or invalid config never blocks the map. When the map root
sits below the config root, path-anchored excludes are rebased onto the map
root, so exclude = ["docs/private"] still hides private/ when you run
tropo map docs. Directory junctions and symlink cycles are pruned by real
path, so a looping tree never inflates counts. Individual files that are
themselves symlinks or reparse points are skipped for the same reason — each is
an alternate route to content the walk may already have counted. Hard-linked
files are not skipped: a hard link is an ordinary directory entry, so both
paths are counted. That means totals are a count of paths and size is the sum
of per-path sizes — map does not report disk usage, and two hard links to one
file contribute twice. To leave something out of the map deliberately, use
exclude or the skipped-directory list above; link type is not a privacy
control. map reads no file contents — only names, sizes and structure.
“Likely modules without an index” = directories at depth 1-2 with 5 or more
files (recursive count) and no index.md/README.md.
$ tropo map --root . --depth 2# tropo map: repo
163 file(s), 65 director(y/ies), depth ≤ 2
## Directories
| Path | Depth | Files | Size | Dominant extensions | Index? ||---|---|---|---|---|---|| . | 0 | 163 | 1.6MB | .md (89), .py (14) | yes || docs | 1 | 22 | 574.0KB | .md (18), .webp (4) | yes || packages/tropo | 2 | 6 | 128.4KB | .py (2), .md (2) | no |
## File extensions (top 10)...
## Likely modules without an index
Directories at depth 1-2 with >= 5 files (recursive) and no `index.md`/`README.md`:
- packages/tropotropo.toml
Section titled “tropo.toml”[base]derive = ["id", "title", "created", "updated"] # never required, never noiseoptional = { tags = "string-list", status = "string" } # any doc MAY carry theseallow_untyped = true # permit docs outside type roots; validate only declared base fieldsstrict = true # warnings fail check (the opinionated default)timezone = "local"
packs = ["dev-project"] # compose reusable type bundles
[types.decision] # table key = the TYPE namefolder = "decisions" # the directory basename that roots itrequired = { status = "enum:proposed|accepted|superseded", date = "date" }optional = { supersedes = "ref", related_modules = "ref-list" }Field specs: string, slug, date, datetime, url, string-list, any,
enum:a|b|c, and the graph types ref / ref-list (these become edges).
Built-in packs: dev-project, repo-graph, and coordination. Local
.tropo/packs/<name>.toml files take precedence over bundled packs. Use
coordination when exo should be allowed to write assignee:
packs = ["repo-graph", "coordination"]strato — the policy layer
Section titled “strato — the policy layer”strato decide --governed [--json] [--strict] <REQUEST.json|->decide is an explicit experimental facade over vivary-core’s pure budget,
capsule/receipt-gate, and next-loop policy. It does not persist loop state, execute
actions, or accept free-form approvals. --governed is required; - reads one JSON
request from standard input.
The request envelope is vivary.strato-decision-request/v0:
{ "schema": "vivary.strato-decision-request/v0", "policy_version": "vivary.strato-policy/v0", "actor": {"kind": "agent", "id": "agent:example"}, "authority_class": "contributor", "workspace": {"fingerprint": "sha256:..."}, "scope": {"project": "example", "paths": ["/workspace"]}, "requested_at": "2026-07-26T12:00:00Z", "decision_at": "2026-07-26T12:00:00Z", "capsule": { "schema": "vivary.task-capsule/v0", "capsule_id": "capsule_...", "fingerprint": "sha256:...", "task": { "question": "What is the next safe loop step?", "scope": ["/workspace"] }, "workspace": { "fingerprint": "sha256:...", "repair_topology_fingerprint": "sha256:...", "observed_at": "2026-07-26T12:00:00Z" }, "claims": [], "conflicts": [], "unknowns": [], "omissions": [], "required_checks": [], "budget": {"max_claims": 8} }, "state": {"turns_used": 0, "actions_used": 0}, "limits": {"max_turns": 8, "max_actions": 32}}capsule must be a complete Task Capsule. Its capsule_id must match the
deterministic identifier derived from its task question, optional filters, and workspace
fingerprint; its body must reproduce its claimed fingerprint without non-canonical or
numerically lossy values. budget.max_claims must be an integer from 0 through
9007199254740991, the largest integer that round-trips through JavaScript without
loss. A capsule altered after compilation, given a fabricated identity, or missing
compiler-owned Task Capsule fields such as budget is an invalid envelope and never
reaches core policy. Compiler callers must omit task.scope or provide a non-empty list
of non-empty path strings. scope.project is a non-empty audit label. scope.paths
must contain absolute roots and match capsule.task.scope; core’s path equivalence
normalizes separators, ignores root order, and folds case on Windows. A missing or
broader capsule scope fails closed. Both requested_at and caller-supplied
decision_at are required. The request, capsule observation, and any receipt must be
no more than 300 seconds old at decision_at, ordered consistently, and
timezone-aware. Passing the clock in the request keeps the facade pure and makes
future/stale decisions deterministic.
receipt is optional. verdict is optional only with a receipt; a receiptless verdict
is rejected instead of silently ignored. When both are present, core independently
binds and validates them before the verdict can clear a gate. Actor kinds are human,
agent, and worker; authority classes are contributor and owner, and only a
human actor may claim owner. These vocabularies and their reason codes come from
core’s authority policy. Unknown envelope fields are rejected; the Python facade also
rejects non-string mapping keys without coercing or sorting them. Free-form text such
as "status": "approved" cannot satisfy a human gate.
By default, output is a short text summary. --json emits either a validated
vivary.strato-decision/v0 document with identity fields plus core’s decision,
reason_codes, budget, and gate, or a vivary.strato-decision-refusal/v0
document with refusal reason codes and no unvalidated identity fields. Malformed JSON,
an invalid envelope, or an input too deeply nested to evaluate safely exits 2;
recursive input uses the explicit request_too_deeply_nested refusal reason. A valid
evaluation is advisory and exits 0; with --strict, a valid blocked or
request_gate decision exits 1, so CI can gate on the exit code without parsing
output.
Envelope reason codes distinguish invalid shapes and policy versions, authority refusals, workspace/scope mismatches, stale/future evidence, and a verdict submitted without its receipt. Core’s loop, budget, gate, receipt-integrity, and Ozone-verdict reason codes pass through unchanged.
ozone — the review layer
Section titled “ozone — the review layer”ozone [review | impact <id> | packs] [--root DIR] [--json] [--strict] [--pack structure|context-budget|editorial|all] [--receipt PATH]ozone verify REQUEST --governed [--json] [--strict] [--receipt PATH]Where tropo check asks “is each document valid?”, ozone reviews the whole graph
and a change’s impact. It reads tropo’s graph in-process (one graph, no fork).
| Command | What it does |
|---|---|
review |
Run a deterministic review pack. Defaults to --pack structure for stable CI; use --pack context-budget for context bloat, --pack editorial for writing workspaces, or --pack all for every pack. Advisory by default (exit 0); --strict makes it a gate (exit 1 on warnings). |
impact <id> |
The blast radius of a node — what (transitively) depends on it, with distance + the edge field it came in by. |
packs |
List the available rule packs. |
verify REQUEST --governed |
Verify a governed capsule, receipt, and named gate through core’s pure integrity/sufficiency contracts. Optionally include a workspace graph for bounded dry-run repair proposals. |
Governed evidence verification
Section titled “Governed evidence verification”verify is opt-in and read-only. --governed is rejected on review, impact, and
packs; it cannot silently select an ordinary command:
ozone verify request.json --governed --json --strictREQUEST is a JSON file, or - for stdin:
| Field | Contract |
|---|---|
schema |
Exactly vivary.ozone-verification-request/v0. |
workspace |
Exactly {"fingerprint": "..."}; must match the capsule. |
verified_at |
Caller-supplied timezone-aware instant. |
capsule |
Complete vivary.task-capsule/v0; its body fingerprint and deterministic ID are recomputed before core delegation. A declared task.scope is a bounded, nonempty list of absolute roots. Optional task.filters must use the compiler’s list-shaped {field, equals|includes} contract with a nonblank value. Both rules apply whether or not the request includes a graph. |
receipt |
Complete Execution Receipt bound to the capsule. Its check records and claim lists are shape-validated; every check must name a capsule effective required check and carry its exact command. claims_verified and claims_unverified must be disjoint and together equal both claims_in_scope and the capsule’s claim IDs. All claims are verified only when the check list is nonempty and every check passed. Otherwise, all claims remain unverified. Omission or malformed/tampered evidence cannot produce a sufficient aggregate result. |
gate |
Named gate with core-owned required_checks, require_claims_verified, max_unresolved_conflicts, and max_unresolved_unknowns constraints. |
graph |
Optional matching vivary.workspace-graph/v0. Core reprojects the graph from checkout paths and facts. Every derived node, edge, conflict, unknown, omission, deterministic ID, evidence field, and canonical allowlist must match that projection. The recomputed workspace_fingerprint must match the request, and observed_at must match the capsule. The capsule’s graph-derived claims and unknowns must equal compiler selection for the same graph, task, filters, scope, and budget. Ozone returns a bounded vivary.context-repair-proposal/v0; every proposal has requires_gate: true and writes_performed: 0. |
content |
Optional complete vivary.workspace-content/v0 source artifact with exact top-level and nested record shapes, a timezone-aware observed_at, a nonempty absolute canonical allowlist, contained checkouts, and reason-consistent refusals. It is required with graph when the capsule carries workspace.content_fingerprint. Core verifies that fingerprint and recompiles the complete capsule, including content-derived claims, unknowns, and omissions. None, {}, {"checkouts": []}, and a valid complete artifact with empty checkouts and refusals are semantically empty and do not change capsule bytes. |
Before recursive canonical validation or Core loading, Ozone applies an iterative
JSON-work ceiling to the complete request. Excess input returns
request_work_unbounded. Core owns the exact top-level capsule and receipt field sets.
Unknown additions return sorted unknown_capsule_field:<name> or
unknown_receipt_field:<name> reasons without a second generic shape reason.
For graph-backed repair, Ozone accepts at most 1,000 scope roots, graph nodes, graph
edges, and graph unknowns, plus 300 graph conflicts. It limits scope-path work to
100,000 comparisons and bounds checkout-pair scans, route evidence, repair products,
and canonical re-projection before proposal construction.
The capsule task question must be a nonblank string. A declared task scope contains
absolute roots and bounds every narrated claim, conflict side, unknown, and omission
path. Known dirty-entry and content-match paths must be normalized, safe
checkout-relative paths. Persisted drive and UNC path identities compare
case-insensitively on Windows and non-Windows verifier hosts. Core accepts at most 1,000
scope roots and bounds checkout/content candidate counts, source-path containment and
prefix construction, plus candidate-by-question-term-and-filter scalar work for direct
compiler callers. Ozone also bounds the scalar work of scope joins against checkout,
execution-root, refusal, unknown, and declared-check paths.
Optional task.required_checks must be a nonempty list of nonblank name, command,
and traversal-free canonical absolute cwd records with unique names. With a graph,
each cwd must name an observed Git checkout execution root related to task.scope; a
package-scoped task may name its nearest enclosing checkout root. Without a graph,
every cwd must lie within the declared task scope.
Declarations add checks, never remove or rewrite commands derived from workspace
evidence, and resolve required_check_undetermined unknowns only for their checkout.
The capsule retains each declaration unchanged in its top-level required-check list.
Without a graph, that effective list must equal the task declaration exactly, or be
empty when no declaration exists. Otherwise Ozone returns
graph_required_for_effective_checks; resubmit with the matching graph so Core can
reconstruct derived checks. With a graph, Ozone derives checks and
undetermined-check unknowns from graph evidence.
Graphless capsules with filtered_out, claims_over_budget, or
collation_domain_excluded omissions return
graph_required_for_compiler_omissions. The matching graph is required because the
omission alone cannot prove whether stripped content affected compilation.
Content containment work, combined graph-plus-content candidate aggregation, and claim
ranking across question terms and structured filters have fixed Core ceilings. Requests
beyond any ceiling return repair_work_unbounded before the nested comparisons run.
Accepted scope, checkout, worktree, Git-common-dir, and content roots are
traversal-free canonical absolute paths; rejected outside-allowlist source paths remain
checkout authority.
Every capsule claim must retain its compiler-owned nonempty identity, subject, path,
fact, text, status, and selection reason plus list-shaped evidence and selection
signals. Core recomputes each claim ID from its subject, fact, and claim text. With task
filters, every claim must retain the exact normalized matched_filters record.
Claim-owned fact and path values must satisfy that record.
When the request includes a graph, Core recompiles the complete capsule from the
capsule’s task and budget plus the supplied source artifacts. A capsule carrying
workspace.content_fingerprint requires the exact fingerprinted content artifact;
Core never substitutes retained claims for that source. Without a content binding,
Core recompiles from the graph alone. The supplied capsule must canonically equal the
result, including workspace identity and freshness, claims, conflicts, unknowns,
required checks, and every omission. Graph-only filtered_out, claims_over_budget,
collation, dirty-path, ignored-path, refusal, and scoped-conflict omissions therefore
match compilation exactly. Omission variants have fixed field shapes; unknown or
reshaped variants are invalid capsule data.
Each graph-derived claim must reproduce the graph fact’s
compiler-generated path, fact, text, status, and evidence. Every claim subject must name
a checkout in that graph, and the claim path must equal the checkout path. Label,
repository, and branch filters must match the graph profile for every claim subject.
Question-match signals must match the named profile field. Repository identity signals
require a known repository identity. Without task filters, the matched_filters record
must be absent.
Question/content match signals imply the question_match tier unless a preserved
conflict takes precedence. A content-match signal belongs only to a known
content_match claim and must reproduce its normalized question term and narrated path.
Signal identities are checked once in linear time; duplicates are invalid. The
allowlisted tier carries only its baseline signal. A re-fingerprinted partial or
semantically inconsistent claim is still an invalid capsule. Question and content
signal terms, fields, and narrated paths must be strings. Other JSON types are invalid
capsule data, not match candidates.
Each capsule unknown must match one complete compiler-owned variant: an unknown graph
fact, undetermined required check, stale content snapshot, or incomplete content search.
Variant-specific identity, status, reason, marker, revision, and evidence fields retain
their compiler-defined types; optional unobserved source fields may remain null.
When a request includes a graph, the capsule must preserve every graph unknown inside
the declared task scope. Content and required-check unknowns may remain in addition.
Each capsule conflict must retain its compiler-owned kind, repository, question, status,
reason codes, and review_required decision. It must preserve at least two distinct
checkout sides, each with a nonempty checkout ID and path plus head_revision,
head_ref, last_fetch, and evidence fields. A re-fingerprinted partial or duplicate
conflict side is still an invalid capsule. Claims whose subjects occur on preserved
conflict sides must retain the compiler’s conflict_side tier and the exact matching
conflict-signal set; claims without a preserved conflict cannot assert that tier or
signal. A conflict that crosses a declared scope becomes a conflict_outside_scope
omission naming only the in-scope subject path and opaque conflict ID. No out-of-scope
side or path enters the capsule.
The receipt field may be omitted, producing core’s valid missing-evidence result. When
the field is present, it must be a complete mapping whose schema is exactly
vivary.execution-receipt/v0; receipt_id and fingerprint must be nonempty strings.
Malformed receipt values are refused before core delegation.
Every receipt check must carry a nonempty name and command plus a valid outcome. Core
requires its name to exist in the capsule’s effective required_checks and its command
to equal that required command, including for direct Core and Strato callers. Duplicate
records for the same name and command remain valid evidence for core’s worst-outcome
aggregation.
The graph workspace_fingerprint commits its emitted checkout paths, effective worktree
roots, fact statuses and semantic values, and normalized observation refusals. Evidence
command text does not enter that identity. Core reprojects the complete derived graph
from the committed facts. Checkout, repository, revision, branch, remote, and
dirty-artifact nodes must retain their exact generated fields and deterministic IDs.
Edges must retain their generated endpoints, evidence, and IDs. Conflicts, unknowns,
omissions, and refusals must match the projection exactly. A copied fingerprint cannot
validate forged graph contents. Known fact values must match their fact-specific type;
unknown facts require a nonblank reason. Invalid records fail closed before projection.
neighbor_of is symmetric but its stored direction follows observation order. Core
normalizes only that direction during comparison and still validates the original edge
ID. All other relationships remain directed. The capsule’s
workspace.repair_topology_fingerprint separately commits checkout IDs and paths,
normalized repository nodes, and checkout_of relationships that can drive repair
proposals. This binds remote-backed and inferred no-remote linked-worktree groups.
Every divergent conflict has at least two sides and covers every checkout related to its repository. The graph must preserve every conflict and side carried by the scoped capsule. It may retain additional full-workspace conflicts so core can withhold unsafe repair proposals outside that scope. Repair identifiers that can repeat during expansion are limited to 128 bytes in JSON string encoding.
A claims_over_budget omission must list exactly
min(omitted_count, 16) entries, whether or not the request includes a graph. When the
count exceeds 16, truncated must be true; at or below the cap, that field must be
absent. This matches core’s compiler contract.
Requests whose total checkout-pair scan count, potential repair count, route-proposal
evidence count, derived estimate, candidate-by-question-term ranking work, or canonical
re-projection work exceeds its ceiling are refused with repair_work_unbounded before
canonical re-projection. Projected neighbor_of pairs must fit the 1,000-edge
repair-graph ceiling. Remaining re-projection work counts graph JSON and repeated
checkout-path expansion, with a cap of 10,000,000 canonical-JSON work units.
Envelope-level validation requires the capsule’s workspace observed_at and the
receipt’s created_at to be at or before the request’s verified_at and no more than
300 seconds earlier; the receipt also cannot predate the capsule observation. It rejects
workspace mismatches, non-canonical or unknown fields, invalid shapes, and deeply nested
request documents with a typed vivary.ozone-verification-refusal/v0, never a traceback.
In default plain-text output, reason fragments are JSON-escaped before they are written
so valid JSON field names cannot cause a terminal encoding failure.
Malformed receipt shapes are envelope refusals with exit code 2. An intact receipt
whose fingerprint or capsule binding was tampered with reaches core’s receipt and gate
verdicts; that valid aggregate evaluation is insufficient.
A valid result is vivary.ozone-verification/v0. Its receipt_verdict,
gate_verdict, and optional repair_proposal are the raw fingerprinted core
documents, not rewritten copies. Pass gate_verdict unchanged to Strato’s verdict
field. --strict exits 1 when a valid evaluation is insufficient;
invalid request documents and refused request envelopes exit 2; advisory mode exits
0 for a valid evaluation. The
--receipt PATH CLI flag records Ozone’s privacy-preserving local run envelope; the
evidence receipt itself belongs inside REQUEST.
For a file-backed request, the run-receipt target must not identify the request
document, including through a hard-link alias. When REQUEST is -, Ozone refuses
run-receipt output because stdin does not expose enough source identity to prove the
receipt target is distinct. Parser-only help and version actions do not read a request;
if their receipt target could alias the request, Ozone shows the requested output and
suppresses that run receipt.
The structure pack
Section titled “The structure pack”| Rule | Severity | Fires when |
|---|---|---|
change-unverified |
warn | a changes/ node has no verification edge |
change-ungated |
info | a changes/ node has no gates edge |
module-unverified |
info | a modules/ node has no verification edge |
orphan |
info | a node has no edges in or out |
broken-edge |
warn | an edge points at a missing node (tropo check enforces this) |
The context-budget pack
Section titled “The context-budget pack”context-budget reviews only public routing/startup surfaces:
AGENTS.md, CLAUDE.md, STRATO.md, STATE.md, SOUL.md, README.md,
modules/index.md, and modules/*/index.md. It does not read private memory files
such as USER.md, MEMORY.md, memory/**, heartbeat reports, .vivary/**, or
.git/**.
| Rule | Severity | Fires when |
|---|---|---|
module-index-missing |
warn | a modules/<name>/ directory has no index.md |
legacy-module-file |
warn | modules/<name>.md coexists with modules/<name>/index.md |
always-on-large |
info | a root routing contract exceeds its fixed line/char threshold |
module-index-large |
info | modules/index.md or modules/*/index.md exceeds 120 lines or 8000 chars |
bulk-load-cue |
info | public routing text tells agents to read/load/scan/open whole repos, docs trees, folders, or everything |
duplicate-routing-block |
info | an exact normalized routing block over 100 chars repeats across public routing surfaces |
The editorial pack
Section titled “The editorial pack”editorial reviews writing workspaces using graph edges only. It stays silent for
non-writing workspaces, and looks for coverage across drafts/, manuscripts/,
reviews/, editorial-reviews/, edits/, revisions/, outlines/,
structures/, and beats/.
| Rule | Severity | Fires when |
|---|---|---|
draft-unreviewed |
warn | a drafts/ or manuscripts/ node has no linked review |
draft-unedited |
info | a draft/manuscript has no linked edit or revision |
draft-structure-missing |
info | a draft/manuscript has no linked outline, beat sheet, or structure note |
review-unlinked |
warn | a review is not linked to a draft or manuscript |
edit-unlinked |
warn | an edit/revision is not linked to a draft, manuscript, or review |
ozone review --root . # advisory reportozone review --root . --strict # gate: exit 1 if any warning (CI / pre-merge)ozone review --root . --pack context-budgetozone review --root . --pack editorialozone review --root . --pack all --jsonozone impact human-gates --root . --jsonexo — the coordination layer
Section titled “exo — the coordination layer”Legacy graph coordination
Section titled “Legacy graph coordination”exo [conflicts | board | claim <id> --agent <handle> | roles] [--root DIR] [--json] [--receipt PATH]| Command | What it does |
|---|---|
conflicts |
Among active work items, report pairs that share an outbound target. |
board |
Group work items by status and, when declared, @assignee. |
claim <id> --agent <handle> |
Update an opted-in work item’s top-level assignee. |
roles |
Print the bounded worker contracts. |
exo conflicts --root .exo board --root .exo claim local-ci-baseline --agent connie --root .exo rolesexo claim is the legacy graph write. It remains separate from governed control. It
accepts an optional leading @ in an agent handle, writes no undeclared field, and
refuses symlinked or out-of-workspace work item targets. For a hard-linked work item,
it replaces the workspace link without changing the other linked file.
Governed control
Section titled “Governed control”exo control REQUEST --governed [--json] [--strict]control dispatches one bounded Core transition. REQUEST names a UTF-8 JSON file, or
- for standard input. The caller supplies every state value and persists every returned
projection. Exo creates no control store.
Each request has exactly these top-level fields:
{ "schema": "vivary.exo-control-request/v0", "operation": "claim", "state": {}, "input": {}}A successful transport response has this outer shape:
{"schema":"vivary.exo-control-result/v0","operation":"claim","result":{}}A transport refusal has this outer shape:
{"schema":"vivary.exo-control-refusal/v0","reason_codes":[]}Unknown or missing fields refuse. A Core refusal remains a result with its own typed reason codes.
Before Core dispatch, Exo rejects request files larger than 1 MiB, values nested more than 64 levels, collections larger than 10,000 entries, strings larger than 1 MiB, and documents requiring more than 100,000 value visits. Direct API values receive the same iterative, cycle-safe value preflight; the file-size bound applies at CLI read.
| Operation | Required state |
Required input |
|---|---|---|
claim |
claims |
scope, actor, now |
release |
claims |
claim_id, actor |
expire_leases |
claims |
now |
dependencies |
tasks |
task_id |
handoff |
claims |
claim_id, receipt, capsule, from_actor, to_actor, workspace_revision, created_at |
record_execution |
execution_log |
receipt, capsule |
complete |
task, execution_log |
none |
task_view |
task, execution_log |
none |
claim may also carry authority_class and lease. handoff may carry
to_authority_class. An actor is exactly { "kind": ..., "id": ... }. now and
handoff timestamps are explicit. Core reads no wall clock. A lease is live only when
granted_at <= now < expires_at.
Claim IDs bind the normalized scope, exact actor, authority class, lease, and creation
time. Caller ledgers must contain unique, recomputable, pairwise-disjoint active claims.
A projection beyond 10,000 active claims or 10,000 total scope paths returns
claim_work_unbounded. Expired claims remain until an explicit expire_leases
projection. A handoff receipt’s runtime actor must be the claim holder, and its creation
time cannot predate the claim or lease or follow the handoff.
Save this request as claim-request.json, then dispatch it:
{ "schema": "vivary.exo-control-request/v0", "operation": "claim", "state": {"claims": []}, "input": { "scope": {"project": "example", "paths": ["src"]}, "actor": {"kind": "agent", "id": "docs-worker"}, "now": "2026-08-01T12:00:00Z", "lease": { "granted_at": "2026-08-01T12:00:00Z", "expires_at": "2026-08-01T13:00:00Z" } }}exo control claim-request.json --governed --json --strictSave this request as expire-leases.json to derive a new caller ledger at an explicit
time:
{ "schema": "vivary.exo-control-request/v0", "operation": "expire_leases", "state": {"claims": []}, "input": {"now": "2026-08-01T13:00:00Z"}}exo control expire-leases.json --governed --json --strictUse --strict for automation. It exits 1 for a transport refusal, a Core
decision: "refused", or any returned reason code. Without --strict, a typed
refusal exits 0. Usage and configuration errors exit 2.
Core owns the lifecycle semantics, including record-only handoffs, complete
capsule-and-authorized-receipt evidence, replay suppression, conflict refusal, and
failed-evidence preservation. See the Core control contract.
control does not add a scheduler, runner, network call, MCP server, repair write, or
publishing path. Requests and results are not telemetry. Local --receipt records
remain local under the receipt policy.
create-vivary — the scaffolder
Section titled “create-vivary — the scaffolder”create-vivary init <target> [--preset coding|second-brain|knowledge-work|writing] [--force] [--adapter agents|claude] [--active-context cocoindex-code] [--storage auto|file|embedded|cloud] [--provider lancedb|sqlite-vec|qdrant|astra] [--memory none|local|cognee] [--auto] [--yes] [--dry-run] [--json] [--size small|medium|large] [--privacy local|cloud] [--receipt PATH]create-vivary wizard <target> [--storage auto|file|embedded|cloud] [--provider lancedb|sqlite-vec|qdrant|astra] [--memory none|local|cognee] [--yes] [--dry-run] [--json] [--receipt PATH]create-vivary capabilities [--preset coding|second-brain|knowledge-work|writing] [--json] [--receipt PATH]create-vivary doctor <target> [--json] [--trend] [--repair] [--yes] [--receipt PATH]create-vivary adopt <target> [--preset coding|second-brain|knowledge-work|writing] [--adapter agents|claude] [--yes --plan PLAN_HASH] [--recover PLAN_HASH] [--json] [--receipt PATH]create-vivary record <target> <modules|changes|decisions|verification|gates>/<slug>.md --from SOURCE --capsule CAPSULE_JSON [--yes --plan PLAN_HASH] [--json] [--receipt PATH]| Command | What it does |
|---|---|
init <target> |
Create the five-file thin-v0.3 contract: three Vivary payload files plus bounded AGENTS.md and .gitignore host integrations. No templates, skills, placeholders, or starter records. |
wizard <target> |
Re-run the setup wizard on an existing workspace to reconfigure storage and optional semantic-memory policy. |
capabilities |
Report optional capabilities plus Core and all four governed role surfaces for the selected preset. |
doctor <target> |
Validate thin metadata and context, startup reachability, privacy, optional adapters, Tropo health, declared capabilities, and pending transaction recovery; read older full workspaces without migrating them. |
adopt <target> |
Produce a deterministic brownfield plan, then apply only its exact approved hash. Creates at most three payload files and may separately create or patch two bounded host-integration blocks. Dry-run by default. |
record <target> <record> |
Validate and plan one typed record earned by real work, bound to one Task Capsule. Apply only the exact human-approved plan hash; never creates a pack or unrelated records. Dry-run by default. |
| Flag | Effect |
|---|---|
--preset coding|second-brain|knowledge-work|writing |
Select thin workspace policy (default coding); does not seed starter content. |
--force |
Compatibility flag. Init still refuses every nonempty target, including an existing thin workspace. Use adopt for governed changes. |
--adapter agents|claude |
Add one bounded runtime projection; repeat for both. Each adapter owns one file of at most 1,200 bytes. |
--active-context cocoindex-code |
For coding workspaces, declare the capability and ignore its local index path within the same five-file seed. Does not copy guidance, install, index, enable MCP, or transmit source. |
--storage auto|file|embedded|cloud |
Storage backend to configure. auto defaults to file with no new dependencies. Explicit cloud locality can select cloud configuration. Embedded storage always requires --storage embedded or the matching wizard choice. |
--provider lancedb|sqlite-vec|qdrant|astra |
Which implementation to use for the selected tier. lancedb is the shipped embedded provider. |
--memory none|local|cognee |
Optional semantic-memory policy. Default: none. local writes local-only policy. cognee writes gated Cognee policy and graph docs, but does not install Cognee or index content. |
--auto |
Agent mode. Skip all interactive prompts. Use explicit storage and privacy choices. Otherwise, keep file storage. |
--yes |
Confirm an install or write already selected by another explicit flag. It does not select a provider. |
--dry-run |
Print what would be written or installed; do not mutate the workspace. |
--json |
Machine-readable output. Init reports the thin contract and files; adoption reports the deterministic plan/apply envelope described below. |
--size small|medium|large |
Workspace classification hint. Size never selects or installs a provider. |
--privacy local|cloud |
Local keeps file storage unless another tier is explicit. Cloud can select cloud configuration. |
--repair |
Doctor-only. Include conservative repair diagnostics. Recognized legacy-full workspaces are always report-only. |
--yes |
With doctor --repair, apply deterministic safe repairs only to supported non-legacy contracts. It never writes a legacy-full workspace. |
Capability status
Section titled “Capability status”Version 0.4.2 reports a fixed Core-and-role inventory and passive MCP interoperability status alongside the existing storage, semantic-memory, and preset sidecar rows:
| Capability | Command | Authority |
|---|---|---|
governed-context:core |
Library only | library-only |
governed-context:tropo |
tropo find --governed |
read-only-context |
governed-policy:strato |
strato decide --governed |
decision-only |
governed-verification:ozone |
ozone verify --governed |
verification-and-proposal-only |
governed-control:exo |
exo control --governed |
projection-only |
interop:mcp |
vivary-mcp local stdio entry point |
read-only-context |
Each capability row has installed, install_status, reason_codes, and
missing_install. Status is installed, not-installed, incompatible, or
probe-failed. Resolution checks probe-failed first, then incompatible,
not-installed, and installed.
Missing role or Core artifacts report capability_dependency_missing.
A role reports capability_contract_incompatible when its installed manifest, Core
floor, normalized distribution identity, or module record violates the public contract.
The same status applies to an invalid exact console-script target, generated launcher,
or competing import artifact.
A bounded read or parse failure reports capability_probe_failed. Only not-installed
rows include ordered install hints.
The MCP row is optional and not-installed by default. A compatible row requires the
recorded vivary-mcp entry point, its exact mcp==2.0.0 dependency declaration, the
matching SDK version, local standard-input/output transport, and the exact four tool
names. Doctor reports external conformance as unproven; it does not import or start
the adapter.
The status reader considers the interpreter’s canonical purelib and platlib roots,
at most eight system-site candidates, and at most eight user-site candidates. It then
selects at most eight unique roots that appear on the active interpreter path. It
inspects up to 256 sys.path entries and 10,000 entries across the selected roots.
Each distribution must include exactly one non-empty Metadata-Version, Name,
Version, and Requires-Python field. It may declare at most 64 extras and 256
dependency records. Each dependency record may contain no more than 4 KiB.
For every selected distribution, bounded final-release comparison checks whether the
active interpreter satisfies Requires-Python. Unsupported or unsatisfied constraints
are incompatible. The combined 256 KiB metadata-and-entrypoint byte cap bounds
unrelated metadata headers.
When an install hint names an extra on the same distribution, the reader verifies the
normalized Provides-Extra declaration. It also verifies the complete installed
dependency closure selected by that extra. Nested extras use the same rule.
The reader follows only selected-extra edges. It accepts at most eight extra nodes and
16 dependency edges. Maximum depth is four levels. Each dependency may name four child
extras and four version clauses. A missing dependency remains not-installed.
Malformed, ambiguous, unsupported, or unsatisfied selected dependency declarations
are incompatible. Malformed distribution metadata, I/O failures, and work ceilings
report probe-failed.
When an optional provider’s install hint names another package’s extra, the reader
derives the provider’s minimum version from that installed package’s matching
Requires-Dist declaration. Floor extraction uses the owner’s independently
validated distribution metadata before projecting its separate governed-role
dependency contract. A pre-governed but otherwise valid owner can therefore establish
the optional provider floor. The reader validates an installed owner’s floor even when
the provider is absent, and a present provider also requires the owner. A missing,
duplicate, malformed, or unsatisfied floor is incompatible. When both are absent, the
capability remains not-installed. The inventory does not copy the floor. Provider
version comparison accepts bounded PEP 440 release, post-release, and local forms.
Pre-release, development, and invalid forms fail closed.
For every declared console script, the reader maps the selected active installation
root to its matching interpreter scheme. It checks the generated launcher in the
scripts directory for that scheme. The launcher must be a regular executable file.
Its distribution RECORD must contain exactly one matching row. The reader also binds
modules and control metadata to that exact distribution. It accepts at most 20,000
RECORD rows and 2 MiB.
The reader does not import role or provider packages, dispatch ambient import or
distribution hooks, invoke entrypoints, spawn commands, or use the network.
Optional absence, incompatibility, and probe failure remain visible but do not make
capabilities or Doctor unhealthy. Doctor derives its preset from the workspace
README.md. A missing, unsupported, or unreadable Preset: declaration yields
"preset": null and omits only preset-specific active-context rows. Existing
configured-Cognee health checks remain separate from this passive capability subreport.
doctor always requires active ignore rules for USER.md, MEMORY.md, memory/*,
and .strato/private/. Published workspaces can predate heartbeat-reports/* or
*.vivary-tmp; without declared semantic memory, Doctor reports those missing newer
rules as warnings and names the line to add. A published semantic-memory profile makes
heartbeat-reports/* strict while leaving its newer *.vivary-tmp gap as an upgrade
warning. A current semantic-memory profile makes all six rules strict. Comments,
negations, and unrelated patterns that merely contain those names do not count.
If .vivary/memory.toml exists, doctor reports semantic memory as disabled,
healthy, configured, unavailable, misconfigured, or privacy-failed without
requiring optional Cognee support to be installed.
Doctor compatibility and declared configuration
Section titled “Doctor compatibility and declared configuration”Doctor classifies a workspace before selecting its validation baseline. A valid
.vivary/workspace.toml selects the three-file thin-v0.3 payload baseline and then
validates metadata version, relative paths, context, privacy/runtime exclusions,
declared adapters and capabilities, startup reachability, and transaction recovery.
Older full workspaces remain read-compatible. They report
workspace_contract = "legacy-full" and preserve their detected
legacy_layout = "legacy-v0.1" or "indexed-v0.2+". Doctor does not migrate,
regenerate, or silently normalize those files. Their existing strict baseline and
declared storage/memory validation remain intact.
The compatibility object uses schema_version = 2 and contains
workspace_contract, legacy_layout, baseline_missing, contract_missing,
declared_capability_problems, recommended_missing, and recommended_upgrade.
Recommendations never make ok false. A workspace with neither a valid thin contract
nor a recognized legacy signature is not silently claimed as healthy Vivary state.
When a storage or semantic-memory config is declared, Doctor validates its recognized
published or current profile. Embedded storage needs nonempty path and provider;
Qdrant cloud storage needs nonempty provider, url, api_key, and collection;
Astra needs nonempty provider, endpoint, api_key, and collection. Unknown cloud
providers are errors.
Local memory needs state_path, allow_network, and require_explicit_index. The
published Cognee profile adds api_key_env; its empty value remains a valid explicit
opt-out. The current Cognee profile also emits allow_without_api_key and
allow_telemetry; if either current-profile field is present, both are required and
type-checked. Both profiles require enabled, provider,
mode = "semantic-provider", and every [memory.privacy] field. The three privacy
booleans must stay fail-closed. private_paths must retain the four-path published
floor (USER.md, MEMORY.md, memory/**, and heartbeat-reports/**); the current
template adds .strato/private/**, but Doctor accepts the published v0.3.1 profile
without it. A published profile missing the newer *.vivary-tmp ignore receives an
upgrade warning; the same gap remains an error for a current memory profile.
memory.enabled = true with memory.provider = "none" is a misconfiguration, not an
enabled no-op.
Plain doctor (including --json) is read-only, including legacy recommendations and
failing reports. It exits 0 exactly when errors is empty and 1 otherwise;
warnings do not change the exit code. doctor --trend is the explicit state-write
exception, and doctor --repair --yes is the explicit repair-write exception.
doctor --repair --json reports repair.actions. Each action has kind, status,
path, summary, and applied, with extra details when useful. Recognized
legacy-full workspaces always return repair.mode: "report-only"; --yes does not
write them, and --trend does not create state during that report. Use a reviewed
thin adoption plan for an approved legacy change.
For supported non-legacy contracts, doctor --repair --yes --json applies only
deterministic safe actions, reruns Doctor, and remains nonzero while errors exist.
Non-workspace targets, symlinked, junctioned, hardlinked, non-file, and non-UTF-8
targets are refused or kept as manual guidance. Broken references, complex content,
active-work conflicts, and missing coordination setup are never auto-mutated.
Legacy-full diagnostics retain the published private-placeholder exceptions. These are compatibility rules, not thin-workspace output or permission to recreate files:
memory/*!memory/.gitkeepheartbeat-reports/*!heartbeat-reports/.gitkeepdoctor --trend is opt-in and is the only thing that writes Doctor trend state
(.vivary/runtime/doctor-state.json for thin workspaces; the prior .vivary path for
legacy workspaces). Plain doctor stays read-only. It compares this run’s graph health,
module-index count, and file count under modules/ against the prior run and reports
signed deltas — a short “trend vs <date>” section in human mode, or a trend object
(prior/current/deltas) in --json mode. The first --trend run on a workspace
has no prior state, so it reports “first recorded run” and just writes the baseline. A
corrupt or unreadable state file is treated the same way — a warning, not a failure —
and gets overwritten with a fresh one.
When --storage embedded is selected and vivary-tropo[embedded] is not installed, init installs it through pip. --dry-run prevents the install. In JSON mode, "installed": ["lancedb"] reports the added provider. Without --yes, one confirmation prompt appears before the install. For scripted embedded storage, pass --no-wizard --storage embedded --yes or --auto --storage embedded --yes. Plain --auto keeps file storage and never selects Cognee.
adopt — governed brownfield setup
Section titled “adopt — governed brownfield setup”adopt inspects an existing repo or vault and returns a deterministic plan without
writing. Its default payload ceiling is three files: .vivary/context.md,
.vivary/workspace.toml, and STATE.md when absent. It may separately create or patch
the bounded generated blocks in AGENTS.md and .gitignore.
| Flag | Effect |
|---|---|
--preset coding|second-brain|knowledge-work|writing |
Select thin workspace policy. Default is inferred from the file mix; no starter content is seeded. |
--adapter agents|claude |
Add one optional bounded projection; repeat for both. |
--yes |
Apply a plan. Requires --plan from the reviewed dry-run. |
--plan PLAN_HASH |
Bind apply to the exact deterministic plan. Inputs and kept files are revalidated before writes. |
--recover PLAN_HASH |
Produce a read-only rollback plan for the interrupted transaction bound to that adoption hash. Apply only with --yes --plan RECOVERY_PLAN_HASH. |
--json |
Report mode, root, preset, contract, creates, patches, optional_projections, kept, conflicts, privacy, and plan_hash; applied results also include Doctor proof. |
The two host edits are managed blocks, not arbitrary rewrites. Existing user content
outside those blocks is retained byte-for-byte. Exact older generated adapter content
may be proposed for bounded replacement; newer, partial, or user-edited content is a
conflict. Nested .gitignore rules that expose private/runtime paths also fail closed.
Before writing, adoption rechecks the entire plan and privacy boundary. Apply then uses
a plan-bound transaction marker, journal, and exact-byte backups. Ordinary exceptions
roll back immediately. A process interruption at any replacement boundary is recovered
only through the explicit matching --recover command.
Adoption does not scan for candidate modules, copy routers, create graph records, generate templates or skills, or exclude arbitrary existing content. Pre-existing Vivary-looking files are either valid kept inputs or explicit conflicts.
# Preview; writes nothing.create-vivary adopt . --json
# Apply the exact reviewed plan.create-vivary adopt . --yes --plan sha256:<plan-hash> --json
# Plan recovery only after an interrupted transaction reports this adoption hash.create-vivary adopt . --recover sha256:<plan-hash> --json
# Apply only the exact separately approved recovery plan.create-vivary adopt . --recover sha256:<plan-hash> \ --yes --plan sha256:<recovery-plan-hash> --jsonrecord
Section titled “record”record is the bounded write seam for a healthy thin-v0.3 workspace. It accepts one
complete UTF-8 Markdown source, validates it against the workspace’s actual Tropo type
policy, and proposes one create or update under
.vivary/records/{modules,changes,decisions,verification,gates}/. The source is capped
at 256 KiB. The record path is exactly one folder plus one safe .md filename; nested
paths, unknown folders, links, and hard-linked destinations are refused.
Planning is read-only. The command accepts either the full JSON output of
tropo find --governed --json or the complete public capsule object returned as the
MCP vivary_capsule result. It verifies the capsule’s canonical integrity, exact
workspace scope or fingerprint, and current workspace state. The plan binds the
action, destination, before and after hashes, capsule id/fingerprint/workspace
fingerprint, and an exact plan_hash. Apply requires --yes and that reviewed hash,
replans before writing, rejects changed source or destination bytes, writes atomically,
and runs Doctor. A failed post-write Doctor restores the exact previous record or
removes the newly created record tree.
# Save the complete governed capsule JSON, then prepare one typed Markdown file.tropo find "record the verified slice" --governed --json > task-capsule.jsoncreate-vivary record . changes/verified-slice.md \ --from ./verified-slice.md \ --capsule ./task-capsule.json \ --json
# Inspect the one-record plan, then apply only the exact approved hash.create-vivary record . changes/verified-slice.md \ --from ./verified-slice.md \ --capsule ./task-capsule.json \ --yes --plan sha256:<plan-hash> \ --receipt .vivary/runtime/receipts.jsonl --json--receipt is optional and uses the existing privacy-preserving local run envelope;
it does not record target/source paths or capsule values. The record file itself is
the durable typed artifact. This command deliberately has no batch, starter-pack, or
automatic materialization mode. The optional MCP adapter still exposes exactly four
read-only tools; a capsule returned over MCP supplies evidence binding, not write
authority. The human-approved record transaction is a separate CLI action.
vivary-cognee
Section titled “vivary-cognee”vivary-cognee ships from the optional vivary-memory-cognee package. It is not part
of core Vivary and does not run unless a workspace explicitly configures
--memory cognee, installs the adapter, and approves provider writes.
vivary-cognee doctor --root . [--json]vivary-cognee index --root . [--dry-run] [--yes] [--json]vivary-cognee recall "<query>" --root . [--k N] [--json]vivary-cognee forget --root . --yes [--json]| Command | What it does |
|---|---|
doctor |
Reports Cognee adapter readiness, typed node count, manifest path, and stale/healthy/unavailable status without importing Cognee runtime. |
index |
Builds privacy-filtered typed Tropo node packets and sends them to Cognee. Requires --yes unless --dry-run is set, and requires memory.cognee.allow_network = true before provider runtime calls. |
recall <query> |
Calls Cognee recall when network/provider runtime is explicitly allowed and the manifest identity matches the current graph, then returns only hits that contain known Vivary node ids from the current typed graph. |
forget |
Removes the workspace dataset from Cognee provider memory. Requires --yes and explicit provider runtime allowance. |
The adapter uses tropo graph truth for ids, types, paths, and edges. Provider state
under .vivary/memory/cognee/ is rebuildable cache, not source truth. The generated
Cognee policy starts with allow_network = false; that is an enforced gate so
doctor/dry-run receipts can prove readiness without importing provider runtime or
making embedding or LLM calls. Runtime provider calls also require api_key_env or
the explicit local-provider setting allow_without_api_key = true. Third-party
Cognee telemetry is disabled by default unless the workspace explicitly sets
allow_telemetry = true.
Approved index replaces the prior workspace-bound dataset, and recall refuses stale
or missing manifests so provider results cannot outrun Tropo graph truth. Dataset
names include a workspace path hash even when a label is configured. Tropo refuses
workspace-local vivary_cognee.py adapter imports for semantic query mode; installed
adapters must resolve outside the workspace and current working tree, and must expose
the hardened vivary-memory-cognee 0.1.2+ adapter contract.
# Human flow — interactive wizard:create-vivary init my-workspace
# Agent flow — fully non-interactive, file-backed, and dependency-free:create-vivary init . --preset coding --auto --size large --privacy local --json
# Inspect available optional pieces for a preset:create-vivary capabilities --preset knowledge-work --json
# Inspect without doing anything:create-vivary init my-workspace --auto --dry-run --json
# Existing examples:create-vivary init my-workspace --preset knowledge-work --memory localcreate-vivary init my-workspace --preset writingcreate-vivary init my-notes --preset second-brain --memory cognee --no-wizard --dry-run --jsoncreate-vivary init my-codebase --preset coding --active-context cocoindex-codecreate-vivary doctor my-workspace# expected for a plain coding workspace: doctor: ok (9 node(s), 28 edge(s), 0 broken)The four presets share the same five-file contract and differ only in compact policy metadata. They do not create preset-owned modules, tasks, decisions, or verification records. Real records are added lazily when work produces evidence.
See GETTING-STARTED.md for a first run, HOWTO.md for task recipes, SKILLS.md for the agent skills, and the homepage FAQ.
