"never accept the model's summary as evidence, only artifacts it can't fake by narrating"
Tycho runs as a completion hook on your agent's harness. It records what every turn actually did — files touched, commands run and what they returned, what the agent claimed — and checks that against the artifacts: git, the filesystem, exit codes, the harness event stream.
Only a hook on the event stream sees the process. A diff sees a result. CI sees a result. A
second model sees a diff. Tycho was there when the agent ran pytest, got exit 1, and then said
tests pass.
Instant, free, silent, always-on — no API key, no second session, no LLM. Nothing leaves the machine (no LLM anywhere in the trust path). Stdlib only, Python ≥ 3.11. Claude Code and Codex. Linux, macOS, Windows — WSL2 counts as Linux, install inside the distro.
On Claude Code, Tycho costs you no tokens and no context: it has a channel that reaches you and not the model. On Codex it costs a little of both — see Per-harness notes.
Install
curl -fsSL https://swail.dev/tycho/install.sh | sh # standalone binary, no Python
brew install swail-labs/tap/tycho # same binary, upgradable
uv tool install tycho-cli # have Python? (pipx works too)
npx @swail-labs/tycho <command> # npm wrapper, same binary
The PyPI package is tycho-cli (tycho was taken); it installs the tycho command.
Pick one channel per machine — npm -g and brew both want to own bin/tycho.
The installer sets Tycho up as it finishes, so there is nothing to run afterwards. If you installed another way, or said no:
tycho install # once, for every repo on this machine
That writes two things — the completion hook, status badge and slash commands into your
harness's user-level config, and one line (.tycho/) in your global git ignore file. It names
every path before it writes any of them.
The second write is what lets Tycho stay out of your repos: with .tycho/ ignored
machine-wide, it never edits any repo's .gitignore, so git status stays clean everywhere.
It only runs inside git repos, and defers to any repo that has its own install.
That's it. Every repo you open is verified from the next turn on, and Tycho says so once, the first time it meets a repo.
tycho off # stop verifying this repo (tycho on to resume)
tycho uninstall # remove it from this machine
TYCHO_AUTO=0 # only verify repos where you ran `tycho init`
Per-harness notes
Claude Code — nothing to do.
Codex (the CLI and the ChatGPT desktop app alike) — two things to know.
Approve Tycho's hooks the first time Codex asks. Codex will not run a hook it hasn't shown
you: the first session after one appears says "hooks need review before they can run", and
until you approve, the config is read and nothing runs. tycho doctor reports it if you skipped
it. After tycho install that approval is once for the machine; after tycho init it's once
per repo.
Tycho uses a little of your context and usage here. Claude Code gives a hook two separate channels — one the human reads, one the model reads — so a verdict reaches you for free. Codex has one field, and both of you read it. So whenever Tycho has something to say on Codex, it ends the agent's turn and hands it the text to show you, which spends a small amount of context and one model turn. That is the cost of being told at all: the alternative, and what Tycho did before 0.2.1, is silence.
It stays small and it stays bounded. Verdicts are capped to a handful of lines with tycho show
for the rest; a routine turn says nothing; the relay is opt-in and limited to three re-checks per
turn; and anything sent this way carries an instruction telling the agent to relay it and stop,
not to act on it. Nothing about the verification itself changes — the checks, the record and the
verdict are the same offline, stdlib-only code on every harness.
Adopting a repo
tycho install deliberately writes nothing into your repos. When you want a repo to carry
Tycho with it — shared with your team, checked in CI:
tycho init # in the repo
That adds a .tycho.toml to commit (scope, relay — settings the whole team gets) and a
prepare-commit-msg hook stamping a Tycho-Attestation: trailer onto commits an agent worked
on. It merges with hooks you already have, backs up what it changes, and refuses to touch a
config it can't parse. On a machine where you never ran tycho install, tycho init also
wires that repo's hooks, so a single repo works on its own.
Upgrading from 0.1.x
Your per-repo installs keep working untouched, and take precedence over a machine-wide one.
Two things changed names: tycho uninstall now removes Tycho from the whole machine — it
used to mean this repo, so it refuses and asks when you run it in a repo that has its own
install. Use tycho off to stop verifying one repo, or tycho uninstall --here to remove its
hooks. tycho init --global still works as a spelling of tycho install.
After a turn
Most turns print nothing. Tycho speaks when a check says the turn isn't proven, when the prose claims done but the evidence doesn't reach it, when a run of proven turns breaks, or when a turn touches far more files than this repo's turns usually do. A condition that fired the last two turns goes quiet, so a standing failure doesn't repeat at you forever.
🔍 Tycho: FAILED — file_state: docs/product-direction.md — missing
✓ attempted · executed ✓ artifact_changed ✓ claim_supported
2 files · pytest -q… failed · +7 more
`tycho show` for the full receipt
The second line is the acceptance ladder: attempted → executed → artifact_changed → claim_supported. Unreached rungs render ·, not ✗ — a gap in the evidence isn't a failure.
Verdicts are VERIFIED · FAILED · STALE (evidence exists but predates the code) ·
INDETERMINATE (ran, couldn't conclude) · UNSUPPORTED · OVERRIDDEN.
What you can do with it
tycho show # the full receipt for the last turn (or `show <id>`)
tycho log # what agents did here, newest first (--verdict, --since, -n)
tycho blame src/app.py # which turns touched this file, what they claimed, what backed it
tycho review # which changed hunks no test covered and no command exercised
tycho backfill # record what agents did here before Tycho was installed
tycho verify # re-run the engine over a session on demand
tycho count --ledger # catch and blind rates, per model and per check
tycho exec -- pytest -q # run it and put the real exit status on the record
tycho attest --verify # check a commit's trailer against the record
tycho scope add 'src/**' # bound where the agent may edit (opt-in; `--exclude` denies)
tycho relay --on # let the agent see its own verdict and keep working (off by default)
tycho rewrite --on # route a piped runner through `tycho exec` for you (off by default)
tycho off # stop verifying this repo (`tycho on` resumes)
tycho doctor # is the hook installed, current, and firing?
tycho help lists everything, including hook, install, init, on/off, uninstall,
statusline, run, override and update.
Exit codes — one per adverse kind, so a gate picks what blocks. 1 FAILED, 3 STALE
(verify); 6 unexercised (review --exit-code); 7 trailer mismatch (attest --verify); 5
installed-but-broken (doctor); 2 usage, 4 couldn't complete. The hook itself always exits 0
and never blocks your agent.
What Tycho won't claim
Tycho answers can't tell rather than guessing, so there are places it deliberately says less than you might expect:
blame PATH:LINEis file-level. The record stores which turns touched a file, never which lines. Joining throughgit blamewould put a confident wrong name on a line.reviewspeaks about the record, not execution. "No command Tycho recorded ran after this hunk" — never "this line never ran". There's no per-line coverage here.- The attestation proves consistency, not authenticity. Unkeyed SHA-256 over a record this
machine wrote: tamper-evidence against accidents, not a defence against a motivated agent.
--verifycan answer cannot tell, and a pruned record must never read as a forged one. tool_call_provenanceis advisory. Two claim families (web, issue trackers), matched on family presence only. Tuned to never emit a false FAIL, at the cost of recall.- A backfilled turn has no verdict, and never will.
tycho backfillreplays transcripts written before Tycho was here, but most checks are functions of state that no longer exists — whether an edited file is on disk now, an mtime now against a run three weeks ago. So those rows record what the transcript proves (files, commands, claims, when) and readUNVERIFIED. They're excluded from every rate incount --ledger. Tycho will not tell you what it "would have caught". verifier_integrityraises the cost of tampering; it doesn't make it impossible. It reports a turn that edits.tycho/,.tycho.toml, a harness hook config or a git hook — via the edit tools, a shell redirect, or a mutating command. It cannot see a write smuggled through an interpreter (python -c "open('.tycho.toml','w')") or a path the shell expanded from a variable, and a process running as you can always reach the file. Tycho's own state is judged inside this repo only — another tree's.tycho/isscope_drift's business, not a verdict here. It is the check[checks].disablemay not switch off.- A piped runner's exit status is gone before Tycho can read it.
pytest | tail -20hands the harness tail's status, so the verdict falls back to reading pytest's summary line — inference, and only as good as the vocabulary inengine/runlog.py.tycho exec -- pytest | tail -20fixes it at the source, andtycho rewrite --ondoes that for you. Rewriting is opt-in because Claude Code matches yourBash(...)rules against the rewritten command, so an allow rule you already granted can stop covering it; under--permission-mode bypassPermissionsthere are no rules to void and it always applies. - Nothing polls in the background. A hook that died goes undiagnosed until someone runs
doctor.
Configuration
Settings in .tycho.toml (scope, relay, override, rewrite), state in <repo>/.tycho/. Command
strings, evidence and agent prose are pattern-filtered for secrets before hitting disk — a match
becomes a visible [REDACTED] — but it's best-effort, so keep .tycho/ out of git regardless.
TYCHO_HOME · TYCHO_CLAUDE_HOME · TYCHO_AUTO · TYCHO_STATUS · TYCHO_RELAY_MAX ·
TYCHO_TURNS_MAX · TYCHO_COMMANDS_MAX · TYCHO_NO_UPDATE_CHECK · TYCHO_INSTALL.
The one thing that touches the network: a SessionStart hook asks pypi.org once a day whether a
newer Tycho exists, disclosing your version and IP. TYCHO_NO_UPDATE_CHECK=1 turns it off.
License
Apache-2.0. Contributing: CONTRIBUTING.md.
Metadata
Release files for tycho-cli 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| tycho_cli-0.2.1.tar.gz | 830.4 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tycho_cli-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.1 MB
Release files / tycho_cli-0.2.1.tar.gz
| Download URL | tycho_cli-0.2.1.tar.gz |
|---|---|
| Size | 830.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e134bc2d711c553957eafe45bd666c2fd318e55f7801b32fdbe9921f3b0070c2
|
|
BLAKE2b-256 checksum How to use checksums |
f93f52c08b7ee894b52d22c7a6223ab79e67e0bccebc1bc00f5e565fc5955b21
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 30, 2026.
Transparency logRelease files / tycho_cli-0.2.1-py3-none-any.whl
| Download URL | tycho_cli-0.2.1-py3-none-any.whl |
|---|---|
| Size | 235.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e94dce2c93ffdcbfc74e835c5569178c866222862b8dc27b2962b37e458df480
|
|
BLAKE2b-256 checksum How to use checksums |
da6fdf0bd889421d76650bf40f51efb080e9f2f5908ae218c889943652631d18
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 30, 2026.
Transparency logSecurity for tycho-cli 0.2.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for tycho_cli-0.2.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 30, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.