Files
Skills/docs/conventions.md
claude b2c92db725 feat(fieldtest): bundle findings into collective issues; document the harvest sweep
Encode the tracker-growth practice ratified in the aura field run:

- fieldtest/SKILL.md — tracker filing defaults to bundling: one
  collective issue per fieldtest for related friction/spec_gap
  findings, consolidate-before-filing ahead of ANY new issue,
  per-issue filing only for real bugs (low-severity may wait in
  the collective issue). The fieldtester's per-finding report is
  unchanged; this is an orchestrator-side filing rule.
- docs/pipeline.md — new § Harvest sweep: the recognised batch
  shape for draining settled single-issue backlog (issue bodies
  as spec source, sequential implementer batches of 2-3, one
  non-removable independent whole-diff review, audit close,
  compact fieldtest under its normal skip rules). Registered as
  the specify/planner exception under § Skip rules.
- specify/SKILL.md, planner/SKILL.md — codify the sweep as a
  named alternative / skip case, per "skipping is codified per
  skill, not ad hoc".
- docs/conventions.md — register the noun in § Vocabulary.

closes #36
2026-07-24 20:33:28 +02:00

269 lines
14 KiB
Markdown

# Conventions
The skills plugin used to read a per-project `dev-cycle-profile.yml`.
It no longer does. There was never a parser — the profile was prose
the skill bodies told the model to read, and almost every slot was
either dead, constant across all projects, or derivable. So the
plugin now splits cleanly in two:
- **Fixed conventions** (this file) — the things that were constant
across every project. They are not configurable; the skill and agent
bodies name them directly.
- **Per-project facts** — the handful of things that genuinely vary
per project (where the code lives, how to build and test it, the
tracker slug, …). These live as prose in each project's own
`CLAUDE.md` under a `## Skills plugin: project facts` heading. See
`../templates/CLAUDE.md.fragment` for the template, and the section
list at the bottom of this file.
## File layout
| Artefact | Location |
|----------|----------|
| Specs (from `specify`) | `docs/specs/` |
| Plans (from `planner`) | `docs/plans/` |
These directories are fixed. A project that keeps specs and plans
elsewhere is the rare exception and states the override in its
`CLAUDE.md` project facts.
Both directories are **git-ignored** — their contents are transient
working files, never committed (see § Lifecycle). The ignore is
**project-local**: each project commits a `docs/.gitignore` whose two
entries, `specs/` and `plans/`, are relative to `docs/` and so match
`docs/specs/` and `docs/plans/`. That `docs/.gitignore` is the one
committed thing — it travels with the repo; the spec and plan files it
covers never are. It is created once (whichever skill first writes a
working file — `specify`, or `planner` on a direct tidy dispatch —
creates it if absent) and committed like any other repo file, after
which both directories are invisible to git for every future cycle. A
new project sets this up as part of adopting the plugin
(`../templates/CLAUDE.md.fragment`).
## Lifecycle
`docs/specs/` and `docs/plans/` hold **only the active cycle's
artefacts** — they are working space, not an archive. A spec or plan is
valid for the cycle that produces it; its code shapes drift the moment
the code moves, so a stale one left lying around reads as a live API
reference and misleads the next agent that opens it.
The two directories are **git-ignored** — their contents are never
committed. A spec/plan is a working file: present on disk while its
cycle is live (readable across sessions, since the working tree
persists), and at **cycle close** — after `audit` is drift-clean — the
orchestrator **discards** it with a plain shell `rm`. There is nothing
to `git rm` (it was never tracked) and no deletion commit is produced;
the working tree is simply left without a stale artefact. This is the
same shell-delete-an-uncommitted-file mechanic `specify` already uses on
its grounding-`BLOCK` failure path.
A spec/plan is therefore **transient on disk**: present for its own
cycle, `rm`'d afterwards, and — never having been committed — leaving no
trace in git. It is **not recoverable, by design**. The durable record
of a past cycle's design intent is the **design ledger only** (where the
rationale is lifted during audit); the implementation is committed as
normal code.
**No durable artefact may cite a spec or plan** — not by number, not by
path. Code, tests, docs, and ledger entries outlive the spec/plan that
prompted them, so any reference to `docs/specs/…` or "spec NNNN" would
dangle the moment the cycle closes. Cite the **design-ledger contract**
instead (e.g. a `C`-id); it is the durable record the spec's rationale
was lifted into.
## Naming
Files are named `slug.md` — a short kebab-case identifier of the
cycle's topic (slug separator `-`). There is **no numeric prefix**:
because the directories are git-ignored, shell-`rm`'d at cycle close,
and cited by nothing (see § Lifecycle), they hold at most the active
cycle's handful of files, so a slug is already unique enough — the old
scan-for-the-next-free-number ritual bought nothing and is gone. A
`fieldtest`-produced spec takes a `fieldtest-` prefix
(`fieldtest-slug.md`) so it is recognisable in the working tree as the
next cycle's planning input rather than this cycle's own spec — it is
written after audit's cycle-close sweep and discarded by the next
cycle's sweep (see `../fieldtest/SKILL.md`).
**The file name is not the cycle number.** The **cycle number** that
appears in commit subjects (`feat(NNNN)`, `audit(NNNN)`) and in `git
log` cycle ranges is a distinct, monotonic identifier that must survive
spec/plan discard. Source it from the highest `NNNN` in the
`feat(NNNN)` / `audit(NNNN)` git-log subjects, plus one — never from a
file name (there is none) and never from a per-directory scan. Cycle
commits therefore MUST carry the zero-padded `(NNNN)` cycle number in
their subject.
## Vocabulary
The pipeline's nouns are fixed:
| Term | Meaning |
|------|---------|
| **cycle** | One round in the pipeline graph (`brainstorm → specify → planner → implement → audit → [fieldtest]`). NOT the top-level container. |
| **iteration** | A sub-unit of a cycle. |
| **milestone** | Tracker container spanning many cycles; closes only when complete AND functional (see `pipeline.md` § Milestone-close gate). |
| **harvest sweep** | Batch shape draining accumulated settled single-issue backlog in one pass (see `pipeline.md` § Harvest sweep). |
| **contract** | A single design-ledger entry. |
When a project declares a glossary (in its CLAUDE.md project facts),
that glossary is the source of truth for domain nomenclature and
overrides these names where they collide (see `glossary-convention.md`).
## Standing reading
Every agent reads, at the start of every dispatch:
- the project's `CLAUDE.md`
- `git log -10 --format=full`
A project may add more — globally or per role — in its CLAUDE.md
project facts (`standing reading`). If the project declares a
glossary, that file is implicitly standing reading for every role
too.
**Schema-bound workflow stages are tiered down** (issue #31). The
anonymous script stages in `implement/workflows/*.js` have no agent
file and author no code, and a freshly-primed context costs ~40k+ real
tokens regardless of task size — so they do not take the full read:
verify-type stages (mini-verify, the standard end-verify, the
compiler-driven build/suite verify) read only the project-facts
build/test commands; tree/extraction stages (preflight, plan-index,
plan-extract[-all], snapshot) read nothing beyond their own
instructions. This extends the narrowed per-role standing reading the
named agents already practise (`tester` reads `git log -3`,
`plan-recon`/`grounding-check` `-5`, `fieldtester` `-8`, `docwriter`
`--oneline`); judgment roles keep the full convention.
## Git discipline
- **Only the orchestrator commits.** No skill agent runs `git commit`.
Agents write into the working tree as unstaged changes; the
orchestrator inspects, decides commit shape, and commits.
- **main HEAD is sacrosanct — below the session anchor.** Nobody runs
`git reset` / `git revert` on user-ratified main history (or any
protected branch). main moves forward only via orchestrator commits; a
wrong agent diff is discarded — by the orchestrator, between
iterations — with `git checkout -- <paths>` / `git stash`, never by
rewinding ratified history. That discard idiom is the orchestrator's
alone: an agent running INSIDE a multi-task `implement` iteration
never checkouts/restores a file — nothing commits between tasks, so
it would silently destroy sibling tasks' uncommitted work; in-loop
recovery is forward-editing or `BLOCKED` (`implement/SKILL.md` Iron
Law, issue #23).
**One narrow `/boss` exception — the rollback sandbox:** inside an
autonomous run the orchestrator anchors on the HEAD it started
from (the last user-ratified state; under the worktree bullet below
that is the run branch's base, so a reset moves only the branch and
can never reach `main`) and MAY `git reset --hard` its
*own* commits *above* that anchor — autonomous, **unpushed** work only
— back toward (never below) the anchor when it has run into a dead end.
A pushed commit has left the sandbox and is forward-only (`git revert`).
See `../boss/SKILL.md` § Direction freedom.
- **Substantive work runs in a git worktree — merge is human-in-the-loop.**
Any substantive work stream targeting `main` — a feature, a bug fix, a
refactor; anything beyond a trivial mechanical edit — is isolated into
a git worktree on its own branch before work starts, in every mode:
autonomous (`/boss`, see `../boss/SKILL.md` § The run worktree) and
interactive alike. (Background sessions are worktree-isolated by the
harness already; this convention extends the same isolation to the
sessions the harness does not guard.) The worktree branches from the
current **local** HEAD — pin `worktree.baseRef: "head"` in settings;
the harness default `fresh` bases the branch on origin's tip, which
is routinely stale under this convention's no-autonomous-push
discipline. Name the branch after the
tracker issue where one exists (`issue-N-<slug>`); a live worktree
branch is then also the visible being-worked-on signal in
`git worktree list`. The branch reaches `main` only through a
**user-ratified merge**: the user reviews the result, then the
orchestrator merges — fast-forward preferred (rebase the branch onto
`main` first if `main` has moved) — pushes (the ratification covers
the push), and removes the worktree and
deletes the branch. No pull request is opened, and the merge is never
autonomous. `closes #N` markers consequently fire at merge-and-push
time, when the commits reach the default branch.
**One exception:** the skills meta-repo itself forbids worktree
development outright (its `CLAUDE.md`: the install symlinks resolve to
the primary clone, so worktree edits are silently ineffective) and
stays direct-on-`main`.
These also appear in the universal-discipline fragment
(`../templates/CLAUDE.md.fragment`) that each project's CLAUDE.md
imports.
## Issue tracker
The tracker is **Gitea** and the commit close-marker is `closes #N`
(`refs #N` for non-closing work). Work-start is signalled by
**assignee, not label**: the actor that begins work on an issue
assigns it to the agent tracker user (`claude`) — one write, no
removal; the assignee survives the close as provenance. There is no
`in-progress` label. The live worktree branch (`issue-N-<slug>`,
§ Git discipline) is the git-side counterpart. The per-project repo slug and the
list/show commands live in the project's CLAUDE.md project facts
(see below) — the `boss` skill reads the forward queue from there and
records its fork decisions on the run's reference issue there, and an
ad-hoc `spec-skeptic` `scope-fork` dispatch reads single issues with
their comment threads from there.
Separately, `/boss` files **skill-system deficiencies** — faults in the
plugin itself, hit mid-run — against the **plugin's own** tracker (slug
derived from the plugin checkout's git remote), provenance carried by a
body block and never a new label. The skills meta-repo is not driven
through its own pipeline, so this tracker and a project's tracker never
coincide. See `../boss/SKILL.md` § Skill-system feedback.
## Pipeline
The phase set, the gates, and the conditional dispatch are **fixed**
and documented once in `pipeline.md`. There is no per-project pipeline
configuration. In particular: `brainstorm`, `specify`, `planner`,
`implement`, `audit`, `debug`, `tdd`, `fieldtest`, and `docwriter` are
always available. `tdd` is a standard entry path for test-specifiable
work — not an opt-in. `audit` is mandatory at cycle close.
`fieldtest` / `docwriter` are orchestrator-dispatched. There is no
per-project behavioural toggle: spec auto-sign is always on under
`/boss`.
## Per-project facts (in each project's CLAUDE.md)
Under `## Skills plugin: project facts`, where applicable:
| Fact | Used by | Notes |
|------|---------|-------|
| **code roots** | architect, quality-reviewer, fieldtester | Directories reviewers walk. Required. |
| **build / test command** | implement, audit | Required. Exit 0 = success. |
| **lint command** | (quality) | Optional. |
| **doc-build command** | docwriter | Optional; prints warnings on stderr. |
| **regression scripts** | audit (bencher) | Optional list; non-zero exit = regress. |
| **architect sweeps** | audit (architect) | Optional list; non-zero exit = drift suspicion. |
| **design ledger** | architect, most agents | Optional path (e.g. `design/INDEX.md`). |
| **glossary** | every role | Optional path; implicitly standing reading. |
| **design contracts / models** | docwriter, specify | Optional dirs. Aspirational-source frontmatter marker recommended (see below). |
| **bench dir** | fieldtest, bencher | Optional path. |
| **public interface** | fieldtester | Optional list — the only surface the fieldtester may read; everything else (code roots, bench) is forbidden to it. |
| **fieldtest examples** | fieldtester | Optional path where fixtures are written. |
| **by-role standing reading** | named agent | Optional; extra files/commands a specific role reads. |
| **issue tracker** | boss, spec-skeptic | Repo slug + list command + show command (the latter MUST render an issue WITH its comments). |
### Aspirational-source marker (recommendation)
Files under a project's design-models / RFCs / proposals directory
commonly carry aspirational code — constructs written before the
surface that parses them exists. To let a later `specify` tell
aspirational content from validated contract, give each such file a
frontmatter marker:
```yaml
---
status: aspirational
validated-against: <commit-sha | "no validation">
---
```
When the marker is absent the signal is simply absent — `specify`
degrades to treating the content as unmarked, never hard-failing.
Content lifted from an aspirational source is flagged as a target,
not verified fact, before it ships in a spec.