pincer-workflow 0.1.0 → 0.2.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.
Files changed (30) hide show
  1. package/README.md +79 -13
  2. package/bin/pincer.js +11 -5
  3. package/package.json +3 -3
  4. package/template/.claude/commands/pincer-code.md +35 -16
  5. package/template/.claude/commands/pincer-evaluate.md +4 -1
  6. package/template/.claude/commands/pincer-narrow.md +11 -4
  7. package/template/.claude/commands/pincer-plan.md +4 -0
  8. package/template/.claude/commands/pincer-release.md +7 -3
  9. package/template/.claude/commands/pincer-status.md +21 -0
  10. package/template/.claude/hooks/ticket-guard.sh +66 -0
  11. package/template/.claude/references/ticket-template.md +7 -1
  12. package/template/.claude/settings.json +9 -0
  13. package/template/.codex/README.md +18 -3
  14. package/template/.codex/prompts/pincer-code.md +35 -16
  15. package/template/.codex/prompts/pincer-evaluate.md +4 -1
  16. package/template/.codex/prompts/pincer-narrow.md +11 -4
  17. package/template/.codex/prompts/pincer-plan.md +4 -0
  18. package/template/.codex/prompts/pincer-release.md +7 -3
  19. package/template/.codex/prompts/pincer-status.md +19 -0
  20. package/template/.github/prompts/pincer-code.prompt.md +35 -16
  21. package/template/.github/prompts/pincer-evaluate.prompt.md +4 -1
  22. package/template/.github/prompts/pincer-narrow.prompt.md +11 -4
  23. package/template/.github/prompts/pincer-plan.prompt.md +4 -0
  24. package/template/.github/prompts/pincer-release.prompt.md +7 -3
  25. package/template/.github/prompts/pincer-status.prompt.md +23 -0
  26. package/template/AGENTS.md +8 -1
  27. package/template/docs/dry-run-checklist.md +9 -3
  28. package/template/scripts/pincer-status.sh +111 -0
  29. package/template/scripts/pincer-ticket.sh +134 -0
  30. package/template/scripts/sync-prompts.sh +2 -1
package/README.md CHANGED
@@ -7,6 +7,10 @@ GitHub Copilot. One idea goes from brief to reviewed, verified code through five
7
7
  commands, and every step leaves an auditable artifact: a PRD, tickets, one
8
8
  commit per ticket, review notes, and a pass/fail release audit.
9
9
 
