@reforma/agentflow 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Reforma contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,252 @@
1
+ # AgentFlow
2
+
3
+ An opinionated workflow for producing high-quality, production-ready changes
4
+ with coding agents.
5
+
6
+ Medium and large tasks are difficult to carry through in a single chat without
7
+ losing quality. The scope grows during implementation, earlier requirements
8
+ disappear into the history, and unanswered questions are left for the agent to
9
+ decide. Those decisions are often wrong. The longer the chat runs, the less
10
+ reliable its context becomes, while compacting it or moving the work to a new
11
+ chat loses the reasoning behind earlier decisions.
12
+
13
+ For anything bigger than a quick fix, we usually start by exploring the existing
14
+ feature, code, or technology. Once the agent knows the ground, we grill the idea
15
+ and talk through the tricky parts before touching code. Then we usually write a
16
+ plan and split the work into PR-sized chunks. Small tasks can skip the plan and
17
+ go straight to implementation.
18
+
19
+ Work through the chunks in order, however you prefer: stay in the same chat,
20
+ summarize it between PRs, start a fresh chat, or write the code yourself. Review
21
+ and commit each PR before moving to the next one, while the diff is still small
22
+ enough to clean up properly. When the context gets noisy, summarize it or write
23
+ a handoff to disk before continuing.
24
+
25
+ The core loop is:
26
+
27
+ ```text
28
+ Research → Grill → Plan → PR → Review → Commit
29
+ ↑ │
30
+ └──── Next PR ─────┘
31
+ ```
32
+
33
+ Research is optional, small tasks can skip the plan, and a handoff is only
34
+ needed when you move the work to a fresh context.
35
+
36
+ Reusable agent skills are published from `skills/`:
37
+
38
+ - `/research` — save technology or landscape research for later chats
39
+ - `/grill` — question an idea until the important decisions are clear
40
+ - `/plan` — split the work into PR-sized slices
41
+ - `/code-review` — review each slice for reuse and unnecessary complexity
42
+ - `/handoff` — save context before moving to a fresh chat
43
+ - `/tdd` — work through one red-green slice at a time
44
+
45
+ ---
46
+
47
+ ## The Loop
48
+
49
+ Research helps when the agent does not know the area, and most larger tasks also need a plan. But almost every task goes through Grill. Small, obvious changes can go straight from Grill to implementation.
50
+
51
+ 1. Learn how it works (optional)
52
+ 2. Sharpen the idea with Grill
53
+ 3. Slice the work into PRs
54
+ 4. Implement one PR
55
+ 5. Agent review
56
+ 6. Your review, then commit
57
+ 7. Refresh the context when needed or start the new chat
58
+
59
+ Repeat steps 4–7 until every PR in the plan is complete.
60
+
61
+ ### Step 1. Learn how it works (optional)
62
+
63
+ Research does not require a skill. Even a simple prompt like "Find out how
64
+ authentication works in this project" can make the implementation much easier.
65
+
66
+ This step is optional, but highly recommended when the agent has not worked in
67
+ the area before. It lets the agent understand what it will be changing before
68
+ the implementation starts.
69
+
70
+ Use `/research` when the findings should survive the current chat. It saves the
71
+ research artifacts under `.agentflow/<feature>/research/`, ready to attach after
72
+ context compaction or in a new chat.
73
+
74
+ Even if you skip this step, Grill will fill in any gaps.
75
+
76
+ ### Step 2. Sharpen the idea with Grill
77
+
78
+ Grill is the core of AgentFlow and the one step used for the most tasks.
79
+
80
+ Run `/grill` after research, or start with it when research is not needed.
81
+
82
+ The agent explains how it understands the task, surfaces assumptions and
83
+ decision points, and recommends an answer for each one. You confirm or correct
84
+ that understanding. If your answers open new questions, the agent continues
85
+ until nothing important is left unclear.
86
+
87
+ This gives the implementation an agreed starting point instead of leaving the
88
+ agent to fill in gaps on its own.
89
+
90
+ ### Step 3. Plan and slice the work into PRs
91
+
92
+ For a larger task, planning usually follows Grill in the same chat. If you asked
93
+ Grill to plan next, it loads `/plan` automatically. You can also run `/plan`
94
+ yourself.
95
+
96
+ In a normal chat, `/plan` saves the result to `.agentflow/<slug>/plan.md`. In
97
+ Native Plan mode, the agent uses its built-in planning flow and native plan
98
+ artifact instead.
99
+
100
+ The plan breaks the feature into PRs that can be shipped one by one. A PR is the
101
+ smallest complete change with one coherent outcome and a clear way to verify it,
102
+ not a fixed number of files or lines.
103
+
104
+ Larger work is usually sliced from foundations to consumers. A full-stack
105
+ feature might start with behavior-preserving refactoring, continue with shared
106
+ types and backend work, and finish with the frontend. A frontend feature might
107
+ move from reusable components, through shared runtime or wiring, to the
108
+ user-facing interface.
109
+
110
+ These are examples, not a required template. A small feature stays as one
111
+ vertical slice. Feature-local components and wiring stay with the interface
112
+ that first uses them instead of becoming placeholder PRs.
113
+
114
+ Keep the plan updated as the work changes.
115
+
116
+ **Done when:** someone can open the plan, find the next PR, and see how to
117
+ verify it.
118
+
119
+ ### Step 4. Implement one PR
120
+
121
+ Implement the first unchecked PR and nothing beyond it. Without a plan, keep the
122
+ change small enough to review. You can stay in the current chat, start a fresh
123
+ one, or write the code yourself. Bring the plan and latest handoff when moving
124
+ to another chat. Use `/tdd` for test-first work.
125
+
126
+ Load any relevant skills named in `AGENTS.md`. If work spills into a later PR,
127
+ update the plan instead of silently expanding the current one.
128
+
129
+ **Done when:** the PR does what the plan promised, and the plan matches the work.
130
+
131
+ ### Step 5. Agent review
132
+
133
+ Run `/code-review` on the completed PR. It looks for code to reuse, unnecessary
134
+ wrappers, and avoidable complexity. It fixes small local problems and returns a
135
+ verdict: keep, shrink, or burn.
136
+
137
+ **Done when:** the review has run and its local fixes are applied.
138
+
139
+ ### Step 6. Your review, then commit
140
+
141
+ Read the diff yourself, then commit it using the project's normal workflow.
142
+
143
+ **Done when:** the PR is reviewed and committed.
144
+
145
+ ### Step 7. Refresh the context when needed
146
+
147
+ Keep the current chat if it still has useful context. When it gets noisy,
148
+ summarize it or start a fresh one. Run `/handoff` to save what shipped, what
149
+ changed, and which PR comes next.
150
+
151
+ Attach the plan and handoff to a fresh chat when they exist.
152
+
153
+ **Done when:** you can start the next PR without reconstructing the previous
154
+ work. Return to step 4.
155
+
156
+ ## Install
157
+
158
+ Install the complete workflow in a project:
159
+
160
+ ```bash
161
+ npx @reforma/agentflow init
162
+ ```
163
+
164
+ The command installs every AgentFlow skill, writes `AGENTFLOW.md`, and ignores
165
+ the local `.agentflow/` artifacts. It prints a short pointer you can add to
166
+ `AGENTS.md` when you want agents in that project to follow the full loop.
167
+
168
+ Install only one skill without the workflow:
169
+
170
+ ```bash
171
+ npx skills add reforma-dev/agentflow --skill plan
172
+ ```
173
+
174
+ Update the complete workflow:
175
+
176
+ ```bash
177
+ npx @reforma/agentflow@latest update
178
+ ```
179
+
180
+ Or update one independently installed skill:
181
+
182
+ ```bash
183
+ npx skills update plan
184
+ ```
185
+
186
+ ---
187
+
188
+ ## Why AgentFlow?
189
+
190
+ Coding agents can handle large changes, but chat history is a poor place to
191
+ keep requirements, scope, and decisions. AgentFlow moves that information into
192
+ small files and gives each PR a clear boundary.
193
+
194
+ - **Learn the ground before estimating the work** — the agent sees the existing
195
+ behavior and code before discussing a solution.
196
+ - **Resolve decisions before coding** — `/grill` exposes assumptions while they
197
+ are still cheap to change.
198
+ - **Keep changes reviewable** — the plan divides a large feature into PR-sized
199
+ slices that are implemented and reviewed in order.
200
+ - **Preserve context between sessions** — the review artifacts, plan and handoff tell the next agent what is true and what comes next.
201
+ - **Use an opinionated workflow without heavy artifacts** — the sequence is
202
+ fixed, while the documentation is limited to one plan and one current
203
+ handoff.
204
+
205
+ ### How we compare
206
+
207
+ OpenSpec and Spec Kit try to cover most of spec-driven development with their
208
+ own commands, templates, and artifacts. AgentFlow does not try to be an
209
+ all-in-one system. It adds a small, opinionated loop to the way you already
210
+ build software.
211
+
212
+ **vs. [OpenSpec](https://github.com/Fission-AI/OpenSpec)** — OpenSpec manages
213
+ proposals, requirements, designs, tasks, and completed changes. That works when
214
+ specs are the center of the process, but it also means adopting OpenSpec's
215
+ artifact system. AgentFlow does not require a spec tree. Most work needs one
216
+ plan and a handoff only when the context moves to a new chat.
217
+
218
+ **vs. [Spec Kit](https://github.com/github/spec-kit)** — Spec Kit provides a
219
+ thorough, phase-based process with a constitution, specifications, plans, and
220
+ task lists. AgentFlow gives you an order of work without asking you to move the
221
+ rest of your development process into the framework.
222
+
223
+ **vs. an unstructured chat** — Working directly in chat is enough for a small
224
+ fix. On larger changes, AgentFlow keeps decisions out of transient history,
225
+ limits scope to one reviewable slice, and gives the next session a reliable
226
+ starting point.
227
+
228
+ ## Releasing
229
+
230
+ Add a changeset with every publishable change:
231
+
232
+ ```bash
233
+ bun run changeset
234
+ ```
235
+
236
+ When that changeset reaches `main`, the publish workflow tests the package,
237
+ updates its version and changelog, publishes it to npm through trusted
238
+ publishing, and commits the release files back to `main`. A separate job then
239
+ publishes the matching versioned Agent Skills release on GitHub.
240
+
241
+ The npm trusted publisher for `@reforma/agentflow` must point at
242
+ `reforma-dev/agentflow/.github/workflows/publish_npm.yml`.
243
+
244
+ Validate a release locally without publishing:
245
+
246
+ ```bash
247
+ gh skill publish --dry-run
248
+ bun run test
249
+ bun publish --dry-run
250
+ ```
251
+
252
+ AgentFlow is available under the [MIT License](LICENSE).
@@ -0,0 +1,189 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
5
+ import { dirname as dirname2, resolve as resolve3 } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ // src/project.ts
9
+ import {
10
+ existsSync,
11
+ readFileSync,
12
+ renameSync,
13
+ writeFileSync
14
+ } from "node:fs";
15
+ import { resolve } from "node:path";
16
+ var GENERATED_MARKER = "<!-- Generated by AgentFlow CLI. Run `npx @reforma/agentflow@latest update` to refresh. -->";
17
+ var AGENTS_SNIPPET = "**Agent loop:** [AGENTFLOW.md](AGENTFLOW.md) — optional research → grill → usually `/plan` → sequential PRs (implement → `/code-review` → commit) → refresh context when needed.";
18
+ function generatedWorkflow(workflow) {
19
+ return `${GENERATED_MARKER}
20
+
21
+ ${workflow.trimEnd()}
22
+ `;
23
+ }
24
+ function isGeneratedWorkflow(path) {
25
+ return existsSync(path) && readFileSync(path, "utf8").startsWith(GENERATED_MARKER);
26
+ }
27
+ function writeAtomically(path, contents) {
28
+ const temporaryPath = `${path}.agentflow-tmp`;
29
+ writeFileSync(temporaryPath, contents);
30
+ renameSync(temporaryPath, path);
31
+ }
32
+ function ensureIgnored(cwd) {
33
+ const path = resolve(cwd, ".gitignore");
34
+ const current = existsSync(path) ? readFileSync(path, "utf8") : "";
35
+ const alreadyIgnored = current.split(/\r?\n/).some((line) => /^\/?\.agentflow\/?$/.test(line.trim()));
36
+ if (alreadyIgnored) {
37
+ return;
38
+ }
39
+ const separator = current.length === 0 || current.endsWith(`
40
+ `) ? "" : `
41
+ `;
42
+ writeAtomically(path, `${current}${separator}.agentflow/
43
+ `);
44
+ }
45
+
46
+ // src/skills.ts
47
+ import { spawnSync } from "node:child_process";
48
+ import { readFileSync as readFileSync2 } from "node:fs";
49
+ import { createRequire } from "node:module";
50
+ import { dirname, resolve as resolve2 } from "node:path";
51
+ var AGENTFLOW_SKILLS = [
52
+ "research",
53
+ "grill",
54
+ "plan",
55
+ "code-review",
56
+ "handoff",
57
+ "tdd"
58
+ ];
59
+ var SKILLS_SOURCE = "reforma-dev/agentflow";
60
+ var runSkills = (args) => {
61
+ const require2 = createRequire(import.meta.url);
62
+ const packagePath = require2.resolve("skills/package.json");
63
+ const packageJson = JSON.parse(readFileSync2(packagePath, "utf8"));
64
+ const relativeBin = typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin.skills;
65
+ if (!relativeBin) {
66
+ throw new Error("The installed skills package does not expose a skills bin.");
67
+ }
68
+ const result = spawnSync(process.execPath, [resolve2(dirname(packagePath), relativeBin), ...args], { stdio: "inherit" });
69
+ if (result.error) {
70
+ throw result.error;
71
+ }
72
+ return result.status ?? 1;
73
+ };
74
+
75
+ // src/cli.ts
76
+ var packageRoot = resolve3(dirname2(fileURLToPath(import.meta.url)), "..");
77
+ var packageJson = JSON.parse(readFileSync3(resolve3(packageRoot, "package.json"), "utf8"));
78
+ function parseOptions(args, command) {
79
+ const forwarded = [];
80
+ for (let index = 0;index < args.length; index += 1) {
81
+ const argument = args[index];
82
+ if (argument === "--agent" || argument === "-a") {
83
+ if (command !== "init") {
84
+ throw new Error(`${argument} is only supported by init.`);
85
+ }
86
+ const agent = args[index + 1];
87
+ if (!agent || agent.startsWith("-")) {
88
+ throw new Error(`${argument} requires an agent name.`);
89
+ }
90
+ forwarded.push(argument, agent);
91
+ index += 1;
92
+ continue;
93
+ }
94
+ if (argument === "--global" || argument === "-g") {
95
+ forwarded.push(argument);
96
+ continue;
97
+ }
98
+ if (argument === "--project" || argument === "-p") {
99
+ if (command !== "update") {
100
+ throw new Error(`${argument} is only supported by update.`);
101
+ }
102
+ forwarded.push(argument);
103
+ continue;
104
+ }
105
+ if (argument === "--yes" || argument === "-y") {
106
+ forwarded.push(argument);
107
+ continue;
108
+ }
109
+ throw new Error(`Unknown option: ${argument}`);
110
+ }
111
+ return forwarded;
112
+ }
113
+ function help() {
114
+ return `AgentFlow ${packageJson.version}
115
+
116
+ Usage:
117
+ agentflow init [--agent <name>] [--global] [--yes]
118
+ agentflow update [--global | --project] [--yes]
119
+ agentflow --version
120
+
121
+ Commands:
122
+ init Install all AgentFlow skills and write AGENTFLOW.md
123
+ update Update installed AgentFlow skills and refresh AGENTFLOW.md
124
+
125
+ AgentFlow prints the AGENTS.md snippet for you to add manually.
126
+ `;
127
+ }
128
+ function runCli(args, {
129
+ cwd = process.cwd(),
130
+ stdout = process.stdout,
131
+ stderr = process.stderr,
132
+ runSkills: executeSkills = runSkills,
133
+ workflow = readFileSync3(resolve3(packageRoot, "README.md"), "utf8")
134
+ } = {}) {
135
+ const [command, ...optionArgs] = args;
136
+ if (!command || command === "--help" || command === "-h") {
137
+ stdout.write(help());
138
+ return 0;
139
+ }
140
+ if (command === "--version" || command === "-v") {
141
+ stdout.write(`${packageJson.version}
142
+ `);
143
+ return 0;
144
+ }
145
+ if (command !== "init" && command !== "update") {
146
+ stderr.write(`Unknown command: ${command}
147
+
148
+ ${help()}`);
149
+ return 1;
150
+ }
151
+ try {
152
+ const forwarded = parseOptions(optionArgs, command);
153
+ const workflowPath = resolve3(cwd, "AGENTFLOW.md");
154
+ if (existsSync2(workflowPath) && !isGeneratedWorkflow(workflowPath)) {
155
+ throw new Error("AGENTFLOW.md is not managed by AgentFlow CLI. Move it or merge it manually before continuing.");
156
+ }
157
+ if (command === "update" && !existsSync2(workflowPath)) {
158
+ throw new Error("AgentFlow is not initialized here. Run `agentflow init`.");
159
+ }
160
+ const skillsArgs = command === "init" ? ["add", SKILLS_SOURCE, "--skill", "*", ...forwarded] : ["update", ...AGENTFLOW_SKILLS, ...forwarded];
161
+ const status = executeSkills(skillsArgs);
162
+ if (status !== 0) {
163
+ stderr.write(`Agent skills ${command} failed with exit code ${status}.
164
+ `);
165
+ return status;
166
+ }
167
+ writeAtomically(workflowPath, generatedWorkflow(workflow));
168
+ ensureIgnored(cwd);
169
+ if (command === "init") {
170
+ stdout.write(`AgentFlow initialized.
171
+
172
+ Add this line to AGENTS.md:
173
+
174
+ ${AGENTS_SNIPPET}
175
+ `);
176
+ } else {
177
+ stdout.write(`AgentFlow updated.
178
+ `);
179
+ }
180
+ return 0;
181
+ } catch (error) {
182
+ stderr.write(`${error instanceof Error ? error.message : String(error)}
183
+ `);
184
+ return 1;
185
+ }
186
+ }
187
+
188
+ // src/bin.ts
189
+ process.exitCode = runCli(process.argv.slice(2));
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@reforma/agentflow",
3
+ "version": "0.0.1",
4
+ "description": "An opinionated workflow for shipping production-ready changes with coding agents.",
5
+ "author": "Reforma, Inc. <dev@reforma.ai>",
6
+ "type": "module",
7
+ "bin": {
8
+ "agentflow": "bin/agentflow.js"
9
+ },
10
+ "files": [
11
+ "bin",
12
+ "skills",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "scripts": {
17
+ "build": "bun build src/bin.ts --target=node --outfile=bin/agentflow.js --external skills",
18
+ "typecheck": "tsc --noEmit",
19
+ "changeset": "bunx @changesets/cli",
20
+ "version-packages": "bunx @changesets/cli version",
21
+ "release": "bunx @changesets/cli publish",
22
+ "test": "bun test",
23
+ "validate:skills": "gh skill publish --dry-run",
24
+ "prepublishOnly": "bun run typecheck && bun run test && bun run build"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/reforma-dev/agentflow.git"
29
+ },
30
+ "homepage": "https://github.com/reforma-dev/agentflow#readme",
31
+ "bugs": "https://github.com/reforma-dev/agentflow/issues",
32
+ "keywords": [
33
+ "agent-skills",
34
+ "coding-agents",
35
+ "cursor",
36
+ "claude-code",
37
+ "codex"
38
+ ],
39
+ "license": "MIT",
40
+ "engines": {
41
+ "node": ">=20"
42
+ },
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
46
+ "dependencies": {
47
+ "skills": "1.5.23"
48
+ },
49
+ "devDependencies": {
50
+ "@changesets/cli": "3.0.1",
51
+ "@types/bun": "1.4.0",
52
+ "@types/node": "26.4.0",
53
+ "typescript": "7.0.2"
54
+ }
55
+ }
@@ -0,0 +1,117 @@
1
+ ---
2
+ name: code-review
3
+ description: >-
4
+ Strict review of reuse, wrappers, and architecture. Reviewers collect, then
5
+ you apply fixes. Verdict is keep, shrink, or burn.
6
+ license: MIT
7
+ disable-model-invocation: true
8
+ ---
9
+
10
+ # code-review
11
+
12
+ Reviewers collect; **you apply fixes** — including obvious local defects, without asking. Done when production code is **smaller and less wrapped**, calling what already exists. Verdict: `keep` / `shrink` / `burn`.
13
+
14
+ Bugs are incidental: whoever already read the files reports ones they saw. Do not launch a bot to hunt bugs.
15
+
16
+ ## Scope
17
+
18
+ User-named paths / symbols / area win. Named fixed point (branch, tag, `main`, PR) → `git diff <fixed>...HEAD` (three-dot), still honor a path allowlist inside it.
19
+
20
+ Otherwise: `git diff --no-color` and `git diff --cached --no-color`. No local diff → conversation files. Still nothing → `git show --stat --patch --no-color HEAD`.
21
+
22
+ Do not broaden past that scope except to match existing patterns. Preserve unrelated user changes.
23
+
24
+ **Allowlist** = those files. Every bot and every edit stays inside it (imports from an allowlisted file are ok).
25
+
26
+ ## 1. Triage
27
+
28
+ Read the allowlisted diff. Write **one** launch line, then collect or skip to fix.
29
+
30
+ | Launch line | When |
31
+ | ----------------------- | --------------------------------------------------------------------------------------------------- |
32
+ | `parent-only` | Default. The whole diff fits in one read. |
33
+ | `one: <bot> — <reason>` | One row matches **and** the parent cannot finish the verdict from this read. Reason names that gap. |
34
+ | `army: <bots>` | User said full / strict / army. One wave of matching rows except bugbot. |
35
+ | named bots | User named them. Those names only. Includes `bugbot` if they said it. |
36
+
37
+ **One-bot pick** (not army, not named): first match only.
38
+
39
+ 1. **reuse** — may have reinvented or wrapped something that already exists, **and** that existing API is not obvious from this read (outside the allowlist, or too many files to hunt). A new file on a small diff is not enough.
40
+ 2. **smell** — roughly ≥4 production files and wrappers / pass-throughs / muddy shape; reuse did not match.
41
+ 3. **security** — actual trust surface (auth, secrets, injection, user input, sandbox, network); reuse and smell did not match.
42
+
43
+ No match → `parent-only`. A `.ts` file, “several files”, or “tests in the allowlist” is not a launch.
44
+
45
+ | Bot | Type | Launch |
46
+ | -------- | ----------------- | ------------------------------------------ |
47
+ | Reuse | `reuse-review` | Pick 1, army if that row matches, or named |
48
+ | Smells | `smell-review` | Pick 2, army if that row matches, or named |
49
+ | Security | `security-review` | Pick 3, army if that row matches, or named |
50
+ | Bugbot | `bugbot` | User named `bugbot` only |
51
+
52
+ Parent always does reuse / wrappers / verdict itself (nearest `AGENTS.md`; React / MobX / copy / tests → matching craft skill). A bot is extra eyes, not a replacement.
53
+
54
+ ## 2. Collect
55
+
56
+ `parent-only` → skip this step.
57
+
58
+ Launch **only** the bots on the launch line. Army: one parallel wave. Otherwise exactly one. They must not edit. `run_in_background: false`, no `resume`.
59
+
60
+ **Brief** (every bot, plus the allowlist):
61
+
62
+ ```text
63
+ Primary: reuse (existing APIs vs new wrappers), architecture, cleanliness.
64
+ Verdict required: keep | shrink | burn — one line, why.
65
+ Incidental: bugs you already saw while reading. Do not hunt bugs.
66
+ ```
67
+
68
+ **Reuse / smells** — `description` exactly `Reuse Review` / `Smell Review`. They inspect git. Prompt: allowlist + brief.
69
+
70
+ **Security / bugbot** — `description` exactly `Security Review` / `Bugbot`. They compute the diff. Prompt:
71
+
72
+ ```text
73
+ Full Repository Path: <workspace root>
74
+ Diff: uncommitted changes
75
+ Custom Instructions: Review ONLY these files (ignore other uncommitted changes):
76
+ - <allowlist path>
77
+
78
+ Primary: reuse (existing APIs vs new wrappers), architecture, cleanliness.
79
+ Verdict required: keep | shrink | burn — one line, why.
80
+ Incidental: bugs you already saw while reading. Do not hunt bugs.
81
+ ```
82
+
83
+ Empty diff on committed HEAD/branch work → retry once with `Diff: branch changes`. Bugbot still empty → retry once with `Diff: natural language` and a per-file Change Description. Wrong prompt: retry once, then stop that bot and say so.
84
+
85
+ **Spec (you):** user-passed path, or issue/PR in commit messages (Linear, GitHub), or the plan in this conversation. Do not hunt a specs tree the user did not name. Missing/partial/scope-creep → findings for Fix. No source → skip.
86
+
87
+ ## 3. Fix
88
+
89
+ Normalize: `severity | confidence | file:line | finding | fix`. Drop `confidence: low`. Dedup.
90
+
91
+ Bar (even if a bot under-reported):
92
+
93
+ 1. **Reuse** — existing helper/component/API does the job → call it. No parallel wrapper. Duplicate implementations in the allowlist → keep the better one, retarget imports, delete the rest.
94
+ 2. **Wrappers** — pass-throughs, extra HTML/JSX, one-off barrels/helpers: inline or delete. Do not wrap a wrapper.
95
+ 3. **Shrink** — net-fewer prod lines (tests excluded) by deleting wrappers, dupes, and orphans — not by packing lines. Nested ternaries, dense one-liners, and mashed concerns are not shrink. Clarity wins when they conflict. Preserve behavior: only how, not what. A new file/helper is wrong unless it deletes more than it adds. If prod cannot shrink, say why.
96
+ 4. **Orphans** — unused imports, locals, helpers, exports, files, or commented-out blocks **this allowlisted change** made dead. Before delete: Grep real uses, including dynamic `import()` / string path lookups. Zero uses → delete. Package public export / cross-app API without monorepo Grep proof → defer. Pre-existing dead outside the allowlist → leave. Unsure → defer (no knip/depcheck/ts-prune sweeps).
97
+ 5. **Verdict** — local `burn`/`shrink` (inline, delete the new file, call the existing API) → do it. Whole-shape `burn` → say so and defer; do not nibble.
98
+ 6. **Tests in the allowlist** — keep real behavior tests; delete mock-theater / dupes / empty / greenwash. Do not invent tests for a prod-only change. Do not reshape prod to please a weak test.
99
+ 7. **Obvious** — a named local defect (broken emit, silent wrong path, leftover after a failed write) → fix this turn. Do not ask.
100
+
101
+ Incidental bugs: fix if local; else defer. Do not hunt.
102
+
103
+ Default-accept reuse/smell findings that serve the bar when the fix is local. Skip only with `needs product call` / `this is the contract` / `too large`.
104
+
105
+ Tie-break: existing helper > inline > new helper. You fix — do not delegate. No edits outside the allowlist. No re-hunt. Small in-scope shrink → do it; larger → defer. Do not rerun collectors unless a fix likely created a new bug.
106
+
107
+ ## 4. Verify
108
+
109
+ No pass / done / clean claim without a command you ran in **this** turn. Identify the command → run it full → read exit and failures → then claim. A previous run, “should pass”, or lint-for-compile is not evidence.
110
+
111
+ Targeted: the project's test and lint for the allowlist (nearest `AGENTS.md`).
112
+
113
+ Bug you fixed with no covering test → add a regression test or list `no test: …`.
114
+
115
+ ## Output
116
+
117
+ Launch line; **verdict** (`keep` / `shrink` / `burn` + one line); what changed; prod smaller or why not; wrappers gone / APIs reused; orphans deleted or deferred (Grep gap / public API); incidental bugs fixed or deferred; tests judged or `no tests in allowlist`; spec gaps or `no spec`; deferred (`severity | file:line | reason`); tests/lint **this turn** (command + exit). No “fix or skip?” for local findings.
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: grill
3
+ description: Relentless interview to sharpen a plan or design until every branch is resolved.
4
+ license: MIT
5
+ ---
6
+
7
+ # Grill
8
+
9
+ Interview until you reach a shared understanding. Map the work as a **design tree**: every decision branches into the decisions that hang off it.
10
+
11
+ Use this for non-trivial product/architecture calls. Do not grill a one-line fix.
12
+
13
+ Do **not** create `CONTEXT.md`, ADRs, or tickets as you go. Capture settled decisions in the reply; if a lasting convention belongs in an `AGENTS.md`, say so and wait for the user to ask you to write it.
14
+
15
+ ## Rounds
16
+
17
+ Start by restating the task, the decisions already implied by the request, and
18
+ any assumptions you would otherwise make. If there are no open questions, ask
19
+ the user to confirm this reading.
20
+
21
+ Work the tree in **rounds**. The **frontier** is every decision whose prerequisites are already settled. Ask the whole frontier in one round: number each question and give your recommended answer. Then wait.
22
+
23
+ ```
24
+ ❓ **Q1** - **<title>**: <body, including choices>
25
+
26
+ ➡️ <your recommended answer>
27
+
28
+ ---
29
+
30
+ ❓ **Q2** - **<title>**: <body>
31
+
32
+ ➡️ <your recommended answer>
33
+ ```
34
+
35
+ Each round of answers reshapes the tree. Recompute the frontier. A question that depends on another still open in this round belongs to a _later_ round.
36
+
37
+ ## Facts vs decisions
38
+
39
+ Finding _facts_ is your job. When a frontier question needs something from the repo, look it up (nearest `AGENTS.md`) — don't ask the user for anything you can read. A running lookup is an unsettled prerequisite: ask the rest of the frontier now.
40
+
41
+ The _decisions_ are the user's. Put each to them and wait.
42
+
43
+ Push back when a simpler approach fits. Name blockers instead of designing around them. Recommend the project's default (nearest `AGENTS.md`: surgical, inline, no new files) unless the user has a reason not to.
44
+
45
+ ## Done
46
+
47
+ The frontier is empty, every branch has been visited, and the user has confirmed
48
+ the final reading. Nothing is silently assumed. Do not implement before this
49
+ confirmation.
50
+
51
+ After confirmation:
52
+
53
+ - In Plan mode, continue with the agent's native planning flow.
54
+ - If the user asked for a plan in a normal mode, load the `plan` skill and write
55
+ it in the same chat.
56
+ - Otherwise, let the user choose the next step. A small task may continue
57
+ directly to implementation.
@@ -0,0 +1,63 @@
1
+ ---
2
+ name: handoff
3
+ description: >-
4
+ Save AGENTFLOW context to disk before continuing in a fresh chat.
5
+ license: MIT
6
+ disable-model-invocation: true
7
+ ---
8
+
9
+ # Handoff
10
+
11
+ Use this after an implementation PR when the next slice will continue in a
12
+ fresh chat. Write a file the next chat can `@`-attach. The handoff + plan are
13
+ the durable source of truth.
14
+
15
+ Loop: the repo-root loop doc (`README.md` or `AGENTFLOW.md`).
16
+
17
+ ## Steps
18
+
19
+ 1. **Find the plan.** Path the user named, the last handoff, or the plan in this
20
+ thread.
21
+ 2. **This PR.** What shipped (paths, commit hash if any). Done-when met or not.
22
+ 3. **Bleed.** Work that belongs to a later plan row but landed, or should have.
23
+ Fold it into the plan: check off this PR, rewrite later rows. Do not leave
24
+ the next chat to discover leftover files.
25
+ 4. **Next PR.** Title + done-when from the updated plan. One row only.
26
+ 5. **Write** `.agentflow/<slug>/handoff.md` (create dirs). Reuse the same slug
27
+ for the feature so the next chat overwrites this file. Not OS temp.
28
+ 6. **Print** the path and what the fresh chat should attach.
29
+
30
+ Redact secrets. Do not paste diffs or OpenSpec bodies — point at paths.
31
+
32
+ If the user passed a focus, that is the next chat’s Next PR.
33
+
34
+ ## File
35
+
36
+ ```markdown
37
+ # Handoff: <slug>
38
+
39
+ > Next chat: read this file and the plan. Implement **only** Next PR.
40
+ > Do not resurrect discarded approaches from Cursor summarize.
41
+
42
+ ## Plan
43
+
44
+ path: <file the next chat can open>
45
+ sync: updated | unchanged
46
+
47
+ ## This PR
48
+
49
+ <title>
50
+ done when: <criterion> — met | not met
51
+ shipped: <paths, commit if any>
52
+
53
+ ## Bleed
54
+
55
+ <what leaked into / out of later PRs, and how the plan changed>
56
+ none
57
+
58
+ ## Next PR
59
+
60
+ <title>
61
+ done when: <criterion>
62
+ suggested: tdd / code-review / craft skills for the next slice
63
+ ```
@@ -0,0 +1,81 @@
1
+ ---
2
+ name: plan
3
+ description: >-
4
+ Use when the user asks to write or update an implementation plan split into
5
+ PR-sized slices after decisions are settled. Not the interview (grill) and
6
+ not the end-of-PR baton (handoff).
7
+ license: MIT
8
+ ---
9
+
10
+ # Plan
11
+
12
+ Write **one** plan that another developer or agent can pick up. Do not implement. Do not write a handoff.
13
+
14
+ If important decisions are still open, load `grill` first. Resume the plan after
15
+ the user confirms the final reading. One-line or obvious scope can skip the
16
+ plan.
17
+
18
+ ## Where
19
+
20
+ - **Plan mode:** plan artifact / reply only. No `.agentflow/` files.
21
+ - **Agent mode:** `.agentflow/<slug>/plan.md` (kebab-case from the feature; reuse the slug). Print the path.
22
+ - Another machine or person: OpenSpec or a `*.md` on the branch.
23
+
24
+ ## Shape
25
+
26
+ ```markdown
27
+ # <Feature>
28
+
29
+ **Goal:** one sentence
30
+ **Approach:** 2–3 sentences — the chosen reading
31
+ **Reuse:** existing APIs this plan calls (`path`)
32
+
33
+ ## Files
34
+
35
+ - `path` — what changes (or `create`)
36
+
37
+ - [ ] PR 1 — <title>
38
+ done when: <observable>
39
+ verify: <command>
40
+
41
+ - [ ] PR 2 — …
42
+ ```
43
+
44
+ Each row is the smallest complete, independently shippable change with one
45
+ coherent outcome and one verification story. Order rows from foundations to
46
+ their consumers.
47
+
48
+ ## Slicing
49
+
50
+ Common shapes:
51
+
52
+ - **Full-stack feature:** behavior-preserving refactoring, then shared types and
53
+ backend, then frontend integration.
54
+ - **Frontend feature:** reusable components, then shared runtime or wiring, then
55
+ user-facing interfaces.
56
+ - **Small feature:** one complete vertical slice.
57
+
58
+ Give a foundation its own row when it is independently useful. Keep
59
+ feature-local components and wiring with their first consumer. Split distinct
60
+ outcomes or architectural decisions that can ship separately.
61
+
62
+ ## Rules
63
+
64
+ - Ground every row in files you read. Cite a path → it exists, or mark `create`.
65
+ - One approach. No menu, no TBD, no “handle edge cases”, no “similar to PR n”.
66
+ - File map before rows. Reuse before create.
67
+ - Tight: settled model + rows. Cut prose, keep paths.
68
+ - `done when` is observable. `verify` is the command the implementer runs.
69
+
70
+ ## Self-check
71
+
72
+ 1. Every settled decision has a row or is named out of scope.
73
+ 2. Every path exists or is marked `create`.
74
+ 3. No placeholders.
75
+ 4. Another developer or agent can open the file and ship PR 1 without this conversation.
76
+ 5. Every row leaves the repository working and does not need the next row to
77
+ justify its code.
78
+
79
+ ## Output
80
+
81
+ Print the path. The implementer starts with the first unchecked row, in this context or a new one.
@@ -0,0 +1,133 @@
1
+ ---
2
+ name: research
3
+ description: >-
4
+ Research a codebase, feature, technology, or external landscape and save a
5
+ reusable map under .agentflow/<feature>/research/. Use when later work should
6
+ not repeat the same investigation. Not for a one-file lookup.
7
+ license: MIT
8
+ ---
9
+
10
+ # Research
11
+
12
+ Learn how an area works and leave a map that another chat can use without
13
+ repeating the investigation. Research only. Do not plan or implement the change.
14
+
15
+ The user can ask for research in a regular prompt. Use this skill when the
16
+ findings should survive context compaction or move to another chat.
17
+
18
+ ## Output
19
+
20
+ Write to:
21
+
22
+ ```text
23
+ .agentflow/<slug>/research/
24
+ ├── index.md
25
+ └── <topic>.md
26
+ ```
27
+
28
+ Use a short kebab-case slug for the feature or question. Reuse the directory
29
+ when continuing the same research.
30
+
31
+ `index.md` is a short map, not the full report. It contains:
32
+
33
+ - the question and scope
34
+ - a short summary
35
+ - links to every topic page
36
+ - the important files, modules, or sources
37
+ - open questions that research could not answer
38
+
39
+ Split detailed findings by domain, module, flow, or independent question. Each
40
+ topic page owns one coherent area and must make sense when attached to a chat on
41
+ its own. If a later chat could use one part without the others, put that part in
42
+ its own file.
43
+
44
+ Keep a narrow investigation in `index.md` instead of creating empty pages. For
45
+ larger research, keep details out of `index.md` and link to the topic pages.
46
+
47
+ Write the notes in the user's language.
48
+
49
+ ## Research the code
50
+
51
+ When the question is about an existing project:
52
+
53
+ 1. Read the nearest `AGENTS.md` and the repository's exploration rules.
54
+ 2. Try the current behavior when possible.
55
+ 3. Find the entry points, public interfaces, modules, and data flow.
56
+ 4. Follow the important path end to end. Verify call graph guesses with text
57
+ search.
58
+ 5. Record exact file paths and symbol names with one line explaining each role.
59
+ 6. Capture constraints, gotchas, and existing APIs that later work should reuse.
60
+
61
+ Do not dump search results or source code. Explain how the pieces connect.
62
+ Separate what you observed from what you inferred.
63
+
64
+ ## Research a technology
65
+
66
+ When the question is about a library, platform, or approach:
67
+
68
+ 1. Check the version and setup used by the project.
69
+ 2. Search the web and read current primary documentation and official sources.
70
+ 3. Find the APIs and constraints relevant to the question.
71
+ 4. Connect the findings back to the project's files and current architecture.
72
+ 5. Record dated source links.
73
+
74
+ The result should explain what applies here, not summarize the entire
75
+ technology.
76
+
77
+ ## Research a landscape
78
+
79
+ When comparing tools, competitors, or options:
80
+
81
+ 1. State the question and the filter used to keep or reject candidates.
82
+ 2. Record what the project already has.
83
+ 3. Open current primary sources and date the snapshot.
84
+ 4. Explain why each kept option fits the question.
85
+ 5. Name rejected options and the reason.
86
+ 6. End with ranked next moves when the user asked what to adopt or build.
87
+
88
+ A catalog dump is not research. Every item must help answer the question.
89
+
90
+ ## Suggested page shape
91
+
92
+ ```markdown
93
+ # <Topic>
94
+
95
+ ## Summary
96
+
97
+ <What a later chat needs to know>
98
+
99
+ ## How it works
100
+
101
+ <Behavior or flow>
102
+
103
+ ## Relevant files and modules
104
+
105
+ - `path/to/file.ts` — role
106
+
107
+ ## Constraints
108
+
109
+ - <constraint or gotcha>
110
+
111
+ ## Sources
112
+
113
+ - <dated primary source>
114
+
115
+ ## Open questions
116
+
117
+ - <unanswered question>
118
+ ```
119
+
120
+ Use only the sections that fit the topic.
121
+
122
+ ## Done
123
+
124
+ Research is complete when a fresh chat can answer these questions from the
125
+ notes:
126
+
127
+ 1. How does this area work?
128
+ 2. Where are the important files, modules, or sources?
129
+ 3. What constraints will affect the work?
130
+ 4. What is still unknown?
131
+
132
+ Print the path to `index.md` and a short takeaway. Do not paste the full notes
133
+ into the reply.
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: tdd
3
+ description: >-
4
+ Use when the user wants to build features or fix bugs test-first,
5
+ mentions "red-green-refactor", or wants integration tests.
6
+ license: MIT
7
+ ---
8
+
9
+ # Test-Driven Development
10
+
11
+ TDD is the red → green loop. This skill is the reference that makes that loop produce tests worth keeping: what a good test is, where tests go, the anti-patterns, and the rules of the loop. Every section applies on every cycle — consult them before and during the loop, not after.
12
+
13
+ When exploring the codebase, read the nearest `AGENTS.md` so test names and interface vocabulary match the area's language. Explore per root `AGENTS.md`.
14
+
15
+ ## What a good test is
16
+
17
+ Tests verify behavior through public interfaces, not implementation details. Code can change entirely; tests shouldn't. A good test reads like a specification — "user can checkout with valid cart" tells you exactly what capability exists — and survives refactors because it doesn't care about internal structure.
18
+
19
+ See [tests.md](tests.md) for examples and [mocking.md](mocking.md) for mocking guidelines.
20
+
21
+ ## Seams — where tests go
22
+
23
+ A **seam** is the public boundary you test at: the interface where you observe behavior without reaching inside. Tests live at seams, never against internals.
24
+
25
+ **Test only at pre-agreed seams.** Before writing any test, write down the seams under test and confirm them with the user. No test is written at an unconfirmed seam. You can't test everything — agreeing the seams up front is how testing effort lands on the critical paths and complex logic instead of every edge case.
26
+
27
+ Ask: "What's the public interface, and which seams should we test?"
28
+
29
+ Root `AGENTS.md` wins: do not add a file, port, or wrapper so a test can reach inside. One real adapter is not a seam — two (production + test, or two backends) is. Don't invent `IFooService` for an in-process collaborator you own.
30
+
31
+ ## Anti-patterns
32
+
33
+ - **Implementation-coupled** — mocks internal collaborators, tests private methods, or verifies through a side channel (querying the database instead of using the interface). The tell: the test breaks when you refactor but behavior hasn't changed.
34
+ - **Tautological** — the assertion recomputes the expected value the way the code does (`expect(add(a, b)).toBe(a + b)`, a snapshot derived by hand the same way, a constant asserted equal to itself), so it passes by construction and can never disagree with the code. Expected values must come from an independent source of truth — a known-good literal, a worked example, the spec.
35
+ - **Horizontal slicing** — writing all tests first, then all implementation. Bulk tests verify _imagined_ behavior: you test the _shape_ of things rather than user-facing behavior, the tests go insensitive to real changes, and you commit to test structure before understanding the implementation. Work in **vertical slices** instead — one test → one implementation → repeat, each test a **tracer bullet** that responds to what the last cycle taught you.
36
+
37
+ ## Rules of the loop
38
+
39
+ - **Red before green.** Write the failing test first, then only enough code to pass it. Don't anticipate future tests or add speculative features.
40
+ - **One slice at a time.** One seam, one test, one minimal implementation per cycle.
41
+ - **Watch the color.** Red is not “wrote a test”. Run it. Confirm it fails because the behavior is missing — not a typo, import error, or existing pass. Passes immediately → you tested existing behavior; fix the test. Errors → fix the harness until it fails correctly, then implement. Green: run the same command; claim pass only from that output.
42
+ - **Name the break.** Before the test body, name the production change that should make it fail. Cannot name one → wrong seam.
43
+ - **Refactoring is not part of the loop.** It belongs to the review stage (see the `code-review` skill), not the red → green implementation cycle.
@@ -0,0 +1,50 @@
1
+ # When to Mock
2
+
3
+ Mock at **system boundaries** only:
4
+
5
+ - External APIs (payment, email, third-party)
6
+ - Time / randomness
7
+ - Databases and filesystems — sometimes; prefer a real test DB / tmp dir when cheap
8
+
9
+ Don't mock:
10
+
11
+ - Your own classes / modules
12
+ - Internal collaborators
13
+ - Anything you control
14
+
15
+ Use the project's test runner (nearest `AGENTS.md`). Vitest: `vi.fn`, `vi.mock`.
16
+
17
+ ## Designing for mockability
18
+
19
+ **1. Pass the boundary in** — only when a second adapter is real (production + test, or two backends).
20
+
21
+ ```typescript
22
+ // Easy to mock
23
+ function processPayment(order: Order, paymentClient: PaymentClient) {
24
+ return paymentClient.charge(order.total);
25
+ }
26
+
27
+ // Hard to mock
28
+ function processPayment(order: Order) {
29
+ const client = new StripeClient(process.env.STRIPE_KEY);
30
+ return client.charge(order.total);
31
+ }
32
+ ```
33
+
34
+ Don't invent `IFoo` for an in-process collaborator you own just so a test can mock it.
35
+
36
+ **2. Prefer SDK-style interfaces over a generic fetcher**
37
+
38
+ ```typescript
39
+ // GOOD: each function is independently mockable
40
+ const api = {
41
+ getUser: (id: string) => fetch(`/users/${id}`),
42
+ createOrder: (data: OrderInput) =>
43
+ fetch("/orders", { method: "POST", body: JSON.stringify(data) }),
44
+ };
45
+
46
+ // BAD: mock needs conditional logic
47
+ const api = {
48
+ fetch: (endpoint: string, options?: RequestInit) => fetch(endpoint, options),
49
+ };
50
+ ```
@@ -0,0 +1,74 @@
1
+ # Good and Bad Tests
2
+
3
+ ## Good tests
4
+
5
+ Test through real interfaces, not mocks of internal parts.
6
+
7
+ ```typescript
8
+ // GOOD: observable behavior
9
+ it("should confirm checkout for a valid cart", async () => {
10
+ const cart = createCart();
11
+ cart.add(product);
12
+ const result = await checkout(cart, paymentMethod);
13
+ expect(result.status).toBe("confirmed");
14
+ });
15
+ ```
16
+
17
+ - Behavior callers care about
18
+ - Public API only
19
+ - Survives internal refactors
20
+ - Describes WHAT, not HOW
21
+ - One logical assertion per test
22
+
23
+ ## Bad tests
24
+
25
+ **Implementation-detail** — coupled to internal structure.
26
+
27
+ ```typescript
28
+ // BAD
29
+ it("should call paymentService.process", async () => {
30
+ const process = vi.fn();
31
+ await checkout(cart, { process });
32
+ expect(process).toHaveBeenCalledWith(cart.total);
33
+ });
34
+ ```
35
+
36
+ Red flags: mocking internal collaborators, private methods, call counts/order, test name describes HOW, verifying through a side channel instead of the interface.
37
+
38
+ ```typescript
39
+ // BAD: bypasses interface
40
+ it("should save the user to the database", async () => {
41
+ await createUser({ name: "Alice" });
42
+ const row = await db.query("SELECT * FROM users WHERE name = ?", ["Alice"]);
43
+ expect(row).toBeDefined();
44
+ });
45
+
46
+ // GOOD: verifies through interface
47
+ it("should make a created user retrievable", async () => {
48
+ const user = await createUser({ name: "Alice" });
49
+ const retrieved = await getUser(user.id);
50
+ expect(retrieved.name).toBe("Alice");
51
+ });
52
+ ```
53
+
54
+ **Tautological** — expected value restates the implementation.
55
+
56
+ ```typescript
57
+ // BAD
58
+ it("should sum line items", () => {
59
+ const items = [{ price: 10 }, { price: 5 }];
60
+ const expected = items.reduce((sum, i) => sum + i.price, 0);
61
+ expect(calculateTotal(items)).toBe(expected);
62
+ });
63
+
64
+ // GOOD: independent literal
65
+ it("should sum line items", () => {
66
+ expect(calculateTotal([{ price: 10 }, { price: 5 }])).toBe(15);
67
+ });
68
+ ```
69
+
70
+ **Low-value mapping tests** — asserting `.map` / array shape with no behavior. Skip or drop them rather than warping production code for reachability (root `AGENTS.md`).
71
+
72
+ ## Mutation check
73
+
74
+ A realistic production mutation (wrong branch, missing side effect, empty return) should fail at least one test. Nothing fails → tautological or untested.