Gather Usage

September 9, 2026 ยท View on GitHub

Gather turns difficult source intake into replayable, digest-backed research packets. It is designed for local CLI use, MCP hosts, and larger Project Telos workflows that need provenance before synthesis.

Install

python -m pip install gather-engine

From a source checkout:

python -m pip install -e ".[dev]"

Run

gather status --json
gather doctor --json
gather demo --json
gather --help

The same package can be exercised from source with:

python -m gather --help

Pilot

gather pilot run MANIFEST --output DIR          # capture once, write report + receipt
gather pilot refresh DIR                        # re-capture monitored sources, archive the prior view
gather pilot verify DIR                         # network-free verification of the whole root
gather pilot bundle DIR --output FILE --visibility shared
gather pilot bundle DIR --output FILE --visibility full --include-private-evidence

Exit codes: manifest refusal exits 2; a required-source failure or verification failure exits 1; success exits 0. A full bundle (which carries the private artifact root) requires --include-private-evidence. See docs/PILOT.md for the manifest boundary and the private/shared evidence split.

Web-data engine

Each command prints a receipt as JSON.

gather caps                      # what this install can do (fast / browser / stealth)
gather extract <url|file.html>   # Markdown + a per-block provenance receipt
gather markdown <url|file.html>  # structured Markdown only
gather crawl <url> --depth 2 --max-pages 50   # a witnessed, hash-chained crawl ledger

Optional capability backends (core stays zero-dependency; a missing one degrades to UNVERIFIABLE, never a fake):

pip install 'gather-engine[fast]'      # lxml, about 2x parse speed
pip install 'gather-engine[browser]'   # Playwright JS render (then: playwright install chromium)
pip install 'gather-engine[stealth]'   # curl_cffi TLS/browser impersonation

Federation

Validate a source-federation registry and compile its capture plans, all offline:

gather federation validate registry.json --json
gather federation plan registry.json --json

A registry file is a list of source rows or {"sources": [...]}; each row carries id, system, family, domain, access, adapter, url, scope, and priority. Validation is a closed contract: unknown access tokens, priorities, or extra fields are typed rejections, and the validated snapshot is sealed, so a row cannot be edited after witnessing without breaking the seal. plan adds one deterministic capture plan per source, derived from its access policy. Neither command probes a source, and a registry row is never reported as coverage or availability.

Two further audits treat a federation decision as a sealed claim surface:

gather federation policy policy.json --json
gather federation entity entities.json --json

A policy file is a list of rules or {"rules": [...]}; each rule carries rule, source_capture_ref (a content-hash capture ref), failure_class, and superseded. Each failure class maps to one typed verdict (429 to retryable_source_lead, 403 to access_escalation, 503 to retry_after). A rule with no provenance capture, an unknown failure class, or a superseded flag is a typed rejection.

An entity file is a list of candidates or {"candidates": [...]}; each candidate carries candidate_id, identifier_path (the named join key, such as ror), a confidence in [0, 1], evidence_refs, and exact_id_join. Candidates must be ordered by descending confidence, a match with no named identifier path is rejected, and a promotion to resolved requires the top candidate to be an exact-id join. Both snapshots are sealed under the federation digest, so an edited field breaks the seal.

Corpus and readable context

gather corpus list DIR
gather corpus verify DIR
gather corpus search DIR --terms keyword --json
gather corpus availability DIR
gather corpus context DIR --json
gather corpus context DIR --json --select ROW_REF[:START[:LIMIT]] --expect-digest SHA256

context inspection returns gather.readable-corpus/v1: current corpus digest, bounded row previews, body status, availability state, and row refs. Missing, corrupt, unsafe, oversized, or read-budget-exhausted bodies are named and return no excerpt. Inspection verifies only the returned rows when row caps omit the rest of the catalog.

Selection returns gather.readable-context/v1: selected source/comment text, source refs, full body hashes, ranges, omissions, and a deterministic selection_digest. It requires the current digest from inspection so a stale view cannot be silently selected. It re-hashes selected bodies at read time, refuses unsafe paths or text over budget, and does not claim the selected source is true, complete, secret-free, or supports a claim.

Range semantics are text-level. Gather first verifies the stored body against the exact source text receipt. For readable context, CRLF and CR line endings then become LF, and start/limit are Python string character offsets over that readable view. sha256, verified_sha256, and source_sha256 identify the exact source text; view_sha256 identifies the full LF-normalized readable view, and view_codec names the transformation. A selected slice does not need to hash to either full-body value. The selected slice, range, source refs, source/view hashes, storage status, and omissions are folded into selection_digest.

New corpus rows carry a versioned exact-UTF8 storage witness that is folded into the corpus digest. Rows written before that witness are legacy compatible when Gather can reconstruct exactly one source text from exact UTF-8 bytes or the old Windows text writer's LF-to-CRLF expansion. That reconstruction does not prove old raw object-byte integrity. --expect-digest and MCP expected_corpus_digest are the trust boundary for downgrade detection: a pinned old digest fails after storage metadata is stripped, while recomputing the digest after mutation accepts the current catalog state.

The reader pins the opened corpus root while loading the catalog and bodies. It does not prove that a caller-resolved DIR stayed below an approved workspace parent; hosts that derive a corpus path from workspace authority must bind that parent relationship themselves before calling Gather.

Python hosts that already hold a retained, identity-bound corpus root can call inspect_corpus() or select_context() with a same-process CorpusRootDescriptor. Gather duplicates the borrowed fd/HANDLE, validates the duplicate against the expected CorpusRootIdentity, and then uses the same bounded confined reader. The caller must keep the original descriptor live through the call. On Linux/WSL filesystems where retained directory fds cannot supply stable confined reads, currently including WSL Windows-drive 9p/v9fs mounts, Gather refuses opened corpus, descendant directory, and catalog/body file descriptors before reading catalog.jsonl or body objects. This descriptor handoff is intentionally not exposed through CLI or MCP, where corpus inputs remain directory strings.

MCP

Use gather mcp when a host needs the tool over stdio. The MCP surface should stay aligned with the CLI envelope and receipt fields. gather.context inspects stored corpus rows or exports selected readable context with the same type-strict caps and expected-digest guard as gather corpus context. For catalog tools, scope is a post-fetch content filter that keeps rows whose title or body contains any term as a case-insensitive substring, so verified: true on an empty scoped catalog means the returned digest verified after filtering, not that acquisition returned no source records.

gather mcp

Verify

python -m pytest
python examples/demo.py
python examples/pipeline.py

For public/developer delivery checks:

python -m public_surface_sweeper . --workspace --json

Boundary

Gather may collect material from live sources, but outward-facing receipts should prefer source references, content hashes, timestamps, and verdicts. Do not publish raw private payloads, secrets, credentials, or source material whose license or privacy posture does not allow redistribution.