Imported from yukimemi/dvpm (
AGENTS.md). Install upstream withnpx skills add yukimemi/dvpm. Copyright stays with the author.
Shared conventions
This file is the agent-agnostic source of truth (per the
agents.md convention). Codex and Claude Code
(>=2.1.277) read it directly; the GEMINI.md file is a thin shim
for tools that don't yet auto-load AGENTS.md.
Edit AGENTS.md, not the shim.
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> --merge noneruns 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.- Always pass
--merge noneon amagi reviewyou invoke by hand.[merge] modein config defaults to"pr"for the queue loop (magi serve/magi web) that drainsmagi task add— there, ending in an actual PR is the point.magi reviewrun directly inherits that same config default when no--mergeflag is given, which would open a PR nobody asked for.--merge noneoverrides it for that one invocation and leaves the branch for the operator to turn into a PR themselves. - 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.
Denops plugin workflow
This repo follows the shared Denops plugin conventions. The
language-agnostic conventions block above (kata:agents:base:*)
covers git workflow, PR review cycle, and worktree usage.
Test / lint / format
Tests and lint run through Deno itself:
deno task ci # full CI gate (type-check + lint + fmt-check + test)
deno task check # type-check only
deno task fmt # format code
deno task lint # lint
deno task test # test
Deno's built-in formatter (not prettier, not dprint) is the
authoritative formatter. Run deno fmt before committing; CI
enforces deno fmt --check.
CI workflow
.github/workflows/ci.yml is kata-managed. The source lives in
yukimemi/pj-denops/.github/workflows/ci.yml.tera. Action versions
are pinned in .kata/vars.toml and bumped by Renovate — do not edit
them inline in the workflow; the bump would be clobbered on the next
kata apply.
CI installs real Vim and Neovim and exports
DENOPS_TEST_VIM_EXECUTABLE / DENOPS_TEST_NVIM_EXECUTABLE, because
@denops/test spawns the editors as child processes. Editor versions
are pinned in .kata/vars.toml under [denops]. Do not add a
shell: override to the run step — forcing bash routes Windows
through Git Bash, which mangles those executable paths and hangs the
nvim cases.
Dependency updates and merging
Renovate owns every dependency bump, including the Deno / JSR imports in
deno.json (its deno manager reads deno.json and the workspace
members). There is no deno outdated cron and no
pascalgn/automerge-action workflow — an earlier setup had both, and they
duplicated what Renovate already does.
Merging is GitHub's native auto-merge, armed by Renovate
(platformAutomerge) and by pj-base's kata-apply / apm-bump
workflows via gh pr merge --auto. Two repo-side preconditions, both
required — this is the same setup the pj-rust line uses:
allow_auto_mergeenabled on the repository.- Branch protection on
mainwithci (ubuntu-latest)as a required status check. GitHub only arms auto-merge on a pull request that is currently blocked; with no required check the PR is immediately mergeable and the request is rejected.
Only the ubuntu leg is required on purpose. The macOS and Windows legs still run and still have to be read, but a flaky runner on those two (editor downloads time out often enough) must not wedge every dependency PR.
Minor and patch bumps merge themselves. Majors wait for a human — that
includes Deno / JSR majors, which the old deno outdated --latest cron
used to pull in unreviewed.
