What it does for you
canon keeps one typed record of how you work and what a project is doing, and writes it into the file each tool reads at startup: CLAUDE.md, AGENTS.md, GEMINI.md and others. When you hand a repository to another agent, it starts with the focus, the open work and the decisions, including the alternatives you dropped and why. canon writes only between its own markers and turns any edit made there into a proposal for you to accept.
- Project state, bound to the projectFocus, work items, decisions and constraints are records that name their project. A read that finds another project's record fails.
- Decisions with their alternativesA dropped alternative cannot be recorded without its reason.
- Secrets stay outKeys, tokens and secret-named assignments are redacted, and a record that still looks like one is refused.
- Writes it owns, nothing morecanon rewrites only the span between its markers, in a fixed list of seven paths.
Source: README.md at 802165f (release 0.6.0)
Watch
No concept film fits this tool closely yet. The walkthrough below covers it in text, with real commands and output.
Video walkthrough: coming with the next release.
How it works, one step at a time
Scroll, or use the step buttons. The panel follows a small repository, exporter, through the workspace commands and a switch to Codex. Every line is output from canon at commit 802165f, run with CANON_HOME pointed at a temporary folder.
- 01
Record the focus
The project is shipping a JSON export, working in
src/export.canon workspace focusstores that as a focus record for this project.The project id comes from the repository: here from
git config canon.project exporter. Without one it comes from the remote, or from a nonce canon keeps in.git.Source: README.md, "Moving a project between models"; src/canon/cli_workspace.py
- 02
Record a decision with what you dropped
The team keeps the CSV writer and adds JSON beside it, because downstream scripts parse CSV. The alternative, replacing CSV, was dropped because it breaks three scripts.
--rejecttakes the alternative and its reason together. Give one without the other and the command refuses.Source: README.md, "Moving a project between models"
- 03
A secret is refused before it is stored
Someone records a decision whose text includes
api_key=sk-live-.... The workspace store checks every record for secret shapes and refuses this one, naming the shape it found. Nothing is written.Source: README.md, "Secrets stay out of workspace records"; src/canon/workspace
- 04
A brief for the next agent
canon handoff --to codexlists focus, open work, recent decisions and constraints, in that order, inside the target's size budget. It says plainly that it holds what was recorded, not everything that happened in earlier sessions.Source: src/canon/cli_handoff.py
- 05
Switch: write it between the markers
canon switch --to codex --createwrites the brief intoAGENTS.md, the file Codex reads at startup. Everything canon writes sits between its begin and end markers; every byte outside them is left as it was.Source: README.md, "How one record becomes the file each tool reads"
- 06
An edit inside the region becomes a proposal
An agent changes the goal in
AGENTS.mdto an XML export. The next switch does not overwrite that edit. It turns it into a proposal and stops until you accept or reject it.Source: README.md, "Switch in one command"
Walkthrough
Install it, run it once, then use the main feature. Each command below is real, and so is its output.
Install
Install from PyPI. Python 3.11 or newer; no network call and no model.
$ python -m pip install flywheel-canon $ cd your-repoFirst run: record the focus
Tell canon what you are working on.
$ canon workspace focus --goal "Ship the JSON export" --area src/export focus set: workspace-focus focus: Ship the JSON exportHand off to another agent
Write a brief for the next agent from what canon holds.
$ canon handoff --to codex # Resume brief: exporter It holds what was recorded, not everything that happened in earlier sessions. ## Focus Goal: Ship the JSON export ## Decisions - Keep the CSV writer [accepted] (decision-2): Add JSON beside CSV Why: Downstream scripts parse CSV Rejected: Replace CSV. Reason: breaks three scriptsSwitch tools
Write the same record into another tool's instruction file, between markers canon owns.
$ canon switch --to codex --create Codex CLI: created AGENTS.md
Output from canon at 802165f run from source; flywheel-canon 0.6.0 is the current PyPI release.
What it does not do
- The session importers match fixed patterns with no model in the loop, and the session formats they read are not stable interfaces.
- The secret scrubber recognises secrets by shape. A secret in an unfamiliar shape can pass.
- A brief knows only what was recorded or imported. It is not a transcript of earlier sessions.
- No target file can load a block for some files only, so a block scoped to certain files is written for all of them with an Applies to line.
- canon stores and retrieves context. It runs no model and hosts no inference; your client supplies the model.
Source: README.md at 802165f, "Moving a project between models" and "What it does"
Check what stuck
Answer each one in your head before you open it.
What does canon require along with a rejected alternative?
Its reason. --reject takes both, and one without the other is refused.
A decision's text contains api_key=sk-live-... . What is stored?
Nothing. The store names the secret shape it found and refuses the record.
Which part of AGENTS.md does canon rewrite?
Only the span between its begin and end markers. Everything outside is left byte for byte.
An agent edits the goal inside canon's region. What does the next switch do?
It turns the edit into a proposal and stops, exit 5, until you accept or reject it.