AGENTS.md · git:20261015.c33934f · 2026-10-15 · sha256 af157a6548468f87

AGENTS.md git:20261015.c33934fA

Immutable. This exact content never changes and is served at /api/v1/blob/af157a6548468f87.

# Guidelines for AI Agents

This repository distributes the IDD (Issue-Driven Development)
workflow — a portable set of `.github/instructions/` files that
wire up a multi-agent issue-driven pipeline for any GitHub
project.

This file is the canonical, tool-neutral instruction source for AI
coding agents in this repository, following the
[AGENTS.md](https://agents.md) open standard that Codex CLI, OpenCode,
Grok Build, Cursor CLI, and GitHub Copilot (CLI, coding agent, and Chat)
all discover automatically at the repository root. `CLAUDE.md` and
`GEMINI.md` are thin adapters that import this file for Claude Code and
Antigravity CLI (formerly Gemini CLI), the two tools in this
repository's harness mix that read their own dedicated entry file
instead, and
[.github/copilot-instructions.md](.github/copilot-instructions.md)
carries only the small amount of guidance specific to GitHub Copilot's
own UI terminology. See [docs/ai-strategy.md](docs/ai-strategy.md) for
the reasoning behind this layout.

## Minimum requirements

- Match the conversational language to the user's language.
- Write comments and documentation in English unless there is a
  clear project-specific reason otherwise.
- When editing `README.md`, also apply the equivalent change to
  `README.ja.md`, and vice versa. Keep both files in sync in the
  same commit.
- Avoid hard-coded repository file counts in docs unless the
  number is mechanically maintained. If count-based wording is
  necessary, update every mirrored reference in the same commit.
- When documentation or instruction text names an anti-pattern or
  failure mode, cite the observed incident per
  [docs/idd-design-rationale.md](docs/idd-design-rationale.md#cite-the-observed-incident).
- Continue autonomously for low-risk work, but pause and ask a
  concise question when uncertainty or hidden risk makes the next
  step unsafe. When that pause is needed, provide one or more
  recommended response options.
- Keep changes small and reviewable. Follow the project's
  Conventional Commits rules and keep each commit atomic.
- Do not modify community documents (`CODE_OF_CONDUCT*`,
  `CONTRIBUTING*`) without explicit approval.

## Project standards

- **Indentation**: 2 spaces
- **Line endings**: LF only
- **Trailing whitespace**: trimmed except in Markdown
- **Final newline**: always present
- **File naming**: lowercase with hyphens unless a platform
  convention requires otherwise
- **Helper sources**: the helper migration to TypeScript is complete —
  every `scripts/*.mjs` / `bin/*.mjs` is generated from a
  `src/**/*.mts` source by `pnpm run build` and committed; no
  hand-written helper `.mjs` path remains. `.mts` is the source of
  truth — edit the `.mts`, never the generated `.mjs`.
  See [docs/typescript-sources.md](docs/typescript-sources.md).
- **Inline code span wrapping**: a code span may wrap at a word
  boundary but must never wrap mid-token (right after a hyphen,
  underscore, slash, or dot the token continues through) — CommonMark
  renders the break as a literal space and corrupts the token. Put a
  command too long for one line in a fenced code block instead. If the
  wrap-point character is a hand-added artifact rather than part of
  the real value, delete it instead of just relocating the break.
  Enforced by `node scripts/audit-code-span-wrap.mjs`
  (repository-local; not distributed to `idd-template/`). The same
  script also flags this failure mode outside code spans, in prose
  Markdown text: a hyphenated compound word (e.g. "transport-layer")
  must never wrap right after the hyphen inside `**bold**` / `*italic*`
  emphasis, for the same corrupting-space reason (issue `#2876`).
- **Bare issue/PR reference wrapping**: a bare `#NNN` reference must
  never be the first token of a paragraph, list item, or wrapped
  continuation line in prose Markdown — `dprint fmt`'s automatic
  line-wrap can relocate it there, and markdownlint's MD018 rule
  (`no-missing-space-atx`) then flags it as a malformed ATX heading.
  MD018 only fires at zero leading indentation, so it misses the same
  drift inside an indented list-item continuation line — do not rely
  on the linter alone. Prefer "issue `#NNN`" (or another phrasing that
  keeps a word before the `#`) whenever the reference could plausibly
  open a sentence, bullet, or wrapped line.

## Key workflow rules

`## Branch strategy` and `## Commit rules` below cover those two rule
sets in full. This section records repository-local dogfood policies
layered on top of the distributed IDD defaults:

- **Merge policy**: This source repository records
  `fully_autonomous_merge` as an explicit local IDD dogfooding opt-in
  against the distributed `human_merge` default
  (applies only to `kurone-kito/idd-skill`). An IDD session may
  continue through F3 merge execution only after normal claim,
  freshness, CI, advisory, review, and unresolved-thread gates
  pass.
- **Discover concurrency**: This source repository also records
  `discover.selectionDesync: session-offset` as a local IDD
  dogfooding policy (applies only to `kurone-kito/idd-skill`),
  spreading concurrent-session A4 Step 2 candidate selection across
  a same-score tie band instead of every session converging on the
  same lowest-numbered issue, to cut claim races under this
  repository's heavy concurrent-session load.
- **Advisory-convergence waiver backstop**: This source repository
  also records `ciGate.externalCheckWaivers.mode:
  "maintainer-authorized"` with `idd-advisory-convergence` registered
  under `ciGate.externalChecks.waivable` as a local IDD dogfooding
  policy (applies only to `kurone-kito/idd-skill`), giving a trusted
  maintainer a human off-ramp when the autonomous advisory-convergence
  loop cannot converge on its own (Refs #1465): once the configured
  convergence deadline (default 24h) has elapsed from when GitHub first
  recorded the current PR HEAD (its earliest check suite), a trusted
  maintainer can post a valid
  external-check waiver for `idd-advisory-convergence` to unblock the
  gate. Treat this the same as any other merge-gate bypass — a
  deliberate, short-lived, maintainer-authorized exception for a
  genuinely stuck check, not a routine substitute for a fresh Copilot
  review. See [Customizing IDD](docs/customization.md) and
  [docs/idd-helper-scripts.md](docs/idd-helper-scripts.md#external-check-waiver-contract)
  for the general mechanism.
- **Advisory-convergence self-referential-bootstrap-auto waiver**: A
  second, narrower `idd-advisory-convergence` bypass alongside the
  maintainer-authorized one above (Refs #2657): when a PR's own diff
  touches this check's committed bootstrap trigger files (its waiver
  trust path, policy inputs, or workflow files) and none of its
  decision-rule files, a dedicated CI job
  auto-posts a run-bound waiver as `github-actions[bot]`, verified
  against the GitHub Actions runs API and this PR's own changed files
  before it is trusted. Unlike the maintainer-authorized waiver, this
  one is evaluated **unconditionally** — it can make `ready` true
  immediately, with no fresh Copilot review and before the
  convergence deadline elapses — because its whole purpose is
  bootstrapping a fix to the checker (including the deadline mechanism
  itself) while a PR editing it is still unmerged. It exists only for
  that narrow trigger set, never as a general substitute for review.
  A diff that touches one of the check's decision-rule files gets no
  such waiver; it needs a converged review, an approval of its HEAD
  from a `trustedMarkerActors` login, or the maintainer-authorized
  waiver after the deadline or while Copilot is terminally unavailable
  (issue `#4015`). See
  [docs/idd-helper-scripts.md](docs/idd-helper-scripts.md#automated-self-referential-bootstrap-auto-waiver-kurone-kitoidd-skill2657)
  for the full trust model.
- **ciGate F2 bootstrap**: This source repository follows the
  distributed preload-first procedure in
  [ciGate F2 bootstrap](docs/customization.md#cigate-f2-bootstrap)
  when a new `ciGate.*` key must land before F2 can honor it (observed
  on `kurone-kito/kurone-kito#33` and `kurone-kito/kurone-kito#37`).
  Do not generalize issue `#2657`'s self-referential-bootstrap-auto
  waiver to an arbitrary `ciGate.*` key.
- **Advisory-convergence deadline**: This source repository also
  records `advisoryWait.convergenceDeadline: "PT9H"` as a local IDD
  dogfooding policy (applies only to `kurone-kito/idd-skill`),
  shortening the maintainer-authorized-waiver deadline from the
  distributed `idd-template/` default (`PT24H`) to fit this
  repository's more autonomous, higher-concurrency dogfooding setup
  (Refs #1465, #2076).
- **Secondary-bot quiet window**: This source repository also records
  `advisoryWait.secondaryBotLogin: "coderabbitai[bot]"` and
  `advisoryWait.secondaryQuietWindow: "PT5M"` as a local IDD dogfooding
  opt-in (applies only to `kurone-kito/idd-skill`), waiting up to five
  minutes, measured from the last substantive activity, after E-phase
  convergence conditions are first observed before pre-merge readiness
  treats review as settled. The window was first set
  to one hour (`PT1H`, commit `c0909776c`, 2026-09-02) and was shortened
  to five minutes for the reasons in the 2026-10-13 measurement below
  (issue `#4167`). This repository
  dogfoods CodeRabbit alongside Copilot and has twice hit CodeRabbit
  rate-limiting during a PR's review cycle, each time working around
  it with an ad hoc one-hour wait before merging; this config turns
  that informal practice into a proper `pre-merge-readiness` blocker
  (Refs #2335, #2410). As of #2544, the wait is conditioned on live
  review evidence rather than unconditional: once CodeRabbit has
  already posted a genuine (non-rate-limited) review for the current
  HEAD, only a short fixed confirmation buffer applies instead of the
  full wait -- a HEAD CodeRabbit has not yet reviewed still waits the
  full configured window unchanged, keeping the wait a fallback for
  genuine secondary-bot degradation rather than a tax on every merge.
  As of #2547, a definitive decline verdict for the current HEAD -- a
  rate-limit or skip-review notice with no genuine comment after it --
  is checked before that #2544 short-buffer path and completes the
  wait immediately -- zero delay, not even the confirmation buffer:
  once CodeRabbit has conclusively said it will not review this
  commit, #2335's "might still be mid-review" protection has nothing
  left to protect.
  Measured 2026-10-13 (issue `#4167`): of 9 open pull requests, the
  window was the only blocker on none, and it was among the blockers on
  one (pull request `#4154`, 60 minutes remaining after activity at
  05:49:04Z, plus four other blockers: unresolved threads, missing
  dispositions, a missing watermark, and failing or pending CI). Of 16
  merged pull requests that could be read, the time from the head commit
  to merge had a median of about 27 minutes. CodeRabbit declined (rate
  limit) on 8, reviewed the head on 2, and was silent on 6; silent pull
  requests merged after a median of about 21 minutes, with a maximum of
  43 minutes. A late CodeRabbit wave does happen: on pull request `#4131`,
  one CodeRabbit event came more than five minutes after its first review
  on the head. The evidence sets the floor: `docs/policy-constants.md`
  records that on pull request `#2330`, findings landed 34 seconds and 2
  minutes after a zero-unresolved snapshot, so a one-minute window would
  have missed that case. A one-minute value was considered and rejected,
  because it would clamp both waits to one minute and the measured evidence
  does not support that. `PT5M` matches the settled buffer
  (`SECONDARY_QUIET_WINDOW_SETTLED_BUFFER_MINUTES`), so one value sets both
  waits to five minutes. The weekly merged-pull-request feedback sweep
  (issue `#4166`, `.github/workflows/merged-pr-feedback-sweep.yml`,
  scheduled Mondays at 06:00 UTC) is the safety net for late reviews that a
  shorter window lets through, and it is already on `main` when this value
  lands.
- **New-CI-job dispatch-first rollout**: `idd-pr-submit.instructions.md`'s
  D2 "Adding a new CI job" guidance (distributed via `idd-template/`, not
  itself local) requires landing a new CI job `workflow_dispatch`-first.
  This source repository also records, as a local IDD dogfooding policy
  (applies only to `kurone-kito/idd-skill`), the motivation for that
  guidance: this repository's own `copilot_code_review` ruleset, whose
  Copilot/Codex review cost tracks push count roughly 1:1 regardless of
  which CI jobs a push touches -- landing a new job
  `workflow_dispatch`-first does not reduce that review-cost line item
  by itself, but avoids wasted CI Actions-minutes and false-failure
  noise from an unproven job auto-running on every unrelated push. See
  that guidance (and its linked rationale entry) for the mechanics
  rather than duplicating them here. Refs #2892 (non-blocking).
- **Critique-loop telemetry hook**: This source repository also
  records `critiqueLoop.telemetryHook.command:
  "idd-critique-telemetry"` as a local IDD dogfooding policy (applies
  only to `kurone-kito/idd-skill`). Roadmap #2677 and its children
  issues #2678-#2680 built the `critiqueLoop.telemetryHook` mechanism
  as a distributed, opt-in per-operator feature, but that roadmap's
  own scope stopped at the mechanism and never configured it for this
  repository. Observed 2026-09-14, issue #3001: an in-repo audit found
  `critiqueLoop.delegate` already configured and in genuine, frequent
  use, while C1's own effectiveness at mitigating E-phase Copilot
  review cost -- the purpose `docs/idd-design-rationale.md` documents
  for the critique loop -- had zero observability. The configured
  command is the bare name `idd-critique-telemetry`, not a hardcoded
  absolute path, because this file is committed to a public repository
  and a personal `/home/<user>/...` path would silently fail for any
  other operator or a remote/cloud agent that never reads this
  operator's `$HOME` (preventive; no observed incident yet).
  `idd-critique-telemetry` itself is expected to come from an
  operator's own external tooling (this repository ships the
  `idd-critique-telemetry-hook` resolver that invokes it, never the
  collector binary itself), mirroring how `critiqueLoop.delegate`'s
  configured command already points at an external, not-shipped-in-repo
  binary. Per
  [docs/idd-workflow.md](docs/idd-workflow.md#repository-configurable-critique-telemetry-hook)'s
  fire-and-forget contract, an operator or remote/cloud agent without
  that binary on `PATH` gets a silent no-op, the same as leaving the
  hook unconfigured, rather than a failure.
- **Gist-retrospective field-feedback label**: this repository applies
  the `feedback:idd-core` label to every "IDD loop field-feedback
  intake" gist-retrospective roadmap (title beginning with `roadmap:
  IDD loop field-feedback intake`) and every child issue linked from
  its `## Tracks` list, at authoring time alongside the normal
  `status:authoring` hold label. The label is visibility-only -- it
  lets a maintainer find and review these issues with extra scrutiny,
  since they originate from second-hand narrative gist material about
  another repository rather than a locally reproduced observation --
  and it must never gate Discover, Claim, or any other autopilot
  phase. It is a distinct axis from the adopter-side
  `status:upstream-candidate` label (roadmap `#2700`): that label
  stays local to each adopter repository and is never applied in
  `kurone-kito/idd-skill` itself; `feedback:idd-core` is the
  source-repo-side counterpart applied once the maintainer has already
  decided to report the observed problem here.
- **Adopt-now urgency defer**: This source repository also records
  `critiqueLoop.deferByUrgency: "severity-tiered"` as a local IDD
  dogfooding policy (applies only to `kurone-kito/idd-skill`).
  `low` and `low-and-medium` still never defer High.
  `severity-tiered` scores urgency `very-low` < `low` < `medium` <
  `high` and, below the first threshold, defers High only at
  `very-low` (issue `#3589`). The gradient acts only when
  `deferByUrgency` is `severity-tiered`. This repository opts in
  with `critiqueLoop.deferRelaxAtRounds: [4, 7]`. Over the 100
  merged pull requests numbered #3561 to #3781 (merged 2026-09-27
  to 2026-10-05), the median PR-wide Copilot review count was 2,
  and 30 reached a 4th review, 25 a 5th, 14 a 7th and 10 a 9th, so
  the first threshold leaves a typical pull request unchanged and
  the second acts only on the long tail. A follow-up filed by the
  gradient carries the existing `review-fix-loop-cutoff` marker.
  The motivating observation is
  [kurone-kito/setup.ubuntu#201](https://github.com/kurone-kito/setup.ubuntu/issues/201).
  The earlier opt-in was `low-and-medium` (issue `#3222`): a 60-PR
  baseline (`#3089`-`#3210`) found all 11 Low-only reviews and 25
  Medium-highest reviews followed by another Copilot review wave.
  As of the 2026-09-24 observation,
  `critiqueLoop.deferAfterRounds` (this repository's own local
  `5`) had never fired because most review waves happened well
  before that round threshold. A later count over the same #3561
  to #3781 range found that 25 of 100 pull requests reached a 5th
  Copilot review, and none of the 7 deferral replies in that range
  carries a `round <r>/5` clause. See
  [docs/idd-design-rationale.md](docs/idd-design-rationale.md#e4e5-adopt-now-urgency-defer)
  for the baseline and the severity-tiered matrix.
- **Triage record opt-in (deferred)**: This source repository sets
  `critiqueLoop.triageRecord` to `"off"` explicitly, which is also the
  distributed default that `idd-template/` keeps. The opt-in to `"record"`
  is deferred because `advisory-convergence` prints
  `resolve-review-thread --apply` pointers without the `--tier` and
  `--urgency` flags that `record` requires for an Accepted or Rejected
  reply, so `resolve-review-thread` refuses those pointers. Observed
  2026-10-16 on pull request `#4405` (Copilot discussion `r4288841711`).
  The opt-in depends on an `advisory-convergence` change that emits those
  flags. Until that change lands, the value stays `"off"`; after it lands,
  set the value to `"record"`. With `record`,
  `resolve-review-thread` requires `--tier` and `--urgency` on each
  applied Accepted or Rejected PATH A reply, except a `--path-b` reply or
  a `**Rejection confirmed by maintainer**` reply. The helper writes a
  `Triage:` line for
  each reply that carries both flags, in any mode. The switch to
  `"enforce"` waits for the weekly audit data and is decided in issue
  `#4208`. Evidence for the later opt-in: among the 100
  pull requests that were merged at or before 2026-10-13T06:58:55Z and
  are numbered `#3936` to `#4158`, six
  deferral-form replies appeared on four pull requests, and one of 535
  review-thread replies that begin with `**Accepted**` or `**Rejected**`
  recorded an urgency. Two conversation replies, on `#3966` and `#4044`,
  also recorded urgencies, outside that count (item 2 of the subsection).
  See the
  [review-loop audit (2026-10-13)](docs/idd-design-rationale.md#review-loop-audit-2026-10-13)
  subsection of the design rationale.
- **Effort L refusal opt-in**: This source repository also records
  `autopilotSuitability.effortL: "refuse"` as a local IDD dogfooding
  policy (applies only to `kurone-kito/idd-skill`); `idd-template/` keeps
  the distributed default `"allow"`, so adopters are unchanged. With
  `refuse`, `audit-authored-issue` fails an orphan or child body whose
  effort is `L` (check `effort-l-ready`) unless it is routed to a human
  bucket, and autopilot Discover routes an effort `L` orphan to a human.
  To turn this off, remove the field or set it to `"allow"`. Evidence: in
  the same 100-pull-request cohort as the bullet above, 6 of the 8 pull
  requests whose issue has effort `L` reached 7 or more Copilot reviews,
  against 2 of the 33 pull requests with effort `S`. The `L` group is only
  8 pull requests. The subsection does not separate pull request size from
  effort: changed lines and Copilot review count have a Spearman correlation
  of 0.57 over the cohort, and the 20 pull requests with 7 or more reviews
  have a median of 967.5 changed lines, against 122.5 for the 64 with at
  most 3 (item 5). Item 7 gives the effort groups. See the
  [review-loop audit (2026-10-13)](docs/idd-design-rationale.md#review-loop-audit-2026-10-13)
  subsection of the design rationale.
- **GitHub API load control**: This source repository also records
  `githubApi.loadControl: { enabled: true, maxConcurrent: 4 }` in
  `.github/idd/config.json` as a local IDD dogfooding policy (applies
  only to `kurone-kito/idd-skill`); `idd-template/` keeps the distributed
  default off, so adopters are unchanged. Roadmap `#3583` dogfoods each
  delivered capability here first, and this repository runs many
  concurrent sessions as one OS user, the case the host-local admission
  layer is for (motivating episode: the 2026-09-27 GraphQL rate-limit
  report in issue `#3560`; most of the failing commands it lists were
  typed by an agent, which load control does not cover). The value was
  measured, not guessed (issue `#3702`, 2026-10-01): four parallel
  `discover-roadmap-graph --all-roadmaps --with-claim-state
  --with-readiness --with-progress --no-cache` scans (each at its own
  `--concurrency` of 8) against a fresh state directory per row took, at
  `maxConcurrent` 8, 4, 2 and 1, a slowest scan of 9.9, 14.5, 23.1 and
  43.7 seconds, all sixteen scans exited 0 and none reported a `deadline`
  interruption. The smallest value within twice the 8 row (19.8
  seconds) is 4, so that is the cap; the full table is in the PR that
  enabled it. Exposure: a write or unclassified request that meets a
  full slot is refused at once with nothing sent, and a read that waits
  out `maxWait` (`PT30S` by default) with every slot held is refused
  too, which a scan reports as a `deadline` interruption; a standard
  Discover scan, which passes no `--with-progress`, surfaces that
  refusal as an error instead of a recovery result. To turn it off,
  remove the `loadControl` entry or set `enabled` to `false`; a refused
  request (`notDispatched`) in a real session is the signal to do so
  (preventive; no observed incident yet).
  See [docs/idd-helper-scripts.md](docs/idd-helper-scripts.md#github-api-load-control)
  for the mechanism.

- **Orchestrator worker cap**: This source repository also records
  `orchestrator.maxWorkers: 4` in `.github/idd/config.json` as a local IDD
  dogfooding policy (applies only to `kurone-kito/idd-skill`);
  `idd-template/` keeps the distributed default of 2, so adopters are
  unchanged. The value is measured, not guessed: over the 100 pull requests
  merged from 2026-09-29 to 2026-10-07 (`#3608` to `#3815`), the number of
  pull requests open at once had a median of 2, a 90th percentile of 4, and a
  maximum of 9. The cap is per orchestrator session, so concurrent
  orchestrator sessions multiply it, and only the host and GitHub API checks
  in `idd-worker-budget` (load, memory, and REST or GraphQL rate-limit
  headroom) limit the total. To opt out, remove the
  `orchestrator` field and the `orchestrator-worker-cap-dogfood` rule together,
  because the rule pins the value to exactly 4 and the field alone is not
  enough (refs `#3839`).

## Branch strategy

This project follows
[GitHub Flow](https://docs.github.com/en/get-started/using-git/github-flow):
`main` is the only long-lived branch and every change reaches `main`
through a pull request.

### Rules

- **Never push directly to `main`** — all changes must go through a
  pull request. Branch protection is enforced on GitHub.
- **Rebase onto `main` before publication** — before the first D-phase
  push of a PR branch, rebase onto `main` as needed. Fetch first so the
  local `main` is not stale, e.g.
  `git fetch origin && git rebase origin/main`
  (or `git pull --rebase origin main`). Do not create merge commits
  inside unpublished feature branches.
- **Treat pushed PR branches as published review history** — after the
  first D-phase push, branch-state checks stay read-only until a later
  phase decides an update is required.
- **Default post-push sync: merge `main` into the PR branch** — when an
  already-pushed branch needs synchronization or conflict resolution,
  merge `main` into the PR branch and send that follow-up through the
  normal CI and review gates. Do not rebase or force-push merely
  because the PR is `BEHIND`. This is the active branch-policy
  contract, enforced end to end by
  `idd-review-triage.instructions.md`'s E-phase branch-sync check
  (`Esync`), which uses the `branch-conflict-state` helper when
  helper runtime is enabled.
- **Force-push exceptions stay narrow** — use rebase and
  `--force-with-lease` after publication only when repository policy
  explicitly permits it and merge-based recovery cannot safely fix the
  branch, or when an already-started rebase must be completed or
  aborted during recovery.
- **Rebase between unpublished feature branches** — if one unpublished
  feature branch needs changes from another, use rebase, not merge.
- **Merge commits at PR boundary** — pull requests into `main` are
  merged with a merge commit (squash-merge and rebase-merge are
  disabled in the repository settings).
- **fixup + autosquash for unpublished in-branch fixes** — when a later
  commit in an unpublished feature branch fixes an earlier one, prefer
  `git commit --fixup=<sha>` followed by
  `git rebase -i --autosquash` to fold the fix into its target.
- **Avoid giant commits** — if squashing would produce an
  unreasonably large commit, keep the fix commit separate or
  re-split the history so each commit remains reviewable.

## Commit rules

This project follows
[Conventional Commits](https://www.conventionalcommits.org/).
A `.gitmessage` template is available at the repository root for
guidance when writing commit messages. Git does not use it
automatically, so contributors who want the template prefilled in
their editor should opt in once per clone:

```sh
git config commit.template .gitmessage
```

### Format

```txt
<type>[optional scope]: <user-facing description>

<body: address purpose, context, and what changed>

[optional footer(s)]
```

### Subject line

- Use the format: `<type>[optional scope]: <description>`
- Write from the **user's perspective** — briefly state what this
  commit solves or improves for the end user or developer
- Write in **lowercase**, imperative mood (e.g., "add", not "added")
- Keep the subject line under **72 characters**
- Do **not** end with a period

### Types

Common types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`,
`chore`, `ci`, `build`, `perf`

### Scopes

- Optional, in parentheses: `feat(ci):`, `fix(lint):`, `docs(readme):`
- Keep scopes **lowercase**, short, and consistent
- Use the directory or component name that best describes the area

### Body (line 3+)

The body should address three aspects:

- **Why** — the purpose or motivation behind the change
- **Context** — what was needed, the situation or constraint
- **What changed** — the concrete action taken

Prefer the **why → context → change** order when practical.
Write these as **natural prose** — weave the aspects into
coherent sentences rather than using labeled sections. Labeled
sections (`Why:` / `Context:` / `Change:`) are acceptable only
when explicit paragraph separation improves clarity.

Omit any aspect whose information **cannot be reliably inferred**.
If the subject line is self-explanatory, the body may be omitted
entirely. **Breaking changes must always include a body.**

Wrap body lines at **72 characters**.

- **Avoid a bare `token:` line start**: a body line must not begin
  with a single (optionally hyphenated) token immediately followed by
  a colon and a space — for example a line starting `bullet: ...` or
  `Step-1: ...`. `@commitlint/config-conventional`'s footer/trailer
  parser misreads that shape as the start of a footer section, even
  though it is ordinary prose deep in the body, and emits a spurious
  `footer-leading-blank` warning — a **warning** under this
  repository's current `.commitlintrc.yml`, not a blocking failure,
  but still worth avoiding. This does not conflict with the `Why:` /
  `Context:` / `Change:` labels above: each one only ever opens a
  fresh, blank-line-separated paragraph, so the blank line already
  required before it also satisfies `footer-leading-blank`; the trap
  here is the same shape appearing **without** a preceding blank
  line, buried mid-paragraph. Reword instead: lead with an article or
  an extra word (for example "the bullet: ..." or "in step 1: ...")
  so the line no longer opens with a bare token before the colon.
  Observed 2026-09-12, live against this repository's own
  `.commitlintrc.yml`, in commit
  `bae5e0f1e43b54713f936af6f677773b684dd81e` (PR `#2942`, issue
  `#2943`).

### Breaking changes

- Append `!` after the type/scope: `feat!: remove deprecated endpoint`
- Add a `BREAKING CHANGE:` trailer in the footer with a detailed
  explanation of what breaks and migration steps

### Footers / trailers

- `Closes #<issue>` / `Refs #<issue>` — link to issues
- `Co-authored-by: Name <email>` — credit co-authors
- `BREAKING CHANGE: <description>` — detail the breaking change

### Atomic commits

Keep each commit as **small and focused** as possible:

- **One logical change per commit** — if the subject line needs "and",
  consider splitting
- **Separate refactoring** from behavior changes
- **Separate formatting/style** changes from logic changes
- **Separate dependency updates** from code changes
- When in doubt, prefer smaller commits that are easy to review,
  revert, and bisect

### Examples

#### Good — single-line (trivial change)

```txt
fix: correct typo in feature request template
```

#### Good — prose body

```txt
feat(ci): add concurrency settings to lint workflow

Parallel lint runs on the same branch waste resources and
cause race conditions in status checks. GitHub Actions
supports concurrency groups that automatically cancel
redundant runs, so add a concurrency group keyed on branch
name with cancel-in-progress enabled.

Refs #42
```

#### Good — breaking change

```txt
feat!: require node 20 as minimum version

Node 18 reached end-of-life in April 2025 and no longer
receives security updates, while the project now standardizes
on the active Node 20 LTS baseline. All production
environments have already been upgraded to node 20+, so
update the engines field and CI matrix to require node >= 20.

BREAKING CHANGE: drop support for node 16 and 18. Users
must upgrade to node 20 or later.
Closes #108
```

#### Bad — vague, developer-centric

```txt
fix: update code
```

#### Bad — too large / non-atomic

```txt
feat: add auth system and refactor database layer and update docs
```

## For IDD work

Open `.github/instructions/idd-overview-core.instructions.md` and the
relevant phase file before starting work. See
[docs/idd-workflow.md](docs/idd-workflow.md) for the cross-agent
entry path and phase routing. See
[Model capability expectations](docs/idd-workflow.md#model-capability-expectations)
before running this loop on a lightweight local or compact cloud model.

## Dogfood: token-cost events

This source repository records IDD phase-boundary timestamps as a local
dogfooding measurement (`docs/token-cost.md`). Source-repo only — never
add this call to `idd-template/` or `.github/instructions/` phase files,
which distribute to adopters with no token-cost data to record.

When running the IDD loop in this repository, call

```sh
node scripts/token-cost-event.mjs --stage <id> --enter --vendor <v> --issue <n>
```

when a listed stage starts, and the same with `--exit` when it ends,
using `--vendor codex` for Codex CLI or `--vendor grok` for Grok Build.
Include `--issue <n>` on every call from `claim` onward, once an issue
is claimed — omit it only for `discover`, which precedes any claim. A
call with no `--issue` is unusable for per-issue attribution (#2418):
the harvester can only join it back to an issue when the number is
recorded on the event itself. Also include `--claim-id <claim-id>` —
the active IDD `{claim-id}` — on every call from `claim` onward: it is
the positive-evidence signal a later harvest uses to merge a genuine
cross-session handoff/resume of the same claim lineage instead of
undercounting it (#2432). OpenCode and Cursor CLI have no matching
`vendor` value in `schemas/token-cost-event.schema.json` yet — skip
this call rather than pass a mismatched vendor. Stage ids: `discover`,
`claim`, `work`, `submit-pr`, `review`, `merge`, `cleanup`. Ignore any
failure from this command — it must never block or slow the IDD loop.

## Issue-authoring skill (dogfooded)

The canonical issue-authoring skill bundle lives at
`skills/issue-authoring/`. Edit the canonical bundle, never a
generated copy: `node scripts/sync-docs.mjs --apply` regenerates
derived copies and `node scripts/audit-docs.mjs --check` fails on
drift. The `idd-spec-audit` skill bundle at `skills/idd-spec-audit/`
follows the same rule: `.claude/skills/idd-spec-audit/` is likewise a
generated mirror and must never be edited directly.

## Codex issue-authoring route

For Codex CLI, read the canonical issue-authoring bundle explicitly from
`skills/issue-authoring/`, or install one selected copy under
`.agents/skills/issue-authoring/` when the target runtime supports native
skill discovery there. The existing `.claude/skills/issue-authoring/` copy is
the dogfood route for Claude Code, OpenCode, Grok Build, and Cursor CLI
compatibility; it is not the
canonical source. Do not add checked-in `.agents/skills/`,
`.cursor/skills/`, or `.opencode/skills/` mirrors by default
(preventive; no observed incident yet),
and do not assume the source path is automatically discovered by Codex.

The rule against checked-in `.agents/skills/` mirrors covers repository files
only. An operator's user-global install under `~/.agents/skills/` (see
[User-global installation](docs/user-global-install.md)) lives outside every
repository, so it is not a mirror and this rule does not apply to it.