Portolano
Project knowledge your coding agent writes, reads and keeps honest — and that knows when it has gone out of date.
A portolano was the book of sailing directions: what you see from the deck, where the shoals are, which bearing to hold. Portolan charts were the first maps drawn from real compass bearings instead of theory — made by sailors who had already sailed that coast, for the ones who would sail it next.
Your codebase is the coast. Portolano is the book the last crew left you.
The problem, in one paragraph
An agent can read your code. It cannot read why the code is that way, what was tried and abandoned, which deploy step bites, or where one module's responsibility ends. So every session pays to re-derive what it can, and guesses the rest. The usual fix — a CLAUDE.md or AGENTS.md describing the project — rots: Treude and Baltes call this context rot, "the gradual divergence between what a configuration file says about a codebase, its tools, its architecture, or its conventions, and what actually holds", and found stale references in 23% of 356 sampled repositories (arXiv:2606.09090).
Portolano's answer is one idea: every page records the commit it was checked against and the paths it describes. Staleness stops being a virtue you're supposed to remember and becomes a query you can run.
What it's for
Three payoffs, and the third is the one that pays for the other two.
| Who gains | What they get |
|---|---|
| For the agent | it reads an index and one page instead of grepping its way across the repository. The knowledge is already distilled, so it does not re-derive it every session |
| For you | one place to look. Point your editor at the wiki root and the code sits underneath it as submodules — wiki and source open in the same window, in the same search |
| For the project | documentation that is organised rather than accumulated: a catalog, one page per subject, and a rule for when each page stops being true |
The token argument is the concrete one. Re-deriving how a subsystem works costs thousands of tokens and produces nothing durable; reading a page costs a few hundred and the page stays. Measured on the same four queries against the same domain, a persistent knowledge layer used 47K tokens where a retrieval baseline used 305K (arXiv:2604.11243).
The saving compounds, which is why the freshness contract matters: a wiki nobody trusts gets re-derived anyway, and then you have paid twice.
The whole idea, in one example
A page in the wiki:
---
title: Notification dispatch
slug: notifications/dispatch
type: reference
status: active
summary: How queued notifications are fanned out to email, SMS and push.
related: [queue/workers]
updated: 2026-01-14
verified-at: 4312f47 # the commit this was checked against
covers: [src/services/notification/**] # what this page describes
---
# Notification dispatch
## What it does
Fans a queued notification out to every channel the user has enabled.
## Where it lives
| Concern | Location |
|---|---|
| entry point | `src/services/notification/Dispatcher.php:42` |
## Gotchas
- Retries are **not** idempotent below the channel layer — a requeue can send twice.
A month later, ask the page whether it is still true:
git log --oneline 4312f47..HEAD -- ':(glob)src/services/notification/**'
| Output | Meaning | What you do |
|---|---|---|
| empty | nothing it describes moved | nothing. Costs nothing |
| some commits | the page is suspect, not wrong | read them, fix the page, advance verified-at |
That is the entire mechanism. No service, no database, no index to host: git is the database, and the check is one command. Everything else in Portolano is convention around this one move.
Starting from nothing
You do not write the first page by hand. portolano bootstrap measures every repository in the workspace — how many files git tracks, which top-level areas exist, which commit it sits on — and writes one brief per wiki into briefs/, each telling your agent how high to fly: read the entry points of a small repository, name the areas of a large one, read no implementation at all in a huge one.
It asks for one page: what the repository is, how it is laid out, and what it did not look at. That last list is the queue for everything after.
Portolano makes no model calls, here or anywhere. The brief is text you hand to whichever agent you use. What the command contributes is the measuring — the part an agent guesses badly and git answers exactly.
Why it is built this way
| Principle | What it means in practice |
|---|---|
| Concepts, not code | the code is readable; a page that narrates it is rot waiting to happen. Pages hold what the code cannot say — the why, the boundary, the trap |
| KISS, literally | anything that would need a daemon, a server or an account is out by design |
| Router, not manual | the always-loaded file says which wiki governs what, and nothing else. Knowledge is fetched from an index on demand, not preloaded into every prompt |
| Suspect, not repaired | the mechanism finds candidates; the judgement stays yours. A page that was auto-corrected is a page nobody re-read |
| Vendor-neutral | AGENTS.md is the source; CLAUDE.md and friends are generated |
| Not just for new code | a page can be written today about code written five years ago. Nothing needs to have been done right at the time |
Where to go next
| Page | What is in it |
|---|---|
| 🚀 Quickstart | a working wiki in about ten minutes |
| 📖 Format reference | frontmatter, vocabularies, linking, length rules — the wiki about the wiki |
| 🔭 Prior art | where this sits in the literature and among existing tools |
Background
Three references worth knowing, and they are not the same thing:
| Reference | What it contributes |
|---|---|
| Karpathy's llm-wiki gist (Apr 2026) | the method: let the model keep a wiki, so what it learns accumulates instead of evaporating. Portolano points that method at a target that moves |
| Treude & Baltes, Context Rot (Jun 2026) | the problem, defined and measured for the code setting, plus a research roadmap. Portolano is one answer to the preventive mitigation they list as an open question |
| Agent READMEs, ACM TOSEM (2026) | the evidence: across 2,303 context files, these are not static documentation but artifacts that grow like configuration code through frequent small additions. Which is why the router stays thin |
Status: v0 — the format is settled enough to use and still expected to move. See prior art for what is and isn't new here.
License: Apache-2.0.
Release files for portolano 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| portolano-0.1.0.tar.gz | 56.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| portolano-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 83.9 kB
Release files / portolano-0.1.0.tar.gz
| Download URL | portolano-0.1.0.tar.gz |
|---|---|
| Size | 56.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
da697b8d87439d993cb8d60c98320a73d8d6a09bfd11fecbe201d1020ba161d2
|
|
BLAKE2b-256 checksum How to use checksums |
853bba8559f6d605728c55620bd67e221424913592d310686dd6f1728f146524
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / portolano-0.1.0-py3-none-any.whl
| Download URL | portolano-0.1.0-py3-none-any.whl |
|---|---|
| Size | 27.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
22a053492914d52fb8ee3513a734b6b2336b8acf659d6f7476b8d8a76993f6cc
|
|
BLAKE2b-256 checksum How to use checksums |
e28c858eb505d343893b957f9edc87b299305160f6a2fcd8d10ebb3f1d243189
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|