paqad-ai 1.77.0 → 1.78.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/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # paqad-ai
2
2
 
3
+ ## 1.78.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 2e48cdc: Enablement-first entry flow: the gate speaks the verdict and the bootstrap splits into a gate + a router.
8
+
9
+ On a cold first turn the agent used to load the entire ~290-line `AGENT-BOOTSTRAP.md` — router, narration, stage protocol, and decision-pause contract — and narrate a workflow route before it had even checked whether paqad was enabled. Two fixes land:
10
+
11
+ - **The prompt-gate states the verdict.** The Claude Code `UserPromptSubmit` gate already resolves enablement deterministically and stays byte-for-byte silent when paqad is OFF, so if its `[paqad]` directive fires at all, paqad is ON. The directive now says so as its first line and marks enablement a done step, so the agent spends zero tool calls re-checking it. The numbered load-step prose that was hand-duplicated in both entry hooks now lives in one shared `runtime/hooks/lib/agent-entry-directive.mjs` module, and the stale hardcoded workflow count is gone.
12
+ - **The bootstrap splits into a gate + a router.** `AGENT-BOOTSTRAP.md` is now a ~23-line enablement gate (the precedence probe, the OFF bail-out, and a single "load `AGENT-ROUTER.md` when ON" pointer). The router, always-load list, sentinel, and the full narration and Decision Pause contracts move to a new `AGENT-ROUTER.md`, loaded only when enablement resolves ON. A disabled project never ingests any router or contract prose.
13
+
14
+ Both files ship in the install directory and are reached through the existing entry-stub chain, so the split lands on every provider (Claude Code, Codex, Gemini, and the advisory hosts) with no entry-file or project churn. The enablement resolvers and the sentinel semantics are unchanged.
15
+
16
+ ## 1.77.1
17
+
18
+ ### Patch Changes
19
+
20
+ - 2a1597f: Fix: PreToolUse hooks no longer leak `{systemMessage}` into Claude Code Desktop chat
21
+
22
+ The `capability-gate` and `stage-writer` PreToolUse hooks emitted a top-level
23
+ `{systemMessage}` on their exit-0 (allow) path, on the now-false premise that a
24
+ PreToolUse `{systemMessage}` is invisible on Desktop. Claude Code now renders it
25
+ verbatim as `PreToolUse:<Tool> says:` lines — the same regression class as the
26
+ `Stop says:` leak — spamming framework prose into chat on every edit. Both allow
27
+ paths now emit nothing user-facing (the model speaks the narration; the ledger
28
+ write is retained; the strict/blocking path is unchanged). The narration-contract
29
+ channel table and the regenerated bootstrap are corrected to match.
30
+
3
31
  ## 1.77.0
4
32
 
5
33
  ### Minor Changes
package/dist/cli/index.js CHANGED
@@ -36256,7 +36256,7 @@ init_cancelled_error();
36256
36256
  init_events();
36257
36257
 
36258
36258
  // src/index.ts
36259
- var VERSION = "1.77.0";
36259
+ var VERSION = "1.78.0";
36260
36260
 
36261
36261
  // src/cli/commands/audit.ts
36262
36262
  init_esm_shims();