10
+ **Website:** [orchestratedbyalex.github.io/pincer-workflow](https://orchestratedbyalex.github.io/pincer-workflow/) —
11
+ a twelve-sheet walkthrough of the workflow: why it exists, each command, the
12
+ safety guardrails, and installation.
13
+
10
14
  ## Install
11
15
 
12
16
  ```bash
@@ -14,20 +18,75 @@ cd your-project
14
18
  npx pincer-workflow init # asks which platform(s) you use
15
19
  ```
16
20
 
17
- Then follow the chain — identical on every platform:
21
+ Then follow the chain — the same five steps on every platform:
18
22
 
19
23
  ```
20
24
  /pincer-plan <brief> → /pincer-narrow → /pincer-code → /pincer-evaluate → /pincer-release
21
25
  ```
22
26
 
23
- Per-platform notes printed by `init`:
27
+ `/pincer-status` shows where the workflow stands at any point (PRD, tickets,
28
+ receipts, elapsed time, next command) — run it first in a new session.
29
+
30
+ On Codex CLI the same commands carry the `/prompts:` prefix Codex gives every
31
+ custom prompt: `/prompts:pincer-plan <brief>` → `/prompts:pincer-narrow` →
32
+ `/prompts:pincer-code` → `/prompts:pincer-evaluate` → `/prompts:pincer-release`,
33
+ and `/prompts:pincer-status`.
34
+
35
+ ### Claude Code
36
+
37
+ Works immediately: the commands, the two subagents, the `.env` deny rules and
38
+ the destructive-command and ticket-guard hooks install to `.claude/`. Start
39
+ `claude` in the repo and run `/pincer-plan <brief>`.
40
+
41
+ ### Codex CLI
42
+
43
+ `AGENTS.md` loads natively, so the rules apply with no setup. Codex reads
44
+ custom prompts only from `~/.codex/prompts/` (there is no repo-local location)
45
+ and does not create that directory itself, so install the prompts once:
46
+
47
+ ```bash
48
+ mkdir -p ~/.codex/prompts && cp .codex/prompts/*.md ~/.codex/prompts/
49
+ codex
50
+ ```
51
+
52
+ Then `/prompts:pincer-plan <brief>` and the rest of the chain are available in
53
+ every Codex session. The playbooks refer to each other by their short names
54
+ (`/pincer-narrow` etc.) — read those as `/prompts:pincer-narrow` in Codex.
55
+ Re-run the copy after `npx pincer-workflow@latest update` or after editing a
56
+ playbook; `update` prints the reminder.
57
+
58
+ Codex has no PreToolUse hooks, so the guardrail posture lives in
59
+ `~/.codex/config.toml` instead — `approval_policy = "on-request"` and
60
+ `sandbox_mode = "workspace-write"`; never run with approvals disabled. The
61
+ ticket scripts (`scripts/pincer-ticket.sh`, `scripts/pincer-status.sh`) are
62
+ plain bash and work unchanged; the rule in `AGENTS.md` against hand-editing
63
+ ticket state carries the weight the hook carries on Claude Code, and
64
+ `/prompts:pincer-status` flags any ticket marked done without a receipt.
65
+ Full notes in `.codex/README.md`.
66
+
67
+ ### GitHub Copilot (VS Code)
68
+
69
+ Enable `"chat.promptFiles": true` in VS Code settings, then run `/pincer-plan`
70
+ in chat. `.github/copilot-instructions.md` is wired to `AGENTS.md`.
71
+
72
+ ## Claude Code plugin (alternative)
73
+
74
+ Claude users can install PINCER as a plugin instead — commands arrive
75
+ namespaced (`/pincer:plan` … `/pincer:release`) and update automatically
76
+ through the marketplace:
77
+
78
+ ```
79
+ /plugin marketplace add orchestratedbyalex/pincer-workflow
80
+ /plugin install pincer@pincer-workflow
81
+ ```
24
82
 
25
- - **Claude Code** works immediately; commands, subagents, permission deny
26
- rules, and a destructive-command hook install to `.claude/`.
27
- - **Codex CLI** rules load natively from `AGENTS.md`; copy the prompts once:
28
- `cp .codex/prompts/*.md ~/.codex/prompts/` (posture notes in `.codex/README.md`).
29
- - **Copilot (VS Code)** enable `"chat.promptFiles": true`, then run
30
- `/pincer-plan` in chat; `.github/copilot-instructions.md` is wired to `AGENTS.md`.
83
+ Pick one channel per project: the plugin makes the commands, subagents, and
84
+ the guardrail hook available everywhere, while `npx pincer-workflow init`
85
+ installs everything project-locally *plus* the repo-side files (`AGENTS.md`
86
+ rules, `.env` deny rules in `.claude/settings.json`, the adapters for Codex
87
+ and Copilot). Installing both gives you duplicate commands. Plugin users who
88
+ want the repo-side rules too can copy `AGENTS.md` from the
89
+ [template](template/AGENTS.md).
31
90
 
32
91
  ## Update
33
92
 
@@ -35,7 +94,7 @@ Per-platform notes printed by `init`:
35
94
  npx pincer-workflow@latest update
36
95
  ```
37
96
 
38
- Files you never touched are refreshed in place. Files you edited are left
97
+ 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.1 corrects the Codex install step: `~/.codex/prompts/` must be created first, and the commands are invoked as `/prompts:pincer-*`.) Files you edited are left
39
98
  alone — the new version lands next to them as `<file>.new` for a manual merge.
40
99
  `npx pincer-workflow doctor` checks the health of an install (hook executable,
41
100
  `.gitignore` covering `.env*`, no unmerged `*.new` files, version current).
@@ -45,11 +104,14 @@ alone — the new version lands next to them as `<file>.new` for a manual merge.
45
104
  | Piece | Purpose |
46
105
  | --- | --- |
47
106
  | `AGENTS.md` | Project rules, single cross-platform source (workflow order, security defaults, secrets, untrusted-content and dependency rules) |
48
- | `.claude/commands/` | The five playbooks (canonical — adapters are generated from them) |
107
+ | `.claude/commands/` | The five playbooks plus `/pincer-status` (canonical — adapters are generated from them) |
49
108
  | `.claude/agents/` | `codebase-explorer` and `code-quality-reviewer` subagents, with inline fallbacks for platforms without subagents |
50
- | `.claude/hooks/` + `settings.json` | Mechanical guardrails: `.env` files unreadable, destructive commands blocked |
51
- | `.codex/` · `.github/` | Generated Codex and Copilot adapters + platform wiring |
109
+ | `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) |
110
+ | `scripts/pincer-status.sh` | Read-only state report: PRD, every ticket with clock-based elapsed time, blocked tickets, build time vs budget, next command |
111
+ | `.claude/hooks/` + `settings.json` | Mechanical guardrails: `.env` files unreadable, destructive commands blocked, ticket state fields writable only through the script |
112
+ | `.codex/` · `.github/` | Generated Codex and Copilot adapters + platform wiring (`.codex/README.md` covers the Codex install and posture) |
52
113
  | `scripts/sync-prompts.sh` | Regenerates the adapters after you edit a playbook |
114
+ | `scripts/build-plugin.sh` | Regenerates the Claude Code plugin (`plugin/`) from the template |
53
115
  | `docs/dry-run-checklist.md` | The workflow's own test — audited by `/pincer-release` |
54
116
 
55
117
  ## Design principles
@@ -58,7 +120,11 @@ alone — the new version lands next to them as `<file>.new` for a manual merge.
58
120
  every scope, and every merge; autonomy runs only between gates, bounded by a
59
121
  timebox and one revision loop.
60
122
  - **Nothing is done while its verification fails** — every ticket carries a
61
- runnable check.
123
+ runnable check, and "done" is a state only a passing run of that check can
124
+ unlock: the receipt is stamped by the script, never typed by the agent.
125
+ - **State lives in files, not in the conversation** — a new session runs
126
+ `/pincer-status` and knows exactly where to resume; elapsed time comes from
127
+ timestamps, not from the model's sense of time.
62
128
  - **Scope is a first-class artifact** — cuts are recorded, never silent.
