Skip to content
verifiablyPublic

About

The envelope and orchestrator of Science: what may run unattended, under what tier, and how the next research action is chosen

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1,007 Commits

Folders and files

Repository files navigation

autonomy

The envelope and orchestrator of verifiably: what may run unattended

What may run unattended, under what tier, and how the next research action is chosen: the run baseline and dispositions, tiers as write-class permits, the actor sandbox and session ledger, behavioural profiles and the versioned priority function. It drives science's commands as tools and holds no private kernel access. Built: tiers, the closing check, the sandbox, and autonomy run's launch side.

Part of verifiably: verifiably builds understanding of the world from small, reproducible data analyses: each one a precise question asked of real data, recorded so anyone can run it again, with the data leading and unverified claims kept out. Overview

atoms · nodes · refs · beliefs · science · autonomy

Usage

autonomy run --tier <report-only|coordination|mints> [--route KIND=FAMILY]... \
  [--lease SECONDS] [--report-fd N] [--model-route NAME] -- <actor argv>

The actor gets autonomy run's fds 0, 1 and 2, so each must be a pipe, a connected AF_UNIX stream socket or /dev/null; a terminal or a file is refused. autonomy run never reads stdin or writes stdout, help included, and its diagnostics on stderr are for a person. At a terminal, run it as

(set -o pipefail; autonomy run --tier report-only -- <actor argv> </dev/null 2>&1 | cat -v)

pipefail keeps its exit code, which a pipeline would otherwise replace with cat's:

Exit Meaning
0 clean
3 quarantined
4 unwired, or stopped before its close, its run directory left for run close
2 refused before a run directory exists: a configuration, stdio or usage error

Until science's run endpoint exists, every run that passes its checks closes unwired. A program learns its run from --report-fd: one JSON line with the run's id and directory, then one with its disposition and exit code (docs/specs/2026-10-12-envelope-design.md, decision 12).

autonomy analyst

A fresh pi analyst over declared inputs, under the same sandbox and model route, with no endpoint (docs/specs/2026-10-12-envelope-design.md, decision 14):

autonomy analyst --model-route NAME --model MODEL --thinking LEVEL --input DIR... \
  [--deny PATH]... --release DIR [--report-fd N] [--pi-dir DIR] [--node-dir DIR] \
  [--lease SECONDS] [--deadline SECONDS]

Each --input is bound read-only at its real path; each --deny joins the denied set beside the route's entries, <state> and the release directory. --thinking is required and validated before launch against pi 1.1.0's anthropic-messages model registry (off, minimal, low, medium, high, xhigh, max), refusing any level pi would clamp, omit or collapse for --model (thinking-unsupported, or model-unknown if pi's registry does not list --model). Before launch the static check, a walk of every admitted tree (no submount, no hard link in a user-writable tree, no inode shared with a denied root) and the probe all run. pi 1.1.0 runs from the pinned install (--pi-dir, PI_DIR, or ~/.local/share/pi/1.1.0) with a fixed argv (--model and --thinking from the request) and the fixed tool set read,bash,grep,find,ls,write; the prompt comes over fds 0 and 1 (pi's RPC), which the caller drives as for autonomy run. When pi exits, outputs cross from the outputs directory under scratch to the release directory, which must exist and be empty, by a walk that follows no link: a symlink, FIFO, socket, device or hard-linked file is refused and listed.

Exit Meaning
0 released, the route closed clean, pi exited 0
3 released with findings: pi exited non-zero, the route did not close clean, or an entry was refused at release
4 the analyst never started (a failed static check, walk, probe or launch)
2 refused at configuration: nothing kept

