diff --git a/llms.txt b/llms.txt index c8ac052..71fe7a8 100644 --- a/llms.txt +++ b/llms.txt @@ -9,7 +9,7 @@ URLs below point at the `main` branch and resolve to raw markdown. ## Operating model - [Collaborator Statement](https://raw.githubusercontent.com/ChainSafe/engineering-handbook/main/operating-model/collaborator-statement.md): The operator/agent contract. Load before any non-trivial task. Operator responsibilities, agent responsibilities, refusal cases, escalation cases, failure modes. -- [Gates and Escalation](https://raw.githubusercontent.com/ChainSafe/engineering-handbook/main/operating-model/gates-and-escalation.md): Eight gate categories (production, secrets, irreversible writes, external comms, version control, repo boundaries, cost, HARD FAIL). Stop pattern, escalation matrix. +- [Gates and Escalation](https://raw.githubusercontent.com/ChainSafe/engineering-handbook/main/operating-model/gates-and-escalation.md): Ten gate categories (production, secrets, irreversible writes, external comms, version control, repo boundaries, cost, HARD FAIL, operational contract, oversized/multi-concern changes). Stop pattern, escalation matrix. - [Model and Tool Selection](https://raw.githubusercontent.com/ChainSafe/engineering-handbook/main/operating-model/model-and-tool-selection.md): Model tier mapping (Haiku / Sonnet / Opus), MCP loadout, skill selection, anti-patterns. - [MCP and llms.txt](https://raw.githubusercontent.com/ChainSafe/engineering-handbook/main/operating-model/mcp-and-llm-txt.md): How agents discover this handbook. Two channels (static deep links + GitHub MCP). Index-sync policy. - [Memory Conventions](https://raw.githubusercontent.com/ChainSafe/engineering-handbook/main/operating-model/memory-conventions.md): What to persist across sessions, what not to, sensitive-data rules including confidential-commercial-information handling. @@ -23,6 +23,7 @@ URLs below point at the `main` branch and resolve to raw markdown. ## Workflows - [OneFlow](https://raw.githubusercontent.com/ChainSafe/engineering-handbook/main/workflows/oneflow.md): ChainSafe's branching and release model. Trunk-based, single `main`, tag-driven deployments (`v.*.*` for production, `stage-*` for staging). Hot fixes via `hotfix/*` branches off broken production tags. Adapted from Adam Ruka's OneFlow concept. +- [Work Decomposition](https://raw.githubusercontent.com/ChainSafe/engineering-handbook/main/workflows/work-decomposition.md): Epic → milestones → bite-sized issues. What makes an issue bite-sized (one PR, one reviewable pass, written acceptance criteria, no open design decision — no line-count threshold). How `plan.md` phases and tasks map onto the tracker. Tracker-agnostic; GitHub conventions given. - [PR Authoring](https://raw.githubusercontent.com/ChainSafe/engineering-handbook/main/workflows/pr-authoring.md): Agent-era author guide. Small/focused/self-contained PRs, AI-generated declaration, scope-drift flags. Delegates non-trivial workflow to the `chainsafe-research-plan-implement` skill. - [Code Review](https://raw.githubusercontent.com/ChainSafe/engineering-handbook/main/workflows/code-review.md): Two modes (operator-reviewing-agent, agent-reviewing-PR). Universal principles, what-to-look-for, speed of review, when-reviews-go-wrong. - [Repo and CI Setup](https://raw.githubusercontent.com/ChainSafe/engineering-handbook/main/workflows/repo-and-ci-setup.md): Ten-step per-repo hygiene checklist as an agent-runnable runbook. Branch protection, CODEOWNERS, CI baseline, security baseline. @@ -90,7 +91,7 @@ Packaged Anthropic Skills authored via `skill-creator`. Each skill is self-conta ### Workflow -- [chainsafe-research-plan-implement](https://raw.githubusercontent.com/ChainSafe/engineering-handbook/main/skills/chainsafe-research-plan-implement/SKILL.md): Research-first coding workflow. Gates ALL non-trivial code changes behind a human-approved plan. Trigger: "implement", "refactor", "fix this bug", "add a feature", "migrate from X to Y", "rewrite", "wire up", "extend". Adapted from Boris Tane's "How I Use Claude Code." +- [chainsafe-research-plan-implement](https://raw.githubusercontent.com/ChainSafe/engineering-handbook/main/skills/chainsafe-research-plan-implement/SKILL.md): Research-first coding workflow: research → plan → annotate → decompose → implement. Gates ALL non-trivial code changes behind a human-approved plan, and decomposes that plan into epic / milestones / bite-sized issues before any code is written. Trigger: "implement", "refactor", "fix this bug", "add a feature", "migrate from X to Y", "rewrite", "wire up", "extend", "break this into issues". Adapted from Boris Tane's "How I Use Claude Code." - [chainsafe-pr-author](https://raw.githubusercontent.com/ChainSafe/engineering-handbook/main/skills/chainsafe-pr-author/SKILL.md): Language-agnostic PR authoring. Trigger: "open a pull request", "draft a PR description", "split this PR", "respond to reviewer", "AI declaration", "scope drift". - [chainsafe-code-review](https://raw.githubusercontent.com/ChainSafe/engineering-handbook/main/skills/chainsafe-code-review/SKILL.md): Universal code-review framework. Trigger: "review this PR", "check this diff", "what should I look for", "review disagreement". Pairs with the language-specific reviewer skills. diff --git a/operating-model/gates-and-escalation.md b/operating-model/gates-and-escalation.md index 8020fa4..7cd541d 100644 --- a/operating-model/gates-and-escalation.md +++ b/operating-model/gates-and-escalation.md @@ -106,6 +106,19 @@ Before such a change ships, its operational contract is declared in the PR's **O *Why:* an undeclared contract change is the failure that lands on Infra days later — reverse-engineering which env vars are needed, why old clients broke, what a migration assumed. Declaring it up front is a checklist item; not declaring it is a manual, cross-team firefight. +### 10. Oversized or multi-concern changes + +- **Opening a PR that cannot be reviewed in a single focused pass** — the ~10-minute standard in [`../workflows/pr-authoring.md`](../workflows/pr-authoring.md#small-focused-self-contained). If the agent would have to tell the reviewer to "set aside some time," the gate is tripped. +- **A change that closes more than one issue**, or spans more than one milestone, in a single PR. +- **A change that bundles concerns** — refactor plus feature, migration plus behavior change, formatting sweep plus fix. +- **A diff that has outgrown the slice the operator approved in `plan.md`**, even when every file in it is in scope. + +Not gated by size alone, but still declared and isolated in their own PRs: renames, deletions, generated code, lockfile and dependency bumps, and vendored updates. These trade scope-width for shallow review depth — a large rename is fine; a rename *plus* a logic change is not. + +To clear the gate, the agent stops before opening the PR and either (a) proposes a split into issue-sized PRs, or (b) states why the change cannot be split. The operator's approval is explicit and recorded in the PR description — a line like `Oversized PR approved by @operator: generated protobuf bindings, mechanical, reviewed by regenerating locally.` A silent large PR is the failure mode; an approved and justified one is not. + +*Why:* an unreviewable diff produces one of two outcomes, both bad — the reviewer approves blindly, or blocks on everything out of self-defense. Size is the one property of a change that determines whether review happens at all, and an agent can generate an unreviewable diff faster than any human can. Making the operator say "yes, this one is big, and here is why" converts a silent erosion of the review gate into a logged decision. The precondition for staying under this gate is decomposition ([`../workflows/work-decomposition.md`](../workflows/work-decomposition.md)); by the time the diff exists, it is usually too late. + ## How the agent stops at a gate The stop is structured. Do not stop silently. Do not stop with "I cannot do this." Stop with the information the operator needs to decide. @@ -137,6 +150,7 @@ The agent escalates — stops and asks beyond the operator — when one of the f | Situation | Escalate to | |---|---| | Ambiguous intent, choices have different consequences | Operator | +| Change has outgrown the approved plan slice or its issue | Operator — re-cut the decomposition, or approve the oversized PR explicitly | | Operator asks for something on the refusal list | Section CODEOWNER, then curator | | Conflict between two CODEOWNERs / two authorities | Both, surface the conflict, wait | | Operator unavailable mid-task, gate ahead | No one — leave artifacts, exit, wait | @@ -156,3 +170,5 @@ The gate list is a floor, not a ceiling. Agents are expected to apply judgment a - [`collaborator-statement.md`](./collaborator-statement.md) — the principle these gates implement, and the refusal list (which sits *above* gates: gates can be approved, refusals cannot). - [`../invariants/agent-era-invariants.md`](../invariants/agent-era-invariants.md) — the refusal cases restated as invariants. - [`../languages/`](../languages/) — language-specific reviewer pages name which HARD FAIL checks apply per language. +- [`../workflows/work-decomposition.md`](../workflows/work-decomposition.md) — epic / milestone / issue breakdown; the planning discipline that keeps changes under §10. +- [`../workflows/pr-authoring.md`](../workflows/pr-authoring.md) — where the §10 size standard and the oversized-PR declaration live in practice. diff --git a/skills/chainsafe-research-plan-implement/SKILL.md b/skills/chainsafe-research-plan-implement/SKILL.md index da85c88..766ac87 100644 --- a/skills/chainsafe-research-plan-implement/SKILL.md +++ b/skills/chainsafe-research-plan-implement/SKILL.md @@ -1,6 +1,6 @@ --- name: chainsafe-research-plan-implement -description: Research-first coding workflow that gates ALL non-trivial code changes behind a human-approved written plan. Use this skill whenever the user asks for any substantive code change — implementing a feature, refactoring, fixing a multi-file bug, adding an endpoint, integrating a service, migrating between systems, wiring something up, or changing how an existing system works — EVEN IF they do not explicitly ask for "research" or a "plan." The skill enforces three artifacts (research.md, plan.md, annotated plan) that survive context compaction and gate implementation behind explicit operator approval. Triggers on phrases like "implement", "refactor", "fix this bug", "add a feature", "change how X works", "integrate", "migrate from X to Y", "rewrite", "restructure", "wire up", "build out", "extend", "modify the X system", "I want to add", "let's change". Do NOT use for one-line typo fixes, pure formatting, lint-only changes, or other trivial single-file edits where research would be overkill. +description: Research-first coding workflow that gates ALL non-trivial code changes behind a human-approved written plan. Use this skill whenever the user asks for any substantive code change — implementing a feature, refactoring, fixing a multi-file bug, adding an endpoint, integrating a service, migrating between systems, wiring something up, or changing how an existing system works — EVEN IF they do not explicitly ask for "research" or a "plan." The skill enforces three artifacts (research.md, plan.md, annotated plan) that survive context compaction and gate implementation behind explicit operator approval. Triggers on phrases like "implement", "refactor", "fix this bug", "add a feature", "change how X works", "integrate", "migrate from X to Y", "rewrite", "restructure", "wire up", "build out", "extend", "modify the X system", "I want to add", "let's change". Also covers breaking planned work down into tracked units — "break this into issues", "decompose this", "create the epic", "what milestones do we need", "how should we split this up", "is this issue too big". Do NOT use for one-line typo fixes, pure formatting, lint-only changes, or other trivial single-file edits where research would be overkill. metadata: type: workflow origin: ~/.config/agents/research-plan-implement.md @@ -20,7 +20,7 @@ Separate thinking from typing. Research prevents ignorant changes. The plan prev ## Workflow pipeline ``` -Research → Plan → Annotate (repeat 1–6×) → Implement +Research → Plan → Annotate (repeat 1–6×) → Decompose → Implement ``` All phases run in a **single long session**. Do not split across separate sessions. Context built during research and planning carries through to implementation. The plan document survives compaction and serves as the persistent source of truth. @@ -92,6 +92,37 @@ This is where the human adds the most value. The human opens `plan.md` in their --- +## Phase 3b — Decomposition + +The phased todo list is not just a progress tracker — it is the work breakdown. Before implementation begins, map it onto three levels and get the mapping approved along with the rest of the plan. + +| In `plan.md` | Tracked as | +|---|---| +| The plan as a whole | One **epic** — the outcome, linking `research.md` and `plan.md` | +| Each phase | A **milestone** — a demonstrable slice; `main` is releasable at its end | +| Each individual task | An **issue** — bite-sized, closed by exactly one PR | + +**An issue is bite-sized when all of these hold:** + +- One PR closes it. +- One reviewer can review that PR in a single focused pass. There is no line-count threshold — the bar is reviewability. +- It has written acceptance criteria, decided now, not at review time. +- It carries no open design decision. Implementation of a bite-sized issue is mechanical. +- It names the files or areas it expects to touch. +- Someone can pick it up without reading the whole epic. + +If a task fails any of these, split it in the plan before implementing. Usual seams: interface before implementation, migration before cutover, one call site per issue, tests-for-existing-behavior before the behavior change. + +**Propose, do not create.** Write the breakdown into `plan.md`. Do not create epics, milestones, or issues in GitHub / Linear / Jira until the operator approves — creating tracker items on a human's behalf is a gate, not a default. + +> "Map the todo list onto an epic, milestones, and bite-sized issues. Each issue must be closeable by one reviewable PR and carry acceptance criteria. Write it into the plan — don't create anything in GitHub yet." + +Small work does not need all three levels. A single-issue fix is a single issue. The rule is that work large enough to need a plan is large enough to need decomposition. + +Full reference: [`workflows/work-decomposition.md`](../../workflows/work-decomposition.md). + +--- + ## Phase 4 — Implementation When the human approves the plan, execute everything in one continuous run: @@ -111,6 +142,10 @@ When the human approves the plan, execute everything in one continuous run: Implementation should be **boring**. All creative decisions were made in the annotation cycles. +**Ship it issue by issue.** One issue, one PR, each PR linking its issue. Running implementation continuously does not mean accumulating everything into one branch — the decomposition from Phase 3b is what makes the output reviewable, and collapsing it at the last moment throws that away. + +If a diff outgrows the issue it belongs to — even when every file touched is in scope — **stop**. Do not open an oversized PR and apologize in the description. Surface it, propose a split, and let the operator decide. An oversized PR ships only with an explicit approval recorded in its description (`Oversized PR approved by @operator: `). This is [gate §10](../../operating-model/gates-and-escalation.md#10-oversized-or-multi-concern-changes); the agent never self-approves it. + --- ## Feedback during implementation @@ -141,6 +176,8 @@ Once implementation is running, the human's role shifts from architect to superv 6. **The plan is the source of truth.** Mark tasks complete in the plan. Point back to it when context is needed. 7. **Keep implementation mechanical.** All decisions are pre-made. Execute the plan faithfully. 8. **Run typechecks continuously.** Don't accumulate errors — catch them as they happen. +9. **Decompose before implementing.** Epic → milestones → bite-sized issues, approved as part of the plan. A PR can only be as small as the issue behind it. +10. **Never self-approve an oversized PR.** If the change won't fit one reviewable PR, stop and ask. The operator decides; the approval is recorded in the PR description. --- diff --git a/workflows/work-decomposition.md b/workflows/work-decomposition.md new file mode 100644 index 0000000..4e3a384 --- /dev/null +++ b/workflows/work-decomposition.md @@ -0,0 +1,82 @@ +# Work Decomposition + +How a piece of planned work becomes tracked work — epic, milestones, issues — before any code is written. This is the bridge between the approved `plan.md` produced by [`chainsafe-research-plan-implement`](../skills/chainsafe-research-plan-implement/SKILL.md) and the stream of small PRs described in [`pr-authoring.md`](./pr-authoring.md). + +> **In one line:** Every non-trivial effort decomposes into one epic, a set of milestones, and bite-sized issues. An issue is bite-sized when one PR closes it and one reviewer can hold that PR in their head. + +## Why this page exists + +The handbook already says PRs must be small ([`pr-authoring.md`](./pr-authoring.md)) and that agents stay inside operator-named scope ([agent-era invariant 1](../invariants/agent-era-invariants.md#1-no-silent-edits-outside-the-operator-named-scope)). Neither holds unless the work was *decomposed that way in the first place*. A PR is only as small as the issue behind it; an issue is only as small as the milestone that spawned it. + +In the agent era this matters more, not less. An agent can produce a 3,000-line diff in one pass. The constraint that keeps that diff reviewable is not the agent's restraint — it is the decomposition the operator approved before implementation started. + +## The three levels + +| Level | What it is | Done when | Typical span | +|---|---|---|---| +| **Epic** | One coherent outcome a stakeholder would recognize by name ("Sprinter supports Base", "migrate Forest to the new state store"). Carries the `research.md`, `plan.md`, and any ADR. | Every milestone under it is closed and the outcome is demonstrable. | Weeks | +| **Milestone** | A demonstrable, independently-shippable slice of the epic. `main` is releasable at the end of each one. | Its issues are closed and the slice can be shown working. | Days to a week | +| **Issue** | One bite-sized unit of work. One PR closes it. | Its PR is merged and its acceptance criteria are met. | Hours to ~a day | + +Every level links up and down: an issue names its milestone, a milestone names its epic, the epic links the plan artifact. That chain is what makes the [audit trail](../invariants/agent-era-invariants.md#8-every-agent-generated-change-carries-an-audit-trail) reconstructible six months later. + +**Small work does not need all three levels.** A single-issue bug fix is a single issue. The rule is that work *large enough to need a plan* is large enough to need decomposition — do not invent an epic to hold one issue, and do not hide fifteen issues' worth of work inside one. + +## What makes an issue bite-sized + +An issue is bite-sized when **all** of these hold. There is deliberately no line-count threshold — the bar is reviewability, not arithmetic. + +- **One PR closes it.** If closing it needs two PRs, it is two issues. +- **One reviewer can review that PR in a single focused pass** — the ~10-minute standard from [`pr-authoring.md`](./pr-authoring.md#small-focused-self-contained). If a reviewer would have to schedule time for it, it is too big. +- **It has written acceptance criteria** before work starts. "Done" is testable, not a matter of opinion. +- **It carries no open design decision.** All the creative choices were made in the plan and annotation cycles. Implementation of a bite-sized issue is mechanical. +- **It names the files or areas it expects to touch.** This is the scope statement the reviewer checks the diff against. +- **It is independently understandable.** Someone can pick it up without reading the entire epic. + +If an issue fails any of these, split it. The usual split lines: interface before implementation; migration before cutover; one call site per issue; tests-for-existing-behavior before the behavior change. + +## From `plan.md` to the tracker + +The [`chainsafe-research-plan-implement`](../skills/chainsafe-research-plan-implement/SKILL.md) workflow already ends its annotation cycle by producing a phased todo list. That artifact *is* the decomposition — it just needs to be mapped: + +| In `plan.md` | In the tracker | +|---|---| +| The plan as a whole | The epic | +| Each phase | A milestone | +| Each individual task | An issue | + +Sequence: + +1. **Agent proposes the breakdown inside `plan.md`** — epic statement, milestones, and the issue list with acceptance criteria and expected file scope per issue. It does not create anything in the tracker yet. +2. **Operator annotates and approves it** as part of the normal annotation cycle. Re-cutting the decomposition is cheap here and expensive later. +3. **Issues get created only after approval.** Creating issues on a human's behalf trips the [external-communication gate](../operating-model/gates-and-escalation.md#4-external-communication) — the agent proposes, the operator authorizes. +4. **Implementation runs issue by issue**, one PR per issue, each PR linking its issue. +5. **The plan stays the source of truth.** If implementation reveals the decomposition was wrong, update `plan.md` and re-cut the issues; do not silently merge two issues into one PR. + +## Tracker conventions + +Tracker-agnostic by design — GitHub is the ChainSafe default, and projects on Linear or Jira map the same three levels onto their own primitives ([`repo-and-ci-setup.md`](./repo-and-ci-setup.md#10-notifications-and-integrations)). + +On GitHub: + +- **Epic** — an issue labelled `epic`, listing its milestones as a task list, linking `plan.md`. +- **Milestone** — a native GitHub milestone. Issues are assigned to it; the milestone burns down to zero. +- **Issue** — a normal issue with the standard label taxonomy from [`repo-and-ci-setup.md`](./repo-and-ci-setup.md#8-labels), assigned to a milestone, referenced by its PR with `Closes: #N`. + +## Anti-patterns + +- **The ticket that is really an epic.** "Add multi-chain support" as a single issue. Nobody can review the PR that closes it. +- **Decomposition after the fact.** Writing the issues once the branch is already 2,000 lines deep. The breakdown is a planning artifact, not a filing exercise. +- **Milestones that cannot ship.** A "milestone" that leaves `main` broken until the next one lands is not a milestone; it is a checkpoint. Re-cut it. +- **Issues without acceptance criteria.** These become "done when the author says so," which is exactly what the reviewer cannot verify. +- **The agent that creates the tracker structure unasked.** Proposing the breakdown is the job; creating issues without approval is a gate violation. +- **Silent re-merging.** Closing three issues with one PR because "they were all related." If they really were, the decomposition should have been fixed in `plan.md` first. + +## Related + +- [`../skills/chainsafe-research-plan-implement/SKILL.md`](../skills/chainsafe-research-plan-implement/SKILL.md) — the workflow that produces the plan this page decomposes. +- [`pr-authoring.md`](./pr-authoring.md) — the PR half of the same discipline: one issue, one PR, small enough to review. +- [`code-review.md`](./code-review.md) — where an under-decomposed change gets caught. +- [`repo-and-ci-setup.md`](./repo-and-ci-setup.md) — label taxonomy and tracker integration this page assumes. +- [`../operating-model/gates-and-escalation.md`](../operating-model/gates-and-escalation.md#10-oversized-or-multi-concern-changes) — the gate for changes that outgrow their issue. +- [`../invariants/agent-era-invariants.md`](../invariants/agent-era-invariants.md#1-no-silent-edits-outside-the-operator-named-scope) — scope discipline, which decomposition is the precondition for.