63
129
  - **Security is threaded through every stage** — designed in at Plan, specified
64
130
  as reject-path criteria at Narrow, enforced by a pre-commit sweep at Code,
package/bin/pincer.js CHANGED
@@ -21,12 +21,12 @@ const VERSION = createRequire(import.meta.url)('../package.json').version;
21
21
  const MANIFEST = '.pincer.json';
22
22
 
23
23
  const PLATFORM_ROOTS = {
24
- common: ['AGENTS.md', 'docs/dry-run-checklist.md', 'scripts/sync-prompts.sh'],
24
+ common: ['AGENTS.md', 'docs/dry-run-checklist.md', 'scripts/sync-prompts.sh', 'scripts/pincer-ticket.sh', 'scripts/pincer-status.sh'],
25
25
  claude: ['CLAUDE.md', '.claude'],
26
26
  codex: ['.codex'],
27
27
  copilot: ['.github'],
28
28
  };
29
- const EXECUTABLES = ['scripts/sync-prompts.sh', '.claude/hooks/block-dangerous.sh'];
29
+ const EXECUTABLES = ['scripts/sync-prompts.sh', 'scripts/pincer-ticket.sh', 'scripts/pincer-status.sh', '.claude/hooks/block-dangerous.sh', '.claude/hooks/ticket-guard.sh'];
30
30
  const GITIGNORE_LINES = ['.env', '.env.*', '!.env.example'];
31
31
 
32
32
  const sha = (buf) => crypto.createHash('sha256').update(buf).digest('hex');
@@ -123,18 +123,24 @@ function report({ written, skipped, conflicted }) {
123
123
  }
124
124
  }
125
125
 
126
+ // Codex loads custom prompts from ~/.codex/prompts/ only and never creates that
127
+ // directory itself — a bare `cp` there fails with "Not a directory" on a fresh machine.
128
+ const CODEX_INSTALL = 'mkdir -p ~/.codex/prompts && cp .codex/prompts/*.md ~/.codex/prompts/';
129
+
126
130
  function nextSteps(platforms) {
127
131
  console.log('\nNext steps:');
128
132
  if (platforms.includes('claude')) {
129
133
  console.log(' Claude Code start `claude` in this repo and run /pincer-plan <brief>');
130
134
  }
131
135
  if (platforms.includes('codex')) {
132
- console.log(' Codex CLI cp .codex/prompts/*.md ~/.codex/prompts/ (once; posture notes in .codex/README.md)');
136
+ console.log(` Codex CLI ${CODEX_INSTALL}`);
137
+ console.log(' (once; Codex has no repo-local prompts) then `codex` and /prompts:pincer-plan <brief> — posture notes in .codex/README.md');
133
138
  }
134
139
  if (platforms.includes('copilot')) {
135
140
  console.log(' Copilot enable "chat.promptFiles": true in VS Code settings, then /pincer-plan in chat');
136
141
  }
137
142
  console.log(' All rules live in AGENTS.md — fill in its Conventions section once you know the stack.');
143
+ console.log(' Any session scripts/pincer-status.sh shows where the workflow stands (also /pincer-status)');
138
144
  }
139
145
 
140
146
  async function askPlatforms() {
@@ -180,7 +186,7 @@ async function cmdUpdate() {
180
186
  console.log(`\nUpdating PINCER ${manifest.version} -> ${VERSION} for: ${manifest.platforms.join(', ')}\n`);
181
187
  report(install(dir, manifest.platforms, manifest.files));
182
188
  if (manifest.platforms.includes('codex')) {
183
- console.log('\n Codex uses global prompts — re-run: cp .codex/prompts/*.md ~/.codex/prompts/');
189
+ console.log(`\n Codex uses global prompts — re-run: ${CODEX_INSTALL}`);
184
190
  }
185
191
  }
186
192
 
@@ -237,6 +243,6 @@ Usage:
237
243
  npx pincer-workflow update
238
244
  npx pincer-workflow doctor
239
245
 
240
- Docs: https://github.com/lexanderg/pincer-workflow`);
246
+ Docs: https://github.com/orchestratedbyalex/pincer-workflow`);
241
247
  process.exit(cmd ? 1 : 0);
242
248
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pincer-workflow",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
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"
17
+ "test": "node test/smoke.test.js && node test/ticket.test.js"
18
18
  },
19
19
  "keywords": [
20
20
  "claude-code",
@@ -30,6 +30,6 @@
30
30
  "license": "MIT",
31
31
  "repository": {
32
32
  "type": "git",
33
- "url": "git+https://github.com/lexanderg/pincer-workflow.git"
33
+ "url": "git+https://github.com/orchestratedbyalex/pincer-workflow.git"
34
34
  }
35
35
  }
@@ -10,40 +10,59 @@ user confirms the starting point, run continuously and report progress between t
10
10
 
11
11
  **Initial request:** $ARGUMENTS
12
12
 