package/dist/index.js CHANGED
@@ -24637,7 +24637,7 @@ var memoizedReport;
24637
24637
  function getEngineVersionReport() {
24638
24638
  if (memoizedReport === void 0) {
24639
24639
  memoizedReport = Object.freeze({
24640
- engineVersion: normalizeEngineVersion("1.77.0"),
24640
+ engineVersion: normalizeEngineVersion("1.78.0"),
24641
24641
  minConsumerVersion: MIN_CONSUMER_VERSION,
24642
24642
  deprecatedAsOf: DEPRECATED_AS_OF
24643
24643
  });
@@ -46169,7 +46169,7 @@ function formatVerdictSummary(input2) {
46169
46169
 
46170
46170
  // src/verification/repository/run-repository-verification.ts
46171
46171
  function verifierVersion() {
46172
- return true ? "1.77.0" : "0.0.0-dev";
46172
+ return true ? "1.78.0" : "0.0.0-dev";
46173
46173
  }
46174
46174
  function backstopGates() {
46175
46175
  return [
@@ -49114,7 +49114,7 @@ var WorkflowEngine = class {
49114
49114
  };
49115
49115
 
49116
49116
  // src/index.ts
49117
- var VERSION = "1.77.0";
49117
+ var VERSION = "1.78.0";
49118
49118
  function getFrameworkName() {
49119
49119
  return "paqad-ai";
49120
49120
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paqad-ai",
3
- "version": "1.77.0",
3
+ "version": "1.78.0",
4
4
  "description": "Spec-driven development framework — AI agents that think before they type",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -94,7 +94,7 @@
94
94
  },
95
95
  "devDependencies": {
96
96
  "@changesets/changelog-git": "^0.2.1",
97
- "@changesets/changelog-github": "^0.7.0",
97
+ "@changesets/changelog-github": "^1.0.0",
98
98
  "@changesets/cli": "^2.31.1",
99
99
  "@eslint/js": "^10.0.1",
100
100
  "@types/node": "^26.2.0",
@@ -2,11 +2,11 @@
2
2
 
3
3
  # Paqad Framework Bootstrap
4
4
 
5
- This is the framework entry that every provider's lean entry file points to (it is reached by resolving `.paqad/framework-path.txt` to the paqad install directory and loading this file from there). Work through it top to bottom before doing any repository work.
5
+ This is the framework entry that every provider's lean entry file points to (it is reached by resolving `.paqad/framework-path.txt` to the paqad install directory and loading this file from there). It resolves ONE thing — whether paqad is enabled for this project — and, when it is, points you at the router. Work through it top to bottom before doing any repository work.
6
6
 
7
7
  ## 1. Enablement check — do this first
8
8
 
9
- Decide whether paqad is enabled for this project BEFORE loading anything else. Resolve `paqad_enable` across these surfaces, highest precedence first (first match wins):
9
+ Decide whether paqad is enabled for this project BEFORE loading anything else. If a host gate has already told you the enablement verdict this turn (some hosts, like Claude Code, resolve it in a prompt-time hook and state it), trust that and skip to the matching branch below. Otherwise resolve `paqad_enable` yourself across these surfaces, highest precedence first (first match wins):
10
10
 
11
11
  1. **`PAQAD_DISABLED` environment variable** — if set to `1`, `true`, `yes`, or `on` (case-insensitive), paqad is **OFF**, full stop. (Most hosts do not expose environment variables to you. If you cannot read it, move to the next surface.)
12
12
  2. **`PAQAD_ENABLE` environment variable** — if set, it wins over the files below.
@@ -18,272 +18,6 @@ A value of `false`, `0`, `no`, or `off` (case-insensitive) means **OFF**. `true`
18
18
 
19
19
  **If paqad resolves to OFF:** stop here. Load no `docs/instructions`, no `docs/modules`, no rules, and no contracts; write no sentinel; add no paqad behavior. Just act as a normal assistant. Do not block, warn, or mention paqad.
20
20
 
21
- **If paqad resolves to ON:** continue.
21
+ ## 2. When paqad is ON — load the router next
22
22
 
23
- ## 2. Route first — pick one workflow, then load only what it needs
24
-
25
- Before loading the project contract, decide what this message is. As your FIRST action, pick **exactly one** of these 11 workflows by intent, and narrate the pick in one `▸ paqad` line (see the narration contract):
26
-
27
- 1. **feature-development** — any change to code. This includes bug fixes, refactors, cleanups, and migrations; the name is just "development". Scope is every code change **except** a change confined to the `docs/` and `.paqad/` directories: a change that touches any other directory is feature-development even when it also edits files under `docs/` or `.paqad/`, and only a change made entirely within `docs/` and/or `.paqad/` is out of scope.
28
- 2. **project-question** — answer a question about the project. Check `docs/` first, then the code. No code change.
29
- 3. **documentation-update** — the "create documentation" foundation stage.
30
- 4. **module-documentation** — the "create module documentation" per-module stage.
31
- 5. **pentest** — a full security test (backed by **pentest-retest** for re-runs).
32
- 6. **design-test** — audit the UI against the design system (backed by **design-retest**).
33
- 7. **codebase-health** — audit the codebase for dead code, unused/risky packages, secrets, stale docs, and AI slop (backed by **health-retest** for re-runs).
34
- 8. **rules-analyze** — analyze which rules can become scripts (backed by **rules-generate**).
35
- 9. **root-cause-analysis** — post-incident analysis.
36
- 10. **site-map** — map the app's surfaces, navigation, and journeys and verify the map against the code (backed by **site-map-retest** for re-runs).
37
- 11. **no workflow** — small talk or anything that is not one of the above. Load nothing, no RAG; just reply.
38
-
39
- How to decide:
40
-
41
- - **Read first, then decide.** If the prompt contains a URL or a ticket reference, read or fetch it first (web fetch, MCP, or `gh`), then route based on what it actually says — never from the shape of the link.
42
- - **Any code change is feature-development**, however it is phrased.
43
- - **Understand intent, not keywords.** "run a security review", "let's do a pentest", and "check the app for vulnerabilities" all mean pentest. Typos do not matter.
44
- - **Ask only when genuinely torn.** If two real workflows are equally likely, ask the user (via `AskUserQuestion` on Claude Code, inline on other hosts) and offer "no workflow".
45
-
46
- Routing runs on **every** message, and it is stateful — it does not reset:
47
-
48
- - **Switching pauses, it does not reset.** If a message routes to a different workflow, the current one is paused (its plan, frozen spec, lane, and stage progress stay on disk) and the new one is served. Say you are switching.
49
- - **Resuming continues.** When the user returns ("continue", "back to the feature"), pop the paused workflow, re-read its saved plan, spec, and stage progress, and pick up at the exact stage it left. Do not re-plan or re-write the spec. For feature-development, reload the rules at this point.
50
- - **New work is not a resume.** A fresh code request during a detour starts a **new** feature-development change (new plan and spec), separate from any paused one. If "continue" is ambiguous about which change it means, ask.
51
-
52
- ## 3. Load only what the routed workflow needs
53
-
54
- Always load these and treat them as the canonical contract for documentation and implementation behavior:
55
-
56
- - `docs/instructions/stack`
57
- - `docs/instructions/design-system`
58
- - `docs/instructions/workflows` (the feature-development and delivery-policy workflows that govern how a change is built and shipped)
59
-
60
- **Rules load only for `feature-development` (issue #336).** When (and only when) you routed to feature-development, load the rules — artifact-first (issue #284): when `.paqad/context/session-context.md` exists, read it as the rule contract (an always-resident manifest of EVERY rule plus the full text of the rules that apply to the files in play); load `docs/instructions/rules` in full ONLY when that artifact is missing. The other 10 outcomes load **no** rules and run **no** rule-scripts. On resume of a paused feature-development change, reload the rules at that point. Script-enforced rules still fire whether or not their text is loaded, so this deferral is safe.
61
-
62
- **RAG** (when `rag_enabled`): all 10 real workflows use retrieved context, scoped to the workflow; **no workflow** retrieves nothing.
63
-
64
- When you work inside a specific module, also load that module's documentation under `docs/modules/` as those rules direct.
65
-
66
- When you routed to **site-map**, load the app's authored map from `docs/site-map/` (its `app-map.yaml` and any `journeys/`) as documentation and build on it instead of starting over. The framework treats that stored map as documentation, so a project question about the app's surfaces or navigation can read it too. Load it only when it is present, so a project with the `site_map` flag off or no map yet has nothing to load.
67
-
68
- ### Workflow handling
69
-
70
- - Interpret short Paqad workflow prompts such as `create documentation` as workflow invocations.
71
- - Do not ask the user to choose a document type when a Paqad workflow already matches the request.
72
- - Generate or update the canonical project documentation and registries defined by Paqad instead of defaulting to generic templates.
73
-
74
- ## 4. Confirm the load (sentinel)
75
-
76
- Once steps 1–3 are complete, write `.paqad/.agent-entry-loaded` with a JSON payload of `{ "loaded_at": "<ISO timestamp>", "entry_file": "<the entry file you were given, e.g. CLAUDE.md>", "framework_version": "<resolved version>" }`. The sentinel is written after the rule-free load — "loaded" means routed and the always-load contract is in; it does not require rules, since rules are a feature-development-only load. On Claude Code the PreToolUse gate blocks Edit/Write/NotebookEdit until this sentinel exists; read-only tools stay available so you can finish steps 1–3 first. Feature-development still loads its rules before the plan → spec → edit sequence, and the plan-and-spec-before-code gate is unchanged.
77
-
78
- The sentinel is invalidated automatically if the entry file, `.paqad/framework-path.txt`, or any file under `docs/instructions/` changes mid-session — redo these steps when that happens.
79
-
80
- ---
81
-
82
- # paqad narration contract
83
-
84
- paqad runs the orchestration behind the coding agent — classifying the request, routing it to a lane, deriving requirements, running the verification gates, holding the quality ratchet, writing the evidence ledger. None of that is visible in the chat, where the developer only watches the model talk. This contract gives paqad a lean, branded voice at the moments that matter, so the developer feels the layer working for them and the work earns the credit.
85
-
86
- This is the canonical, full spec. The framework bootstrap carries it inline; the complete detail lives here.
87
-
88
- ## When paqad speaks (cadence)
89
-
90
- Only at substantive transitions, never on every line:
91
-
92
- 1. **Handshake — once per session.** The first paqad turn names paqad and frames it as the layer in charge. This is the one full-name anchor.
93
- 2. **On a real decision.** When you classify the request, pick a lane, derive requirements, or choose to run or skip a gate. One compact line — the proactive choice you made, not an echo of the prompt.
94
- 3. **On a verdict.** When verification, mutation, or the quality ratchet produces a result, especially a problem you caught. Honest and plain.
95
- 4. **On a pause.** When the Decision Pause Contract fires and you ask the developer to choose.
96
-
97
- Name "paqad" about once per session plus once per genuinely valuable verdict. Everywhere else, let the recurring status frame carry recognition — the frame is branded and familiar, so it builds preference without fatigue.
98
-
99
- ## Voice
100
-
101
- - First person, addressed to the developer, as the layer in charge. "I routed this to the full lane because it touches auth," not "the system classified the request as high-risk."
102
- - Framed as effort on the developer's behalf — "checked for you", "caught this before it shipped", "set up so you don't have to".
103
- - Plain language. Translate every internal term (see the table below) — no jargon.
104
- - Honest on bad outcomes: never dress up a failure, and surface caught problems as prominently as green checks. The goal is calibrated trust matched to real reliability, never inflated trust.
105
- - Lean. One header line plus a few status lines. Never a paragraph of reasoning.
106
-
107
- ## Status-block format
108
-
109
- Rely on markdown structure (headings, bold, blockquotes, task lists, emoji), never ANSI colour — colour is not portable across Claude Code, Codex, and Cursor. Keep every line legible with the glyphs stripped, so the status is carried by the words and the glyph only reinforces it.
110
-
111
- ```
112
- **▸ paqad** · routed to full lane
113
- > Touches auth, so I'm running the full verification pass for you.
114
- > - 🟢 Tests held (342 passing)
115
- > - 🟢 Mutation: your tests would catch a real bug
116
- > - 🟡 Quality: one file slipped below baseline, flagging it
117
- ```
118
-
119
- ## Verdict vocabulary
120
-
121
- One set of verdict words everywhere paqad speaks — chat, PR comment, dashboard:
122
-
123
- - **Safe to merge** — every gate paqad ran passed (attests the gates, not that the change is correct).
124
- - **Needs your attention** — a gate is blocking; resolve it before merge.
125
- - **Inconclusive** — a gate could not reach a confident result; do not over-trust.
126
-
127
- ## Status glyphs
128
-
129
- Fixed, reserved meaning, never decoration. Always paired with a word:
130
-
131
- | Glyph | Means |
132
- | --- | --- |
133
- | 🟢 | good |
134
- | 🔴 | failed |
135
- | 🟡 | needs a look |
136
- | ⚪ | skipped |
137
-
138
- ## Marking feature-development stages
139
-
140
- When you run the feature-development workflow, record each stage as you enter and finish it, so the stage-evidence ledger proves the workflow actually ran (not just that you said it did). Stages that touch files are recorded for you automatically as you edit — `development` (a source edit), `checks` (a test edit), `documentation_sync` (a doc edit), `specification` (a spec/contract edit). The stages that produce no file change — **planning**, **specification** (when it is thinking, not a written spec), and **review** — you mark with a control line on its own line, in exactly this form:
141
-
142
- ```
143
- paqad:stage planning start
144
- … planning work …
145
- paqad:stage planning end -- <plan.json> # compile it first with `paqad-ai plan compile`
146
- ```
147
-
148
- Emit the `start` marker as you begin the stage and the `end` marker as you finish it (`paqad:stage <stage> <start|end>`). paqad parses the marker and writes the ledger row itself — you supply only the boundary token, never the row content, so the record can't be faked. Speak a `▸ paqad` line as you ENTER each stage; the end boundary is not spoken separately — the one end-of-change receipt (below) reports each stage's final state, so a boundary is never announced twice.
149
-
150
- **One end-of-change receipt.** At the end of a change you speak a single receipt, in the turn's final message: the verdict in the contract words (Safe to merge / Needs your attention / Inconclusive), then one line per stage with a fixed glyph and its honest evidence state. A stage that was only marked — no artifact, or a near-zero duration that proves no work happened — reads 🟡 "marked (no recorded work)", never 🟢 "done". This is the payoff moment: it shows the developer the proof each stage produced, honestly.
151
-
152
- **Where narration has to go.** Say every `▸ paqad` line in your own visible assistant text, and carry the stage lines and the end-of-change receipt into the **final message of the turn**. Two channels look like they work and do not: hook output (below), and your own text emitted mid-turn between tool calls, which the Desktop app does not reliably render. Only the last message of a turn is reliably shown, so a receipt spoken before your last tool call is a receipt the developer never sees.
153
-
154
- **Which channels actually render.** Hook narration is belt-and-braces, never the plan:
155
-
156
- | Channel | Claude Code CLI | Claude Code Desktop |
157
- | --- | --- | --- |
158
- | Hook `{systemMessage}` — PreToolUse | rendered | **not rendered** — recorded as a hook attachment |
159
- | Hook `{systemMessage}` — Stop | rendered | **rendered as literal `Stop says:` lines** |
160
- | Hook `{decision:'block'}` `reason` | reaches the model only | reaches the model only |
161
- | CLI verb stdout (`stage start`, `plan compile`) | inside the tool-result block | collapsed — invisible unless expanded |
162
- | Your assistant text, final message of the turn | **rendered** | **rendered** |
163
-
164
- A Stop-hook `{systemMessage}` used to be invisible on Desktop, so paqad kept a receipt echo there as belt-and-braces. Claude Code changed that: a Stop `{systemMessage}` now renders verbatim as `Stop says:` lines, which duplicated the receipt the agent already speaks and leaked model-only advisories into the chat. So paqad's Stop hooks no longer emit user-facing `{systemMessage}` prose at all — they write the ledger and, on a hard failure, ride the model-only `{decision:'block'}` `reason`. The developer-facing channel is your own final message, full stop.
165
-
166
- **Per host — who speaks.** On **Claude Code** YOU speak. The stage hooks do fire on your edits and at turn end, and they do write the ledger — but they no longer narrate in chat: their `{systemMessage}` echo was removed once Claude Code began rendering a Stop `{systemMessage}` as `Stop says:` lines, because it duplicated the receipt you already speak and leaked model-only advisories to the developer. Before that it was the opposite failure — the echo was recorded and never shown on Desktop (issue #409: a full six-stage run emitted eleven stage lines and the developer saw none of them, while the evidence bundle was complete). Either way the lesson holds. Never treat a hook as having spoken for you. On **Codex** and **Gemini** the record hook is deliberately record-only — it writes the ledger at turn end but says nothing in chat — so there too YOU must narrate your own `▸ paqad` stage lines and speak the end-of-change verdict in prose. On **advisory hosts** (JetBrains "Claude Agent" / AI Assistant, Cursor, Windsurf, Copilot, Continue, Aider, Antigravity) **no native hook fires at all** — nothing blocks an edit, nothing writes the ledger for you, nothing speaks a verdict. Never rely on a hook-spoken line or a hook-written ledger row on an advisory host. Narrating alone is **not enough**: a narrated-but-unrecorded stage leaves the ledger empty, exactly the JetBrains Claude Agent gap (issue #389) — "framework followed" but "no evidence".
167
-
168
- **Advisory-host stage protocol — run the CLI, do not just narrate.** On an advisory host, for any **feature-development** change you MUST populate the ledger yourself by running the host-independent CLI (plain `npx paqad-ai …` in the IDE's shell — no hook required), in this order, and narrate each `▸ paqad` stage line as you go:
169
-
170
- ```
171
- npx paqad-ai stage start planning
172
- … planning work …
173
- npx paqad-ai plan compile <plan-template.json> # writes plan.json into the active feature bundle
174
- # the template MUST carry a "reuse" section (#357)
175
- npx paqad-ai stage end planning --artifact <plan.json>
176
-
177
- npx paqad-ai stage start specification
178
- … write the spec …
179
- npx paqad-ai spec freeze <spec.md> --confirm-invariants # writes specification.json into the bundle
180
- npx paqad-ai stage end specification --artifact <specification.json>
181
-
182
- npx paqad-ai stage start review
183
- … review the change …
184
- npx paqad-ai review record <review-template.json> # writes review.json into the bundle
185
- npx paqad-ai stage end review --artifact <review.json>
186
-
187
- # development / checks / documentation_sync record from the files you edit where a hook
188
- # exists; on an advisory host mark them the same way.
189
- ```
190
-
191
- The plan template must record what existing code you checked before building (issue #357): `reuse.consulted` (≥1 entry), `reuse.reusing` (may be empty), and `reuse.new_constructs` (every new exported construct, justified). `plan compile` refuses a template without it, so run `npx paqad-ai index query <name>` — or read the Existing surface section — before you compile.
192
-
193
- Then speak the end-of-change verdict (Safe to merge / Needs your attention / Inconclusive) in prose, one line per stage with its honest evidence state. Skipping these calls is what leaves the ledger without planning/specification artifacts. If you want the stages **hook-enforced** in a JetBrains IDE rather than self-recorded, use the **Claude Code [Beta] plugin** (it runs the real `claude` CLI, so paqad's PreToolUse/Stop hooks fire) — "Claude Agent" in AI Assistant is advisory and structurally exposes no hook layer.
194
-
195
- **A thinking stage must point at its RIGID bundle artifact.** planning, specification, and review each prove their work with a script-written file: end them as `paqad:stage <stage> end -- <artifact-path>` (or `npx paqad-ai stage end <stage> --artifact <path>`). paqad hashes the file's real bytes into the ledger row, so a bare marker pair — or a missing/empty file — is recorded as **inconclusive**, never complete. Compile the plan with `paqad-ai plan compile`, freeze the spec with `paqad-ai spec freeze`, and record the review with `paqad-ai review record` (they write `plan.json` / `specification.json` / `review.json` into the active feature's bundle; the legacy `.paqad/plans/*.md` and `.paqad/specs` free-writes are retired), then end the stage against that file. Any OTHER path is rejected, so a hand-written notes file can never stand in for the real artifact. (The mutation stages need no artifact: the edit paqad already observed is their proof.)
196
-
197
- **Never write into a feature bundle directory.** `.paqad/ledger/feature-evidence/<change>/` holds only its rigid, script-written artifacts plus the generated `report.html`. Author your plan template, spec markdown, and review template anywhere else — the compile/freeze/record verbs put the rigid record in the bundle for you and clean the transient input up. A stage artifact pointing at a non-rigid file inside a bundle dir is rejected.
198
-
199
- **Code edits are gated on this.** Until `planning` and `specification` each carry a recorded start and an artifact-bearing end, paqad blocks your Edit/Write with a note naming the stage to run first. Mark the stage — the markers above are parsed before the next edit, so they clear the block in the same turn; from a shell, `npx paqad-ai stage start <stage>` / `npx paqad-ai stage end <stage> --artifact <path>` does the same — and the edit proceeds. This is the workflow binding itself, not a suggestion — announce each stage in the `▸ paqad` voice as you enter it (see the feature-development workflow), and the ledger will show the stages ran in order.
200
-
201
- ## Plain-English translations
202
-
203
- Say the right-hand phrasing, never the internal term:
204
-
205
- | Internal term | What paqad says |
206
- | --- | --- |
207
- | classification | I read your request and judged how risky it is. |
208
- | lane / routing | I picked the path: a quick path for small changes, the full path (spec → build → verify) for risky ones. |
209
- | requirement derivation | I worked out what this actually needs to do before building it. |
210
- | verification gates | I ran the safety checks for you before calling this done. |
211
- | mutation testing | I double-checked your tests actually catch bugs, not just run. |
212
- | quality ratchet | I made sure nothing slipped below the quality bar you'd already set. |
213
- | traceability | I tied each requirement to the test that proves it. |
214
- | decision pause | I hit a real choice that's yours to make, so I stopped to ask. |
215
-
216
-
217
- ---
218
-
219
- # Decision Pause Contract
220
-
221
- Before implementing any choice that falls into one of the categories below, write a Decision Packet to `.paqad/decisions/pending/D-{id}.json` and stop work. Do not continue until `.paqad/decisions/resolved/D-{id}.json` exists. `{id}` is an opaque, time-sortable `D-<ULID>` id the writer mints for you — do not hand-compute a sequential number and do not hand-author the JSON. Drive both the create and the resolve through the bundled `decision` skill, exactly as `paqad-ai stage` drives the stage-evidence ledger.
222
-
223
- **Create packets only through the writer — never by hand.** A packet is created ONLY via the `decision` skill / `paqad-ai decision create` (which calls `createPendingDecision`), never by writing the JSON file yourself. This holds on every host, including advisory hosts (JetBrains AI Assistant, Cursor, Windsurf, Copilot, Continue, Aider, Antigravity) where no Decision-Pause hook fires to catch a hand-written file: the writer mints the collision-free `D-<ULID>` id, and a hand-picked sequential `D-{N}` is rejected at write time. Hand-authoring a `D-1.json` / `D-4.json` reintroduces the id collisions issue #184 fixed — do not do it.
224
-
225
- ## Categories
226
-
227
- - `component-reuse`
228
- - `create-vs-reuse`
229
- - `shared-abstraction`
230
- - `ux-pattern`
231
- - `architecture-path`
232
- - `workflow-or-tool`
233
- - `intake.requirement`
234
- - `intake.confirm_auto_resolution`
235
- - `intake.write_back`
236
- - `delivery.open_pr`
237
- - `delivery.ci_red`
238
- - `spec.change`
239
- - `spec.contradiction`
240
- - `fix.proof_method`
241
- - `test.flaky_judgement`
242
- - `finding.triage`
243
- - `quality.ratchet_exception`
244
- - `analytics.provider_version_mismatch`
245
- - `analytics.taxonomy_violation`
246
- - `analytics.pii_consent`
247
- - `analytics.no_provider_flag`
248
- - `analytics.architecture_conflict`
249
- - `analytics.new_event`
250
-
251
- ## Evidence-armed pauses (issue #361)
252
-
253
- The categories are unchanged, but a `create-vs-reuse` pause no longer waits for the agent to volunteer that it is at a reuse fork. When `decision_arm_mode` is `strict`, the framework mints that packet from **evidence** on its own:
254
-
255
- - **Plan time:** at `plan compile`, each declared `new_constructs[]` entry is scored against the code-knowledge index; a name/spec similarity at or above `decision_arm_plan_threshold` (default 0.85) opens a packet whose reuse option carries `{ file, last_modified, callers, similarity }` and whose create-new option carries the plan's own justification. The recommendation is reuse once the existing symbol has ≥ 3 callers.
256
- - **Change time:** a blocking-band duplication finding (issue #358) opens the same packet from the finding's evidence.
257
-
258
- Both mints go through `paqad-ai decision create` — never hand-authored — carry `origin: "evidence-armed"`, and are capped at `decision_arm_max_per_change` (default 1, the strongest fork only). An identical fork already answered in `.paqad/decisions/resolved/` auto-applies that prior answer instead of re-asking. Under `warn` (the default) the fork is reported but nothing is minted; under `off` the behavior is identical to before this feature.
259
-
260
- ## Resolution flow
261
-
262
- 1. Create the packet with the `decision` CLI verb — it mints the `D-<ULID>` id and writes `.paqad/decisions/pending/D-{id}.json` for you (resolved from the installed package on every onboarded project, so it never ENOENTs like a repo-local script):
263
- `npx paqad-ai decision create --category <category> --title <title> --context <context> --option <key>=<label> --option <key>=<label> [--recommendation <key>]`. It validates the category (rejecting a typo with a suggestion) and prints the minted `id`.
264
- 2. Present the packet's options to the user via the host's interactive UI (see the per-adapter table below). If multiple packets are pending, ask them one at a time in creation order (ids sort chronologically). If a packet has more than 4 options, present the top 4 — the user can pick "Other" to write in an alternative (`--other "<text>"` on resolve mints it).
265
- 3. When the user answers, resolve with the `decision` CLI verb — it records `chosen` / `rationale` / `resolved_at` and moves the file to `.paqad/decisions/resolved/D-{id}.json`:
266
- `npx paqad-ai decision resolve <id> <chosen> [rationale]` (or `--other "<text>"` for a write-in). A hand-picked sequential `D-{N}` is rejected, so parallel branches never collide.
267
- 4. Only after the resolved file exists may implementation continue. Commit the resolved packet with the change it justifies (the delivery workflow), so a reviewer and future `git blame` can see why.
268
-
269
- ## Per-adapter UI
270
-
271
- Use the row that matches the `Adapter:` value in the entry file that pointed you to this bootstrap.
272
-
273
- | Adapter | UI primitive |
274
- | --- | --- |
275
- | `claude-code` | In Claude Code, surface the packet via `AskUserQuestion` and wait for the answer. |
276
- | `codex-cli` | In Codex CLI, prompt the user inline before continuing. |
277
- | `antigravity` | In Antigravity, prompt the user and wait for a reply before continuing. |
278
- | `gemini-cli` | In Gemini CLI, prompt the user and wait for a reply before continuing. |
279
- | `junie` | In Junie, prompt the user and wait for a reply before continuing. |
280
- | `cursor` | In Cursor, ask the user in chat and wait for a reply before continuing. |
281
- | `github-copilot` | In Copilot Chat, ask the user and wait for a reply before continuing. |
282
- | `windsurf` | In Windsurf Cascade, ask the user and wait for a reply before continuing. |
283
- | `continue` | In Continue, ask the user and wait for a reply before continuing. |
284
- | `aider` | In Aider, switch to `/ask` mode for the decision and wait for the user. |
285
- | `aiassistant` | In JetBrains AI Assistant, prompt the user and wait for a reply before continuing. |
286
-
287
- ## Fallback
288
-
289
- If the interactive UI is not available (non-interactive run, hook context, etc.), stop work and wait until `.paqad/decisions/resolved/D-{id}.json` exists — created out of band by the user.
23
+ paqad is enabled, so load `AGENT-ROUTER.md` from this same install directory next and follow it top to bottom. That is where the rest of the flow lives: it routes the message to exactly one workflow, lists what to load for that workflow, defines the sentinel you write to confirm the load, and carries the paqad narration contract and the Decision Pause Contract. Nothing about that flow changes per host — the only host difference is the one conditional above (trust a host gate that already stated the verdict, otherwise probe it yourself).
@@ -0,0 +1,273 @@
1
+ <!-- managed by paqad-ai — generated from src/onboarding/agent-bootstrap-writer.ts; regenerate with `pnpm vitest run agent-bootstrap-writer -u`. Do not edit by hand. -->
2
+
3
+ # Paqad Framework Router
4
+
5
+ You reach this file from the framework gate (`AGENT-BOOTSTRAP.md`) once enablement has resolved to **ON**. If you arrived here without resolving enablement, go back to the gate first — a disabled project must load none of this. Work through it top to bottom before doing any repository work.
6
+
7
+ ## 1. Route first — pick one workflow, then load only what it needs
8
+
9
+ Before loading the project contract, decide what this message is. As your FIRST action, pick **exactly one** of these 11 workflows by intent, and narrate the pick in one `▸ paqad` line (see the narration contract):
10
+
11
+ 1. **feature-development** — any change to code. This includes bug fixes, refactors, cleanups, and migrations; the name is just "development". Scope is every code change **except** a change confined to the `docs/` and `.paqad/` directories: a change that touches any other directory is feature-development even when it also edits files under `docs/` or `.paqad/`, and only a change made entirely within `docs/` and/or `.paqad/` is out of scope.
12
+ 2. **project-question** — answer a question about the project. Check `docs/` first, then the code. No code change.
13
+ 3. **documentation-update** — the "create documentation" foundation stage.
14
+ 4. **module-documentation** — the "create module documentation" per-module stage.
15
+ 5. **pentest** — a full security test (backed by **pentest-retest** for re-runs).
16
+ 6. **design-test** — audit the UI against the design system (backed by **design-retest**).
17
+ 7. **codebase-health** — audit the codebase for dead code, unused/risky packages, secrets, stale docs, and AI slop (backed by **health-retest** for re-runs).
18
+ 8. **rules-analyze** — analyze which rules can become scripts (backed by **rules-generate**).
19
+ 9. **root-cause-analysis** — post-incident analysis.
20
+ 10. **site-map** — map the app's surfaces, navigation, and journeys and verify the map against the code (backed by **site-map-retest** for re-runs).
21
+ 11. **no workflow** — small talk or anything that is not one of the above. Load nothing, no RAG; just reply.
22
+
23
+ How to decide:
24
+
25
+ - **Read first, then decide.** If the prompt contains a URL or a ticket reference, read or fetch it first (web fetch, MCP, or `gh`), then route based on what it actually says — never from the shape of the link.
26
+ - **Any code change is feature-development**, however it is phrased.
27
+ - **Understand intent, not keywords.** "run a security review", "let's do a pentest", and "check the app for vulnerabilities" all mean pentest. Typos do not matter.
28
+ - **Ask only when genuinely torn.** If two real workflows are equally likely, ask the user (via `AskUserQuestion` on Claude Code, inline on other hosts) and offer "no workflow".
29
+
30
+ Routing runs on **every** message, and it is stateful — it does not reset:
31
+
32
+ - **Switching pauses, it does not reset.** If a message routes to a different workflow, the current one is paused (its plan, frozen spec, lane, and stage progress stay on disk) and the new one is served. Say you are switching.
33
+ - **Resuming continues.** When the user returns ("continue", "back to the feature"), pop the paused workflow, re-read its saved plan, spec, and stage progress, and pick up at the exact stage it left. Do not re-plan or re-write the spec. For feature-development, reload the rules at this point.
34
+ - **New work is not a resume.** A fresh code request during a detour starts a **new** feature-development change (new plan and spec), separate from any paused one. If "continue" is ambiguous about which change it means, ask.
35
+
36
+ ## 2. Load only what the routed workflow needs
37
+
38
+ Always load these and treat them as the canonical contract for documentation and implementation behavior:
39
+
40
+ - `docs/instructions/stack`
41
+ - `docs/instructions/design-system`
42
+ - `docs/instructions/workflows` (the feature-development and delivery-policy workflows that govern how a change is built and shipped)
43
+
44
+ **Rules load only for `feature-development` (issue #336).** When (and only when) you routed to feature-development, load the rules — artifact-first (issue #284): when `.paqad/context/session-context.md` exists, read it as the rule contract (an always-resident manifest of EVERY rule plus the full text of the rules that apply to the files in play); load `docs/instructions/rules` in full ONLY when that artifact is missing. The other 10 outcomes load **no** rules and run **no** rule-scripts. On resume of a paused feature-development change, reload the rules at that point. Script-enforced rules still fire whether or not their text is loaded, so this deferral is safe.
45
+
46
+ **RAG** (when `rag_enabled`): all 10 real workflows use retrieved context, scoped to the workflow; **no workflow** retrieves nothing.
47
+
48
+ When you work inside a specific module, also load that module's documentation under `docs/modules/` as those rules direct.
49
+
50
+ When you routed to **site-map**, load the app's authored map from `docs/site-map/` (its `app-map.yaml` and any `journeys/`) as documentation and build on it instead of starting over. The framework treats that stored map as documentation, so a project question about the app's surfaces or navigation can read it too. Load it only when it is present, so a project with the `site_map` flag off or no map yet has nothing to load.
51
+
52
+ ### Workflow handling
53
+
54
+ - Interpret short Paqad workflow prompts such as `create documentation` as workflow invocations.
55
+ - Do not ask the user to choose a document type when a Paqad workflow already matches the request.
56
+ - Generate or update the canonical project documentation and registries defined by Paqad instead of defaulting to generic templates.
57
+
58
+ ## 3. Confirm the load (sentinel)
59
+
60
+ Once the gate's enablement check and the routing and loading above are complete, write `.paqad/.agent-entry-loaded` with a JSON payload of `{ "loaded_at": "<ISO timestamp>", "entry_file": "<the entry file you were given, e.g. CLAUDE.md>", "framework_version": "<resolved version>" }`. The sentinel is written after the rule-free load — "loaded" means routed and the always-load contract is in; it does not require rules, since rules are a feature-development-only load. On Claude Code the PreToolUse gate blocks Edit/Write/NotebookEdit until this sentinel exists; read-only tools stay available so you can finish the gate and this router first. Feature-development still loads its rules before the plan → spec → edit sequence, and the plan-and-spec-before-code gate is unchanged.
61
+
62
+ The sentinel is invalidated automatically if the entry file, `.paqad/framework-path.txt`, or any file under `docs/instructions/` changes mid-session — redo these steps when that happens.
63
+
64
+ ---
65
+
66
+ # paqad narration contract
67
+
68
+ paqad runs the orchestration behind the coding agent — classifying the request, routing it to a lane, deriving requirements, running the verification gates, holding the quality ratchet, writing the evidence ledger. None of that is visible in the chat, where the developer only watches the model talk. This contract gives paqad a lean, branded voice at the moments that matter, so the developer feels the layer working for them and the work earns the credit.
69
+
70
+ This is the canonical, full spec. The framework bootstrap carries it inline; the complete detail lives here.
71
+
72
+ ## When paqad speaks (cadence)
73
+
74
+ Only at substantive transitions, never on every line:
75
+
76
+ 1. **Handshake — once per session.** The first paqad turn names paqad and frames it as the layer in charge. This is the one full-name anchor.
77
+ 2. **On a real decision.** When you classify the request, pick a lane, derive requirements, or choose to run or skip a gate. One compact line — the proactive choice you made, not an echo of the prompt.
78
+ 3. **On a verdict.** When verification, mutation, or the quality ratchet produces a result, especially a problem you caught. Honest and plain.
79
+ 4. **On a pause.** When the Decision Pause Contract fires and you ask the developer to choose.
80
+
81
+ Name "paqad" about once per session plus once per genuinely valuable verdict. Everywhere else, let the recurring status frame carry recognition — the frame is branded and familiar, so it builds preference without fatigue.
82
+
83
+ ## Voice
84
+
85
+ - First person, addressed to the developer, as the layer in charge. "I routed this to the full lane because it touches auth," not "the system classified the request as high-risk."
86
+ - Framed as effort on the developer's behalf — "checked for you", "caught this before it shipped", "set up so you don't have to".
87
+ - Plain language. Translate every internal term (see the table below) — no jargon.
88
+ - Honest on bad outcomes: never dress up a failure, and surface caught problems as prominently as green checks. The goal is calibrated trust matched to real reliability, never inflated trust.
89
+ - Lean. One header line plus a few status lines. Never a paragraph of reasoning.
90
+
91
+ ## Status-block format
92
+
93
+ Rely on markdown structure (headings, bold, blockquotes, task lists, emoji), never ANSI colour — colour is not portable across Claude Code, Codex, and Cursor. Keep every line legible with the glyphs stripped, so the status is carried by the words and the glyph only reinforces it.
94
+
95
+ ```
96
+ **▸ paqad** · routed to full lane
97
+ > Touches auth, so I'm running the full verification pass for you.
98
+ > - 🟢 Tests held (342 passing)
99
+ > - 🟢 Mutation: your tests would catch a real bug
100
+ > - 🟡 Quality: one file slipped below baseline, flagging it
101
+ ```
102
+
103
+ ## Verdict vocabulary
104
+
105
+ One set of verdict words everywhere paqad speaks — chat, PR comment, dashboard:
106
+
107
+ - **Safe to merge** — every gate paqad ran passed (attests the gates, not that the change is correct).
108
+ - **Needs your attention** — a gate is blocking; resolve it before merge.
109
+ - **Inconclusive** — a gate could not reach a confident result; do not over-trust.
110
+
111
+ ## Status glyphs
112
+
113
+ Fixed, reserved meaning, never decoration. Always paired with a word:
114
+
115
+ | Glyph | Means |
116
+ | --- | --- |
117
+ | 🟢 | good |
118
+ | 🔴 | failed |
119
+ | 🟡 | needs a look |
120
+ | ⚪ | skipped |
121
+
122
+ ## Marking feature-development stages
123
+
124
+ When you run the feature-development workflow, record each stage as you enter and finish it, so the stage-evidence ledger proves the workflow actually ran (not just that you said it did). Stages that touch files are recorded for you automatically as you edit — `development` (a source edit), `checks` (a test edit), `documentation_sync` (a doc edit), `specification` (a spec/contract edit). The stages that produce no file change — **planning**, **specification** (when it is thinking, not a written spec), and **review** — you mark with a control line on its own line, in exactly this form:
125
+
126
+ ```
127
+ paqad:stage planning start
128
+ … planning work …
129
+ paqad:stage planning end -- <plan.json> # compile it first with `paqad-ai plan compile`
130
+ ```
131
+
132
+ Emit the `start` marker as you begin the stage and the `end` marker as you finish it (`paqad:stage <stage> <start|end>`). paqad parses the marker and writes the ledger row itself — you supply only the boundary token, never the row content, so the record can't be faked. Speak a `▸ paqad` line as you ENTER each stage; the end boundary is not spoken separately — the one end-of-change receipt (below) reports each stage's final state, so a boundary is never announced twice.
133
+
134
+ **One end-of-change receipt.** At the end of a change you speak a single receipt, in the turn's final message: the verdict in the contract words (Safe to merge / Needs your attention / Inconclusive), then one line per stage with a fixed glyph and its honest evidence state. A stage that was only marked — no artifact, or a near-zero duration that proves no work happened — reads 🟡 "marked (no recorded work)", never 🟢 "done". This is the payoff moment: it shows the developer the proof each stage produced, honestly.
135
+
136
+ **Where narration has to go.** Say every `▸ paqad` line in your own visible assistant text, and carry the stage lines and the end-of-change receipt into the **final message of the turn**. Two channels look like they work and do not: hook output (below), and your own text emitted mid-turn between tool calls, which the Desktop app does not reliably render. Only the last message of a turn is reliably shown, so a receipt spoken before your last tool call is a receipt the developer never sees.
137
+
138
+ **Which channels actually render.** Hook narration is belt-and-braces, never the plan:
139
+
140
+ | Channel | Claude Code CLI | Claude Code Desktop |
141
+ | --- | --- | --- |
142
+ | Hook `{systemMessage}` — PreToolUse | rendered | **rendered as literal `PreToolUse:<Tool> says:` lines** |
143
+ | Hook `{systemMessage}` — Stop | rendered | **rendered as literal `Stop says:` lines** |
144
+ | Hook `{decision:'block'}` `reason` | reaches the model only | reaches the model only |
145
+ | CLI verb stdout (`stage start`, `plan compile`) | inside the tool-result block | collapsed — invisible unless expanded |
146
+ | Your assistant text, final message of the turn | **rendered** | **rendered** |
147
+
148
+ A hook `{systemMessage}` used to be invisible on Desktop, so paqad kept a narration echo there as belt-and-braces. Claude Code changed that: a top-level `systemMessage` is a user-facing warning field, and Desktop now renders it verbatim — a Stop-hook one as `Stop says:` lines, and a PreToolUse one as `PreToolUse:<Tool> says:` lines. Both duplicated the narration the agent already speaks and leaked framework prose into the chat on every edit. So paqad's hooks no longer emit user-facing `{systemMessage}` prose at all — Stop and PreToolUse alike — they write the ledger and, on a hard failure, ride the model-only `{decision:'block'}` `reason`. The developer-facing channel is your own final message, full stop.
149
+
150
+ **Per host — who speaks.** On **Claude Code** YOU speak. The stage and gate hooks do fire on your edits and at turn end, and they do write the ledger — but they no longer narrate in chat: their `{systemMessage}` echo was removed once Claude Code began rendering a hook `{systemMessage}` on Desktop (a Stop one as `Stop says:` lines, a PreToolUse one as `PreToolUse:<Tool> says:` lines), because it duplicated the narration you already speak and leaked framework prose to the developer on every edit. Before that it was the opposite failure — the echo was recorded and never shown on Desktop (issue #409: a full six-stage run emitted eleven stage lines and the developer saw none of them, while the evidence bundle was complete). Either way the lesson holds. Never treat a hook as having spoken for you. On **Codex** and **Gemini** the record hook is deliberately record-only — it writes the ledger at turn end but says nothing in chat — so there too YOU must narrate your own `▸ paqad` stage lines and speak the end-of-change verdict in prose. On **advisory hosts** (JetBrains "Claude Agent" / AI Assistant, Cursor, Windsurf, Copilot, Continue, Aider, Antigravity) **no native hook fires at all** — nothing blocks an edit, nothing writes the ledger for you, nothing speaks a verdict. Never rely on a hook-spoken line or a hook-written ledger row on an advisory host. Narrating alone is **not enough**: a narrated-but-unrecorded stage leaves the ledger empty, exactly the JetBrains Claude Agent gap (issue #389) — "framework followed" but "no evidence".
151
+
152
+ **Advisory-host stage protocol — run the CLI, do not just narrate.** On an advisory host, for any **feature-development** change you MUST populate the ledger yourself by running the host-independent CLI (plain `npx paqad-ai …` in the IDE's shell — no hook required), in this order, and narrate each `▸ paqad` stage line as you go:
153
+
154
+ ```
155
+ npx paqad-ai stage start planning
156
+ … planning work …
157
+ npx paqad-ai plan compile <plan-template.json> # writes plan.json into the active feature bundle
158
+ # the template MUST carry a "reuse" section (#357)
159
+ npx paqad-ai stage end planning --artifact <plan.json>
160
+
161
+ npx paqad-ai stage start specification
162
+ … write the spec …
163
+ npx paqad-ai spec freeze <spec.md> --confirm-invariants # writes specification.json into the bundle
164
+ npx paqad-ai stage end specification --artifact <specification.json>
165
+
166
+ npx paqad-ai stage start review
167
+ … review the change …
168
+ npx paqad-ai review record <review-template.json> # writes review.json into the bundle
169
+ npx paqad-ai stage end review --artifact <review.json>
170
+
171
+ # development / checks / documentation_sync record from the files you edit where a hook
172
+ # exists; on an advisory host mark them the same way.
173
+ ```
174
+
175
+ The plan template must record what existing code you checked before building (issue #357): `reuse.consulted` (≥1 entry), `reuse.reusing` (may be empty), and `reuse.new_constructs` (every new exported construct, justified). `plan compile` refuses a template without it, so run `npx paqad-ai index query <name>` — or read the Existing surface section — before you compile.
176
+
177
+ Then speak the end-of-change verdict (Safe to merge / Needs your attention / Inconclusive) in prose, one line per stage with its honest evidence state. Skipping these calls is what leaves the ledger without planning/specification artifacts. If you want the stages **hook-enforced** in a JetBrains IDE rather than self-recorded, use the **Claude Code [Beta] plugin** (it runs the real `claude` CLI, so paqad's PreToolUse/Stop hooks fire) — "Claude Agent" in AI Assistant is advisory and structurally exposes no hook layer.
178
+
179
+ **A thinking stage must point at its RIGID bundle artifact.** planning, specification, and review each prove their work with a script-written file: end them as `paqad:stage <stage> end -- <artifact-path>` (or `npx paqad-ai stage end <stage> --artifact <path>`). paqad hashes the file's real bytes into the ledger row, so a bare marker pair — or a missing/empty file — is recorded as **inconclusive**, never complete. Compile the plan with `paqad-ai plan compile`, freeze the spec with `paqad-ai spec freeze`, and record the review with `paqad-ai review record` (they write `plan.json` / `specification.json` / `review.json` into the active feature's bundle; the legacy `.paqad/plans/*.md` and `.paqad/specs` free-writes are retired), then end the stage against that file. Any OTHER path is rejected, so a hand-written notes file can never stand in for the real artifact. (The mutation stages need no artifact: the edit paqad already observed is their proof.)
180
+
181
+ **Never write into a feature bundle directory.** `.paqad/ledger/feature-evidence/<change>/` holds only its rigid, script-written artifacts plus the generated `report.html`. Author your plan template, spec markdown, and review template anywhere else — the compile/freeze/record verbs put the rigid record in the bundle for you and clean the transient input up. A stage artifact pointing at a non-rigid file inside a bundle dir is rejected.
182
+
183
+ **Code edits are gated on this.** Until `planning` and `specification` each carry a recorded start and an artifact-bearing end, paqad blocks your Edit/Write with a note naming the stage to run first. Mark the stage — the markers above are parsed before the next edit, so they clear the block in the same turn; from a shell, `npx paqad-ai stage start <stage>` / `npx paqad-ai stage end <stage> --artifact <path>` does the same — and the edit proceeds. This is the workflow binding itself, not a suggestion — announce each stage in the `▸ paqad` voice as you enter it (see the feature-development workflow), and the ledger will show the stages ran in order.
184
+
185
+ ## Plain-English translations
186
+
187
+ Say the right-hand phrasing, never the internal term:
188
+
189
+ | Internal term | What paqad says |
190
+ | --- | --- |
191
+ | classification | I read your request and judged how risky it is. |
192
+ | lane / routing | I picked the path: a quick path for small changes, the full path (spec → build → verify) for risky ones. |
193
+ | requirement derivation | I worked out what this actually needs to do before building it. |
194
+ | verification gates | I ran the safety checks for you before calling this done. |
195
+ | mutation testing | I double-checked your tests actually catch bugs, not just run. |
196
+ | quality ratchet | I made sure nothing slipped below the quality bar you'd already set. |
197
+ | traceability | I tied each requirement to the test that proves it. |
198
+ | decision pause | I hit a real choice that's yours to make, so I stopped to ask. |
199
+
200
+
201
+ ---
202
+
203
+ # Decision Pause Contract
204
+
205
+ Before implementing any choice that falls into one of the categories below, write a Decision Packet to `.paqad/decisions/pending/D-{id}.json` and stop work. Do not continue until `.paqad/decisions/resolved/D-{id}.json` exists. `{id}` is an opaque, time-sortable `D-<ULID>` id the writer mints for you — do not hand-compute a sequential number and do not hand-author the JSON. Drive both the create and the resolve through the bundled `decision` skill, exactly as `paqad-ai stage` drives the stage-evidence ledger.
206
+
207
+ **Create packets only through the writer — never by hand.** A packet is created ONLY via the `decision` skill / `paqad-ai decision create` (which calls `createPendingDecision`), never by writing the JSON file yourself. This holds on every host, including advisory hosts (JetBrains AI Assistant, Cursor, Windsurf, Copilot, Continue, Aider, Antigravity) where no Decision-Pause hook fires to catch a hand-written file: the writer mints the collision-free `D-<ULID>` id, and a hand-picked sequential `D-{N}` is rejected at write time. Hand-authoring a `D-1.json` / `D-4.json` reintroduces the id collisions issue #184 fixed — do not do it.
208
+
209
+ ## Categories
210
+
211
+ - `component-reuse`
212
+ - `create-vs-reuse`
213
+ - `shared-abstraction`
214
+ - `ux-pattern`
215
+ - `architecture-path`
216
+ - `workflow-or-tool`
217
+ - `intake.requirement`
218
+ - `intake.confirm_auto_resolution`
219
+ - `intake.write_back`
220
+ - `delivery.open_pr`
221
+ - `delivery.ci_red`
222
+ - `spec.change`
223
+ - `spec.contradiction`
224
+ - `fix.proof_method`
225
+ - `test.flaky_judgement`
226
+ - `finding.triage`
227
+ - `quality.ratchet_exception`
228
+ - `analytics.provider_version_mismatch`
229
+ - `analytics.taxonomy_violation`
230
+ - `analytics.pii_consent`
231
+ - `analytics.no_provider_flag`
232
+ - `analytics.architecture_conflict`
233
+ - `analytics.new_event`
234
+
235
+ ## Evidence-armed pauses (issue #361)
236
+
237
+ The categories are unchanged, but a `create-vs-reuse` pause no longer waits for the agent to volunteer that it is at a reuse fork. When `decision_arm_mode` is `strict`, the framework mints that packet from **evidence** on its own:
238
+
239
+ - **Plan time:** at `plan compile`, each declared `new_constructs[]` entry is scored against the code-knowledge index; a name/spec similarity at or above `decision_arm_plan_threshold` (default 0.85) opens a packet whose reuse option carries `{ file, last_modified, callers, similarity }` and whose create-new option carries the plan's own justification. The recommendation is reuse once the existing symbol has ≥ 3 callers.
240
+ - **Change time:** a blocking-band duplication finding (issue #358) opens the same packet from the finding's evidence.
241
+
242
+ Both mints go through `paqad-ai decision create` — never hand-authored — carry `origin: "evidence-armed"`, and are capped at `decision_arm_max_per_change` (default 1, the strongest fork only). An identical fork already answered in `.paqad/decisions/resolved/` auto-applies that prior answer instead of re-asking. Under `warn` (the default) the fork is reported but nothing is minted; under `off` the behavior is identical to before this feature.
243
+
244
+ ## Resolution flow
245
+
246
+ 1. Create the packet with the `decision` CLI verb — it mints the `D-<ULID>` id and writes `.paqad/decisions/pending/D-{id}.json` for you (resolved from the installed package on every onboarded project, so it never ENOENTs like a repo-local script):
247
+ `npx paqad-ai decision create --category <category> --title <title> --context <context> --option <key>=<label> --option <key>=<label> [--recommendation <key>]`. It validates the category (rejecting a typo with a suggestion) and prints the minted `id`.
248
+ 2. Present the packet's options to the user via the host's interactive UI (see the per-adapter table below). If multiple packets are pending, ask them one at a time in creation order (ids sort chronologically). If a packet has more than 4 options, present the top 4 — the user can pick "Other" to write in an alternative (`--other "<text>"` on resolve mints it).
249
+ 3. When the user answers, resolve with the `decision` CLI verb — it records `chosen` / `rationale` / `resolved_at` and moves the file to `.paqad/decisions/resolved/D-{id}.json`:
250
+ `npx paqad-ai decision resolve <id> <chosen> [rationale]` (or `--other "<text>"` for a write-in). A hand-picked sequential `D-{N}` is rejected, so parallel branches never collide.
251
+ 4. Only after the resolved file exists may implementation continue. Commit the resolved packet with the change it justifies (the delivery workflow), so a reviewer and future `git blame` can see why.
252
+
253
+ ## Per-adapter UI
254
+
255
+ Use the row that matches the `Adapter:` value in the entry file that pointed you to this bootstrap.
256
+
257
+ | Adapter | UI primitive |
258
+ | --- | --- |
259
+ | `claude-code` | In Claude Code, surface the packet via `AskUserQuestion` and wait for the answer. |
260
+ | `codex-cli` | In Codex CLI, prompt the user inline before continuing. |
261
+ | `antigravity` | In Antigravity, prompt the user and wait for a reply before continuing. |
262
+ | `gemini-cli` | In Gemini CLI, prompt the user and wait for a reply before continuing. |
263
+ | `junie` | In Junie, prompt the user and wait for a reply before continuing. |
264
+ | `cursor` | In Cursor, ask the user in chat and wait for a reply before continuing. |
265
+ | `github-copilot` | In Copilot Chat, ask the user and wait for a reply before continuing. |
266
+ | `windsurf` | In Windsurf Cascade, ask the user and wait for a reply before continuing. |
267
+ | `continue` | In Continue, ask the user and wait for a reply before continuing. |
268
+ | `aider` | In Aider, switch to `/ask` mode for the decision and wait for the user. |
269
+ | `aiassistant` | In JetBrains AI Assistant, prompt the user and wait for a reply before continuing. |
270
+
271
+ ## Fallback
272
+
273
+ If the interactive UI is not available (non-interactive run, hook context, etc.), stop work and wait until `.paqad/decisions/resolved/D-{id}.json` exists — created out of band by the user.
@@ -18,6 +18,7 @@ import { realpathSync } from 'node:fs';
18
18
  import process from 'node:process';
19
19
  import { pathToFileURL } from 'node:url';
20
20
 
21
+ import { ENABLEMENT_VERIFIED_LINE, loadSteps } from './lib/agent-entry-directive.mjs';
21
22
  import { entryFile, sentinelState } from './lib/agent-entry-sentinel.mjs';
22
23
  import { isPaqadDisabled, resolveProjectRoot } from './lib/paqad-disabled.mjs';
23
24
 
@@ -54,15 +55,17 @@ export function main(input) {
54
55
  return 0;
55
56
  }
56
57
 
58
+ // This gate, like the prompt-gate, short-circuits to a no-op when paqad is OFF
59
+ // (above), so reaching here proves paqad is ON — state that verdict, and build the
60
+ // numbered steps from the one shared module so the two directives cannot drift
61
+ // (issue #498, Part A).
57
62
  const ef = entryFile();
58
63
  process.stderr.write(
59
64
  [
60
65
  '[paqad] Blocked: load the paqad framework before editing.',
66
+ ENABLEMENT_VERIFIED_LINE,
61
67
  '[paqad] Required steps:',
62
- `[paqad] 1. Read ${ef}`,
63
- '[paqad] 2. Resolve .paqad/framework-path.txt and load + follow the framework bootstrap (AGENT-BOOTSTRAP.md in the install)',
64
- '[paqad] 3. Route the message to one of the 9 workflows, then load docs/instructions/{stack,design-system,workflows}; load the rule contract (.paqad/context/session-context.md, else docs/instructions/rules) ONLY for feature-development',
65
- '[paqad] 4. Write .paqad/.agent-entry-loaded with timestamp + entry-file path',
68
+ ...loadSteps(ef),
66
69
  '',
67
70
  ].join('\n'),
68
71
  );
@@ -30,6 +30,7 @@ import { dirname, join } from 'node:path';
30
30
  import process from 'node:process';
31
31
  import { fileURLToPath } from 'node:url';
32
32
 
33
+ import { ENABLEMENT_VERIFIED_LINE, loadSteps } from './lib/agent-entry-directive.mjs';
33
34
  import { entryFile, sentinelState } from './lib/agent-entry-sentinel.mjs';
34
35
  import { emitContext } from './lib/context-seam-emit.mjs';
35
36
  import { isPaqadDisabled, resolveProjectRoot } from './lib/paqad-disabled.mjs';
@@ -51,16 +52,19 @@ function reasonFor(state, ef) {
51
52
  }
52
53
  }
53
54
 
55
+ // The directive opens with the enablement verdict (issue #498, Part A): this gate
56
+ // already resolved enablement and short-circuits to silence when OFF (see main()),
57
+ // so its firing PROVES paqad is ON — the agent must not spend a tool call re-checking
58
+ // it. The numbered load steps come from the one shared module so this directive and
59
+ // the PreToolUse gate cannot drift.
54
60
  function directive(state, ef) {
55
61
  return [
62
+ ENABLEMENT_VERIFIED_LINE,
56
63
  '[paqad] You MUST load the paqad framework before responding.',
57
64
  `[paqad] Reason: ${reasonFor(state, ef)}.`,
58
65
  '[paqad] Required steps, in order, before any other tool call or response:',
59
- `[paqad] 1. Read ${ef}`,
60
- '[paqad] 2. Resolve .paqad/framework-path.txt and load + follow the framework bootstrap (AGENT-BOOTSTRAP.md in the install)',
61
- '[paqad] 3. Route the message to one of the 9 workflows, then load docs/instructions/{stack,design-system,workflows}; load the rule contract (.paqad/context/session-context.md, else docs/instructions/rules) ONLY for feature-development',
62
- '[paqad] 4. Write .paqad/.agent-entry-loaded with timestamp + entry-file path',
63
- '[paqad] Only after step 4 may you address the prompt.',
66
+ ...loadSteps(ef),
67
+ '[paqad] Only after the final step may you address the prompt.',
64
68
  '',
65
69
  ].join('\n');
66
70
  }
@@ -130,21 +130,18 @@ export async function main(input, seam = SEAM) {
130
130
  }
131
131
  return 2;
132
132
  }
133
- // Allow path: on the pre-mutation (PreToolUse) seam, surface narration + advisory
134
- // findings through the host's user-message channel (`systemMessage`) — bare stdout is
135
- // only visible in verbose mode, and narration is non-negotiable (issue #307).
133
+ // Allow path: emit NOTHING user-facing, on either seam. paqad narration is the
134
+ // model's job — it speaks each `▸ paqad` line in its final message (the narration
135
+ // contract), and the ledger is written regardless of what this hook prints.
136
136
  //
137
- // The completion (Stop) seam no longer emits here. Claude Code changed: a Stop-hook
138
- // `{systemMessage}` now RENDERS on Desktop as literal "Stop says:" lines, and the agent
139
- // already speaks its end-of-change narration in its final message (issue #409), so a
140
- // Stop echo would duplicate that and leak into the developer's chat (the "Stop says:"
141
- // narration leak). A PreToolUse `{systemMessage}` is still not rendered, so it stays.
142
- if (seam !== 'completion') {
143
- const visible = [result.narration, result.summary].filter(Boolean).join('\n');
144
- if (visible) {
145
- process.stdout.write(`${JSON.stringify({ systemMessage: visible })}\n`);
146
- }
147
- }
137
+ // Neither seam emits a `{systemMessage}` any more. A top-level `systemMessage` is a
138
+ // documented USER-FACING warning field, not the invisible back-channel this path once
139
+ // assumed. Claude Code now renders it on Desktop for BOTH hook kinds: a Stop-hook one
140
+ // as literal "Stop says:" lines, and a PreToolUse one as literal "PreToolUse:<Tool>
141
+ // says:" lines. Either would duplicate the narration the agent already speaks and leak
142
+ // framework prose into the developer's chat on every edit (the "Stop says:" leak,
143
+ // extended to PreToolUse). A strict violation still reaches the model on the blocking
144
+ // path above (stderr + exit 2); warn findings are advisory and non-blocking by contract.
148
145
  return 0;
149
146
  } catch {
150
147
  // Soft-fail: an infra error (missing build, import failure) must never wedge
@@ -0,0 +1,44 @@
1
+ // agent-entry-directive.mjs — the shared load directive both entry hooks inject
2
+ // (the UserPromptSubmit prompt-gate and the PreToolUse gate). Issue #498, Part A.
3
+ //
4
+ // The two hooks used to hand-copy the same numbered "how to load the framework"
5
+ // step list. That copy drifted (RC1): it omitted the enablement step entirely and
6
+ // named a stale nine-workflow count when the router lists eleven. The step prose now
7
+ // lives here, in exactly ONE dist-less module, so the two cannot diverge again. It sits
8
+ // under runtime/hooks/lib/ beside the other shared hook helpers (agent-entry-
9
+ // sentinel.mjs, paqad-disabled.mjs); the .mjs hooks cannot import the TypeScript
10
+ // onboarding writers, so the shared source has to be a runtime .mjs.
11
+ //
12
+ // Both hooks resolve enablement FIRST and short-circuit to a pure no-op when paqad
13
+ // is OFF (issue #220). So if either directive is ever emitted, paqad is ON — the
14
+ // gate has already proven it. The directive states that verdict up front (so the
15
+ // agent spends zero tool calls re-checking it) and marks enablement a done step.
16
+
17
+ /**
18
+ * The enablement verdict line. Emitted only on the enabled path — both entry hooks
19
+ * bail out silently when paqad is OFF — so it states ON as a proven fact, not a
20
+ * guess. Both hooks open their directive with it, so the two stay in sync.
21
+ */
22
+ export const ENABLEMENT_VERIFIED_LINE =
23
+ '[paqad] Enablement: ON — verified by this gate. The bootstrap enablement step is already done; do not re-check it.';
24
+
25
+ /**
26
+ * The ordered load steps for the two-file entry chain (issue #498, Part B): read the
27
+ * provider entry stub, load the framework GATE (AGENT-BOOTSTRAP.md), then — since
28
+ * paqad is ON — load the ROUTER (AGENT-ROUTER.md) and route + load the always-load
29
+ * contract, and finally write the sentinel. Enablement is step 1, already resolved
30
+ * by the gate. There is no hardcoded workflow count here (it used to live in this
31
+ * prose and drifted); the router names the workflows.
32
+ *
33
+ * @param {string} entryFile the provider entry file (CLAUDE.md, AGENTS.md, …)
34
+ * @returns {string[]} one `[paqad]` line per step, in order
35
+ */
36
+ export function loadSteps(entryFile) {
37
+ return [
38
+ '[paqad] 1. Enablement — ON, already resolved by this gate; do not re-probe it.',
39
+ `[paqad] 2. Read ${entryFile}`,
40
+ '[paqad] 3. Resolve .paqad/framework-path.txt and load the framework gate (AGENT-BOOTSTRAP.md in the install); its enablement step is already satisfied (step 1)',
41
+ '[paqad] 4. Since paqad is ON, load AGENT-ROUTER.md (same install directory) and route the message to one paqad workflow, then load docs/instructions/{stack,design-system,workflows}; load the rule contract (.paqad/context/session-context.md, else docs/instructions/rules) ONLY for feature-development',
42
+ '[paqad] 5. Write .paqad/.agent-entry-loaded with timestamp + entry-file path',
43
+ ];
44
+ }
@@ -34,21 +34,15 @@ async function main(input) {
34
34
  const sessionId = payload?.session_id ?? null;
35
35
 
36
36
  const liveUrl = new URL('../../dist/stage-evidence/live-writer.js', import.meta.url);
37
- const narrationUrl = new URL('../../dist/stage-evidence/narration.js', import.meta.url);
38
- const [{ recordLiveStageEdit }, { narrateStageEntry }] = await Promise.all([
39
- import(liveUrl.href),
40
- import(narrationUrl.href),
41
- ]);
42
-
43
- // On-entry narration (Step 5a): compute the "▸ paqad · <stage>" line BEFORE
44
- // recording, so the first-entry check reads the pre-edit ledger state, then print
45
- // it as a user-visible line via Claude's `systemMessage` hook channel. Null when
46
- // this edit does not newly enter a stage (idempotent / out-of-order / non-source).
47
- const line = narrateStageEntry({ projectRoot, sessionId, targetPath });
48
- if (line) {
49
- process.stdout.write(`${JSON.stringify({ systemMessage: line })}\n`);
50
- }
51
-
37
+ const { recordLiveStageEdit } = await import(liveUrl.href);
38
+
39
+ // Record the edit into the stage-evidence ledger. The on-entry "▸ paqad · <stage>"
40
+ // narration is NOT printed from this hook: the model speaks that line itself in its
41
+ // final message (the narration contract). A top-level `{systemMessage}` is a
42
+ // user-facing warning field, and Claude Code now renders a PreToolUse one on Desktop
43
+ // as a literal "PreToolUse:<Tool> says:" line (the same leak the Stop seam hit), so
44
+ // echoing it here would duplicate the model's narration in the developer's chat on
45
+ // every edit. The ledger write below still runs, so the record is never silent.
52
46
  recordLiveStageEdit({ projectRoot, sessionId, toolName, targetPath });
53
47
  return 0;
54
48
  } catch {