pincer-workflow 0.2.3 → 0.4.0
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/README.md +19 -14
- package/bin/pincer.js +47 -11
- package/package.json +2 -2
- package/template/.agents/skills/pincer-code/SKILL.md +62 -17
- package/template/.agents/skills/pincer-evaluate/SKILL.md +85 -15
- package/template/.agents/skills/pincer-narrow/SKILL.md +67 -22
- package/template/.agents/skills/pincer-plan/SKILL.md +62 -25
- package/template/.agents/skills/pincer-release/SKILL.md +31 -12
- package/template/.agents/skills/pincer-status/SKILL.md +5 -3
- package/template/.claude/commands/pincer-code.md +61 -16
- package/template/.claude/commands/pincer-evaluate.md +85 -15
- package/template/.claude/commands/pincer-narrow.md +65 -20
- package/template/.claude/commands/pincer-plan.md +57 -20
- package/template/.claude/commands/pincer-release.md +30 -11
- package/template/.claude/commands/pincer-status.md +5 -3
- package/template/.claude/hooks/block-dangerous.sh +7 -18
- package/template/.claude/hooks/hook-policy.cjs +351 -0
- package/template/.claude/hooks/ticket-guard.sh +6 -63
- package/template/.claude/references/prd-template.md +41 -9
- package/template/.claude/references/ticket-template.md +37 -4
- package/template/.codex/README.md +4 -4
- package/template/.github/prompts/pincer-code.prompt.md +61 -16
- package/template/.github/prompts/pincer-evaluate.prompt.md +85 -15
- package/template/.github/prompts/pincer-narrow.prompt.md +65 -20
- package/template/.github/prompts/pincer-plan.prompt.md +57 -20
- package/template/.github/prompts/pincer-release.prompt.md +30 -11
- package/template/.github/prompts/pincer-status.prompt.md +5 -3
- package/template/AGENTS.md +11 -5
- package/template/docs/dry-run-checklist.md +143 -27
- package/template/docs/release-checklist.md +35 -0
- package/template/scripts/pincer-evidence.cjs +292 -0
- package/template/scripts/pincer-status.sh +83 -26
- package/template/scripts/pincer-ticket-lib.sh +321 -0
- package/template/scripts/pincer-ticket.sh +58 -47
- package/template/scripts/sync-prompts.sh +6 -1
package/README.md
CHANGED
|
@@ -47,13 +47,13 @@ install as skills under `.agents/skills/`, which Codex discovers from the repo
|
|
|
47
47
|
loaded. Inside the Codex skills every cross-reference already reads
|
|
48
48
|
`$pincer-narrow`, `$pincer-status` and so on.
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
`~/.codex/config.toml`
|
|
50
|
+
Pincer does not currently install a Codex hook adapter, so its Codex guardrail
|
|
51
|
+
posture uses `~/.codex/config.toml` — `approval_policy = "on-request"` and
|
|
52
52
|
`sandbox_mode = "workspace-write"`; never run with approvals disabled. The
|
|
53
53
|
ticket scripts (`scripts/pincer-ticket.sh`, `scripts/pincer-status.sh`) are
|
|
54
54
|
plain bash and work unchanged; the rule in `AGENTS.md` against hand-editing
|
|
55
55
|
ticket state carries the weight the hook carries on Claude Code, and
|
|
56
|
-
`$pincer-status` flags
|
|
56
|
+
`$pincer-status` flags missing, failed, or stale ticket readiness.
|
|
57
57
|
Full notes in `.codex/README.md`.
|
|
58
58
|
|
|
59
59
|
### GitHub Copilot (VS Code)
|
|
@@ -65,7 +65,7 @@ in chat. `.github/copilot-instructions.md` is wired to `AGENTS.md`.
|
|
|
65
65
|
|
|
66
66
|
Claude users can install PINCER as a plugin instead — commands arrive
|
|
67
67
|
namespaced (`/pincer:plan` … `/pincer:release`) and update automatically
|
|
68
|
-
through the marketplace
|
|
68
|
+
through the marketplace. The plugin's structured hook parser requires Node.js 18+:
|
|
69
69
|
|
|
70
70
|
```
|
|
71
71
|
/plugin marketplace add orchestratedbyalex/pincer-workflow
|
|
@@ -86,7 +86,7 @@ want the repo-side rules too can copy `AGENTS.md` from the
|
|
|
86
86
|
npx pincer-workflow@latest update
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
-
Files you never touched are refreshed in place. (Installs older than v0.2.0 gain the ticket state machine, the status report and the ticket-guard hook on update; `.claude/settings.json` conflicts if you edited it — merge the new hook entry from the `.new` file. v0.2.2 replaces the Codex adapter: the commands are now skills in `.agents/skills/` invoked as `$pincer-*`, since Codex no longer loads `~/.codex/prompts/` — you can delete the copies there. v0.2.3 ships the playbooks, rubrics and templates under `.claude/` on every platform, which Codex- and Copilot-only installs were missing.) Files you edited are left
|
|
89
|
+
Files you never touched are refreshed in place. (Installs older than v0.2.0 gain the ticket state machine, the status report and the ticket-guard hook on update; `.claude/settings.json` conflicts if you edited it — merge the new hook entry from the `.new` file. v0.2.2 replaces the Codex adapter: the commands are now skills in `.agents/skills/` invoked as `$pincer-*`, since Codex no longer loads `~/.codex/prompts/` — you can delete the copies there. v0.2.3 ships the playbooks, rubrics and templates under `.claude/` on every platform, which Codex- and Copilot-only installs were missing. v0.3.0 makes trust revocable: every verification attempt is recorded, `done` re-runs the check, tickets carry `prd:`, the release audit is read-only, and `.pincer.json` moves to schema 2 — an install from 0.2.x is treated as an untrusted baseline, so on the first update every changed file arrives as a `.new` proposal once; hooks now need Node 18+. v0.4.0 carries requirement IDs and `Proves:` checks from the PRD to evaluation, adds `profile: small|standard`, and saves candidate evidence under `.prd/evidence/`; the update is additive, but an existing `NOTES.md` without an `evidence:` manifest reads as stale until `/pincer-evaluate` is re-run.) Files you edited are left
|
|
90
90
|
alone — the new version lands next to them as `<file>.new` for a manual merge.
|
|
91
91
|
`npx pincer-workflow doctor` checks the health of an install (hook executable,
|
|
92
92
|
`.gitignore` covering `.env*`, no unmerged `*.new` files, version current).
|
|
@@ -99,31 +99,36 @@ alone — the new version lands next to them as `<file>.new` for a manual merge.
|
|
|
99
99
|
| `.claude/commands/` | The five playbooks plus `/pincer-status` (canonical — adapters are generated from them; ships on every platform together with `agents/` and `references/`) |
|
|
100
100
|
| `.claude/agents/` | `codebase-explorer` and `code-quality-reviewer` subagents, with inline fallbacks for platforms without subagents |
|
|
101
101
|
| `scripts/pincer-ticket.sh` | The ticket state machine: `start` (enforces dependency order) → `verify` (runs the ticket's check, stamps a receipt only on green) → `done` (refuses without a matching receipt or with unticked criteria) |
|
|
102
|
-
| `scripts/pincer-status.sh` | Read-only state report: PRD,
|
|
103
|
-
|
|
|
102
|
+
| `scripts/pincer-status.sh` | Read-only state report: current PRD and profile, associated tickets, wall-clock elapsed time while work is active or against an explicit budget, evidence verdict, warnings, and next command |
|
|
103
|
+
| `scripts/pincer-evidence.cjs` | Read-only validator for candidate evidence (schema 1): `/pincer-evaluate` writes `.prd/evidence/prd-vN/<candidate>/manifest.json` plus logs and screenshots; status and release validate schema, references, candidate association, required results, file containment and SHA-256 digests. Legacy `NOTES.md` without a manifest is readable but never release-ready; an older runtime does not enforce this contract |
|
|
104
|
+
| `.claude/hooks/` + `settings.json` | Claude guardrails for documented destructive command forms and ticket state writes; Node.js 18+ parses hook payloads structurally |
|
|
104
105
|
| `.agents/skills/` · `.codex/` · `.github/` | Generated Codex skills and Copilot prompt files + platform wiring (`.codex/README.md` covers the Codex posture) |
|
|
105
106
|
| `scripts/sync-prompts.sh` | Regenerates the adapters after you edit a playbook |
|
|
106
107
|
| `scripts/build-plugin.sh` | Regenerates the Claude Code plugin (`plugin/`) from the template |
|
|
107
|
-
| `docs/
|
|
108
|
+
| `docs/release-checklist.md` | General, read-only candidate audit used by `/pincer-release` |
|
|
109
|
+
| `docs/dry-run-checklist.md` | Separate manual platform trial for the Pincer kit |
|
|
108
110
|
|
|
109
111
|
## Design principles
|
|
110
112
|
|
|
111
|
-
- **Approval gates scale with decision cost** — a human owns
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
- **Approval gates scale with decision cost** — a human owns architecture, scope,
|
|
114
|
+
and merge decisions; unchanged authorization persists across resume and routine
|
|
115
|
+
verification, while explicit budgets shape the depth of work.
|
|
114
116
|
- **Nothing is done while its verification fails** — every ticket carries a
|
|
115
117
|
runnable check, and "done" is a state only a passing run of that check can
|
|
116
118
|
unlock: the receipt is stamped by the script, never typed by the agent.
|
|
117
119
|
- **State lives in files, not in the conversation** — a new session runs
|
|
118
120
|
`/pincer-status` and knows exactly where to resume; elapsed time comes from
|
|
119
121
|
timestamps, not from the model's sense of time.
|
|
122
|
+
- **Evidence is saved, not narrated** — requirements carry stable IDs from the
|
|
123
|
+
PRD through tickets to evaluation, and the evaluation writes a validated
|
|
124
|
+
manifest under `.prd/evidence/` that release reads instead of trusting chat.
|
|
120
125
|
- **Scope is a first-class artifact** — cuts are recorded, never silent.
|
|
121
126
|
- **Security is threaded through every stage** — designed in at Plan, specified
|
|
122
127
|
as reject-path criteria at Narrow, enforced by a pre-commit sweep at Code,
|
|
123
128
|
audited mechanically at Evaluate and Release.
|
|
124
|
-
- **
|
|
125
|
-
|
|
126
|
-
|
|
129
|
+
- **Layered guardrails** — Claude hooks cover documented high-risk command and
|
|
130
|
+
ticket-state forms, while sandbox and approval controls remain the security
|
|
131
|
+
boundary. Other platforms use their native controls plus `/pincer-release`.
|
|
127
132
|
|
|
128
133
|
## License
|
|
129
134
|
|
package/bin/pincer.js
CHANGED
|
@@ -19,13 +19,14 @@ import { createRequire } from 'node:module';
|
|
|
19
19
|
const TEMPLATE = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'template');
|
|
20
20
|
const VERSION = createRequire(import.meta.url)('../package.json').version;
|
|
21
21
|
const MANIFEST = '.pincer.json';
|
|
22
|
+
const MANIFEST_SCHEMA = 2;
|
|
22
23
|
|
|
23
24
|
// The playbooks, subagent rubrics and PRD/ticket templates under .claude/ are
|
|
24
25
|
// the canonical kit and are read by every platform's adapter (the Codex skills
|
|
25
26
|
// and Copilot prompts point at them), so they ship everywhere; only Claude
|
|
26
27
|
// Code's own wiring (CLAUDE.md, settings.json, hooks) is platform-specific.
|
|
27
28
|
const PLATFORM_ROOTS = {
|
|
28
|
-
common: ['AGENTS.md', 'docs/dry-run-checklist.md', 'scripts/sync-prompts.sh', 'scripts/pincer-ticket.sh', 'scripts/pincer-status.sh', '.claude/commands', '.claude/agents', '.claude/references'],
|
|
29
|
+
common: ['AGENTS.md', 'docs/release-checklist.md', 'docs/dry-run-checklist.md', 'scripts/sync-prompts.sh', 'scripts/pincer-ticket.sh', 'scripts/pincer-ticket-lib.sh', 'scripts/pincer-status.sh', 'scripts/pincer-evidence.cjs', '.claude/commands', '.claude/agents', '.claude/references'],
|
|
29
30
|
claude: ['CLAUDE.md', '.claude/settings.json', '.claude/hooks'],
|
|
30
31
|
codex: ['.codex', '.agents'],
|
|
31
32
|
copilot: ['.github'],
|
|
@@ -53,17 +54,40 @@ function readManifest(dir) {
|
|
|
53
54
|
const p = path.join(dir, MANIFEST);
|
|
54
55
|
if (!fs.existsSync(p)) return null;
|
|
55
56
|
try {
|
|
56
|
-
|
|
57
|
+
const data = JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
58
|
+
if (!data || !Array.isArray(data.platforms) || !data.platforms.length ||
|
|
59
|
+
!data.platforms.every(p => ['claude', 'codex', 'copilot'].includes(p)) ||
|
|
60
|
+
!data.files || typeof data.files !== 'object' || Array.isArray(data.files) ||
|
|
61
|
+
Object.keys(data.files).some(p => path.isAbsolute(p) || p.split(/[\\/]/).includes('..')) ||
|
|
62
|
+
(data.schema !== undefined && data.schema !== MANIFEST_SCHEMA)) {
|
|
63
|
+
fail(`${MANIFEST} has an unsupported or invalid schema — restore a valid manifest before updating.`);
|
|
64
|
+
}
|
|
65
|
+
return data;
|
|
57
66
|
} catch {
|
|
58
67
|
fail(`${MANIFEST} exists but is not valid JSON — fix or remove it first.`);
|
|
59
68
|
}
|
|
60
69
|
}
|
|
61
70
|
|
|
62
71
|
function writeManifest(dir, platforms, hashes) {
|
|
63
|
-
const data = { version: VERSION, platforms, files: hashes };
|
|
72
|
+
const data = { schema: MANIFEST_SCHEMA, version: VERSION, platforms, files: hashes };
|
|
64
73
|
fs.writeFileSync(path.join(dir, MANIFEST), JSON.stringify(data, null, 2) + '\n');
|
|
65
74
|
}
|
|
66
75
|
|
|
76
|
+
// Preserve in-progress merges too. Reuse an identical proposal, never replace
|
|
77
|
+
// a different existing sidecar (including one left by an earlier release).
|
|
78
|
+
function writeProposal(dest, content) {
|
|
79
|
+
const base = dest + '.new';
|
|
80
|
+
let proposal = base;
|
|
81
|
+
let suffix = 0;
|
|
82
|
+
while (fs.existsSync(proposal)) {
|
|
83
|
+
if (fs.readFileSync(proposal).equals(content)) return proposal;
|
|
84
|
+
proposal = `${base}.${sha(content).slice(0, 12)}${suffix ? `.${suffix}` : ''}`;
|
|
85
|
+
suffix++;
|
|
86
|
+
}
|
|
87
|
+
fs.writeFileSync(proposal, content, { flag: 'wx' });
|
|
88
|
+
return proposal;
|
|
89
|
+
}
|
|
90
|
+
|
|
67
91
|
// Copies template files into dir. `baseline` (manifest hashes) tells an update
|
|
68
92
|
// which existing files are unmodified and therefore safe to refresh.
|
|
69
93
|
function install(dir, platforms, baseline) {
|
|
@@ -90,9 +114,11 @@ function install(dir, platforms, baseline) {
|
|
|
90
114
|
fs.writeFileSync(dest, src);
|
|
91
115
|
results.written.push(rel);
|
|
92
116
|
} else {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
117
|
+
const proposal = writeProposal(dest, src);
|
|
118
|
+
// Only a version actually installed by us grants overwrite permission.
|
|
119
|
+
// null means we have never established a trusted upstream baseline.
|
|
120
|
+
hashes[rel] = baseline?.[rel] ?? null;
|
|
121
|
+
results.conflicted.push({ rel, proposal: path.relative(dir, proposal) });
|
|
96
122
|
}
|
|
97
123
|
}
|
|
98
124
|
|
|
@@ -119,11 +145,11 @@ function ensureGitignore(dir) {
|
|
|
119
145
|
function report({ written, skipped, conflicted }) {
|
|
120
146
|
if (written.length) console.log(` wrote ${written.length} file(s)`);
|
|
121
147
|
if (skipped.length) console.log(` skipped ${skipped.length} file(s) already up to date`);
|
|
122
|
-
for (const rel of conflicted) {
|
|
123
|
-
console.log(` CONFLICT ${rel} —
|
|
148
|
+
for (const { rel, proposal } of conflicted) {
|
|
149
|
+
console.log(` CONFLICT ${rel} — local content preserved; the new version is at ${proposal}`);
|
|
124
150
|
}
|
|
125
151
|
if (conflicted.length) {
|
|
126
|
-
console.log('\n
|
|
152
|
+
console.log('\n Review and merge each reported proposal, then remove the resolved sidecars.');
|
|
127
153
|
}
|
|
128
154
|
}
|
|
129
155
|
|
|
@@ -184,7 +210,11 @@ async function cmdUpdate() {
|
|
|
184
210
|
const manifest = readManifest(dir);
|
|
185
211
|
if (!manifest) fail(`no ${MANIFEST} here — run \`pincer init\` first.`);
|
|
186
212
|
console.log(`\nUpdating PINCER ${manifest.version} -> ${VERSION} for: ${manifest.platforms.join(', ')}\n`);
|
|
187
|
-
|
|
213
|
+
// v1 manifests could adopt a local edit as their baseline. There is no safe
|
|
214
|
+
// way to distinguish those from upstream files after the fact.
|
|
215
|
+
const baseline = manifest.schema === MANIFEST_SCHEMA ? manifest.files : null;
|
|
216
|
+
if (!baseline) console.log(' Legacy baselines are untrusted; differing files will be preserved for review.');
|
|
217
|
+
report(install(dir, manifest.platforms, baseline));
|
|
188
218
|
}
|
|
189
219
|
|
|
190
220
|
function cmdDoctor() {
|
|
@@ -221,7 +251,13 @@ function cmdDoctor() {
|
|
|
221
251
|
console.log(` note locally edited (kept as-is on update): ${edited.join(', ')}`);
|
|
222
252
|
}
|
|
223
253
|
|
|
224
|
-
const leftovers = walk(TEMPLATE)
|
|
254
|
+
const leftovers = [...new Set([...walk(TEMPLATE), ...Object.keys(manifest.files)])].flatMap(rel => {
|
|
255
|
+
const parent = path.dirname(path.join(dir, rel));
|
|
256
|
+
const base = path.basename(rel) + '.new';
|
|
257
|
+
return fs.existsSync(parent) ? fs.readdirSync(parent)
|
|
258
|
+
.filter(name => name === base || name.startsWith(base + '.'))
|
|
259
|
+
.map(name => path.join(parent, name)) : [];
|
|
260
|
+
});
|
|
225
261
|
check(leftovers.length === 0, 'no unmerged *.new files', leftovers.map((p) => path.relative(dir, p)).join(', '));
|
|
226
262
|
|
|
227
263
|
console.log(problems ? `\n${problems} problem(s) found.` : '\nAll good.');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pincer-workflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "PINCER — a PRD-driven agentic delivery workflow for Claude Code, Codex CLI, and GitHub Copilot. Plan · Investigate · Narrow · Code · Evaluate · Release.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"pincer": "bin/pincer.js"
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"node": ">=18"
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
|
-
"test": "node test/smoke.test.js && node test/ticket.test.js"
|
|
17
|
+
"test": "node test/smoke.test.js && node test/installer.test.js && node test/ticket.test.js && node test/validation.test.js && node test/verification.test.js && node test/behavioral-verification.test.js && node test/evidence.test.js && node test/candidate.test.js && node test/recovery.test.js && node test/hooks.test.js && node test/workflow.test.js && node test/distribution.test.js"
|
|
18
18
|
},
|
|
19
19
|
"keywords": [
|
|
20
20
|
"claude-code",
|
|
@@ -7,23 +7,25 @@ description: "Implement tickets sequentially with verification and one commit pe
|
|
|
7
7
|
|
|
8
8
|
# $pincer-code — Ticket Implementation
|
|
9
9
|
|
|
10
|
-
You are implementing the tickets in `tickets/` sequentially.
|
|
11
|
-
|
|
10
|
+
You are implementing the tickets in `tickets/` sequentially. The approved PRD, ticket
|
|
11
|
+
breakdown, and existing session authorization define the work; run continuously and report
|
|
12
|
+
progress between tickets unless a material scope or design decision appears.
|
|
12
13
|
|
|
13
|
-
**Initial request:** the text that follows the `$pincer-code` mention
|
|
14
|
+
**Initial request:** the text that follows the `$pincer-code` mention, if any (when omitted, use the playbook's documented default)
|
|
14
15
|
|
|
15
16
|
Ticket state lives in the ticket file's frontmatter and is written **only** by
|
|
16
17
|
`scripts/pincer-ticket.sh` (`start` → `verify` → `done`). `verify` runs the ticket's
|
|
17
18
|
Verification block and stamps a receipt only on a green exit; `done` refuses without a
|
|
18
19
|
receipt that matches the current check, or with unticked acceptance criteria. Never edit
|
|
19
|
-
`status`, `started`, `verified`, or `finished` by hand — on Claude Code a hook blocks it.
|
|
20
|
+
`status`, `started`, `last_check`, `verified`, or `finished` by hand — on Claude Code a hook blocks it.
|
|
20
21
|
|
|
21
22
|
## Before the loop
|
|
22
23
|
|
|
23
24
|
Run `scripts/pincer-status.sh`. It lists every ticket's state, what is blocked, elapsed
|
|
24
25
|
build time from the clock, and the next action. If a ticket is `in_progress`, you are
|
|
25
26
|
resuming: read it, check `git status` / `git diff` for uncommitted work, and continue
|
|
26
|
-
from wherever the receipt says you are.
|
|
27
|
+
from wherever the receipt says you are. Do not ask the user to reconfirm unchanged,
|
|
28
|
+
previously authorized work.
|
|
27
29
|
|
|
28
30
|
## Loop (per ticket, in dependency order)
|
|
29
31
|
|
|
@@ -39,14 +41,17 @@ from wherever the receipt says you are. Confirm the starting point with the user
|
|
|
39
41
|
conventions, and nothing else.
|
|
40
42
|
3. **Verify:** `scripts/pincer-ticket.sh verify T-{NN}` — runs the Verification block and
|
|
41
43
|
writes the receipt only if it exits 0. Red → fix and re-run; report the actual output,
|
|
42
|
-
not assumptions. Green output is the definition of done, not your confidence.
|
|
44
|
+
not assumptions. Green output is the definition of done, not your confidence. If the
|
|
45
|
+
check only validated syntax or a build, say so — that is not behavioral proof. A
|
|
46
|
+
visual judgment is recorded separately in evaluation, not as the receipt, and a tool
|
|
47
|
+
the check needs but cannot run yields an explicit `unverified` result, never
|
|
48
|
+
fabricated output.
|
|
43
49
|
4. **Self-review the diff** before committing: silent failures (empty catches,
|
|
44
50
|
un-awaited promises), leftover debug code, drift from the ticket's acceptance criteria.
|
|
45
51
|
Then a security sweep of the same diff:
|
|
46
|
-
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
`.env.example` as a blocker.
|
|
52
|
+
- Check for secret-like assignments without printing values. If a scanner reports a
|
|
53
|
+
possible secret, report only its file and line until the value is safely redacted;
|
|
54
|
+
environment references and names in `.env.example` are allowed.
|
|
50
55
|
- External input touched by this diff is validated server-side, and untrusted
|
|
51
56
|
content (user input, LLM output) is escaped where rendered — per the
|
|
52
57
|
Security defaults in `AGENTS.md`.
|
|
@@ -55,14 +60,41 @@ from wherever the receipt says you are. Confirm the starting point with the user
|
|
|
55
60
|
5. **Close the ticket:** tick every verified acceptance-criteria checkbox (`- [ ]` → `- [x]`;
|
|
56
61
|
editing the checkboxes is allowed), then `scripts/pincer-ticket.sh done T-{NN}`. A
|
|
57
62
|
criterion that was cut is a scope change to record in the PRD, not a box to skip.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
`
|
|
63
|
+
Inspect `git status --short`, preserve pre-existing staged work, and stage only the
|
|
64
|
+
explicit paths changed for this ticket plus its ticket file. Review `git diff --cached`
|
|
65
|
+
before committing as `T-{NN}: {title}`.
|
|
66
|
+
6. Give a one-line progress update ("T-02 done, 3 remaining") and continue. Quote the
|
|
67
|
+
wall-clock elapsed figure from `scripts/pincer-status.sh` when it shows one — it
|
|
68
|
+
appears while a ticket is in progress or a budget is set, and it is not a measure
|
|
69
|
+
of active execution time.
|
|
61
70
|
|
|
62
|
-
##
|
|
71
|
+
## Recovering a ticket file
|
|
63
72
|
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
Ticket lifecycle fields are written only by `scripts/pincer-ticket.sh`, and the guard
|
|
74
|
+
also blocks shell restores that would touch ticket files from the assistant's shell:
|
|
75
|
+
`git checkout`/`git restore`/`git switch` naming a ticket path or a normalized
|
|
76
|
+
pathspec that cannot be shown to stay outside `tickets/` (the whole tree, `.`, `:/`,
|
|
77
|
+
`:(top)`, globs, absolute or unexpanded paths, `tickets/…` in any spelling, a
|
|
78
|
+
`-C tickets` prefix), force flags (`-f`, `--force`, `--discard-changes`,
|
|
79
|
+
`--pathspec-from-file`), `git reset --hard|--merge|--keep`, `git stash` (except
|
|
80
|
+
`list`, `show`, `create`, `store`), `git clean -f` without a narrow pathspec,
|
|
81
|
+
`git checkout-index -a` and `git read-tree -u|--reset`, including when wrapped in
|
|
82
|
+
`bash -c`, `eval`, `nice`, `time`, `nohup`, `timeout` or `xargs`. Restoring HEAD
|
|
83
|
+
would erase a newer failed attempt and revive an old passing receipt. Branch
|
|
84
|
+
switches and file-specific restores outside `tickets/` stay allowed. The guard is a
|
|
85
|
+
pattern-based safety net for documented mistake forms, not a complete shell
|
|
86
|
+
boundary; the receipt and status checks remain the source of trust. When a ticket file is malformed or its state was hand
|
|
87
|
+
edited, preserve the malformed contents as they are, report the validation error that
|
|
88
|
+
the script or `scripts/pincer-status.sh` printed, and hand the repair to the user, who
|
|
89
|
+
performs it in their own terminal. Then return through the lifecycle — `start`,
|
|
90
|
+
`verify`, `done` — so the ticket carries fresh verification; a restored receipt is
|
|
91
|
+
never evidence. Do not recommend restoring source files or unrelated edits as routine
|
|
92
|
+
ticket repair. Automated recovery that preserves attempt history is later work (M1).
|
|
93
|
+
|
|
94
|
+
## Budget rules
|
|
95
|
+
|
|
96
|
+
- If the user set `PINCER_BUILD_BUDGET_MIN` or stated another budget, use the elapsed
|
|
97
|
+
figure from `scripts/pincer-status.sh` rather than estimating. If the remaining tickets won't fit,
|
|
66
98
|
stop and propose a scope cut: which remaining tickets to drop or shrink. Cutting scope
|
|
67
99
|
deliberately beats an unfinished mess — record the cut in the PRD's Out of Scope.
|
|
68
100
|
- If a ticket reveals the plan was wrong, stop and say so rather than silently diverging.
|
|
@@ -70,5 +102,18 @@ from wherever the receipt says you are. Confirm the starting point with the user
|
|
|
70
102
|
|
|
71
103
|
## When all tickets are done
|
|
72
104
|
|
|
73
|
-
Update the PRD to `status: built
|
|
105
|
+
Update the PRD to `status: built` and commit that change on its own (`PRD vN: built`).
|
|
106
|
+
The built transition is part of the candidate that `$pincer-evaluate` reviews; it is
|
|
107
|
+
never moved into a later evidence-only commit. Then finish with:
|
|
74
108
|
"All tickets built. Run `$pincer-evaluate` for a final quality pass."
|
|
109
|
+
|
|
110
|
+
## Authorization rule (shared by plan, narrow, code and evaluate)
|
|
111
|
+
|
|
112
|
+
Reuse explicit authorization for the same scope and decisions; ask only about a
|
|
113
|
+
material choice not already authorized, and prepare the concrete proposal before
|
|
114
|
+
asking. A decision the user delegated (for example "pick the architecture") does not
|
|
115
|
+
need another approval when you exercise it, but a newly discovered consequential
|
|
116
|
+
choice is surfaced before implementation. Record the authorization basis and the
|
|
117
|
+
scope it covers in the PRD or the handover. An agent-written record or a status
|
|
118
|
+
field is not authenticated human approval. When resuming without the context that
|
|
119
|
+
granted authorization, do not invent it — ask.
|
|
@@ -12,23 +12,42 @@ run the pipeline, then present results.
|
|
|
12
12
|
|
|
13
13
|
## Steps
|
|
14
14
|
|
|
15
|
-
1. Run `scripts/pincer-status.sh`.
|
|
15
|
+
1. Run `scripts/pincer-status.sh`. Review only tickets associated with the selected
|
|
16
|
+
PRD. Every such ticket should be `done` with a current receipt; if one
|
|
16
17
|
is still open or in progress, stop and ask whether it was cut (then it goes in the
|
|
17
18
|
PRD's Out of Scope) or should be finished first via `$pincer-code`. Then get the full
|
|
18
|
-
diff of the
|
|
19
|
+
diff of the change: identify the actual base commit before this change from
|
|
20
|
+
its ticket commits and recorded context. If it cannot be established, resolve
|
|
21
|
+
that uncertainty before claiming a complete review. Record full commit IDs for
|
|
22
|
+
`base` and `candidate` (`git rev-parse HEAD`), then review `git diff <base>..<candidate>`.
|
|
23
|
+
The candidate is the clean, committed tree that already includes the implementation,
|
|
24
|
+
the ticket closures and the PRD `status: built` commit: `git status --short` must be
|
|
25
|
+
empty before review. If anything is uncommitted or the PRD is not yet built, return
|
|
26
|
+
to `$pincer-code`; do not review a dirty tree.
|
|
19
27
|
2. Dispatch a `code-quality-reviewer` agent with: the diff, the PRD's Success Criteria and
|
|
20
28
|
Scope sections, and the list of tickets. If the diff is large, split by area and
|
|
21
29
|
dispatch two in parallel. (No subagents on this platform? Review the diff yourself
|
|
22
30
|
in a separate pass, applying `.claude/agents/code-quality-reviewer.md` as the rubric.)
|
|
23
|
-
|
|
24
|
-
|
|
31
|
+
Keep the reviewer's report — or its explicit no-findings statement — for step 9,
|
|
32
|
+
where it is saved as an artifact; a review that left no record cannot be audited.
|
|
33
|
+
3. Yourself, in parallel, check spec compliance. For every requirement `R-NN` in the
|
|
34
|
+
PRD record one disposition: `delivered` (evidence on this candidate), `blocked`
|
|
35
|
+
(required behavior failed or was left unverified — this blocks PASS; do not relabel
|
|
36
|
+
it a known limitation to pass), or `deferred` (only with explicit user authorization;
|
|
37
|
+
record the scope decision in the PRD's Out of Scope and evaluate the revised
|
|
38
|
+
candidate). Compare what was built against every ticket's acceptance criteria and
|
|
39
|
+
the PRD scope, and list any gaps. Whether the requirement map is complete and each
|
|
40
|
+
check is semantically adequate is your judgment as reviewer — record that judgment
|
|
41
|
+
in the evaluation; the kit is not a mechanical traceability engine and does not
|
|
42
|
+
validate requirement-revision impact.
|
|
25
43
|
4. If the project has a UI, look at it — don't only read the code. Start it, open it in
|
|
26
44
|
the browser (screenshot via Chrome DevTools MCP if available), and check it against
|
|
27
45
|
the PRD's Visual Direction and Success Criteria. Note anything visibly broken or off.
|
|
28
|
-
5. Run a mechanical security audit
|
|
29
|
-
-
|
|
30
|
-
|
|
31
|
-
a secret committed then deleted is
|
|
46
|
+
5. Run a mechanical security audit:
|
|
47
|
+
- Inspect the relevant history with a secret scanner that redacts values, when one is
|
|
48
|
+
available. Otherwise review likely locations without copying candidate values into
|
|
49
|
+
output. Report file, line, and remediation only; a secret committed then deleted is
|
|
50
|
+
still leaked.
|
|
32
51
|
- `.gitignore` covers `.env*` (except `.env.example`), and `git ls-files | grep -i env`
|
|
33
52
|
shows only `.env.example`.
|
|
34
53
|
- `npm audit --omit=dev` (or the ecosystem's equivalent) — report high/critical only.
|
|
@@ -38,15 +57,66 @@ run the pipeline, then present results.
|
|
|
38
57
|
concrete bugs, silent failures, misleading code. Drop nitpicks and style opinions.
|
|
39
58
|
7. Present findings as a short list with `file:line` references, ordered by severity.
|
|
40
59
|
Security findings always rank above style-adjacent ones. For each, say whether you
|
|
41
|
-
recommend fixing now
|
|
42
|
-
8. Fix
|
|
43
|
-
|
|
44
|
-
|
|
60
|
+
recommend fixing now through a ticket or recording it as a known issue.
|
|
61
|
+
8. Fix findings clearly within the authorized PRD through a new ticket associated with
|
|
62
|
+
that PRD. Use `pincer-ticket.sh` to start, verify, and close it, then make a scoped
|
|
63
|
+
`T-{NN}: {title}` commit. Ask only when a fix changes scope, architecture, or another
|
|
64
|
+
material decision; never make an ad-hoc `review: fixes` commit. Every fix commit
|
|
65
|
+
produces a new candidate: re-record `candidate`, re-run the checks against it, and
|
|
66
|
+
write fresh evidence in step 9 — never reuse a manifest from a previous candidate.
|
|
67
|
+
9. Persist evidence for the candidate under `.prd/evidence/prd-vN/<candidate>/`:
|
|
68
|
+
- `checks/C-NN.log` — the command and a redacted summary or safe log of each
|
|
69
|
+
executable check. Never secrets, never an environment dump.
|
|
70
|
+
- `visual/<scenario>.png` — each visual capture from step 4, with its scenario,
|
|
71
|
+
viewport and observed result recorded in the manifest. When nothing renders,
|
|
72
|
+
record `visual_review: {applicable: false, reason}` and say why.
|
|
73
|
+
- `review/code-quality.md` — the reviewer's findings from step 2 with their
|
|
74
|
+
dispositions, or its explicit no-findings statement, recorded as a check of
|
|
75
|
+
kind `review` and referenced by the requirements it covers.
|
|
76
|
+
- `manifest.json` — evidence schema 1 (field list in the header of
|
|
77
|
+
`scripts/pincer-evidence.cjs`): selected PRD, full `base` and `candidate` IDs,
|
|
78
|
+
`created`, `environment` with tool limitations, `coverage_review` (your judgment
|
|
79
|
+
from step 3), one `requirements` entry per `R-NN` with its disposition, tickets
|
|
80
|
+
and check IDs, one `checks` entry per check with `kind`, `required`, `result`,
|
|
81
|
+
`command`, timestamp and artifact paths, and `artifacts` with digests from
|
|
82
|
+
`node scripts/pincer-evidence.cjs digest <file>...`.
|
|
83
|
+
A tool you cannot run yields a check with `result: unverified` and a note — never a
|
|
84
|
+
fabricated artifact. A deferred requirement carries `authorized_by` naming the
|
|
85
|
+
user's explicit authorization. Then run
|
|
86
|
+
`node scripts/pincer-evidence.cjs validate .prd/evidence/prd-vN/<candidate>/manifest.json --candidate <candidate> --prd .prd/prd-vN.md`
|
|
87
|
+
and correct the manifest until it prints `ok`; the same validator runs in status
|
|
88
|
+
and release. It checks the record's consistency, not that the commands ran.
|
|
89
|
+
10. Close out: write a brief `NOTES.md` at the repo root with frontmatter:
|
|
90
|
+
```yaml
|
|
91
|
+
---
|
|
92
|
+
prd: .prd/prd-vN.md
|
|
93
|
+
base: <full reviewed base commit ID>
|
|
94
|
+
candidate: <full reviewed candidate commit ID>
|
|
95
|
+
evidence: .prd/evidence/prd-vN/<candidate>/manifest.json
|
|
96
|
+
---
|
|
97
|
+
```
|
|
98
|
+
Then commit NOTES.md, the manifest and its listed artifacts — and nothing else —
|
|
99
|
+
as `evaluate: PRD vN candidate <short sha>`. Status accepts this later commit only
|
|
100
|
+
when its diff from the candidate is limited to `NOTES.md` and the evidence files
|
|
101
|
+
the manifest lists; changes to source, tests, configuration, tickets, the PRD or
|
|
102
|
+
other evaluations require reevaluation. Legacy notes without these references
|
|
103
|
+
do not establish readiness. Then describe what was built, what was cut
|
|
45
104
|
and why, known issues, and what you'd do next with more time. Then a **Handover**
|
|
46
105
|
section, written for the stranger who inherits this repo in six months: how to get
|
|
47
106
|
oriented (which file to read first), what each dependency is for and why it earned
|
|
48
107
|
its place, and what breaks first as the code ages (the riskiest assumption, the
|
|
49
|
-
least-tested path).
|
|
50
|
-
|
|
51
|
-
|
|
108
|
+
least-tested path). This is the first document a reviewer of this repo should read;
|
|
109
|
+
summarize the requirement dispositions from the manifest in it.
|
|
110
|
+
11. Suggest `$pincer-release` as the final step: "Run `$pincer-release` for a pass/fail audit of the
|
|
52
111
|
whole workflow's artifacts."
|
|
112
|
+
|
|
113
|
+
## Authorization rule (shared by plan, narrow, code and evaluate)
|
|
114
|
+
|
|
115
|
+
Reuse explicit authorization for the same scope and decisions; ask only about a
|
|
116
|
+
material choice not already authorized, and prepare the concrete proposal before
|
|
117
|
+
asking. A decision the user delegated (for example "pick the architecture") does not
|
|
118
|
+
need another approval when you exercise it, but a newly discovered consequential
|
|
119
|
+
choice is surfaced before implementation. Record the authorization basis and the
|
|
120
|
+
scope it covers in the PRD or the handover. An agent-written record or a status
|
|
121
|
+
field is not authenticated human approval. When resuming without the context that
|
|
122
|
+
granted authorization, do not invent it — ask.
|
|
@@ -7,47 +7,92 @@ description: "Turn the approved PRD into local, AI-ready ticket files"
|
|
|
7
7
|
|
|
8
8
|
# $pincer-narrow — PRD to Local Tickets
|
|
9
9
|
|
|
10
|
-
You are decomposing the PRD into
|
|
11
|
-
markdown files (no external tracker needed).
|
|
12
|
-
|
|
10
|
+
You are decomposing the PRD into coherent, independently verifiable tickets stored as
|
|
11
|
+
local markdown files (no external tracker needed). Ticket count and size follow the
|
|
12
|
+
change's dependencies and risk, plus any budget the user supplied. There is no hard
|
|
13
|
+
one-to-two-ticket cap for small PRDs and no default timebox: cohesion and dependencies
|
|
14
|
+
decide the count. A breakdown that follows the PRD needs no second approval; a newly
|
|
15
|
+
discovered consequential choice is surfaced before implementation.
|
|
13
16
|
|
|
14
|
-
**Initial request:** the text that follows the `$pincer-narrow` mention
|
|
17
|
+
**Initial request:** the text that follows the `$pincer-narrow` mention, if any (when omitted, use the playbook's documented default)
|
|
15
18
|
|
|
16
19
|
## Steps
|
|
17
20
|
|
|
18
|
-
1. Run `scripts/pincer-status.sh`. If tickets already exist
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
1. Run `scripts/pincer-status.sh`. If tickets already exist for another PRD, leave them
|
|
22
|
+
as history. New tickets continue the numbering and existing ones are never renumbered.
|
|
23
|
+
If tickets already exist for this PRD, extend them only when the current request already
|
|
24
|
+
authorizes that work; otherwise present the concrete addition before asking. Then read the
|
|
25
|
+
PRD (`the text that follows the `$pincer-narrow` mention, if any (when omitted, use the playbook's documented default)` or the latest `.prd/prd-v*.md`). If its status isn't `draft`, ask
|
|
21
26
|
which PRD to use.
|
|
22
27
|
2. Decompose into tickets. Rules:
|
|
23
|
-
- Each ticket is one coherent unit
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
- Each ticket is one coherent unit. Use S, M, or L as relative scope indicators and
|
|
29
|
+
split work when that improves dependency order, verification, or ownership.
|
|
30
|
+
- For greenfield work, use a walking skeleton when it reduces integration risk. For
|
|
31
|
+
brownfield work, begin with the smallest protected vertical change; add a
|
|
32
|
+
characterization ticket before changing load-bearing code that lacks coverage.
|
|
26
33
|
- Order by dependency; note blockers explicitly ("depends on T-01").
|
|
34
|
+
- Build the requirement map: for every `R-NN` in the PRD and each of its
|
|
35
|
+
scenarios, name the ticket that owns the implementation and the executable
|
|
36
|
+
check that exercises it, or an explicit review method when no executable check
|
|
37
|
+
exists. Record the IDs in each ticket's Context as `Implements: R-NN, R-MM`.
|
|
38
|
+
Enabling work that implements no requirement states its purpose in the ticket
|
|
39
|
+
Objective. Resolve missing coverage and conflicting criteria with the user
|
|
40
|
+
before implementation; do not start with an unmapped required scenario.
|
|
27
41
|
- Every ticket gets a runnable command in its Verification block — a fenced `bash`
|
|
28
42
|
block that exits 0 only when the ticket is done. `scripts/pincer-ticket.sh verify`
|
|
29
43
|
runs it verbatim and stamps the receipt that `done` requires, so it must be
|
|
30
44
|
non-interactive and self-contained (no "check by hand").
|
|
45
|
+
- Each Verification section opens with `Proves:` — what the check establishes and
|
|
46
|
+
which regression it detects. A check for an executable change must exercise
|
|
47
|
+
observable behavior (including relevant rejection paths and, in brownfield work,
|
|
48
|
+
preservation of existing behavior) and fail when the behavior is wrong, not only
|
|
49
|
+
when an identifier is renamed. Reuse adequate focused tests. A build, a syntax
|
|
50
|
+
check, or an identifier grep alone is not proof; static assertions are primary
|
|
51
|
+
evidence only for static contracts such as generated files, and `Proves:` says so.
|
|
52
|
+
- Adequacy is a judgment about what the command observes, never a word match: do
|
|
53
|
+
not call a command sufficient because it contains `grep`, `test`, or a runner
|
|
54
|
+
name, nor insufficient for lacking them. Manual visual judgment is recorded
|
|
55
|
+
separately in evaluation; a tool the check needs but cannot run yields an
|
|
56
|
+
explicit `unverified` result, never fabricated evidence or a silent waiver.
|
|
31
57
|
- If the brief or stack implies automated tests, at least one ticket's verification
|
|
32
58
|
command must be the test runner (e.g. `npm test`) — manual checks alone don't count.
|
|
33
59
|
- Any ticket whose surface accepts external input (HTTP endpoint, form, file,
|
|
34
60
|
LLM output) gets an acceptance criterion for the reject path — what invalid
|
|
35
61
|
input produces (e.g. "empty goal → 400 with a clear message"), not only the
|
|
36
62
|
happy path.
|
|
37
|
-
-
|
|
38
|
-
`.env.example`) and an `.env.example` naming any required secrets
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
is preceded by a characterization ticket — a test that pins the current
|
|
42
|
-
behavior before any ticket is allowed to change it.
|
|
63
|
+
- A greenfield setup ticket includes `.gitignore` covering `.env*` (except
|
|
64
|
+
`.env.example`) and an `.env.example` naming any required secrets before any
|
|
65
|
+
secret can exist in the repo. In brownfield repositories, preserve and verify
|
|
66
|
+
the existing ignore and environment conventions.
|
|
43
67
|
3. Write each ticket to `tickets/T-{NN}-{slug}.md` using
|
|
44
|
-
`.claude/references/ticket-template.md`, with `status: open
|
|
45
|
-
|
|
68
|
+
`.claude/references/ticket-template.md`, with `status: open` and an explicit
|
|
69
|
+
`prd: .prd/prd-vN.md` naming the selected PRD. Never infer this association from
|
|
70
|
+
numbering or old notes. The other state fields
|
|
71
|
+
(`started`, `last_check`, `verified`, `finished`) are added later by `scripts/pincer-ticket.sh` —
|
|
46
72
|
never write them yourself.
|
|
47
|
-
4. Present the ticket list (number, title, size, dependencies) as a table
|
|
73
|
+
4. Present the ticket list (number, title, size, dependencies) as a table, followed
|
|
74
|
+
by the requirement map as a second table (requirement · scenario · ticket · check
|
|
75
|
+
or review method). Whether the map is complete and each check is adequate is your
|
|
76
|
+
judgment as the author; say so rather than presenting the table as mechanical proof.
|
|
48
77
|
|
|
49
|
-
|
|
78
|
+
Present the concrete breakdown and build order as a report, not a question. Reuse
|
|
79
|
+
existing authorization for the same scope and order.
|
|
50
80
|
|
|
51
|
-
5.
|
|
52
|
-
|
|
81
|
+
5. Finalize. A breakdown that follows the PRD is already authorized by the PRD: do not
|
|
82
|
+
ask whether to proceed. Update the selected PRD frontmatter to `status: ticketed`,
|
|
83
|
+
inspect existing staged changes, stage that PRD and the explicit new ticket paths,
|
|
84
|
+
review `git diff --cached`, and commit only those paths. Ask first — and finalize
|
|
85
|
+
once it is resolved — only when step 4 surfaced a newly discovered consequential
|
|
86
|
+
choice or a scope change the PRD does not cover. Finish with:
|
|
53
87
|
"Tickets ready in `tickets/`. Run `$pincer-code` to start implementing."
|
|
88
|
+
|
|
89
|
+
## Authorization rule (shared by plan, narrow, code and evaluate)
|
|
90
|
+
|
|
91
|
+
Reuse explicit authorization for the same scope and decisions; ask only about a
|
|
92
|
+
material choice not already authorized, and prepare the concrete proposal before
|
|
93
|
+
asking. A decision the user delegated (for example "pick the architecture") does not
|
|
94
|
+
need another approval when you exercise it, but a newly discovered consequential
|
|
95
|
+
choice is surfaced before implementation. Record the authorization basis and the
|
|
96
|
+
scope it covers in the PRD or the handover. An agent-written record or a status
|
|
97
|
+
field is not authenticated human approval. When resuming without the context that
|
|
98
|
+
granted authorization, do not invent it — ask.
|