13
+ Ticket state lives in the ticket file's frontmatter and is written **only** by
14
+ `scripts/pincer-ticket.sh` (`start` → `verify` → `done`). `verify` runs the ticket's
15
+ Verification block and stamps a receipt only on a green exit; `done` refuses without a
16
+ receipt that matches the current check, or with unticked acceptance criteria. Never edit
17
+ `status`, `started`, `verified`, or `finished` by hand — on Claude Code a hook blocks it.
18
+
19
+ ## Before the loop
20
+
21
+ Run `scripts/pincer-status.sh`. It lists every ticket's state, what is blocked, elapsed
22
+ build time from the clock, and the next action. If a ticket is `in_progress`, you are
23
+ resuming: read it, check `git status` / `git diff` for uncommitted work, and continue
24
+ from wherever the receipt says you are. Confirm the starting point with the user, then go.
25
+
13
26
  ## Loop (per ticket, in dependency order)
14
27
 
15
- 1. **Read the ticket** and the files it references. Announce: "Starting T-{NN}: {title}."
28
+ 1. **Start:** `scripts/pincer-ticket.sh start T-{NN}` — refuses while a `depends_on` ticket
29
+ isn't done, and stamps the start time. Read the ticket and the files it references.
30
+ Announce: "Starting T-{NN}: {title}."
16
31
  2. **Implement.** Follow the conventions in `CLAUDE.md` and the PRD's architecture and
17
32
  visual direction. Installing a dependency not named in the PRD's architecture is a
18
33
  stop-and-ask: verify it's the real package on the registry (linked repo, downloads —
19
- hallucinated names get typosquatted), say why it earns its place, and wait for a yes. For an S ticket, implement directly. For an M ticket touching
20
- isolated files, you may dispatch a subagent with a clean prompt: paste the full ticket
21
- body, the relevant conventions, and nothing else.
22
- 3. **Verify.** Run the ticket's verification command(s). If they fail, fix before moving
23
- on never mark a ticket done on a red check. Report actual output, not assumptions.
34
+ hallucinated names get typosquatted), say why it earns its place, and wait for a yes.
35
+ For an S ticket, implement directly. For an M ticket touching isolated files, you may
36
+ dispatch a subagent with a clean prompt: paste the full ticket body, the relevant
37
+ conventions, and nothing else.
38
+ 3. **Verify:** `scripts/pincer-ticket.sh verify T-{NN}` runs the Verification block and
39
+ writes the receipt only if it exits 0. Red → fix and re-run; report the actual output,
40
+ not assumptions. Green output is the definition of done, not your confidence.
24
41
  4. **Self-review the diff** before committing: silent failures (empty catches,
25
42
  un-awaited promises), leftover debug code, drift from the ticket's acceptance criteria.
26
43
  Then a security sweep of the same diff:
27
44
  - No secret values: run
28
- `git diff --cached | grep -iE '(api[_-]?key|secret|token|password)[[:space:]]*[:=]'`
45
+ `git diff | grep -iE '(api[_-]?key|secret|token|password)[[:space:]]*[:=]'`
29
46
  and treat any hit that isn't a `process.env` reference or a name in
30
47
  `.env.example` as a blocker.
31
48
  - External input touched by this diff is validated server-side, and untrusted
32
49
  content (user input, LLM output) is escaped where rendered — per the
33
50
  Security defaults in `CLAUDE.md`.
34
51
  - No error path leaks internals (stack traces, key names with values) to the client.
35
- 5. **Commit** with message `T-{NN}: {title}` and mark the ticket done: set
36
- `status: done` in its frontmatter and tick every verified acceptance-criteria
37
- checkbox (`- [ ]` `- [x]`) in the same edit. A ticket is never `done` with
38
- unticked criteria — if a criterion was cut, that's a scope change to record,
39
- not a box to skip.
40
- 6. Give a one-line progress update ("T-02 done, 3 remaining, ~40 min elapsed") and continue.
52
+ If the review changed code, run `verify` again the receipt must match the code you commit.
53
+ 5. **Close the ticket:** tick every verified acceptance-criteria checkbox (`- [ ]` → `- [x]`;
54
+ editing the checkboxes is allowed), then `scripts/pincer-ticket.sh done T-{NN}`. A
55
+ criterion that was cut is a scope change to record in the PRD, not a box to skip.
56
+ Commit code and ticket file together: `git add -A && git commit -m "T-{NN}: {title}"`.
57
+ 6. Give a one-line progress update using the elapsed figure from
58
+ `scripts/pincer-status.sh` ("T-02 done, 3 remaining, 38m elapsed of 75m") and continue.
41
59
 
42
60
  ## Timebox rules
43
61
 
44
- - Track elapsed time against the ~75-minute build budget. If you're at risk of running
45
- out, stop and propose a scope cut: which remaining tickets to drop or shrink. Cutting
46
- scope deliberately beats an unfinished mess record the cut in the PRD's Out of Scope.
62
+ - The budget is ~75 minutes of build time, measured by `scripts/pincer-status.sh` from
63
+ the first ticket's start stamp never estimated. If the remaining tickets won't fit,
64
+ stop and propose a scope cut: which remaining tickets to drop or shrink. Cutting scope
65
+ deliberately beats an unfinished mess — record the cut in the PRD's Out of Scope.
47
66
  - If a ticket reveals the plan was wrong, stop and say so rather than silently diverging.
