Imported from yukimemi/blog (
AGENTS.md). Install upstream withnpx skills add yukimemi/blog. Copyright stays with the author.
Shared conventions
This file is the agent-agnostic source of truth (per the
agents.md convention). The matching
CLAUDE.md and GEMINI.md files are thin shims that point back
here so each tool's auto-load behaviour still finds something.
Edit AGENTS.md, not the shims.
Git workflow
- No direct push to
main. Open a PR.- Exception: trivial typo / whitespace / docs wording fixes.
- Branch names:
feat/...,fix/...,chore/.... - PR titles + bodies in English. Commit messages in English.
- Releases are PR-driven and tagging is automatic — in repos that
ship a release pipeline. Bump the version in the project's own
manifest in a
chore/release-vX.Y.ZPR; on merge tomainthe language layer'sauto-tag.ymldetects the bump, pushes thevX.Y.Ztag, and that tag is what firesrelease.yml. Do not rungit tagby hand — the bot tag will collide and the manual push fails. The specifics belong to the layers shipping those two workflows, which are not the same layer:kata:agents:rust:*for which file holds the version and forauto-tag.yml,kata:agents:rust-{cli,lib}:*for whatrelease.ymlbuilds and publishes. A repo with noauto-tag.ymlhas no release pipeline at all: nothing tags, and the version field in its manifest may well be decoration.
Pre-merge review
Review happens before the pull request, on the operator's machine,
via magi. This layer no longer
ships PR-side review bots: claude-review.yml and claude.yml were
removed from it. Their scope was
human-authored PRs — their own job-level if: already excluded
chore/release-*, kata-apply/auto, apm-bump/auto and
Renovate / Dependabot — which is exactly the set magi reviews, so
keeping them meant reviewing the same diff twice, a
CLAUDE_CODE_OAUTH_TOKEN secret per repository, Actions minutes on
private repos, and one trap that silently cost reviews: a PR editing
either workflow was skipped by claude-code-action's
workflow-validation check and merged with a green check and no
review attached.
"Removed" is a statement about this template layer, not about
every repo's current state. Dropping a [[file]] entry stops kata
from managing the rendered file — it does not delete it. A repo that
had these workflows before this change keeps claude-review.yml /
claude.yml (and the CLAUDE_CODE_OAUTH_TOKEN secret) under
.github/workflows/ until someone deletes them by hand, and until
then they still fire on every human-authored PR. Check
.github/workflows/ before treating a PR as unreviewed-except-magi:
if either file is still there, its comments are a real review, not
noise to ignore.
magi review <branch>runs only the review + verification + gate half of magi's graph: nothing competes, no implementation, no judging, no vote. That is the mode for hand-written work.magi run "<task>"is the full competition, for work handed over whole. Both end at the same gate.- What the loop actually does: each reviewer gets its own detached
worktree pinned at the commit under review (no reviewer can
perturb the tree, and the fixer never races one);
verify.e2eruns in the branch's worktree and its output is fed to the fixer; finding ids (R2-1-3) are assigned by magi, not by the agent, so the fixer's adoption report can be matched against them; the loop is bounded byreview_rounds;verify.gatemust exit 0 before any merge is attempted. magi.tomlis repo-owned, not kata-managed. Pointverify.gateat the exact command CI runs, so a local pass means a green PR, and pointverify.e2eat the invocation that actually covers the repo — feature flags included. A gate that differs from CI turns a clean magi run into a red PR, which is the one failure this arrangement cannot absorb.- If you did not run magi, the change was not reviewed, and nothing
will tell you. Do not open a PR for a hand-written change before
magi reviewcomes back clean; if you must, say so in the PR body and say why. What does not count as a substitute: a green CI run (it compiles and tests, it does not review), and CodeRabbit's silence. - CodeRabbit stays installed and is not part of the gate. It does
not auto-review repositories under 10 stars — the common case here —
so treat it as absent unless it posts. When it does post, its
findings are a real review: address them, reply in the inline
thread with an
@coderabbitaimention (the review-comment replies endpoint,gh api repos/<owner>/<repo>/pulls/<N>/comments/<id>/replies -f body=…), and reply even when declining — say why, because a silent skip reads as overlooked. A "review limit reached" quota notice carries no findings and counts as quiet; re-trigger with@coderabbitai reviewwhen the quota refills if you want a real pass. - Read the report, not the exit status. A reviewer seat that
times out is logged as
WARN agent timed out seat=review-2and then summarised as "raised 0 finding(s)" — indistinguishable from a genuinely clean pass in both the summary andmagi stats. Check for timeouts before believing a clean round: a round where half the panel never answered is not a clean round. - Review artifacts stay local. magi comments on a pull request
only when it stops landing one. Findings, the fixer's adoption
report and reviewer precision live in the run directory
(
magi show,magi stats). When the PR needs a record — a non-obvious fix, a finding declined with an argument — paste that part into the PR body or a comment yourself. - With
merge = "pr", magi opens the pull request and keeps going: watches the checks, reads the review comments (human and bot), runs a bounded fix round when either is unhappy, pushes, and asks before merging.land_approvalis on by default and silence is a hold — nothing merges unanswered.magi answer(or the web UI) is where it asks. Out of rounds leaves the PR open with a comment saying what still fails;checks: unknownnever merges. - Merge gate: magi's gate green — or CI green for a change magi
never touched — and every review that did post resolved (a
leftover
claude-review.yml, CodeRabbit, a human) and the owner's explicit approval. The irreversible step stays a human decision. - No review-monitoring poll loop for bots this layer no longer
ships. The old loop existed to wait on them. Where a repo still
has
claude-review.yml(see above) the old cadence still applies until it is deleted; otherwise, after opening a PR wait for CI and report the wait state to the owner. When magi is landing the PR (land = true), magi does the watching. - Bot-authored PRs (Renovate / Dependabot) need no review pass at all: CI green + owner approval.
- Version-bump-only PRs — a single
chore/release-vX.Y.Zbranch whose entire diff is[workspace.package].version/[package].versionplus the matching inter-crate refs and the lockfile — likewise. There is nothing in a version bump for a reviewer to find, and the release pipeline downstream of merge (auto-tag →release.yml) is time-sensitive.
Worktree workflow
Before your FIRST edit to any file, run
renri add— NEVER edit the main checkout. Read-only inspection (Read / Grep / Glob) stays on the main checkout; the instant you intend to change a file, you must already be in a worktree. The trap that keeps catching agents: diving into a fix the moment the diagnosis lands and editing in place. A concurrent agent shares the main checkout — your in-place edits will clobber theirs or be clobbered, and in a jj-colocated repo a stray working-copy commit entangles unrelated WIP into your branch. If you slip and edit in the main checkout, capture the diff first (jj already snapshotted it into the working-copy commit, sojj diff > patch; for git,git stashor save a patch — if you got as far as committing on a branch, just push it). Then reset the main checkout to pristine main (jj new main@origin, orgit switch -),renri adda worktree, and re-apply the captured diff there.
Use renri for any
commit-bound change. From the main checkout:
renri add <branch-name> --from main@origin # create a worktree (jj-first), off latest upstream main
renri --vcs git add <branch-name> --from origin/main # force a git worktree, off latest upstream main
renri remove <branch-name> -y --non-interactive # cleanup after merge (agent-safe; see note)
renri prune # GC stale worktrees
Read-only inspection can stay on the main checkout.
Always pass --from <upstream main> (main@origin for jj,
origin/main for git). Without it, renri add forks off the cwd
worktree's current HEAD — in a long-lived main checkout that often
lags upstream, so the PR later shows up CONFLICTING against a main
that had already moved (e.g. a refactor merged upstream before the
branch was cut), forcing a manual re-port of the whole change.
renri add does fetch first, but fetching only updates main@origin
— it never moves the checkout's HEAD, so an explicit --from is what
guarantees a fresh base.
Agents / non-interactive shells: renri remove prints a details
panel and waits for a confirmation prompt — without -y it hangs,
and --non-interactive alone errors asking for -y. Always pass
-y, and add --non-interactive so a mistyped/omitted name fails
instead of opening a fuzzy picker (the same picker-fallback applies to
remove / cd / exec with no name). Use -f/--force to remove a
worktree that still has uncommitted changes or conflicts. To sweep
every merged-PR worktree in one shot: renri remove --merged -y.
kata-managed sections
Several files in this repo are managed by kata apply from the
yukimemi/pj-presets
templates — the bytes between <!-- kata:*:begin --> and
<!-- kata:*:end --> markers, plus the overwrite-always files
listed in .kata/applied.toml. Editing those bytes locally
won't survive the next kata apply — push the change to the
upstream template repo (yukimemi/pj-base / yukimemi/pj-rust /
…) instead.
The marker scopes are layered, one per applied layer:
kata:agents:base:* is this section, and each layer adds its own
(kata:agents:rust:*, kata:agents:rust-cli:*,
kata:agents:pnpm:*, kata:agents:firebase:*, …). Which ones apply
here is a grep away: <!-- kata: in this file.
This project's own conventions
Everything a layer ships is generic by construction: it describes the
stack the template assumed, not what this repo grew into. Bytes
outside every marker pair are yours and survive kata apply — so
project-specific conventions belong in a section of their own, outside
the markers (conventionally at the end of the file; if a later layer
appends its block below yours, no matter — kata only ever rewrites
between its own markers). Same mechanism as the .gitignore /
.gitattributes blocks.
Write those conventions down there rather than leaving them in one agent's head, in commit archaeology, or in a README the agent will not read. What earns a line:
- Any layer default that does not hold here. A layer states its assumption flatly ("Hosting is the primary target", "these rules are a placeholder to replace"). When the project has diverged, say so and say why — the layer's text keeps asserting the opposite on every apply, and an agent that only reads the blocks will act on it.
- Facts duplicated across files with no compiler in between — an address or a path that appears in code and in a rules/config file that cannot import it, a timeout that has to stay inside another timeout. List every copy, so the next edit finds them all.
- kata-shipped files this project deleted on purpose, together with
the
once_applied = trueline in.kata/applied.tomlthat keeps them deleted. Otherwise someone helpfully restores one. - Shapes the runtime forces but no tool checks — an export form a platform requires, import specifiers that must (or must not) carry a file extension, a directory whose contents are reachable by URL.
- Invariants that money or access rest on, naming the file and line that actually enforces them.
- Which language the code speaks versus what a user reads, when the two differ.
A repo whose AGENTS.md is nothing but kata blocks is a repo where
every agent re-derives all of that from scratch — and gets the layer
defaults wrong the same way each time.