{
  "markdown": "# Nota\n\nAn AI trading opinion you can audit. A council of specialised agents debates live RYO market\nevidence and records the practice trade it would make as a **replayable decision receipt**:\n`nota replay <id>` rebuilds it from the stored evidence and prints `identical: true`, every cited\nnumber is read back out of the evidence rather than retyped by the model, and independent sources\naudit RYO's own price and indicators before anything is sized. Built for the RYO-CHAN Hackathon 2026.\n\nTracks entered: **1 Autonomous Agents** (council, receipts, `watch` loop), **2 Dashboards**\n(diff-first receipt dashboard), **3 New Skills** (`narrative_convergence`, `news_verify`,\n`price_crosscheck`, `technicals_crosscheck`, all in RYO's envelope).\n\n![Dashboard: thirty-second summary, what changed ranked by impact, open positions, skills panel](docs/img/dashboard.png)\n\n<details><summary>More screenshots (replay verification, receipt card, phone layout)</summary>\n\n![Verify replay from the page](docs/img/verify-replay.png)\n![Receipt card used for link previews](docs/img/card.png)\n![Phone layout](docs/img/mobile.png)\n\n</details>\n\nScreenshots are generated from the shipped demo ledger by `scripts/screenshots.py`.\n\n**Watch it instead:** [nota-ryo.vercel.app/demo](https://nota-ryo.vercel.app/demo) — a narrated\n2:18 walkthrough with a clickable transcript, or the bare file at `/demo.mp4`.\n\n## How a decision is made\n\n```\ngather ──────────► council ──► judge ──► risk (pure ATR math) ──► receipt ──► ledger ──► notify\n 5 RYO tools        macro       weighs      entry / stop / target      id = hash(evidence,   Telegram\n + own skills       technician  opinions    or Blocked when price       model, prompts)      Discord\n (price_check,      narrative   by Brier    or ATR is missing\n  voices, news)                 weights\n```\n\n- **Evidence pack**: `market_overview`, `monitor_market_sentiment_shift`, `deep_analysis`,\n  `analyze_token`, `compare_tokens` (the token against BTC/ETH peers). `scan_market` drives\n  `nota scan`, RYO's recommended funnel. A failed tool becomes a section with status `error`;\n  the pack still exists. Own skills are added as further sections (`price_check` by default,\n  `narrative_signal` with `--voices`, `news_check` with `--news`).\n- **Council**: three agents (macro, technician, narrative) each return a stance, a\n  probability, and **citations as dotted paths into the evidence**. Citations that point at\n  a missing or null value are dropped in code (after normalising `x[0].y` to `x.0.y`) and the\n  opinion is downgraded.\n- **Judge**: weighs opinions by each agent's historical Brier score and decides\n  long / short / no_trade.\n- **RYO's own plan is evidence, not an instruction**: `deep_analysis.data.trade_plan` carries RYO's\n  ATR preview (entry, stop, targets, multiplier, method). Nota sizes independently and then reports\n  the gap on the receipt: on the shipped ETH call, RYO stops at 2357.83 on a 1.5× ATR while this\n  sizing uses 2.0×, so the stop sits 1.8% of entry lower and the first target 5.4% further, both in\n  the same direction.\n- **Risk**: a pure function. Stop = 2×ATR(14), target = 3×ATR, size from 1% account risk,\n  capped at 20% of the account. No price or no ATR means **Blocked**, never a guessed number,\n  and so does an ATR big enough to put the stop or target at or below zero: on an instrument that\n  volatile the fixed-multiple rule does not apply, and a receipt must not print a negative price.\n- **Replay**: LLM outputs are cached by `(evidence hash, role, prompt version, model)`.\n  `nota replay <id>` reproduces the receipt exactly; `--fresh` re-asks the model and prints\n  the drift honestly. The dashboard's \"Verify replay\" button does the cached check only.\n- **Calibration**: `nota resolve --all` scores every decision whose seven-day horizon has\n  passed (Brier per agent) against a fresh RYO price read, falling back to the exchange median\n  from `price_crosscheck` when RYO cannot give a price; the outcome records which source was used.\n- **Autonomy**: `nota watch SOL,BTC --every 3600 --notify` decides on a schedule, resolves\n  matured decisions, and posts each new receipt to Telegram / Discord. `--scan-top 3` lets the\n  loop pick its own candidates from `scan_market` every cycle.\n- **Simulated data never trades**: when RYO marks the primary evidence `data_mode: simulated`,\n  sizing is blocked and the receipt says so.\n- **Transport**: REST (`/tools/{tool}/call`) by default, or MCP JSON-RPC (`tools/list`,\n  `tools/call` on the same base URL) with `RYO_TRANSPORT=mcp`; `nota health` lists the live\n  tool catalog over MCP when a key is set.\n\n## The public surface is treated as public\n\n`/api/skills/<name>/invoke` and `POST /mcp` are unauthenticated on purpose, so they are written for\nstrangers:\n\n- A voice id goes into an outbound URL, so it is validated first. `../../evil` used to resolve to\n  `https://t.me/evil`, which handed a caller the path on the target host; with redirects followed\n  that is a step towards making this server fetch somewhere of their choosing. Handles are letters,\n  digits, underscore, dot and hyphen, up to 64 characters, and anything else is refused before a\n  request is built.\n- `tools/call` over MCP is metered per address exactly like the REST route, 60 an hour, because it\n  reaches third-party APIs. `initialize`, `tools/list` and `resources/*` stay free: they touch\n  nothing outside the process.\n- A JSON-RPC batch is capped at 25 messages, and `Origin` is validated on every MCP request as the\n  transport spec's security section requires.\n- Backing is capped at 30 an hour per address. It used to answer 503 on the hosted deployment, which\n  meant the one place anyone could try it was the one place it did not work: a serverless filesystem\n  cannot be written, and the ledger there is a snapshot. Backings now go to Postgres when\n  `DATABASE_URL` is set (`nota/backings.py`) and to the ledger's own table otherwise, so nothing\n  local needs a database to run or to test. The write is a single `INSERT ... ON CONFLICT\n  (decision_id, handle) DO UPDATE`: reading a map of handles, changing one and writing it back would\n  drop a stance whenever two people backed at the same moment, and a public record that silently\n  loses someone's vote is worse than no public record. The 503 is still there for the case it was\n  written for - nowhere to write at all.\n\n## Honesty rules this code enforces\n\n- Every number in a receipt carries its source path, RYO `as_of`, `data_mode` and trace id.\n- `null` / `unavailable` is never converted to 0 (`Envelope.get`, `first_present`, risk,\n  every skill).\n- Recorded fixtures keep RYO's original `as_of` and `data_mode` and are labelled\n  `source: recorded`. Synthetic test fixtures live only under `tests/fixtures/` and are\n  labelled `source: fixture`; receipts print the label. There is no fake-LLM mode in the CLI.\n- The RYO surface is read-only; practice trades exist only in `nota.db`.\n- A receipt says what it cost: model calls, cache hits, prompt and completion tokens, the provider's\n  own billed figure, and wall time. Every one of those is what the provider reported, never derived\n  from a price table this repository would have to keep correct - a figure that was not reported\n  reads \"not reported\", and one silent call makes the whole total unknown rather than smaller.\n  `spend` is deliberately outside the replay comparison: a cached rebuild spends nothing, so the\n  field is a measurement, not a reproducibility claim.\n- External sources say what they cannot do: Venice web search carries no dates, the X mirror\n  is unofficial, RSS feeds that fail are listed, exchange prices never replace RYO's value.\n\n## Run\n\n```bash\nuv sync\ncp .env.example .env            # RYO_MCP_KEY + an LLM key (Anthropic, or NOTA_LLM=openai for Venice/OpenRouter)\nuv run nota health             # MCP health (no key) + whoami/quota (with key)\nuv run nota decide SOL         # live evidence + price cross-check, council, receipt\nuv run nota decide SOL --voices tg:WatcherGuru,bs:decrypt.co,bs:unusualwhales.bsky.social --news --notify\nuv run nota scan --top-n 5 --decide-top 2      # scan_market -> analyze_token -> council\nuv run nota watch SOL,BTC --every 3600 --notify\nuv run nota replay <id>        # identical: True\nuv run nota replay <id> --fresh\nuv run nota resolve --all      # after 7 days: Brier scores per agent\nuv run nota scores\nuv run nota record SOL         # capture all six live tools into fixtures/recorded\nuv run nota decide SOL --source recorded   # replay those recordings without a key (after `record`)\nuv run nota positions                      # open practice positions vs the latest independent price\nuv run nota serve                          # http://127.0.0.1:8000 overview, /app dashboard, /mcp\nuv run nota skill spec                     # Track 3 definitions\nuv run nota skill run price_crosscheck '{\"symbol\":\"SOL\",\"reference_price\":150}'\nuv run pytest -q\n```\n\n## Skills (Track 3)\n\nAll four return RYO's public envelope field for field (`docs/skills/SKILL-SPEC.md`) and are\nserved on RYO's own skill paths, so plugging them into RYO is a route registration, not a port:\n`GET /api/skills/` (SkillDefinition list), `GET /api/skills/{name}`, and\n`POST /api/skills/{name}/invoke` taking `SkillCallRequest {name, args, conversation_id}` and\nreturning `SkillCallResponse {name, status: success|error, result, latency_ms, xp, guard_decision}`.\nThe dashboard's \"Run a skill\" panel builds its form from those definitions and shows the envelope.\n\n- `narrative_convergence`: up to 20 voices (`tg:` public Telegram previews, `bs:` Bluesky public\n  API, `x:` through X's own public syndication endpoint, the one that serves embedded timelines,\n  with a Tavily fallback), VADER sentiment plus a crypto lexicon, conviction, urgency, and\n  convergence detection. Silence is `null`, not 0. Nitter, which `x:` used to go through, was served\n  cease-and-desist letters in August 2026 and its public mirrors went dark, so that reader was\n  advertising a source that could not answer; syndication is keyless, dated and still open, and\n  every failure is reported as `unavailable` rather than guessed. One measured limit worth knowing\n  before you try it: syndication throttles hard, and it has tightened. It hit data-centre addresses\n  first, so `x:` came back `unavailable` from the hosted demo while reading fine from an ordinary\n  connection; as of 2026-09-10 an ordinary connection gets `syndication HTTP 429` too. Treat `x:` as\n  best effort and `tg:` / `bs:` as the dependable readers - those two answer from both. Setting\n  `TAVILY_API_KEY` turns the throttle into a search-backed fallback, which the envelope then labels\n  as undated; without that key the warning says so in as many words.\n- `news_verify`: dated headlines from CoinDesk, Cointelegraph, The Block and Decrypt RSS,\n  plus Tavily or Venice web search for breadth; counts independent domains and attaches RYO\n  `analyze_token` context.\n- `price_crosscheck`: keyless CoinGecko, Coinbase and Kraken spot prices, median, spread, and\n  deviation of a reference price (RYO's) from the exchanges, plus the alternative.me Fear &\n  Greed index against RYO's reading.\n- `technicals_crosscheck`: RSI(14), ATR(14) and 1d/7d/30d performance recomputed with Wilder's\n  method from CoinGecko public OHLC (4-hour candles aggregated to UTC days), with the deviation\n  of reference values (RYO's `technicals.rsi_14` / `atr_14`) from the independent calculation.\n  The Technician sees it as `technicals_check` on every decision.\n\n## Nota is also an MCP server (Track 3)\n\nNota is an MCP client of RYO. It is also an MCP server, so RYO, Claude Desktop, Cursor or any other\nMCP host can call the four skills directly with no wrapper:\n\n```jsonc\n// claude_desktop_config.json, or any MCP client that speaks Streamable HTTP\n{ \"mcpServers\": { \"nota\": { \"url\": \"https://nota-ryo.vercel.app/mcp\" } } }\n```\n\n```bash\ncurl -s https://nota-ryo.vercel.app/mcp -H 'content-type: application/json'   -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\"}' | jq '.result.tools[].name'\n\ncurl -s https://nota-ryo.vercel.app/mcp -H 'content-type: application/json'   -d '{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/call\",\"params\":{\"name\":\"technicals_crosscheck\",\"arguments\":{\"symbol\":\"SOL\"}}}'\n```\n\nIt serves all four MCP primitives, not just the easy one:\n\n- **tools**: `tools/list` and `tools/call` for the four skills, each `inputSchema` generated from the\n  same definition the REST route and the dashboard form use.\n- **resources**: `resources/list`, `resources/templates/list` (`nota://receipt/{id}`) and\n  `resources/read`, which returns a receipt as markdown plus its own JSON. A client that never\n  touches this project's HTTP API can still list its decisions and read one.\n- **prompts**: `prompts/list` and `prompts/get`. `audit_a_token` tells the caller's model to\n  cross-check RYO against the independent sources and carries the honesty rules with it, including\n  that a null stays null. `read_a_receipt` walks a stored decision.\n- **completions**: `completion/complete` offers the receipt ids and symbols this deployment actually\n  holds, so a client never has to guess one.\n\nNota is **published in the official MCP registry** as `io.github.PugarHuda/nota` (version 0.1.0,\n2026-09-10), so a client can find it without being handed the URL:\n`curl \"https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.PugarHuda/nota\"`.\n\n`server.json` at the repository root is this server's entry for the official MCP registry, and the\ndeployment serves it at `/.well-known/mcp/server.json` and `/server.json`, so the description and the\nendpoint it names cannot drift apart. Two rules the registry enforces and a test here mirrors: the\ndescription is capped at 100 characters, and **a published version is immutable** - every republish\nneeds a new `version`, so changing anything in that file means bumping it. Publish with\n`mcp-publisher login github && mcp-publisher publish` from the repository root; `mcp-publisher\nvalidate` checks the file against the live registry without publishing.\n\n`GET /llms.txt` follows the llms.txt convention: one generated page telling an agent what is here,\nhow to call the MCP endpoint, which skills exist and which receipts the ledger holds. It is built\nfrom the routes and the ledger, so it cannot drift from them.\n\nOne endpoint, POST only, stateless. It negotiates the protocol version the client asks for\n(`2026-07-28`, `2025-06-18`, `2025-03-26` or RYO's own `2024-11-05`), validates the `Origin` header\nagainst DNS rebinding as the transport spec requires, answers a batch with one response per request,\nreturns `202 Accepted` with no body when the body holds only notifications, and answers `405` to GET\nand DELETE because there is no stream to open and no session to delete. Each tool's `inputSchema` is\ngenerated from the same skill definition the REST route and the dashboard form use, so the three can\nnever drift apart. `tools/call` returns the RYO envelope twice: as text for clients that only read\ntext, and as `structuredContent` for clients that parse.\n\n## Dashboard (Track 2)\n\n`nota serve` exposes a read-only API over the ledger (`/api/decisions`, `/api/decisions/{id}`,\n`/api/decisions/{id}/replay`, `/api/positions`, `/api/scores`, `/api/health`, exports\n`/r/{id}.json` and `/r/{id}.md`, OpenAPI at `/docs`) and a single-page dashboard:\n\n- **Thirty-second summary** at the top of every receipt: what to do now (trade or block reason),\n  the single biggest change and why it matters, whether the open practice position would be\n  stopped out or at target at the latest independent price, and what happens next (when it\n  is scored, or the score).\n- **What changed**: every receipt is diffed against the previous receipt for the same symbol\n  and the rows are ranked by impact. Verdict flips, trade unlock/block, availability and model\n  changes come first, then the numbers the risk engine reads (price, ATR, RSI) by % move, then\n  every other evidence leaf. A value that became `null` is shown as `null`, never as 0.\n- **Degraded mode**: a banner names each evidence section that is not `ok`; provenance shows\n  `as_of`, `data_mode` and trace id per section.\n- **Open practice positions** against the latest evidence price, with distance-to-stop.\n- **Agent leaderboard** by Brier score with the judge weights currently in force, and a judge\n  calibration table (stated `p_up_7d` bucket vs realised hit rate) once decisions resolve.\n- **Two languages, one page**: `/` and `/ja` share `landing.css` and `landing.js`, so only the prose\n  is translated and the behaviour is the same object rather than a copy of it. Tests bind the two:\n  identical section ids, both loading the one script, and neither allowed to write a receipt id or an\n  evidence hash into the file.\n- **Which sources helped** (`sources` in `/api/scores`): for every evidence section, the judge's\n  mean Brier on decisions where that section answered against decisions where it did not, and the\n  difference between them. It answers a question the agent leaderboard cannot - not \"which agent is\n  right\" but \"does having this feed make the call better\". An empty bucket scores `null`, and the\n  table carries `enough_to_read`, false until 20 decisions are scored: the gap between two\n  three-sample means is noise, and printing it as a finding would be the same offence as turning a\n  null into a zero.\n- **Verify replay** from the page (cached outputs only, never spends), share to X, exports.\n- **Works for everyone**: skip link, real buttons, visible focus, `aria-live` updates,\n  keyboard `j`/`k` move, `Enter` open, `p` previous receipt, `/` filter, `?` help.\n- Polls the ledger every 30 s so a running `watch` loop shows up without a reload.\n\nThe dashboard reads receipts only. It cannot show a number that has no receipt behind it.\n\n## SocialFi layer\n\n- **Receipt cards**: `/r/<id>.png` renders a 1200×630 card from the receipt (Pillow, bundled\n  font); `/r/<id>` carries Open Graph and X card tags pointing at it, so a shared permalink\n  previews as the receipt. Discord notifications embed the same image.\n- **Backing**: anyone can back or disagree with a call under a handle (`POST\n  /api/decisions/<id>/back`, one stance per handle per receipt, latest wins). When the call\n  resolves, backers are scored against the outcome (`/api/backers`): agreeing with a long that\n  went up is right, disagreeing with it is wrong, `no_trade` calls are never scored. It is\n  public and unauthenticated on purpose; the ledger keeps every stance with its timestamp.\n\n## Evaluate with zero keys\n\nThe repository ships a ledger snapshot that starts with four receipts made on live RYO evidence\n(2026-09-07, SOL / BTC / ETH, each labelled with its own source) and grows: a daily cycle\n(`.github/workflows/ledger.yml`) scores whatever reached its seven-day horizon and commits the\nsnapshot back. Every receipt in it verifies, and a test says so:\n\n```bash\nuv sync   # on PowerShell, set the variable first: $env:NOTA_DB = \"data/demo.db\"\nNOTA_DB=data/demo.db uv run nota replay b80b42835b01   # identical: True - verified with no key at all\nNOTA_DB=data/demo.db uv run nota serve      # then open http://127.0.0.1:8000/app for the dashboard\nNOTA_DB=data/demo.db uv run nota positions\nuv run nota skill run price_crosscheck '{\"symbol\":\"SOL\"}'   # live exchanges, no key\nuv run pytest -q                              # 176 tests\n```\n\nThe first line is the point of the project: a cached replay rebuilds the receipt from the ledger's\nown evidence and the model outputs stored beside it, keyed by the model that produced them, so it\ntouches no API and cannot drift. Every shipped receipt verifies this way, the recorded-source one\nincluded, because a cached replay is keyed by the model that produced the output rather than by\nwhatever model is configured today. `--fresh` is the opposite: it calls today's model on the\nsame evidence and prints the differences as drift.\n\nA council decision needs one LLM key (Anthropic, or any OpenAI-compatible provider such as\nVenice) and live RYO evidence needs the builder key; everything else, verification included, runs\nwithout either.\n\n## The walkthrough, and how it is made\n\n`/demo` serves a narrated video and, beside it, the transcript as chapters: clicking a line seeks\nto it. Three steps build it, and each one hands the next its timing rather than a guess:\n\n```bash\nuv run --with edge-tts python scripts/narration.py   # the voice, and it sets the pace\nuv run python scripts/demo_video.py                  # Playwright records the real pages\ncd video && npx remotion render                      # the two are composed\n```\n\n1. **`scripts/narration.py`** holds the spoken script as beats, each one a sentence paired with the\n   selector it is about. Microsoft's `en-US-AndrewNeural` reads them through `edge-tts`, which needs\n   no key, and `ffprobe` measures what came back. Nothing is timed by ear.\n2. **`scripts/demo_video.py`** drives the app with Playwright against `data/demo.db` and holds every\n   beat for exactly the length of its own audio. It draws two things a raw screen capture lacks: a\n   cursor that travels to whatever is being described, and a frame around that element, so it is\n   never ambiguous which part the voice means. It writes down the second each beat actually began —\n   a page load takes time the narration does not — and that file is what both the composition and\n   `/demo`'s transcript read.\n3. **`video/`** is a Remotion composition that places each line at its recorded offset and renders\n   the MP4.\n\nNothing in it is staged. The verify button is pressed on camera and its answer is whatever the API\nreturns; every figure spoken is one this deployment produces on demand.\n\n## Hosted demo\n\nA read-only copy of the dashboard runs at https://nota-ryo.vercel.app (Vercel, framework-detected\nFastAPI via `main.py`). It serves the committed ledger snapshot `data/demo.db` (receipts on live\nRYO evidence, each carrying its trace ids) with `NOTA_READONLY=1`: reads, replay verification, cards and exports\nwork; backing answers 503 because a serverless filesystem cannot be written. The full system,\nincluding live RYO evidence, the `watch` loop, notifications and backing, runs with\n`uv run nota serve` on any machine with a writable disk.\n\n## Failure handling\n\n- RYO client: exponential backoff with jitter on 429/503/network, honours `Retry-After`,\n  never retries 4xx argument errors, records `X-RateLimit-*` headers.\n- Evidence gathering continues past failed tools and skills; the judge is told which sections\n  are missing and is instructed to prefer `no_trade` when primary evidence is gone.\n- SQLite ledger in WAL mode; every write is idempotent by content hash, so a restart resumes.\n- `watch` survives a failing symbol, a failing notifier and a failing resolution.\n- RSS is parsed with `defusedxml` (no entity expansion from untrusted feeds).\n- Some ISPs DNS-block exchange domains (seen from Indonesia: Coinbase and Kraken resolve to a\n  block page with a bad certificate). `price_crosscheck` then reports those sources\n  `unavailable` and works from whatever remains; the hosted demo on Vercel reaches all three.\n\n## Layout\n\n```\nnota/\n  envelope.py     RYO public response contract + REST/MCP parsers\n  ryo_client.py   RyoClient (httpx) + RecordedRyoClient + record()\n  ledger.py       SQLite: evidence, llm_cache, decisions, outcomes\n  evidence.py     EvidencePack, ryo_args(), gather(), candidate_symbols(), path lookups\n  paths.py        candidate paths for price / ATR (one place to fix when the live schema is recorded)\n  llm.py          LLM protocol, AnthropicLLM (messages.parse), OpenAICompatLLM (Venice/OpenRouter)\n  council.py      role prompts, Opinion/Verdict, citation validation, cached run_council()\n  risk.py         size_trade(): PracticeTrade | Blocked\n  receipt.py      Receipt + markdown rendering\n  notify.py       Telegram Bot API + Discord webhook publishing\n  api.py          FastAPI read API + static/index.html dashboard\n  skills/         contract, sources (Telegram, Bluesky, X syndication, RSS, Tavily, Venice), narrative, news, price_check\n  decide.py / replay.py / calibration.py / cli.py\n  static/         landing.html + landing.ja.html (same page, `/` and `/ja`), index.html (dashboard),\n                  demo.html (walkthrough + transcript), landing.css + landing.js shared by both languages\nscripts/          screenshots.py, narration.py, demo_video.py\nvideo/            Remotion composition that puts the narration onto the recording\ndocs/             hackathon analysis, MCP builder guide copy, design spec, skill spec, submission draft\ntests/            pytest, no network (respx + FakeLLM test double)\n```\n\n## Disclosed third-party libraries\n\nhttpx, pydantic, anthropic, typer, python-dotenv, fastapi, uvicorn, vaderSentiment (MIT),\ndefusedxml, pillow; dev: pytest, respx, playwright (browser end-to-end tests in\n`tests/test_dashboard_e2e.py` and QA in `tests/test_qa_browser.py`, run after\n`uv run playwright install chromium`), edge-tts and Remotion (walkthrough only, see below). Data sources:\nRYO MCP, t.me/s previews, Bluesky public AppView, X public syndication, CoinDesk /\nCointelegraph / The Block / Decrypt RSS, CoinGecko, Coinbase, Kraken and alternative.me public\nAPIs, Tavily or Venice web search.\n\nNo starter template was used: the repository began empty and every line of application code was\nwritten during the hackathon. Two external things touched the work without entering it, and are\nnamed here for completeness: the `taste-skill` design ruleset (Leon Lin, MIT) was installed with\n`npx skills add` and read while reshaping the interface, and it is gitignored rather than vendored,\nso no file of it ships here; and the demo assets under `docs/img` and `docs/demo` are generated by\n`scripts/screenshots.py` and `scripts/demo_video.py` from this project's own dashboard, not sourced\nfrom anywhere.\n\nThree tools build the walkthrough and none of them ships inside the application: `edge-tts`\n(GPL-3.0, Microsoft's public neural voices, no key) reads the narration, Playwright records the\npages, and **Remotion** (Remotion License — free for individuals and for companies of three people\nor fewer, paid above that; see <https://remotion.dev/license>) composes the result. They are\ndevelopment dependencies of `video/`, invoked from `scripts/`, and no Remotion or `edge-tts` code is\nserved to a visitor or imported by `nota`. The narration is written here, not generated: the voice\nis synthetic, the script is not. Fixtures under `tests/fixtures/` are hand-built and labelled `source: fixture`;\n`fixtures/recorded/` holds real RYO responses captured with `nota record` and labelled\n`source: recorded`; `tests/fixtures/x/` holds one real payload captured from X's public syndication\nendpoint on 2026-09-07.\n\nNo secret has ever been committed. Verified across the whole history, not just the working tree:\n49 commits, 448 blobs, twelve credential patterns (RYO builder keys, Anthropic, OpenAI, OpenRouter,\nVenice, Tavily, Telegram bot tokens, Discord webhooks, AWS, GitHub and Vercel tokens, PEM private\nkeys). The single match is `ryo_mcp_your_private_key`, the placeholder inside RYO's own builder\nguide quoted at `docs/MCP-Builder-Guide.md`. No `.env` or credential file was added in any commit.\n",
  "bytes": 27347,
  "sha": "695e3b81f86247d178f4bebcb3b0f431faa201d43c90ae912a20e18df3a35428",
  "repo_slug": "pugarhuda/nota",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_pugarhuda_nota_e55ae7be/readme"
}