48
67
  Update the ticket/PRD, then continue.
49
68
 
@@ -10,7 +10,10 @@ run the pipeline, then present results.
10
10
 
11
11
  ## Steps
12
12
 
13
- 1. Get the full diff of the session: `git log --oneline` and `git diff <first-commit>..HEAD`.
13
+ 1. Run `scripts/pincer-status.sh`. Every ticket should be `done` with a receipt; if one
14
+ is still open or in progress, stop and ask whether it was cut (then it goes in the
15
+ PRD's Out of Scope) or should be finished first via `/pincer-code`. Then get the full
16
+ diff of the session: `git log --oneline` and `git diff <first-commit>..HEAD`.
14
17
  2. Dispatch a `code-quality-reviewer` agent with: the diff, the PRD's Success Criteria and
15
18
  Scope sections, and the list of tickets. If the diff is large, split by area and
16
19
  dispatch two in parallel. (No subagents on this platform? Review the diff yourself
@@ -13,14 +13,19 @@ build window.
13
13
 
14
14
  ## Steps
15
15
 
16
- 1. Read the PRD (`$ARGUMENTS` or the latest `.prd/prd-v*.md`). If its status isn't
17
- `draft`, ask which PRD to use.
16
+ 1. Run `scripts/pincer-status.sh`. If tickets already exist, ask before adding to them
17
+ new tickets continue the numbering, existing ones are never renumbered. Then read the
18
+ PRD (`$ARGUMENTS` or the latest `.prd/prd-v*.md`). If its status isn't `draft`, ask
19
+ which PRD to use.
18
20
  2. Decompose into tickets. Rules:
19
21
  - Each ticket is one coherent unit: sized S or M, never L. Split anything larger.
20
22
  - Ticket 1 is always the walking skeleton: project scaffold + a thin end-to-end slice
21
23
  that runs. Everything after builds on a working base.
22
24
  - Order by dependency; note blockers explicitly ("depends on T-01").
23
- - Every ticket gets a verification command or check the builder can actually run.
25
+ - Every ticket gets a runnable command in its Verification block a fenced `bash`
26
+ block that exits 0 only when the ticket is done. `scripts/pincer-ticket.sh verify`
27
+ runs it verbatim and stamps the receipt that `done` requires, so it must be
28
+ non-interactive and self-contained (no "check by hand").
24
29
  - If the brief or stack implies automated tests, at least one ticket's verification
25
30
  command must be the test runner (e.g. `npm test`) — manual checks alone don't count.
26
31
  - Any ticket whose surface accepts external input (HTTP endpoint, form, file,
@@ -34,7 +39,9 @@ build window.
34
39
  is preceded by a characterization ticket — a test that pins the current
35
40
  behavior before any ticket is allowed to change it.
36
41
  3. Write each ticket to `tickets/T-{NN}-{slug}.md` using
37
- `.claude/references/ticket-template.md`.
42
+ `.claude/references/ticket-template.md`, with `status: open`. The other state fields
43
+ (`started`, `verified`, `finished`) are added later by `scripts/pincer-ticket.sh` —
44
+ never write them yourself.
38
45
  4. Present the ticket list (number, title, size, dependencies) as a table.
39
46
 
40
47
  **Gate (medium):** Ask for approval of the breakdown and build order. Adjust if pushed back.
@@ -11,6 +11,10 @@ timebox (~2 hours total), so discovery is brief and the PRD is lean. The PRD fee
11
11
 
12
12
  **Initial request:** $ARGUMENTS
13
13
 
14
+ First run `scripts/pincer-status.sh`. If a PRD already exists, say so and ask whether
15
+ this is a new version of it (`.prd/prd-v{N+1}.md` — old versions are never overwritten)
16
+ or a fresh start; if tickets are in progress, stop and point at `/pincer-code` instead.
17
+
14
18
  ## Phase 1: Discovery (~5 min)
15
19
 
16
20
  1. If `$ARGUMENTS` contains the brief, extract what you can before asking anything.
@@ -13,12 +13,16 @@ report pass/fail, never fix anything — fixes belong to the stage commands.
13
13
  ## Steps
14
14
 
15
15
  1. Read `docs/dry-run-checklist.md` — it is the source of truth for what to check.
16
- 2. Determine which stages have run, from the artifacts present (`.prd/`, `tickets/`,
17
- git log, `NOTES.md`). If `$ARGUMENTS` names a stage, check only up to that stage.
16
+ 2. Run `scripts/pincer-status.sh` to determine which stages have run (it reads `.prd/`,
17
+ `tickets/`, `NOTES.md`; add `git log`). If `$ARGUMENTS` names a stage, check only up
18
+ to that stage.
18
19
  3. Check every applicable item mechanically where possible:
19
20
  - File existence and frontmatter: read the files.
20
21
  - Commit format and story: `git log --oneline`.
21
- - Ticket verification commands: run at least two, report actual output.
22
+ - Receipts: every done ticket carries `verified:` (a status warning means one was
23
+ marked done by hand). Re-run at least two checks with
24
+ `scripts/pincer-ticket.sh verify T-{NN}` — on a done ticket it re-checks without
25
+ touching the receipt — and report actual output.
22
26
  4. For judgment items (tickets genuinely S/M, history reads as a story), give your
23
27
  verdict AND one sentence of evidence — never a bare pass.
24
28
  5. Present a table: checklist item | pass/fail/skipped | evidence. Order by stage.
@@ -0,0 +1,21 @@
1
+ ---
2
+ description: "Where the workflow stands — PRD, tickets, receipts, elapsed time, next action"
3
+ argument-hint: "[none]"
4
+ ---
5
+
6
+ # /pincer-status — Where are we?
7
+
8
+ You are orienting in a repo that uses PINCER, typically after a context reset or at the
9
+ start of a session. Read-only: change nothing.
10
+
11
+ ## Steps
12
+
13
+ 1. Run `scripts/pincer-status.sh`. It reads the artifacts on disk (`.prd/`, `tickets/`,
14
+ `NOTES.md`) and prints the PRD state, every ticket with its state and clock-based
15
+ elapsed time, what is blocked, build time against the budget, any warnings (a ticket
16
+ marked done without a verification receipt), and the next command to run.
17
+ 2. Report in three lines: where the workflow is, what is in progress or blocked, and the
18
+ next command. Quote the `Next` line as-is.
19
+ 3. If a ticket is `in_progress`, read it and `git status`, then offer to resume it with
20
+ `/pincer-code T-{NN}`. If the script printed a warning, surface it — a done ticket
21
+ without a receipt was marked by hand and needs `scripts/pincer-ticket.sh verify T-{NN}`.
@@ -0,0 +1,66 @@
1
+ #!/bin/bash
2
+ # PreToolUse guard for ticket state. A ticket's state fields — status
3
+ # (in_progress / done), started, verified, finished — are written only by
4
+ # scripts/pincer-ticket.sh, whose `done` needs a passing verification receipt.
5
+ # This hook makes that script the only door on Claude Code: editing tools may
6
+ # not write those fields into tickets/T-*.md, and Bash may not sed/echo them in.
7
+ # Creating a ticket with `status: open` and ticking acceptance boxes stay allowed.
8
+ # Exit 2 blocks the tool call; stderr goes back to the agent.
9
+ #
10
+ # A guard against carelessness, not an adversary: the agent could still route
11
+ # around it, but it can no longer do so by accident or habit.
12
+
13
+ input=$(cat)
14
+
15
+ field() { # dotted path into the hook JSON, e.g. tool_input.file_path
16
+ if command -v jq >/dev/null 2>&1; then
17
+ printf '%s' "$input" | jq -r ".$1 // empty" 2>/dev/null
18
+ elif command -v python3 >/dev/null 2>&1; then
19
+ printf '%s' "$input" | python3 -c '
20
+ import json, sys
21
+ d = json.load(sys.stdin)
22
+ for k in sys.argv[1].split("."):
23
+ d = d.get(k, "") if isinstance(d, dict) else ""
24
+ print(d if isinstance(d, str) else json.dumps(d))' "$1" 2>/dev/null
25
+ else # crude fallback: last path segment, first match, no unescaping
26
+ printf '%s' "$input" | grep -oE "\"${1##*.}\"[[:space:]]*:[[:space:]]*\"([^\"\\\\]|\\\\.)*\"" | head -1 | sed -E 's/^"[^"]*"[[:space:]]*:[[:space:]]*"//; s/"$//'
27
+ fi
28
+ }
29
+
30
+ tool=$(field tool_name)
31
+ # `status: open` is fine; these are the fields only the script may write.
32
+ PROTECTED='(^|\\n|[[:space:]])(status:[[:space:]]*(in_progress|done)|started:|verified:|finished:)'
33
+ TICKET='(^|/)tickets/T-[0-9]+[^/]*\.md$'
34
+
35
+ block() {
36
+ echo "Blocked by PINCER ticket guard: $1 Use pincer-ticket.sh (path in the /pincer-code playbook): 'verify T-NN' writes the receipt when the check passes, 'done T-NN' flips the status. Ticking acceptance boxes and editing the body are fine." >&2
37
+ exit 2
38
+ }
39
+
40
+ case "$tool" in
41
+ Edit|Write|MultiEdit)
42
+ file=$(field tool_input.file_path)
43
+ printf '%s' "$file" | grep -qE "$TICKET" || exit 0
44
+ case "$tool" in
45
+ Edit) new=$(field tool_input.new_string) ;;
46
+ Write) new=$(field tool_input.content) ;;
47
+ *) new=$(field tool_input.edits) ;;
48
+ esac
49
+ [ -n "$new" ] || new=$input
50
+ if printf '%s' "$new" | grep -qE "$PROTECTED"; then
51
+ block "ticket state fields (status in_progress/done, started, verified, finished) are never written by hand."
52
+ fi
53
+ ;;
54
+ Bash)
55
+ cmd=$(field tool_input.command)
56
+ [ -n "$cmd" ] || cmd=$input
57
+ printf '%s' "$cmd" | grep -q 'pincer-ticket.sh' && exit 0
58
+ if printf '%s' "$cmd" | grep -qE 'tickets/|T-[0-9][0-9]' &&
59
+ printf '%s' "$cmd" | grep -qE 'status:[[:space:]]*(in_progress|done)|started:|verified:|finished:' &&
60
+ printf '%s' "$cmd" | grep -qE '(sed|perl)[[:space:]]+(-[a-zA-Z]*i|-i)|>|tee[[:space:]]|python|node|ruby'; then
61
+ block "that command writes ticket state fields from the shell."
62
+ fi
63
+ ;;
64
+ esac
65
+
66
+ exit 0
@@ -5,7 +5,7 @@ Used by `/pincer-narrow` for every file in `tickets/`. Filename: `T-{NN}-{slug}.
5
5
  ```markdown
6
6
  ---
7
7
  ticket: T-{NN}
8
- status: open # open | done
8
+ status: open # open | in_progress | done
9
9
  size: S # S (≤15 min) | M (≤30 min)
10
10
  depends_on: [] # e.g. [T-01]
11
11
  ---
@@ -35,6 +35,12 @@ One sentence: what to build and why.
35
35
  ```
36
36
 
37
37
  Rules:
38
+ - `status` and the stamps `started`, `verified`, `finished` are written only by
39
+ `scripts/pincer-ticket.sh` (`start` / `verify` / `done`). `verify` runs the
40
+ Verification block verbatim and writes a receipt only on exit 0; `done`
41
+ requires that receipt to match the current block. Never write these by hand.
42
+ - The Verification block is a fenced `bash` block that exits 0 only when the
43
+ ticket is done — non-interactive, no "check by hand".
38
44
  - Every ticket must be verifiable without human judgment where possible.
39
45
  - If the ticket's surface accepts external input (HTTP, form, file, LLM output),
40
46
  Requirements must state the validation and the rejection behavior, and
@@ -17,6 +17,15 @@
17
17
  "command": "bash \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-dangerous.sh"
18
18
  }
19
19
  ]
20
+ },
21
+ {
22
+ "matcher": "Edit|Write|MultiEdit|Bash",
23
+ "hooks": [
24
+ {
25
+ "type": "command",
26
+ "command": "bash \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/ticket-guard.sh"
27
+ }
28
+ ]
20
29
  }
21
30
  ]
22
31
  }
@@ -5,11 +5,20 @@ with no setup. The workflow commands need one install step, because Codex
5
5
  loads custom prompts from your home directory, not the repo:
6
6
 
7
7
  ```bash
8
- cp .codex/prompts/*.md ~/.codex/prompts/
8
+ mkdir -p ~/.codex/prompts && cp .codex/prompts/*.md ~/.codex/prompts/
9
9
  ```
10
10
 
11
- Then `/pincer-plan`, `/pincer-narrow`, `/pincer-code`, `/pincer-evaluate`,
12
- and `/pincer-release` are available in any Codex session. These files are
11
+ (`mkdir -p` matters: Codex does not create `~/.codex/prompts/` on its own, and
12
+ `cp` into a missing directory fails with "Not a directory".) Codex exposes custom
13
+ prompts under a `/prompts:` prefix, so the commands are `/prompts:pincer-plan`,
14
+ `/prompts:pincer-narrow`, `/prompts:pincer-code`, `/prompts:pincer-evaluate`,
15
+ `/prompts:pincer-release` and `/prompts:pincer-status` in any Codex session.
16
+ The playbooks refer to each other by their short names (`/pincer-narrow` etc.);
17
+ read those as `/prompts:pincer-narrow` here.
18
+
19
+ The copy is not tracked by `pincer update`: after `npx pincer-workflow@latest update`
20
+ (or after editing a playbook and re-running `scripts/sync-prompts.sh`), run the
21
+ `mkdir -p … && cp …` line again so `~/.codex/prompts/` matches the repo. These files are
13
22
  generated from `.claude/commands/` by `scripts/sync-prompts.sh` — edit the
14
23
  source playbooks, not these copies, and re-copy after a re-sync.
15
24
 
@@ -23,6 +32,12 @@ approval_policy = "on-request" # agent asks before escalating
23
32
  sandbox_mode = "workspace-write" # writes confined to the repo; no network by default
24
33
  ```
25
34
 
35
+ The ticket scripts are plain bash and work here unchanged:
36
+ `scripts/pincer-ticket.sh start|verify|done T-NN` and `scripts/pincer-status.sh`.
37
+ What Codex lacks is the hook that stops an agent hand-editing ticket state, so the
38
+ rule in `AGENTS.md` carries that weight; `/pincer-status` warns about any ticket
39
+ marked done without a receipt.
40
+
26
41
  Never run with approvals disabled. The destructive-command rule in `AGENTS.md`
27
42
  (no force-pushes, absolute-path deletes, or `curl | sh` by an agent) applies as
28
43
  a standing instruction here; `/pincer-release` audits the git artifacts
@@ -8,40 +8,59 @@ user confirms the starting point, run continuously and report progress between t
8
8
 
9
9
  **Initial request:** $ARGUMENTS
10
10
 
11
+ Ticket state lives in the ticket file's frontmatter and is written **only** by
12
+ `scripts/pincer-ticket.sh` (`start` → `verify` → `done`). `verify` runs the ticket's
13
+ Verification block and stamps a receipt only on a green exit; `done` refuses without a
14
+ receipt that matches the current check, or with unticked acceptance criteria. Never edit
15
+ `status`, `started`, `verified`, or `finished` by hand — on Claude Code a hook blocks it.
16
+
17
+ ## Before the loop
18
+
19
+ Run `scripts/pincer-status.sh`. It lists every ticket's state, what is blocked, elapsed
20
+ build time from the clock, and the next action. If a ticket is `in_progress`, you are
21
+ resuming: read it, check `git status` / `git diff` for uncommitted work, and continue
22
+ from wherever the receipt says you are. Confirm the starting point with the user, then go.
23
+
11
24
  ## Loop (per ticket, in dependency order)
12
25
 
13
- 1. **Read the ticket** and the files it references. Announce: "Starting T-{NN}: {title}."
26
+ 1. **Start:** `scripts/pincer-ticket.sh start T-{NN}` — refuses while a `depends_on` ticket
27
+ isn't done, and stamps the start time. Read the ticket and the files it references.
28
+ Announce: "Starting T-{NN}: {title}."
14
29
  2. **Implement.** Follow the conventions in `CLAUDE.md` and the PRD's architecture and
15
30
  visual direction. Installing a dependency not named in the PRD's architecture is a
16
31
  stop-and-ask: verify it's the real package on the registry (linked repo, downloads —
17
- hallucinated names get typosquatted), say why it earns its place, and wait for a yes. For an S ticket, implement directly. For an M ticket touching
18
- isolated files, you may dispatch a subagent with a clean prompt: paste the full ticket
19
- body, the relevant conventions, and nothing else.
20
- 3. **Verify.** Run the ticket's verification command(s). If they fail, fix before moving
21
- on never mark a ticket done on a red check. Report actual output, not assumptions.
32
+ hallucinated names get typosquatted), say why it earns its place, and wait for a yes.
33
+ For an S ticket, implement directly. For an M ticket touching isolated files, you may
34
+ dispatch a subagent with a clean prompt: paste the full ticket body, the relevant
35
+ conventions, and nothing else.
36
+ 3. **Verify:** `scripts/pincer-ticket.sh verify T-{NN}` runs the Verification block and
37
+ writes the receipt only if it exits 0. Red → fix and re-run; report the actual output,
38
+ not assumptions. Green output is the definition of done, not your confidence.
22
39
  4. **Self-review the diff** before committing: silent failures (empty catches,
23
40
  un-awaited promises), leftover debug code, drift from the ticket's acceptance criteria.
24
41
  Then a security sweep of the same diff:
25
42
  - No secret values: run
26
- `git diff --cached | grep -iE '(api[_-]?key|secret|token|password)[[:space:]]*[:=]'`
43
+ `git diff | grep -iE '(api[_-]?key|secret|token|password)[[:space:]]*[:=]'`
27
44
  and treat any hit that isn't a `process.env` reference or a name in
28
45
  `.env.example` as a blocker.
29
46
  - External input touched by this diff is validated server-side, and untrusted
30
47
  content (user input, LLM output) is escaped where rendered — per the
31
48
  Security defaults in `CLAUDE.md`.
32
49
  - No error path leaks internals (stack traces, key names with values) to the client.
33
- 5. **Commit** with message `T-{NN}: {title}` and mark the ticket done: set
34
- `status: done` in its frontmatter and tick every verified acceptance-criteria
35
- checkbox (`- [ ]` `- [x]`) in the same edit. A ticket is never `done` with
36
- unticked criteria — if a criterion was cut, that's a scope change to record,
37
- not a box to skip.
38
- 6. Give a one-line progress update ("T-02 done, 3 remaining, ~40 min elapsed") and continue.
50
+ If the review changed code, run `verify` again the receipt must match the code you commit.
51
+ 5. **Close the ticket:** tick every verified acceptance-criteria checkbox (`- [ ]` → `- [x]`;
52
+ editing the checkboxes is allowed), then `scripts/pincer-ticket.sh done T-{NN}`. A
53
+ criterion that was cut is a scope change to record in the PRD, not a box to skip.
54
+ Commit code and ticket file together: `git add -A && git commit -m "T-{NN}: {title}"`.
55
+ 6. Give a one-line progress update using the elapsed figure from
56
+ `scripts/pincer-status.sh` ("T-02 done, 3 remaining, 38m elapsed of 75m") and continue.
39
57
 
40
58
  ## Timebox rules
41
59
 
42
- - Track elapsed time against the ~75-minute build budget. If you're at risk of running
43
- out, stop and propose a scope cut: which remaining tickets to drop or shrink. Cutting
44
- scope deliberately beats an unfinished mess record the cut in the PRD's Out of Scope.
60
+ - The budget is ~75 minutes of build time, measured by `scripts/pincer-status.sh` from
61
+ the first ticket's start stamp never estimated. If the remaining tickets won't fit,
62
+ stop and propose a scope cut: which remaining tickets to drop or shrink. Cutting scope
63
+ deliberately beats an unfinished mess — record the cut in the PRD's Out of Scope.
45
64
  - If a ticket reveals the plan was wrong, stop and say so rather than silently diverging.
46
65
  Update the ticket/PRD, then continue.
47
66