The run directory <state>/analyst/<run-id>/ holds the events log (recording the route, model and thinking level at allocation and launch), the epoch record (model, thinking, pi's lock hash and the node, bwrap, rg and fd versions), the walk report, the route's records and proxy log, the launcher's log and the release report (released files with their hashes, refused entries with their reasons); --report-fd carries its path out as for autonomy run. Scratch under <cache>/analyst/<run-id>/ is left for the caller: pi's session files there hold everything the analyst read.

The model route

An actor has no network of its own. --model-route NAME gives it one path out: a proxy on VERIFIABLY_MODEL_URL (http://127.0.0.1:4780) that forwards only the route's operations to its one upstream and adds the provider credential there. The actor never holds the credential. Point the harness at that URL with a placeholder key. For pi, that is a provider baseUrl in its models.json:

{"providers": {"anthropic": {"baseUrl": "http://127.0.0.1:4780", "apiKey": "placeholder"}}}

The proxy streams body-bearing responses with its own HTTP/1.1 chunked framing. Only a completed upstream body gets the final zero chunk, so an HTTP client that validates framing and reads to the end detects a route stop or upstream framing failure as an incomplete response. A clean EOF from an unframed upstream body cannot establish semantic completeness. Local 403/502 errors remain length delimited.

That configuration only directs pi. The proxy enforces the route whatever the actor does to it (decision 13). The route is a TOML file at $XDG_CONFIG_HOME/verifiably-autonomy/routes/NAME.toml:

api = "anthropic-messages"
upstream = "https://api.anthropic.com"
max_request_bytes = 8000000
forward_headers = ["anthropic-version", "anthropic-beta", "content-type", "accept", "user-agent"]

[header_tokens]
anthropic-version = ["2023-06-01"]
anthropic-beta = ["fine-grained-tool-streaming-2025-05-14", "interleaved-thinking-2025-05-14",
                  "mid-conversation-output-config-2026-07-01", "thinking-binding-controls-2026-08-01",
                  "inline-tools-2026-09-15"]

[credential]
file = "/home/you/.config/verifiably-autonomy/credentials/anthropic"  # 0600, yours, one link
header = "x-api-key"

[[operations]]
method = "POST"
path = "/v1/messages"
queries = ["beta=true"]
models = ["claude-haiku-5-5"]
fields = ["model", "messages", "system", "max_tokens", "stream", "tools", "tool_choice",
          "thinking", "temperature", "metadata", "stop_sequences", "top_p", "top_k",
          "output_config"]
block_types = ["text", "image", "tool_use", "tool_result", "thinking", "redacted_thinking"]
source_types = ["base64"]

What the route leaves out, the proxy refuses with a 403 that names the code, for example body-refused for an MCP server or a URL image source. just live-egress NAME runs pi through the proxy to the real API once and prints what the proxy recorded, including any beta token pi sends that the route does not list. The example is what pi 1.1.0 needed on 2026-10-17. The anthropic-messages dialect now admits only the fourteen fields shown above and client tools. Listing fallbacks, mcp_servers, container, an unknown field or a server tool type makes the route route-invalid, before the actor runs. A client tool may omit type; an explicit "custom" type needs tool_types = ["custom"] on that operation.

The dialect checks message roles, every admitted control object's keys, required fields, primitive types and nullability, content/source pairings, and recursive route permissions. System-message effort also needs output_config in fields. Client tool input_schema and tool_use.input stay opaque JSON, and valid request bytes cross unchanged. A listed beta token never grants an undefined body shape.

The supported subset preserves pi's plain/effort requests and deferred placeholder. It excludes inline tool changes, clear_at, output format/task budget, text citations, image transformations, caller/toolset metadata and tool examples, as well as server tools, MCP and containers. Unsupported requests are refused rather than rewritten. URL/file sources remain explicit operator opt-ins. See decision 13 for exact shapes and content contexts. These checks do not validate provider numeric ranges or prove that a provider accepts a particular compatibility option.

Documentation

  • User and autonomy layer design — the governing design, in the kernel repository: §7 is this layer, §8 orders the sub-projects. Work here starts at sub-project #6, the envelope, after the command framework, and ends with #7, the loop and the priority function (science.priority.v1).
  • The envelope's design, docs/specs/2026-10-12-envelope-design.md, and the plans it cites are under docs/specs and docs/plans; task records and newer working documents live in the project's records repository.
  • This layer is split from science on code-lineage independence: the envelope must not share a package with the surface it constrains, so its only seam is science's commands as tools.

License

MIT, see LICENSE.

About

The envelope and orchestrator of Science: what may run unattended, under what tier, and how the next research action is chosen

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages