{"id":"851f0aa2-bb17-4c13-a141-d3bbf3bbdf44","slug":"hanko","kind":"mcp","name":"hanko","summary":"An agent that files receipts. RYO-CHAN Hackathon 2026.","owner":"IamHarrie-Labs","repoUrl":"https://github.com/IamHarrie-Labs/hanko","license":"MIT","licenseTier":"permissive","trust":"community","stars":0,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-09-03T21:39:00.000Z","addedAt":"2026-10-30T22:38:23.669Z","logoUrl":"https://avatars.githubusercontent.com/u/271234626?u=edc03b753dfb44fa3a5ae43f07bf3e97b34958e5&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["files-documents","data-analytics"],"tags":["agentic-ai","ai-agents","crypto-trading","hackathon","python"],"runtime":"remote","local":false,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-30T22:39:33.836Z","sample":false,"url":"https://conduits.dev/tools/hanko","language":"Python","transports":["http"],"install":{"remoteUrl":"https://tryhanko.vercel.app/api/mcp"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# hanko\n\n<div align=\"center\">\n\n[![CI](https://github.com/IamHarrie-Labs/hanko/actions/workflows/ci.yml/badge.svg)](https://github.com/IamHarrie-Labs/hanko/actions/workflows/ci.yml)\n[![Tests: 349 passing](https://img.shields.io/badge/tests-349_passing-555555)](tests/)\n[![RYO tools confirmed live: 6](https://img.shields.io/badge/RYO_tools_confirmed_live-6-555555)](ARCHITECTURE.md#transport-mcp-and-rest-reach-the-same-facts)\n[![Tracks entered: 3](https://img.shields.io/badge/tracks_entered-3-8b6a27)](https://tryhanko.xyz/judges)\n[![License: MIT](https://img.shields.io/badge/license-MIT-555555)](LICENSE)\n\n</div>\n\n**Hanko turns crypto market chatter into a sized position, or an honest\nrefusal, with the evidence attached, and can prove that same decision\nbyte for byte, years later.**\n\nPoint it at a token and the voices you trust, on X, Telegram or Bluesky. It\nreads what each one actually said, checks whether they're independent voices\nor one thesis repeated three times, and decides:\n\n```\nENTER TOKENA  size 3.28%  confidence 0.66\n  + independent_voices: 2 independent voice(s): voice_alpha, voice_gamma\n  ~ echo voice_beta: marked as a repost or quote by the source\n  ! wrong if price_usd < 1.0625 at any point within 72.0h (thesis fails if price falls 15% from entry)\n  review at 2026-08-30T12:00:00Z\n```\n\n<sub>Sample output: real engine, synthetic inputs. `voice_alpha`/`voice_beta`/`voice_gamma` and `TOKENA` are fictional test fixtures. None of them is a real account or token.</sub>\n\nRun that same input again in a year and it produces the identical decision\nid, because nothing about deciding touches the network, a clock, or a model.\nIt only ever reads what was actually stored. That is the whole project: \"prove it\" is a command you can run.\n\n**Judging? Start at [/judges](https://tryhanko.xyz/judges)**: every line of\nthe published rubric, mapped to a page or command you can check yourself.\n**[tryhanko.xyz](https://tryhanko.xyz)** has the pitch and a sample\nreceipt (synthetic inputs, labelled). **[/try](https://tryhanko.xyz/try)** runs a piece of it, live,\nfrom your browser, no install. Full technical detail is at `/docs`.\n\n![The landing page: the sample receipt above, typed out by the real engine against synthetic sample inputs](.github/images/landing.png)\n\n---\n\n**RYO-CHAN Hackathon 2026 submission, three tracks.** **Track 1 (Autonomous\nAgents)** is the agent above, built to the Multi-KOL Narrative Agent spotlight.\n**Track 3 (New Skills)** is [`exit_liquidity`](hanko/skills/exit_liquidity/README.md):\nwhat it costs to get *out* of a position, from an executable venue quote where\none exists, in RYO's own response envelope. It is also the size gate the agent\nruns before committing. **Track 2 (Dashboards & Interfaces)** is three screens:\nthe [workbench](https://tryhanko.xyz/workbench), the\n[failure drill](https://tryhanko.xyz/drill) and the\n[Echo Index](https://tryhanko.xyz/echo).\n\n### What it has shown, live\n\n- **A ledger that grows every three hours, with no key.** A scheduled\n  workflow reads public Telegram, Bluesky and news voices on BTC, ETH, SOL\n  and BONK, decides, seals the run with its replay bundle, stamps it on\n  OpenTimestamps and commits it to [`live/`](live/). Every decision is graded\n  72 hours later against the conditions it wrote down first.\n  [/ledger](https://tryhanko.xyz/ledger) shows what changed since the\n  last run, most important first (D-20).\n- **Graded against its own conditions, including when it was wrong.** The\n  first 32 scheduled decisions came due on 2026-10-27 and 28: every ENTER\n  held (7 of 7, realised returns between -0.9% and +1.1%), and 7 of 25\n  refusals were proved wrong because a voice they said would flip them\n  arrived. Those reviews also exposed two grading bugs, fixed in D-21.\n- **A sealed live ENTER that anyone can re-derive.** BTC, 2026-10-23,\n  `runs/run_836b976720f63dc0.json`. The manifest lists every watchlist entry,\n  including the PASS and both ABSTAINs. It is anchored in Bitcoin block\n  972,817, mined 35 minutes after the seal, and `hanko verify-run` replays\n  all four decisions from the committed bundle with no network and no key.\n  (Until 2026-10-24 that bundle was not actually in the repository; D-19.)\n- **A measured exit cost.** BONK, $50,000: the Jupiter quote\n  says 2.3%. The skill's own constant-product model said 7.2%, and the report prints the 3.1x gap.\n- **A study that audited the agent.** The [Echo Index](study/echo-index/REPORT.md)\n  ran Hanko's echo check over 2,094 real posts from 15 crypto channels. Echo\n  inflation was 1.005x, published as found. First, though, it found that\n  Hanko's own detector missed verbatim copies padded with an ad or a footer\n  (D-14, fixed).\n- **Nine failures injected on purpose.** RYO down, every source down, a\n  rate-limited quote venue, the model down, a prompt injection the model\n  actually obeyed, tampered evidence, an edited sealed run, a source dying\n  mid-sweep, a restart. All nine hold, computed live on\n  [/drill](https://tryhanko.xyz/drill). The restart scenario broke the\n  first time it ran (D-18, fixed).\n\nA 判子 is the seal a person stamps on a document to commit to it. The mark\ngoes on before the outcome is known and can't be taken back, which is exactly\nwhat this agent does with every decision it makes: sizes it, commits to it,\nand states in advance what would prove it wrong.\n\n## Submission\n\n- **[Project Submission Form](RYOCHAN-Project-Submission-Form.pdf)** (PDF, this repo)\n- **[Demo video](https://youtu.be/fm0B4hZQCwQ)**\n- **[Announcement post on X](https://x.com/IamHarrie/status/2096908693967179844?s=20)**\n\n## Contents\n\n- [Verify it yourself in 60 seconds](#verify-it-yourself-in-60-seconds)\n- [Who this is for](#who-this-is-for)\n- [Explore without running anything](#explore-without-running-anything)\n- [The one rule](#the-one-rule)\n- [Layout](#layout)\n- [Why it is built this way](#why-it-is-built-this-way)\n- [Use](#use)\n- [Status](#status)\n- [exit_liquidity, the new Track 3 skill](#exit_liquidity-the-new-track-3-skill)\n- [Decision records](#decision-records)\n- [The review loop](#the-review-loop)\n- [The sweep](#the-sweep)\n- [Architecture, decisions, limitations](#architecture-decisions-limitations)\n\n## Verify it yourself in 60 seconds\n\nNo live credential, no account, no network. Every number below regenerates\nfrom synthetic fixtures committed in this repository (fictional sample voices and a fictional token, written for testing):\n\n```bash\ngit clone https://github.com/IamHarrie-Labs/hanko && cd hanko\npip install -e \".[dev]\"\n\npytest                                          # 349 tests, offline, ~20s\n\n# make a decision, then prove it replays from stored bytes\nhanko decide x --token TOKENA --subject voice_alpha --subject voice_beta \\\n  --subject voice_gamma --market fixtures/market_tokena.json \\\n  --as-of 2026-08-27T12:00:00Z --fixture fixtures/x_three_voices.json\nhanko audit                                     # re-derives it, insists it reproduces\n\n# the Track 3 skill, against the same fixture, no network\nhanko exit-liquidity TOKENA --size 50000 --market fixtures/market_tokena.json\n\n# the first live ENTER, re-derived from its committed bundle\nhanko verify-run runs/run_836b976720f63dc0.json\n\n# break it on purpose: nine failure scenarios, offline\npython -c \"from hanko.drill import run; print([r['held'] for r in run()])\"\n\n# re-derive every number in the Echo Index\nhanko echo-index publish\n```\n\n## Who this is for\n\nBuilders and traders who want an agent's reasoning to be auditable rather\nthan taken on faith. Someone who has been burned by a bot that says\n\"strong buy signal\" with nothing behind it, and wants to see the evidence,\nthe falsifiers, and the honest gaps instead. Also the judges reviewing this\nsubmission, who can replay any decision from the exact stored bytes it saw.\n\nNot built for anyone who wants the agent to execute trades. It doesn't, and\nby the hackathon's own rules, none of the RYO tools it's built on can either.\nEverything here researches and sizes a position; nothing here moves funds.\n\n## Explore without running anything\n\n| Open | What it shows |\n|---|---|\n| [tryhanko.xyz](https://tryhanko.xyz) | The pitch, and a sample decision receipt (real engine, synthetic fixture inputs, labelled as such) |\n| [/try](https://tryhanko.xyz/try) | `exit_liquidity` live: RYO facts, DexScreener depth, a Jupiter quote, in RYO's envelope |\n| [/judges](https://tryhanko.xyz/judges) | Every rubric line, mapped to where to check it |\n| [/workbench](https://tryhanko.xyz/workbench) | Remove a voice or tighten a threshold on the sample decision (synthetic inputs, labelled as such) and watch the actual `decide()` verdict move, and which rule caused it |\n| [/drill](https://tryhanko.xyz/drill) | Nine real failures injected into the real code, each defence computed live |\n| [/echo](https://tryhanko.xyz/echo) | The Echo Index: how much crypto consensus is one voice repeated, on 2,094 real posts |\n| [/docs#track1-live](https://tryhanko.xyz/docs#track1-live) | Two real decisions, live X evidence and live RYO facts, neither cherry-picked |\n| [/docs#mcp-transport](https://tryhanko.xyz/docs#mcp-transport) | What a live MCP handshake and a live `x_search` call actually return |\n\n<table>\n<tr>\n<td width=\"50%\"><img src=\".github/images/try.png\" alt=\"The /try page: pick a token and a size, run a live exit_liquidity check against the real RYO platform\"></td>\n<td width=\"50%\"><img src=\".github/images/docs.png\" alt=\"The /docs page: the six-package layer table and the purity boundary, in full\"></td>\n</tr>\n<tr>\n<td align=\"center\"><code>/try</code>, live against the real platform</td>\n<td align=\"center\"><code>/docs</code>, the full technical writeup</td>\n</tr>\n</table>\n\n## The one rule\n\n```\nfetch()  touches the network, is allowed to fail, and is never deterministic\nparse()  touches nothing, and is a pure function of its payload\n```\n\nThe agent only ever sees the output of `parse()`. Because `parse()` is pure and\npayloads are content-addressed, any past decision can be re-derived from stored\nbytes and must reach the same verdict. Put a network call inside `parse()` and\nthe reasoning trail stops being reproducible, and that property is what the\nwhole submission rests on.\n\n## Layout\n\n```\nhanko/provenance.py      canonical JSON, sha256 addressing, Status, Coverage\nhanko/evidence.py        Evidence + Provenance: the source-agnostic unit\nhanko/sources/base.py    the adapter contract\nhanko/sources/xsearch.py X, via the xAI Responses API and its x_search tool\nhanko/sources/rss.py     RSS/Atom: free, and the only source with trustworthy timestamps\nhanko/sources/fixture.py local JSON: free development, and every failure mode in CI\nhanko/snapshot/store.py  append-only content-addressed store, replay, integrity\nhanko/ryotools/          the six RYO tools over MCP and REST; structural fact extraction\nhanko/skills/exit_liquidity/  Track 3: what it costs to exit a position\nhanko/review/outcome.py     grading a decision against its own commitment\nhanko/review/reliability.py calibration, per-voice and per-rule track records\nhanko/review/ledger.py      append-only reviews, one per decision\nhanko/cli.py             collect / decide / review / scorecard / audit / verify\n```\n\nFull tree:\n\n```\nhanko/\n  sources/       X, RSS, fixtures -- fetch() impure, parse() pure\n  snapshot/       append-only content-addressed store\n  ryotools/       the six RYO tools, MCP + REST\n  decision/       evidence quality, verdicts, falsifiers, the ledger\n  review/         falsifier checking, calibration, reliability\n  skills/exit_liquidity/   Track 3: exit cost, capacity, drift, turnover\n  sweep.py, cli.py, config.py, provenance.py, evidence.py\nsite/            static landing page, /docs, /try -- the live web demo\n  api/exit_liquidity.py    the /try endpoint, real assess() call, no fixture\ntests/           349 tests, one file per hanko/ package\nfixtures/        evidence and market fixtures used offline and in CI\n.github/workflows/ci.yml       pytest on every push, Python 3.11 and 3.12\n.claude/skills/hanko-exit-liquidity/SKILL.md   packages exit_liquidity as a Claude Skill\nARCHITECTURE.md  the one rule, the pipeline, the layers\nDECISIONS.md     engineering decisions and the bugs that shaped them\nLIMITATIONS.md   what hasn't been shown yet, and why\nLICENSE          MIT\n```\n\n## Why it is built this way\n\n**Sources are interchangeable.** The agent never learns where a piece of\nevidence came from. Swapping X for Telegram, or Grok for a direct API, changes\none adapter and nothing downstream. If `x_search` is deprecated or priced out on\nday 9, nothing else moves.\n\nThat swap is a real option. X's own API\nreturns structured posts directly, with no model paraphrasing a prose summary\nin between, at the cost of a separate paid tier and a different credential\nthan the one this build already has. Worth doing later; not worth the rewrite\nrisk this close to submission when the adapter boundary already makes it\ncheap whenever it happens.\n\n**Failure is recorded as data.** A source that was asked and did not\nanswer produces a `FAILED` snapshot with the reason attached. An adapter that\nraises produces one too. Silence is the single outcome this store cannot\nrepresent, which is what lets position sizing respond honestly to missing evidence.\n\n**Three different kinds of nothing stay distinct.** `FAILED` (the source did not\nanswer), `OK` with zero items (nobody posted), and `PayloadShapeError` (bytes\narrived that we cannot read) are separate facts. Collapsing them would let a\nschema change look like a quiet market.\n\n**Coverage is never overclaimed.** `x_search` is a search tool. It cannot list a full timeline, so its coverage is `UNKNOWN` by construction. Claiming `COMPLETE` is\nprecisely the lie that would inflate a convergence count across KOLs.\n\n**Collect once, develop free.** Real snapshots are captured once and everything\nafterwards runs against stored bytes. Development and CI cost nothing and hit no\nrate limit. At an hourly sweep over 20 handles, live collection runs about\n$0.005/call, roughly $25 to $35 for the whole build.\n\n## Use\n\n```bash\npip install -e \".[dev]\"\npytest\n```\n\n```bash\nhanko collect x --subject voice_alpha --subject voice_beta\nhanko collect rss --subject https://example.com/feed.xml\nhanko ls\nhanko replay snap_7723f3520b7dfa2efa6856e3\nhanko verify\n```\n\nOffline, against a fixture:\n\n```bash\nhanko collect x --subject voice_alpha --fixture fixtures/x_three_voices.json\n```\n\n`XAI_API_KEY` must be set for live X collection. Without it the adapter records\na `FAILED` snapshot saying so and carries on.\n\n## Status\n\n352 tests, all offline, ~20s. Covered at the evidence layer: canonical form and\naddress stability, payload deduplication, tamper detection, adapter-version\nmismatch on replay, replay determinism, evidence identity pinned to bytes and\nposition, and each failure mode (rate limit, partial window, empty result,\nraising adapter, malformed feed, unreadable payload). At the decision layer:\necho demotion, PASS vs ABSTAIN separation, mechanical size reduction under\nmissing safety data / a failed source / stale evidence, falsifier generation for\nboth entries and refusals, ledger tamper detection, and end-to-end reproduction\nof a stored decision from stored bytes. At the review layer: falsifier checking,\nprofit not rescuing a falsified thesis, uncheckable metrics staying unresolved,\ninconclusive reviews excluded from every rate, echoes earning neither credit nor\nblame, calibration, and the refusal to re-review a decision.\n\nAt the MCP layer: the JSON-RPC handshake, session handling, SSE decoding, every\ntransport failure mode, and a test asserting that facts extracted from an MCP\npayload are identical to facts extracted from a REST payload.\n\n**Confirmed against the live API, 28 Aug 2026.** `x_search` does not return\nstructured post objects. It returns the model's *prose* rendering of the posts\nplus an array of URL citations. Prose cannot be attributed to a specific post,\nso the adapter requests a JSON schema and then verifies every returned post id\nagainst the tool's own citations: cited posts are kept, uncited posts are\ndropped. That check proves a post exists and that the tool saw it; it does not\nprove the model transcribed the text or timestamp faithfully, so those fields\nare flagged as model-transcribed on every item.\n\n**Confirmed against the live RYO MCP server, 5 Sep 2026.** `hanko tools`,\n`hanko facts SOL`, and `hanko decide` have all run against the real endpoint. Three things that changed once the guessing stopped:\n\n  The catalog has six tools. The hackathon's public tool list names seven. `check_safety` and `supported_tokens` are not on the authenticated\n  catalog; `monitor_market_sentiment_shift` is, and wasn't guessed at all.\n  There is no numeric safety score anywhere in the six, so `safety_score` on\n  `MarketFacts` now stays `None` every time.\n\n  Argument keys are `symbol` (`analyze_token`, `deep_analysis`) and `symbols`\n  as one comma-separated string (`compare_tokens`). Both transports were first built guessing `token` and are now fixed to match.\n\n  The response is `{\"tool\": ..., \"result\": {envelope}, \"latency_ms\": ...}`\n  over REST, and the envelope's own `status` (`ok` / `partial` /\n  `unavailable`) and `data_mode` (`live` / `mixed` / `simulated` / `unknown`)\n  are what `_degraded()` now reads. A response that admits to being\n  `simulated` is treated exactly like a partial one, since simulated data\n  presented as live is the one fabrication the platform says it never does.\n\n> **Correction, 22 Sep 2026.** A routine `hanko audit` crashed outright with\n> `PayloadShapeError: no message item in the response output`. The cause was\n> a labelling bug: a decision built from local fixture data had been tagged\n> with the *live* X source's own id, so replay handed the real adapter bytes it was never built to\n> read. Every existing replay test had missed it, because each one supplied\n> its own resolver that returned a fixture unconditionally rather than going\n> through the same lookup `hanko audit` actually uses. Full write-up, the\n> fix, and the regression test that proves it, in\n> [`DECISIONS.md`](DECISIONS.md) D-07.\n\nNot yet shown: a calibration curve from real outcomes, a live `ENTER`, and a\nreal priced pool. Stated plainly, with why, in\n[`LIMITATIONS.md`](LIMITATIONS.md).\n\n## exit_liquidity, the new Track 3 skill\n\nThe six real tools all answer some version of *is this worth entering?*\nNothing answers *can this position be closed, and what does closing it cost?*\nThat's the number that turns research into a trade. A token can clear every\nmeasured signal and still be a trap if exiting the position moves the price\ndouble digits.\n\n```bash\nhanko exit-liquidity BONK --size 50000\n```\n\n```\nOK  BONK  confidence high\n  price $0.00000331 · 24h volume $1,395,538  (dexscreener)\n  quoted: selling ~$50,000 on jupiter returns $48,886, 2.34% under its own near-mid rate (price impact plus fees, 5-hop route)\n  model said 7.15% from $1,299,172 of on-chain depth\n  the model overstates this exit's cost 3.1x: the route reaches concentrated liquidity a constant-product curve cannot see\n  depth: 29 pools on solana, deepest $327,472, 1 rejected\n  or exit over 8.6h at 10% of volume\n  verdict from an executable jupiter quote; quotes move with the market\n```\n\nLive output from 2026-10-23, recorded while RYO's market tools were down. RYO\nhas never published pool depth, so version 2.0 asks two keyless venues.\nDexScreener supplies pools for the token's canonical contract: never searched\nby symbol, because that returns impostor pools. Jupiter supplies an executable\nquote for the exact size. A measured quote outranks the model, and the\nmodel's error is printed beside it. Where neither venue answers, cost fields\nare null, never zero. Every answer is RYO's public response envelope, also\nserved on RYO's own REST route shape (`POST /api/tools/exit_liquidity/call`)\nand as an MCP server (`/api/mcp`).\n\nFull model, honesty conventions, and test coverage in\n[`hanko/skills/exit_liquidity/README.md`](hanko/skills/exit_liquidity/README.md).\nRunnable live, against the real platform, at\n**[tryhanko.xyz/try](https://tryhanko.xyz/try)**, restricted to\na few tokens and a capped size, so a public page can't turn into an unbounded\ntap on a live credential.\n\n### Claude Skill\n\n[`.claude/skills/hanko-exit-liquidity/SKILL.md`](.claude/skills/hanko-exit-liquidity/SKILL.md)\npackages this exact command as a Claude Skill: point Claude Code at this repo\nand it can answer \"what does it cost to exit this position\" directly, scoped\nby an explicit rule that every number in the answer has to come from the\ncommand's own output, never invented or rounded by the model. The same\nhonesty convention the tool enforces on itself, extended to whoever is\nrunning it.\n\n## Decision records\n\nA record states, at the moment of the decision: what was concluded, on what\nevidence, by what reasoning, what was missing, and **what would prove it wrong**.\n\n```\nhanko/decision/reading.py      interpretation, recorded once and frozen\nhanko/decision/convergence.py  independence: is this many voices, or one echoed?\nhanko/decision/quality.py      gaps, and the evidence-quality score that sets size\nhanko/decision/policy.py       risk limits, hashed into every record\nhanko/decision/engine.py       the verdict function -- pure by contract\nhanko/decision/record.py       the record, and the commitment that names it\nhanko/decision/ledger.py       append-only ledger, and the replay proof\n```\n\n**The engine is pure.** `decide()` reads its arguments and nothing else: no\nclock, no network, no model call, no randomness. Interpretation is the one\nsubjective step, so it happens upstream and is frozen into the record. Put an\nLLM in the decision path and replay proves nothing.\n\n**Three verdicts.** `ENTER`, `PASS` (the evidence was adequate and\nargued against it), and `ABSTAIN` (the evidence was not adequate to argue either\nway). Collapsing the last two would let the agent claim a market view it never\nhad.\n\n**Missing data shrinks the position, mechanically.** No rule says \"if safety is\nunavailable then halve\". A gap lowers completeness, completeness lowers evidence\nquality, and quality sets size. The four quality components combine as a\nweighted *geometric* mean, so a component at zero takes the whole score to zero.\nEnthusiasm can't average away an absent safety check.\n\n**Agreement is checked for independence.** Three accounts quoting one thesis is\none observation in disguise, and counting it as three is the easiest way for a\nmulti-KOL agent to be confidently wrong. Every echo is recorded with the post it\nrepeats and the reason it was demoted.\n\n**Every decision pre-registers its own falsification.** Falsifiers are written\nbefore the outcome is known, evaluate mechanically, and are hashed into the\n`decision_id`. Move a threshold, a size, or a review date after the fact and you\nget a different id, so any edit shows as a new decision. Refusals commit too: a\n`PASS` records what would flip it.\n\n### Seeing it\n\n```bash\nhanko decide x --token TOKENA --subject voice_alpha --subject voice_beta --subject voice_gamma --market fixtures/market_tokena.json --as-of 2026-08-27T12:00:00Z --fixture fixtures/x_three_voices.json\n```\n\n```\nENTER TOKENA  size 3.28%  confidence 0.66\n  dec_1c1fb9e7ae2020cb260d6f32\n  evidence quality 0.82  (completeness 1.0, freshness 0.75, corroboration 1.0, independence 0.67)\n  + independent_voices: 2 independent voice(s): voice_alpha, voice_gamma\n  + safety: safety score 0.82\n  ~ echo voice_beta: marked as a repost or quote by the source\n  ! wrong if price_usd < 1.0625 at any point within 72.0h (thesis fails if price falls 15% from entry)\n  ! wrong if independent_voices < 2.0 at review, within 72.0h (entry rested on convergence that no longer holds)\n  review at 2026-08-30T12:00:00Z\n\n  replayed from stored bytes: same decision id\n```\n\nThree authors mentioned the token; two of them were independent. Swap in the\nmarket fixture with no safety score and the same evidence sizes at 3.08%, down from 3.28%. Nothing in the rules changed, only what was known.\n\n`hanko audit` re-derives every recorded decision from stored bytes and insists it\nreproduces. That is the CI gate that turns \"preserves a repeatable reasoning\ntrail\" from a README claim into a failing build.\n\n## The review loop\n\nAt `review_at`, each falsifier is evaluated against observed facts and the\ndecision is graded **against what it committed to**. Whether it made money is recorded but does not decide the grade.\n\nThose come apart more often than is comfortable. An entry can be profitable and\nstill falsified: the price rose while the liquidity that justified the size\ndrained away, meaning the reasoning was wrong and the outcome was luck. Both\nfacts are recorded and reported separately. Grading on profit alone is how an\nagent learns to repeat lucky mistakes.\n\n**The third outcome carries the weight.** When the metric a falsifier names is\nunavailable at review time, the check is `UNCHECKABLE`, never quietly counted\nas passing. A decision that could not be checked is not a decision that was\nright. Inconclusive reviews are excluded from every rate and reported as their\nown number, because an agent that silently drops what it could not verify is\ngrading itself on a sample it chose after the fact.\n\n**Refusals are graded too.** A `PASS` commits to what would have flipped it, so\nan agent that only marks the trades it took never learns what its caution cost.\n\n**One review per decision, enforced.** Re-reviewing until the answer improves is\nthe exact failure pre-registration exists to prevent, so the ledger refuses a second review.\n\n```bash\nhanko review --observations fixtures/observations_day3.json --now 2026-08-30T12:00:00Z\n```\n\n```\nFALSIFIED  TOKENA  (enter at confidence 0.66)\n  dec_1c1fb9e7ae2020cb260d6f32 -> rev_24903844c6020591a568451b\n  x price_usd observed at 1.02 at 2026-08-30T12:00:00Z; committed to being wrong if < 1.0625 at any point in the window -- thesis fails if price falls 15% from entry\n  + liquidity_usd held against < 630000.0 across 1 sample at 2026-08-30T12:00:00Z -- no intermediate readings; last observed 880000.0\n  + independent_voices observed at 2.0; holds against < 2.0\n  realised return -18.4%  (recorded, not used to grade)\n```\n\nThe `liquidity_usd` line says exactly what it checked: one reading, no\nintermediate ones. `price_usd` and `liquidity_usd` are graded against every\nsample `hanko sweep --samples` logs between the decision and the review\n([D-13](DECISIONS.md#d-13-a-falsifier-said-within-72h-and-only-checked-the-last-tick)) --\na breach that recovers before review time still falsifies the thesis it\nbroke, and a review built from a single end-of-window reading says so.\n\n### The scorecard\n\n```bash\nhanko scorecard\n```\n\n```\nreviewed 2   scored 2   unverifiable 0\nheld 1   falsified 1   hit rate 50.0%   brier 0.2461\n\ncalibration        said     actual    n\n  0.6-0.8         0.7       50.0%    2\n\nvoices\n  voice_gamma               0.0%   1 scored\n  voice_alpha              50.0%   2 scored\n\nrules satisfied on decisions that later\n  independent_voices       0.0% held\n  conviction              50.0% held\n```\n\nSaid 0.7, was right 50% of the time. The agent is overconfident, and says so.\n\nPer-voice reliability is **earned from the agent's own audited history**. Follower counts play no part, and echoes earn neither credit nor blame. A voice with no scored decisions gets `None`. Reporting it as zero would defame\nit; reporting it as 100% would promote it. Per-rule reliability answers the\nuncomfortable question: a rule that is always satisfied on losers is not a\nfilter, it is decoration.\n\n## The sweep\n\n`hanko decide` handles one token, from one source, by hand. A real watchlist\nneeds the same thing done for twenty tokens across several sources on a\nschedule, without one dead source or one bad token taking the rest of the\nrun down with it. `hanko/sweep.py` is that orchestration.\n\n```bash\nhanko sweep --watchlist watchlist.json --as-of 2026-08-27T12:00:00Z\n```\n\nOne pass: decide on every entry in the watchlist, then grade every decision\nthe ledgers say is due. A failing token is recorded in the report and the\nsweep moves on. A token dropped from the watchlist still gets its outstanding decisions reviewed.\n\n**Every snapshot in one sweep is stamped at the sweep's own `as_of`.** The several source calls inside one pass take a few\nreal seconds, but they all belong to the same logical instant, and a sweep\nre-run against unchanged sources should reach the same `decision_id`, even if the retry lands a second later.\n\n**Review takes a fresh look.** `fresh_observations()`\nre-collects evidence and re-derives `independent_voices` at review time. The whole point of a\nreview is what's true *now*; a stale copy of \"true then\" would grade a\ndecision against itself.\n\n**`--samples samples.jsonl` gives the window something to actually check.**\nEvery pass appends one market reading per watched token, reusing the facts\nthat pass already fetched. A decision that becomes due later is graded against every reading logged since it was made. That lets an `ANY_POINT_IN_WINDOW` falsifier\n([D-13](DECISIONS.md#d-13-a-falsifier-said-within-72h-and-only-checked-the-last-tick))\ncatch a price or liquidity breach that recovers before the clock gets there.\nOmit it and `hanko sweep` keeps the single end-of-window reading it always\ntook; the review says which of the two it used.\n\nMeant to be invoked by something that already knows how to schedule things:\ncron, a scheduled GitHub Action, Windows Task Scheduler. Each call does one pass and exits, so a failed run is just a failed invocation.\n\nThis repository runs one. [`.github/workflows/sealed-sweep.yml`](.github/workflows/sealed-sweep.yml)\nseals [`watchlist.live.json`](watchlist.live.json) every three hours with no\nkey at all, then verifies the run from its committed files, upgrades any\npending Bitcoin proofs, and writes `live/summary.json` for\n[/ledger](https://tryhanko.xyz/ledger):\n\n```bash\nhanko seal --watchlist watchlist.live.json --ledger live/decisions.jsonl   --reviews live/reviews.jsonl --samples live/samples.jsonl   --manifest-dir live/runs --interpreter lexicon --lookback-hours 24 --ots\nhanko live-summary --dir live\n```\n\n`--interpreter lexicon` is the keyword reader taught to recognise \"Bitcoin\"\nas well as \"$BTC\", matching words whole. `--lookback-hours 24` reads only\nthe last day of a feed, since a Telegram page or an RSS feed keeps days of\nhistory that would otherwise score as stale (D-20).\n\n```bash\nhanko sweep --watchlist fixtures/sweep_demo/watchlist.json \\\n  --fixture-dir fixtures/sweep_demo --as-of 2026-08-27T12:00:00Z\n```\n\n```\nsweep at 2026-08-27T12:00:00+00:00\n  ENTER   TOKENA  dec_5b2eadbf21bc8c073927b4ac\n```\n\nSweep tests run offline, using a fixture stand-in for RYO tool responses\n(`FixtureFactsSource`, shaped like the live tool sources) alongside the existing evidence fixtures.\nCovered: a failed evidence source or fact tool becoming a recorded gap, one bad token not cancelling the rest of the sweep, a decision\nalready in the ledger being skipped, a token dropped\nfrom the watchlist still getting reviewed, and review pulling fresh observations.\n\n## Architecture, decisions, limitations\n\n- **[`ARCHITECTURE.md`](ARCHITECTURE.md)**: the one rule, the pipeline as a\n  diagram, what each package owns, and how `exit_liquidity` sizes itself as\n  the same size gate `decide()` runs internally.\n- **[`DECISIONS.md`](DECISIONS.md)**: 21 engineering decisions in the\n  order they happened, including several real bugs found and fixed during\n  this build and what each one actually broke.\n- **[`LIMITATIONS.md`](LIMITATIONS.md)**: what hasn't been shown yet, stated plainly.\n- **[`SEALING.md`](SEALING.md)**: `hanko seal` -- a numbered manifest of an\n  entire watchlist run (errors included), anchored to\n  public OpenTimestamps calendars, published before the outcome is known.\n\n## Next\n\nWire `sweep`, `audit`, and `scorecard` into CI or a scheduled run, and feed\nper-voice reliability back into evidence weighting so the agent's own track\nrecord starts shaping what it believes.\n# Evidence update, 30 Oct 2026\n\nHanko 2.0.0 is published in the official MCP registry as `io.github.IamHarrie-Labs/hanko`.\n\n- [Retrospective baseline comparison](study/baseline/README.md): 76 eligible reviewed opportunities produce identical entry selections under Hanko and a simple rule. This sample establishes no selection advantage. Reproduce with `python scripts/compare_baseline.py`.\n- [A sealed SOL refusal proved wrong](study/baseline/failure-case.md): the original commitment, late review-time observations, and what the extra voice does and does not establish.\n\nThe first successful model-read workflow sealed `run_547a6eaf6c69d992` on 30 Oct: 21 model calls, no fallbacks, three unnamed-token readings rejected, and all four decisions independently replayed. Its Bitcoin timestamp is confirmed.\n"}