What a session sees
Source: docs/guide.md · v0.1.2
At the start of a session in a project that hasn’t adopted yokoten yet:
yokoten: stage 1 of 5, safety net
Next No AGENTS.md, so the agents have no written rules here.
Why An agent follows the rules it can read, and only those.
Fix `yokoten init`
Tell the user in plain words, one step at a time, and offer to do it (as-is,
lighter, or not now with a reason).
yokoten: ~/.config/yokoten/private-marks is missing or has no marks
Why Without your marks, the privacy check knows only email addresses, IP
addresses and secrets.
Fix `yokoten marks add`, then type your name, your city or a domain; it
is not shown.
More https://github.com/yokoten-labs/yokoten/blob/main/docs/guide.md#privacy
The session hears about one step only: the stage, the next step, and why it matters. The
second block, about your private marks, shows only while no marks are saved.
The stage is worked out from the files each time; nothing saves it. A step you marked
“not now” is skipped. The order is advice: a later step done early counts as done.
When every step is done or “not now”, the session hears one line that says so.
A file missing here but on the default branch is named with that branch, not with init.
After Claude Code compacts the context, the session hears only notes that changed.
Each session keeps what it heard in ~/.config/yokoten/sessions/, and files older than 7 days are removed.
The agent tells you in your own language. yokoten prints English, which agents read most
reliably.
yokoten doctor shows the whole road. Each step is done, next, to do, not
now (with your reason) or can’t check. Steps that yokoten can’t check yet are not
listed:
| Stage | Steps |
|---|---|
| 1 · Safety net | rules, tool-hook, commit-check, runtime, tests (a test command, and CI runs it) |
| 2 · Issues and pull requests | remote (the repo is on the code host, and a branch from it is pushed), ci (CI runs check, or uses the yokoten Action), issues (no todo file), templates, labels (the standard set), milestone (an open one), no-build-output (nothing in dist/, build/ or out/, no built binaries) |
| 3 · Release | changelog, titles (CI runs check --pr, or uses the Action on pull requests without pr-check: false) |
| 4 · A team of agents | codeowners, bot, skills, sandbox (each agent may write the worktree, and nothing wider), review (a merge to the default branch needs one approval) |
| 5 · Going public | security-files (a security policy, Dependabot, a license), protection (rulesets on the default branch and v* tags), merge-settings (squash only, delete branch on merge), security-settings (secret scanning, push protection, private reporting, Dependabot alerts) |
The last three are server settings. Full doctor reads them through gh api, and only reads.
Without gh, a sign-in, the network or a GitHub remote, they are can’t check, never done.
A session never calls the network, so there they are always can’t check.
Another host. doctor reads the host from the origin remote. On GitHub it reads the
server as above. On Gitea, name the host in yokoten.json ("host": "gitea") and give the
bot user a token in GITEA_TOKEN; doctor then reads the branch and tag protection, the
labels, the milestones and the forgotten branches through Gitea’s API. gitea.com is
recognised without the key. On another host, such as GitLab, the core works the same: the
rules, the guard and the commit check. The steps that only the host can judge are tests,
ci, labels, milestone, titles, codeowners, bot, review and the three server
steps. There each one is can’t check, and says what it asks, so you can set it up by
hand. They never hold the stage. A self-hosted server’s name is never printed: doctor calls
it “this host”.
labels and milestone in stage 2 are read the same way. The standard labels are bug, feat,
docs, refactor, ci, owner, incident, good first issue, hotfix, security, and the
tier labels tier A, tier B and tier C. init shows which are missing, and
init --github --yes adds them.
review in stage 4 reads the server the same way. It is done when the default branch’s ruleset, or
its branch protection, asks for at least one approving review. GitHub does not let you
approve your own pull request. So the bot opens the pull requests, and you approve and
merge them. To set it up, edit the ruleset on the default branch: in “Require a pull
request”, set “Required approvals” to 1. Also keep “Allow GitHub Actions to create and
approve pull requests” off, in Settings, Actions, General. If it is on, a workflow the bot
pushes could approve the bot’s own pull request, so review is not done. OpenSSF
Scorecard should then count each change as reviewed, because the person who merges it
did not write it. We read this in the Scorecard docs and have not checked it in a real
Scorecard run yet. A private repo on a free plan has no rulesets, so there review is
can’t check and waits for stage 5, when the repo goes public. It is not not now:
that mark is only for a decision you make.
init --github --yes also adds your own areas from yokoten.json “areas” as labels, in one
grey colour. It does this only for 8 areas or fewer: a longer list is noise, so make the few you
use by hand. An area label that is there is kept as it is, and yokoten never deletes one.
The board is optional. init --github --yes --board makes one named after the repo and links
it. Its Status field gets Todo, In progress, In review and Done. A board needs the gh token’s
project scope; without it, init skips the board and says to run gh auth refresh -s project.
A second run finds the board by its title and makes nothing. The API can’t set a board’s views
or workflows. So when a board is linked, full doctor lists them as “by hand” lines. They never
fail doctor.
After a step, yokoten doctor names the outside standard it meets, when there is one,
for example ci (Scorecard: CI-Tests). The session message leaves it out.
Record 0004 says why the road looks like this.
This repo walks the same road, so you can see each stage working:
| Stage | You learn | See it here |
|---|---|---|
| 1 · Safety net | rules your agents read, and checks that stop them when rules aren’t enough | AGENTS.md |
| 2 · Issues and pull requests | every change starts as an issue and merges only when CI passes; labels, milestones, and a board keep track | issues, pull requests, the board |
| 3 · Release | commit messages that say what changed and why, a changelog, a version number for every release | CHANGELOG.md |
| 4 · A team of agents | a bot opens pull requests, workers take tasks, a reviewer checks them; only you merge | Managing agents like a team |
| 5 · Going public | scan every past commit for private data, turn on security settings, protect main |
security policy |
And when an agent tries to push a release tag:
yokoten stopped this: git push --tags pushes tags [release]
Why Only the owner decides when to release.
Instead Stop and tell the owner: only the owner releases, with the steps in
How to release; do not look for another way.
More https://github.com/yokoten-labs/yokoten/blob/main/docs/guide.md#how-to-release
The refusal is the same for every agent: it tells the agent to stop and let the owner release. The approval way, and the command that starts each agent with it, are in How to release. Muse Code and OpenCode are not known to pass the variable to their hooks, so the owner releases outside them. If Gemini CLI’s strict environment sanitization is on, the variable is dropped, and the owner releases outside Gemini CLI.
Every refusal has the same four parts. The first line says what happened, and ends
with the rule id in square brackets, as yokoten stopped this: <what> [<id>]. The
Why row says why it matters. The Instead row is the fix, a command or an
edit. The More row links to where the rule is explained in full. For a problem in a
file, the place comes right after the title: yokoten stopped this: file:line: <what> [<id>]. A hit names the kind of thing
it found, never the thing: a secret or a private mark is never printed.
To ask why a rule exists, run yokoten explain <id>. The id is the rule or step name,
such as secret or commit-check. With no id, or an unknown one, it lists every id.
$ yokoten explain force-push
force-push: no plain force push (core rule 7).
Why: a plain force push can throw away work that someone else pushed.
Check: the tool-call guard stops `git push --force`, `-f` and a `+ref`; `--force-with-lease` passes; a branch ruleset on the server is the lock.
Docs: https://github.com/yokoten-labs/yokoten/blob/main/docs/guide.md#the-core-rules
Work only on this machine
A power cut, a crash or a dead disk loses work that lives only on one machine. So
doctor lists that work, each item with the one command that saves it:
| Note | What it means | The command |
|---|---|---|
uncommitted-changes |
A worktree has changed files: how many, and how old the oldest is | git add -A and git commit -m "chore: save work" there, then push; with .yokoten/, the message is chore: add yokoten, and committing the copy is the point |
unpushed-commits |
A branch has commits that no remote has, or a detached HEAD has commits that no branch holds | git push, or git push -u origin <branch> for a branch with no upstream of its own; git switch -c <name> first for a detached HEAD |
stashes |
There are stashes. A stash is never pushed | git stash branch <name>, then push it |
stale-index-lock |
An index.lock is left from a crash, and no git process runs in the repo |
rm the file |
repo-on-tmpfs |
The repo is on a tmpfs, so a restart empties it | git push --all origin, then clone it again in a folder that lasts |
repo-in-temp-folder |
The repo is under the temp folder, which the system may empty on restart | git push --all origin, then clone it again in a folder that lasts |
finished-worktree |
A worktree with no changes whose branch is merged, or whose upstream is gone, or whose detached HEAD is in the default branch | git worktree remove <path>; the branch stays |
missing-worktree |
git still lists a worktree whose folder is gone | git worktree prune |
A commit that is not pushed is still only on this machine. A branch made from
origin/main tracks it, so its commits go to a branch of its own, never to main.
A squash merge leaves no sign in the branch’s history. So a branch whose upstream is gone counts as merged too. A worktree with uncommitted changes is never called safe to remove. A branch, or a detached HEAD, that has not moved since it was made is not called merged: it is new.
For an index.lock, doctor looks for a git process that works in the repo. On Linux it
reads each process’s folder. On macOS and Windows it can’t, so any git process means “can’t
tell”. Then doctor never says to delete the file. git’s long-running helpers, such as the
credential cache, do not count. The tmpfs check reads /proc/mounts, so it runs on Linux only.
The temp folders are the system’s own (TMPDIR, or %TEMP% on Windows) and /tmp. WSL
empties /tmp on restart, even though it is not a tmpfs. To check other folders, list them
in YOKOTEN_TEMP_DIRS, split like PATH. Set it empty to turn the check off.
The repo itself in such a folder always gets a note: that is a setup problem. A worktree
there is named only when it holds work at risk, and then in that work’s own note. For
example: “../w has 3 uncommitted files, the oldest changed 2 days ago, and is under the temp
folder (/tmp), which the system may empty on restart”. The worktrees there that hold nothing
at risk get one line in full doctor, with the count, and none in a session. Each fix
says to make worktrees under .worktrees/, never under a temp folder.
All of these are notes: they never fail doctor. Doctor only reads here. It changes no
file, no ref and no worktree, and git runs with optional locks off. Paths show relative to
the repo, or from ~.
A session hears only what is at risk, three items at most. It hears uncommitted changes
only once the oldest is an hour old, because work going on now is not forgotten. Worktrees
to tidy up are left to full doctor. When two or more worktrees in the temp folder hold work
at risk, one line gives the count first.
A session spends at most one second on this, because a session must not wait on a cold
disk. When the time is up, it shows what it read and says “doctor stopped early”. Full
doctor has no time limit.