@pareto4/pp-core 1.13.0 → 1.17.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 (201) hide show
  1. package/.claude-plugin/plugin.json +3 -3
  2. package/README.md +26 -9
  3. package/agents/pp-code-reviewer.md +2 -1
  4. package/agents/pp-debug-session-manager.md +9 -6
  5. package/agents/pp-executor.md +10 -13
  6. package/agents/pp-plan-checker.md +4 -4
  7. package/agents/pp-planner.md +23 -9
  8. package/agents/pp-vault-reviewer.md +244 -21
  9. package/agents/pp-verifier.md +7 -3
  10. package/bin/install.js +10 -3
  11. package/commands/pp-execute-phase.md +1 -3
  12. package/commands/pp-ns-review.md +1 -1
  13. package/commands/pp-plan-phase.md +1 -2
  14. package/commands/pp-vault-review.md +5 -3
  15. package/commands/pp-verify-work.md +1 -2
  16. package/gemini-extension.json +1 -1
  17. package/hooks/dist/pp-check-update.js +0 -0
  18. package/hooks/dist/pp-cursor-session-start.js +1 -1
  19. package/hooks/dist/pp-statusline.js +1 -1
  20. package/hooks/dist/pp-update-banner.js +0 -0
  21. package/hooks/pp-check-update.js +0 -0
  22. package/hooks/pp-cursor-session-start.js +1 -1
  23. package/hooks/pp-statusline.js +1 -1
  24. package/hooks/pp-update-banner.js +0 -0
  25. package/package.json +14 -19
  26. package/pareto-skills/_shared/create-project-board.sh +0 -0
  27. package/pareto-skills/pp-project-setup/SKILL.md +1 -1
  28. package/pp-core/bin/check-latest-version.cjs +0 -0
  29. package/pp-core/bin/detect-build-commands.sh +2 -2
  30. package/pp-core/bin/lib/capability-activation.cjs +83 -9
  31. package/pp-core/bin/lib/capability-registry.cjs +396 -82
  32. package/pp-core/bin/lib/capability-state.cjs +160 -4
  33. package/pp-core/bin/lib/capability-writer.cjs +19 -5
  34. package/pp-core/bin/lib/check-command-router.cjs +3334 -946
  35. package/pp-core/bin/lib/clusters.cjs +3 -0
  36. package/pp-core/bin/lib/command-aliases.cjs +0 -35
  37. package/pp-core/bin/lib/commands.cjs +9 -4
  38. package/pp-core/bin/lib/compose-bindings.cjs +251 -88
  39. package/pp-core/bin/lib/config-loader.cjs +7 -103
  40. package/pp-core/bin/lib/config-schema.cjs +1 -1
  41. package/pp-core/bin/lib/config.cjs +154 -62
  42. package/pp-core/bin/lib/decisions.cjs +4 -4
  43. package/pp-core/bin/lib/discovery-vault-check.cjs +246 -0
  44. package/pp-core/bin/lib/frontmatter.cjs +1 -38
  45. package/pp-core/bin/lib/git-base-branch.cjs +13 -11
  46. package/pp-core/bin/lib/git-provider.cjs +4 -4
  47. package/pp-core/bin/lib/init-command-router.cjs +5 -8
  48. package/pp-core/bin/lib/init-contract.cjs +648 -738
  49. package/pp-core/bin/lib/init-interactive.cjs +76 -4
  50. package/pp-core/bin/lib/init.cjs +24 -51
  51. package/pp-core/bin/lib/install-profiles.cjs +1 -1
  52. package/pp-core/bin/lib/io.cjs +8 -0
  53. package/pp-core/bin/lib/loop-resolver.cjs +189 -129
  54. package/pp-core/bin/lib/org-profile.cjs +469 -152
  55. package/pp-core/bin/lib/package-identity.cjs +3 -3
  56. package/pp-core/bin/lib/phase-command-router.cjs +1 -29
  57. package/pp-core/bin/lib/phase-scope.cjs +1 -1
  58. package/pp-core/bin/lib/phase.cjs +11 -253
  59. package/pp-core/bin/lib/platform-bindings.cjs +40 -19
  60. package/pp-core/bin/lib/practices-review-check.cjs +15 -9
  61. package/pp-core/bin/lib/profile-output.cjs +1 -1
  62. package/pp-core/bin/lib/shell-command-projection.cjs +104 -1
  63. package/pp-core/bin/lib/state.cjs +11 -85
  64. package/pp-core/bin/lib/template.cjs +1 -1
  65. package/pp-core/bin/lib/vault-audit.cjs +650 -126
  66. package/pp-core/bin/lib/vault-command-router.cjs +2604 -367
  67. package/pp-core/bin/lib/vault-coordinates.cjs +13 -4
  68. package/pp-core/bin/lib/vault-index.cjs +699 -0
  69. package/pp-core/bin/lib/vault-mode.cjs +235 -0
  70. package/pp-core/bin/lib/vault-practices.cjs +139 -72
  71. package/pp-core/bin/lib/vault-review.cjs +194 -451
  72. package/pp-core/bin/lib/vault-seal.cjs +1936 -0
  73. package/pp-core/bin/lib/vault-standards.cjs +1505 -0
  74. package/pp-core/bin/lib/vault-trace.cjs +607 -254
  75. package/pp-core/bin/lib/vault-update.cjs +9 -33
  76. package/pp-core/bin/lib/vault-write-back.cjs +70 -42
  77. package/pp-core/bin/lib/vault.cjs +2734 -920
  78. package/pp-core/bin/lib/verify.cjs +0 -11
  79. package/pp-core/bin/portable-timeout.sh +1 -1
  80. package/pp-core/bin/pp-tools.cjs +184 -54
  81. package/pp-core/bin/shared/config-defaults.manifest.json +0 -7
  82. package/pp-core/bin/shared/config-schema.manifest.json +0 -3
  83. package/pp-core/bin/shared/model-catalog.json +2 -2
  84. package/pp-core/bin/verify-reapply-patches.cjs +0 -0
  85. package/pp-core/org-profile.json +26 -0
  86. package/pp-core/references/autonomous-best-effort.md +8 -7
  87. package/pp-core/references/loop-hook-dispatch.md +0 -25
  88. package/pp-core/references/planner-gap-closure.md +0 -1
  89. package/pp-core/references/planner-tdd.md +0 -1
  90. package/pp-core/references/planner-vault-practices.md +17 -34
  91. package/pp-core/references/planner-vault-standards.md +320 -0
  92. package/pp-core/references/planning-config.md +0 -8
  93. package/pp-core/references/practice-arithmetic-contract.md +121 -0
  94. package/pp-core/references/tdd.md +0 -1
  95. package/pp-core/references/verifier-practice-verdicts.md +263 -0
  96. package/pp-core/templates/README.md +1 -0
  97. package/pp-core/templates/VAULT-REVIEW.md +202 -0
  98. package/pp-core/templates/phase-prompt.md +2 -6
  99. package/pp-core/templates/practices/README.md +20 -23
  100. package/pp-core/workflows/_runtime-launcher.snippet.sh +0 -0
  101. package/pp-core/workflows/add-tests.md +55 -4
  102. package/pp-core/workflows/audit-fix.md +63 -2
  103. package/pp-core/workflows/audit-milestone.md +1 -1
  104. package/pp-core/workflows/autonomous.md +85 -11
  105. package/pp-core/workflows/check-todos.md +13 -2
  106. package/pp-core/workflows/code-review-fix.md +59 -3
  107. package/pp-core/workflows/code-review.md +10 -1
  108. package/pp-core/workflows/complete-milestone.md +1 -1
  109. package/pp-core/workflows/debug.md +54 -4
  110. package/pp-core/workflows/discovery-sprint/prefill.md +19 -7
  111. package/pp-core/workflows/discovery-sprint/prototype.md +46 -49
  112. package/pp-core/workflows/discovery-sprint/session-13-gates.md +2 -0
  113. package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +1 -3
  114. package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +1 -3
  115. package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +1 -3
  116. package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +20 -24
  117. package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +1 -3
  118. package/pp-core/workflows/discovery-sprint/sessions/06-security.md +1 -3
  119. package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +1 -3
  120. package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +1 -3
  121. package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +1 -3
  122. package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +1 -6
  123. package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +1 -3
  124. package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +1 -3
  125. package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +1 -3
  126. package/pp-core/workflows/discovery-sprint/templates/context-template.md +1 -1
  127. package/pp-core/workflows/discovery-sprint/vault-context.md +213 -0
  128. package/pp-core/workflows/discovery-sprint.md +7 -3
  129. package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +4 -4
  130. package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +43 -1
  131. package/pp-core/workflows/discovery-to-build.md +10 -2
  132. package/pp-core/workflows/discuss-phase.md +3 -10
  133. package/pp-core/workflows/execute-phase/steps/check-query-gate.md +329 -0
  134. package/pp-core/workflows/execute-phase/steps/client-context-gate.md +3 -2
  135. package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +1 -1
  136. package/pp-core/workflows/execute-phase/steps/execute-pre-dispatch.md +98 -0
  137. package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
  138. package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +6 -8
  139. package/pp-core/workflows/execute-phase/steps/vault-fetch.md +115 -14
  140. package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +322 -20
  141. package/pp-core/workflows/execute-phase/steps/verifier-practice-payload.md +165 -0
  142. package/pp-core/workflows/execute-phase/steps/verify-post-gate.md +163 -0
  143. package/pp-core/workflows/execute-phase.md +155 -128
  144. package/pp-core/workflows/execute-plan.md +56 -6
  145. package/pp-core/workflows/fast.md +53 -4
  146. package/pp-core/workflows/help/modes/full.md +3 -3
  147. package/pp-core/workflows/init/steps/client-context-capture.md +101 -0
  148. package/pp-core/workflows/init/steps/gitlab-provisioning.md +130 -0
  149. package/pp-core/workflows/init/steps/project-md-deferral-narration.md +66 -0
  150. package/pp-core/workflows/init/steps/reachability-outcome.md +70 -0
  151. package/pp-core/workflows/init.md +232 -272
  152. package/pp-core/workflows/new-project.md +31 -23
  153. package/pp-core/workflows/plan-phase.md +98 -45
  154. package/pp-core/workflows/quick.md +117 -3
  155. package/pp-core/workflows/secure-phase.md +1 -1
  156. package/pp-core/workflows/ship.md +57 -28
  157. package/pp-core/workflows/undo.md +1 -1
  158. package/pp-core/workflows/update.md +3 -3
  159. package/pp-core/workflows/validate-phase.md +58 -3
  160. package/pp-core/workflows/vault-review.md +282 -22
  161. package/pp-core/workflows/verify-work.md +12 -14
  162. package/scripts/changeset/cli.cjs +6 -5
  163. package/scripts/changeset/lint.cjs +0 -0
  164. package/scripts/changeset/new.cjs +33 -50
  165. package/scripts/dev-env.cjs +25 -85
  166. package/scripts/gen-capability-registry.cjs +199 -11
  167. package/scripts/gen-practice-arithmetic-contract.cjs +412 -0
  168. package/scripts/lib/allowlist-ratchet.cjs +1 -103
  169. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -2
  170. package/scripts/lint-docs-required.cjs +0 -0
  171. package/scripts/lint-pr-check-project-dir.cjs +1 -3
  172. package/scripts/lint-test-file-count.allowlist.json +12 -10
  173. package/scripts/practice-arithmetic-contract.allow.json +35 -0
  174. package/scripts/prompt-injection-scan.sh +22 -17
  175. package/scripts/record-vault-mode-golden.cjs +89 -0
  176. package/scripts/release-channel.cjs +0 -0
  177. package/scripts/sync-manifest-versions.cjs +5 -4
  178. package/scripts/sync-rulesets.sh +1 -1
  179. package/scripts/update-size-baseline.cjs +30 -274
  180. package/scripts/vault-calibrate.cjs +996 -0
  181. package/scripts/verify-opengsd-sweep.cjs +3 -5
  182. package/scripts/workflow-size.cjs +9 -61
  183. package/pp-core/bin/lib/dispatch-plan.cjs +0 -483
  184. package/pp-core/bin/lib/install-run.cjs +0 -536
  185. package/pp-core/bin/lib/loop-gates.cjs +0 -213
  186. package/pp-core/bin/lib/phase-manifest.cjs +0 -527
  187. package/pp-core/bin/lib/phase-tier.cjs +0 -480
  188. package/pp-core/bin/lib/plan-graph.cjs +0 -368
  189. package/pp-core/references/executor-vault-trace.md +0 -47
  190. package/pp-core/references/planner-plan-graph.md +0 -66
  191. package/pp-core/workflows/discovery-sprint/vault-fetch.md +0 -37
  192. package/pp-core/workflows/execute-phase/steps/checkpoint-handling.md +0 -45
  193. package/pp-core/workflows/execute-phase/steps/install-run.md +0 -29
  194. package/pp-core/workflows/execute-phase/steps/phase-close-rebaseline.md +0 -23
  195. package/pp-core/workflows/execute-phase/steps/serial-group-dispatch.md +0 -73
  196. package/pp-core/workflows/execute-phase/steps/vault-door-gate.md +0 -19
  197. package/pp-core/workflows/plan-phase/steps/phase-tier.md +0 -43
  198. package/scripts/lib/size-budget.cjs +0 -311
  199. package/scripts/lint-neutrality-names.cjs +0 -694
  200. package/scripts/lint-org-coordinates.allowlist.json +0 -35
  201. package/scripts/lint-org-coordinates.cjs +0 -426
@@ -67,6 +67,47 @@ Present banner:
67
67
  ```
68
68
  </step>
69
69
 
70
+ <step name="vault_admission_gate">
71
+ Vault admission and load gate (D-11, D-12, D-28): runs before a test is written or committed. A config the gate cannot
72
+ read halts here with the bindings present (D-05); without bindings, or with `vault.enabled` false,
73
+ nothing below prints or writes anything (D-13).
74
+
75
+ ```bash
76
+ VAULT_ADMIT_PRESENT=$(pp_run query platform-bindings --pick present 2>/dev/null || echo true)
77
+ VAULT_ADMIT=inert
78
+ if [ "$VAULT_ADMIT_PRESENT" = true ]; then VAULT_ADMIT=$(pp_run vault admission 2>/dev/null || echo unrecognised); fi
79
+ case "$VAULT_ADMIT" in on) echo "VAULT_ADMIT=on" ;; off|inert) : ;; *) pp_run vault admission --explain; echo "PARETO_GATE_BLOCKED:vault.admission"; exit 1 ;; esac
80
+ ```
81
+
82
+ **If the fence printed no `VAULT_ADMIT=on` line** (`off` and `inert` print nothing): skip the rest of this block silently. Otherwise run the load
83
+ gate on the `execute:pre` payload, filtered in the fence to the `vault` and `vault-practices`
84
+ capabilities (D-30):
85
+
86
+ ```bash
87
+ AT_EXECUTE_PRE_HOOKS_JSON=$(pp_run loop render-hooks execute:pre --raw | node -e "const d=require('fs').readFileSync(0,'utf-8');try{const j=JSON.parse(d);j.activeHooks=(j.activeHooks||[]).filter((x)=>x&&(x.capId==='vault'||x.capId==='vault-practices'));process.stdout.write(JSON.stringify(j));}catch{process.stdout.write('');}")
88
+ printf '%s' "$AT_EXECUTE_PRE_HOOKS_JSON" | node -e "let h=[];try{h=JSON.parse(require('fs').readFileSync(0,'utf-8')).activeHooks||[]}catch{}console.log('AT_EXECUTE_PRE_HOOKS_JSON='+JSON.stringify({activeHooks:h}))"
89
+ ```
90
+
91
+ Set `VAULT_FETCH_HOOKS_JSON=$AT_EXECUTE_PRE_HOOKS_JSON`, then Read and execute
92
+ `pp-core/workflows/execute-phase/steps/vault-fetch.md` (hook mode). Then, in the
93
+ `AT_EXECUTE_PRE_HOOKS_JSON=` line that fence printed, find active entries where `kind == "gate"` and `check.query ==
94
+ "vault.status"`; if none, skip silently. Otherwise set `GATE_POINT=execute:pre` and
95
+ `GATE_HOOKS_JSON=$AT_EXECUTE_PRE_HOOKS_JSON`, leave `GATE_POSITIONAL_ARG`, `GATE_WS` and
96
+ `GATE_STAGE` empty, then Read and follow `pp-core/workflows/execute-phase/steps/check-query-gate.md`;
97
+ it owns the halt.
98
+
99
+ Then record the door's START anchor, before the first test file is written (`execute_tdd_generation`) (D-31):
100
+
101
+ ```bash
102
+ pp_run vault door-record --begin --door add-tests --raw
103
+ ```
104
+
105
+ It records HEAD in `.planning/.vault-door-starts.json`, where the record step reads it back: a shell
106
+ variable would not survive into that later step, and the agent never types a sha. It prints nothing
107
+ and writes nothing unless `vault admission` answers `on`.
108
+ Take `padded_phase` from the same init JSON for the record below.
109
+ </step>
110
+
70
111
  <step name="analyze_implementation">
71
112
  Extract the list of files modified by the phase from SUMMARY.md ("Files Changed" or equivalent section).
72
113
 
@@ -212,10 +253,6 @@ If "Cherry-pick": ask user which tests to include.
212
253
  If "Adjust plan": apply changes and re-present.
213
254
  </step>
214
255
 
215
- <step name="vault_door_gate">
216
- Before any test file is written, Read and execute `pp-core/workflows/execute-phase/steps/vault-door-gate.md`; a stop ends this run with no test written.
217
- </step>
218
-
219
256
  <step name="execute_tdd_generation">
220
257
  For each approved TDD test:
221
258
 
@@ -315,6 +352,20 @@ git add {test files}
315
352
  git commit -m "test(phase-${phase_number}): add unit and E2E tests from add-tests command"
316
353
  ```
317
354
 
355
+ Vault door record (D-12): after the door's last source commit, set `DOOR_DIR="${phase_dir}"`,
356
+ `DOOR_PADDED="${padded_phase}"` and `DOOR_PHASE="${phase_number}"`, then run:
357
+
358
+ ```bash
359
+ DOOR_RUN=$(date -u +%Y%m%dT%H%M%SZ)
360
+ pp_run vault door-record --door add-tests --artifact "${DOOR_DIR}/${DOOR_PADDED}-door-${DOOR_RUN}-ADD-TESTS.md" --phase "$DOOR_PHASE" --raw
361
+ ```
362
+
363
+ The verb writes a `## Vault Door Record` (mode, vault ref and sha, governing phase, the start `--begin`
364
+ recorded and the end commit; the agent never types a sha) into a fresh per-run artifact, because it refuses a second record
365
+ in one file (D-57) and a rewritten report would drop the earlier run's coverage (D-79). It prints
366
+ nothing and writes nothing unless `vault admission` answers `on`. When it wrote the file and
367
+ `commit_docs` is true, commit the `artifact` path it printed: `pp_run query commit "docs(phase-${phase_number}): vault door record for add-tests" --files "<that artifact>"`.
368
+
318
369
  Present next steps:
319
370
 
320
371
  ```
@@ -91,9 +91,49 @@ If `--dry-run` was specified, **stop here and exit**. The classification table i
91
91
  final output — do not proceed to fixing.
92
92
  </step>
93
93
 
94
- <step name="fix-loop">
95
- Before the first fix, Read and execute `pp-core/workflows/execute-phase/steps/vault-door-gate.md`; a stop ends this run with no fix applied.
94
+ <step name="vault_admission_gate">
95
+ Skipped under `--dry-run` (nothing is fixed).
96
+
97
+ Vault admission and load gate (D-11, D-12, D-28): runs before any finding is fixed. A config the gate cannot
98
+ read halts here with the bindings present (D-05); without bindings, or with `vault.enabled` false,
99
+ nothing below prints or writes anything (D-13).
100
+
101
+ ```bash
102
+ VAULT_ADMIT_PRESENT=$(pp_run query platform-bindings --pick present 2>/dev/null || echo true)
103
+ VAULT_ADMIT=inert
104
+ if [ "$VAULT_ADMIT_PRESENT" = true ]; then VAULT_ADMIT=$(pp_run vault admission 2>/dev/null || echo unrecognised); fi
105
+ case "$VAULT_ADMIT" in on) echo "VAULT_ADMIT=on" ;; off|inert) : ;; *) pp_run vault admission --explain; echo "PARETO_GATE_BLOCKED:vault.admission"; exit 1 ;; esac
106
+ ```
107
+
108
+ **If the fence printed no `VAULT_ADMIT=on` line** (`off` and `inert` print nothing): skip the rest of this block silently. Otherwise run the load
109
+ gate on the `execute:pre` payload, filtered in the fence to the `vault` and `vault-practices`
110
+ capabilities (D-30):
111
+
112
+ ```bash
113
+ AF_EXECUTE_PRE_HOOKS_JSON=$(pp_run loop render-hooks execute:pre --raw | node -e "const d=require('fs').readFileSync(0,'utf-8');try{const j=JSON.parse(d);j.activeHooks=(j.activeHooks||[]).filter((x)=>x&&(x.capId==='vault'||x.capId==='vault-practices'));process.stdout.write(JSON.stringify(j));}catch{process.stdout.write('');}")
114
+ printf '%s' "$AF_EXECUTE_PRE_HOOKS_JSON" | node -e "let h=[];try{h=JSON.parse(require('fs').readFileSync(0,'utf-8')).activeHooks||[]}catch{}console.log('AF_EXECUTE_PRE_HOOKS_JSON='+JSON.stringify({activeHooks:h}))"
115
+ ```
116
+
117
+ Set `VAULT_FETCH_HOOKS_JSON=$AF_EXECUTE_PRE_HOOKS_JSON`, then Read and execute
118
+ `pp-core/workflows/execute-phase/steps/vault-fetch.md` (hook mode). Then, in the
119
+ `AF_EXECUTE_PRE_HOOKS_JSON=` line that fence printed, find active entries where `kind == "gate"` and `check.query ==
120
+ "vault.status"`; if none, skip silently. Otherwise set `GATE_POINT=execute:pre` and
121
+ `GATE_HOOKS_JSON=$AF_EXECUTE_PRE_HOOKS_JSON`, leave `GATE_POSITIONAL_ARG`, `GATE_WS` and
122
+ `GATE_STAGE` empty, then Read and follow `pp-core/workflows/execute-phase/steps/check-query-gate.md`;
123
+ it owns the halt.
124
+
125
+ Then record the door's START anchor, before the first `pp-executor` spawn of the fix loop (D-31):
126
+
127
+ ```bash
128
+ pp_run vault door-record --begin --door audit-fix --raw
129
+ ```
130
+
131
+ It records HEAD in `.planning/.vault-door-starts.json`, where the record step reads it back: a shell
132
+ variable would not survive into that later step, and the agent never types a sha. It prints nothing
133
+ and writes nothing unless `vault admission` answers `on`.
134
+ </step>
96
135
 
136
+ <step name="fix-loop">
97
137
  For each **auto-fixable** finding (up to `--max`, ordered by severity desc):
98
138
 
99
139
  **a. Spawn executor agent** (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)**:**
@@ -142,6 +182,27 @@ must halt to avoid cascading issues. Remaining auto-fixable findings will appear
142
182
  report as `not-attempted`.
143
183
  </step>
144
184
 
185
+ <step name="vault_door_record">
186
+ After the loop, once for EACH phase directory whose findings were fixed in this run (the directory of
187
+ the UAT or VERIFICATION file the finding came from): the record's range, from the start captured
188
+ before the loop to HEAD now, covers every finding commit, and it lands in each fixed phase so that
189
+ phase's gate sees it.
190
+
191
+ Vault door record (D-12): after the door's last source commit, set `DOOR_DIR="<that phase directory>"`,
192
+ `DOOR_PADDED="<its padded phase>"` and `DOOR_PHASE="<that phase directory>"`, then run:
193
+
194
+ ```bash
195
+ DOOR_RUN=$(date -u +%Y%m%dT%H%M%SZ)
196
+ pp_run vault door-record --door audit-fix --artifact "${DOOR_DIR}/${DOOR_PADDED}-door-${DOOR_RUN}-AUDIT-FIX.md" --phase "$DOOR_PHASE" --raw
197
+ ```
198
+
199
+ The verb writes a `## Vault Door Record` (mode, vault ref and sha, governing phase, the start `--begin`
200
+ recorded and the end commit; the agent never types a sha) into a fresh per-run artifact, because it refuses a second record
201
+ in one file (D-57) and a rewritten report would drop the earlier run's coverage (D-79). It prints
202
+ nothing and writes nothing unless `vault admission` answers `on`. When it wrote the file and
203
+ `commit_docs` is true, commit the `artifact` path it printed: `pp_run query commit "docs: vault door record for audit-fix" --files "<that artifact>"`.
204
+ </step>
205
+
145
206
  <step name="report">
146
207
  Present the final summary:
147
208
 
@@ -146,7 +146,7 @@ For each REQ-ID, determine status using all three sources:
146
146
  Skip if the Nyquist capability is inactive.
147
147
 
148
148
  ```bash
149
- VERIFY_POST_HOOKS_JSON=$(pp_run loop render-hooks verify:post --raw) || exit 1
149
+ VERIFY_POST_HOOKS_JSON=$(pp_run loop render-hooks verify:post --raw)
150
150
  ```
151
151
 
152
152
  Resolve active step hooks from `VERIFY_POST_HOOKS_JSON` where `kind == "step"` and `ref.skill == "validate-phase"`.
@@ -267,18 +267,65 @@ PHASE_STATE=$(pp_run query init.phase-op ${PHASE_NUM})
267
267
 
268
268
  Check `has_context`. If false → go to handle_blocker: "Discuss for phase ${PHASE_NUM} did not produce CONTEXT.md."
269
269
 
270
- **3a.5. UI Design Contract (Frontend Phases)**
270
+ **3a.5. plan:pre dispatch — fetch, design contract, then gate**
271
+
272
+ Admission first (D-28): a config the gate cannot read renders no vault hook, so this runs before the render.
273
+
274
+ ```bash
275
+ VAULT_ADMIT_PRESENT=$(pp_run query platform-bindings --pick present 2>/dev/null || echo true)
276
+ VAULT_ADMIT=inert
277
+ if [ "$VAULT_ADMIT_PRESENT" = true ]; then VAULT_ADMIT=$(pp_run vault admission 2>/dev/null || echo unrecognised); fi
278
+ case "$VAULT_ADMIT" in on) echo "VAULT_ADMIT=on" ;; off|inert) : ;; *) pp_run vault admission --explain; echo "PARETO_GATE_BLOCKED:vault.admission"; exit 1 ;; esac
279
+ ```
271
280
 
272
281
  Resolve active `plan:pre` hooks:
273
282
 
274
283
  ```bash
275
284
  UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
276
- HOOKS_JSON=$(pp_run loop render-hooks plan:pre --raw --phase "${PHASE_NUM}") || exit 1
285
+ HOOKS_JSON=$(pp_run loop render-hooks plan:pre --raw)
286
+ printf '%s' "$HOOKS_JSON" | node -e "let h=[];try{h=JSON.parse(require('fs').readFileSync(0,'utf-8')).activeHooks||[]}catch{}console.log('HOOKS_JSON='+JSON.stringify({activeHooks:h}))"
277
287
  ```
278
288
 
279
- Read the `activeHooks` array directly from `HOOKS_JSON` (in-context — do NOT invoke a shell pipeline). **Compute the active UI step hooks** = entries from `activeHooks` where `kind == "step"` and `ref.skill` is set. **If there are NO active step hooks → skip silently to 3b.** (This covers `workflow.ui_phase=false` — including configurations where only a gate-only entry is present, e.g. `ui_phase=false` + `ui_safety_gate=true` produces `activeHooks=[{kind:"gate"}]`. Autonomous never runs the plan:pre gate — it is always pipeline mode — so a gate-only active set is equivalent to no active step and is silently skipped here. This matches OLD §3a.5 behaviour.)
289
+ Read the `activeHooks` array directly from the printed `HOOKS_JSON=` line (in-context — do NOT invoke a shell pipeline).
290
+
291
+ **Policy — pipeline mode is a reason to gate, not a reason to skip (D-VH-6).** Autonomous **runs**
292
+ the `plan:pre` gate, and its step filter admits `ref.command`-shaped hooks as well as `ref.skill`
293
+ ones. Both reverse what this section previously shipped. `--auto` dispatches every step-hook shape
294
+ it understands, then runs the gate, in that order.
295
+
296
+ Run the three sub-steps below **in order**. §3a.5-iii is reached on every path — the early exits in
297
+ §3a.5-ii skip to it, never past it.
298
+
299
+ **3a.5-i. Step hooks — fetch first.**
300
+
301
+ **Compute the active step hooks** = entries from `activeHooks` where `kind == "step"`. Route each by
302
+ the shape of its `ref`, in array order:
303
+
304
+ - **`ref.skill` is set** → a skill-shaped step hook; it is handled by §3a.5-ii below (this is the
305
+ UI-SPEC path and its existing behaviour is unchanged).
306
+ - **`ref.command` is set and `capId == "vault"`** → set `VAULT_FETCH_HOOKS_JSON=$HOOKS_JSON`,
307
+ then Read and execute
308
+ `pp-core/workflows/execute-phase/steps/vault-fetch.md`, the same bespoke `plan:pre` vault fetch +
309
+ summary dispatch `plan-phase.md` uses. Do not invent a second shape for it.
310
+ - **`ref.command` is set and `capId` is anything else** → not handled here. Display
311
+ `⚠ plan:pre step hook from capability '${capId}' has an unhandled ref shape (ref.command) and was skipped in autonomous mode.`
312
+ and continue — a silently dropped hook shape is the defect this section exists to fix.
313
+
314
+ **If there are NO active step hooks at all →** Continue to §3a.5-ii anyway: there is nothing to dispatch here, but §3a.5-ii owns `ui.plan-gate` on every activation set that renders it — the gate-only set included — while §3a.5-iii skips that entry unconditionally, and §3a.5-ii self-skips silently when no UI hook of either kind is active.
280
315
 
281
- (At least one active step hook ⇒ `workflow.ui_phase` is on.) Run the UI-SPEC gate:
316
+ **3a.5-ii. UI design contract (frontend phases).**
317
+
318
+ Let the **active UI step hooks** be the `kind == "step"` entries with `ref.skill` set, and the **UI
319
+ gate entry** be a `kind == "gate"` entry whose `check.query` is `ui.plan-gate`. Run this sub-step
320
+ whenever **any** `plan:pre` UI hook is active — step hooks, the gate entry, or both — mirroring
321
+ `plan-phase.md` §5.6's own rule. **If there is no active step hook AND no gate entry → skip silently
322
+ to 3a.5-iii** (`workflow.ui_phase` and `workflow.ui_safety_gate` both off; unchanged inertness).
323
+
324
+ This sub-step OWNS `ui.plan-gate` on every activation set that renders it — the gate-only set
325
+ (`ui_phase=false` + `ui_safety_gate=true`, producing `activeHooks=[{kind:"gate"}]`) included, where
326
+ there is no UI step to dispatch but the gate is still active and still gets run here.
327
+
328
+ Run the UI-SPEC gate:
282
329
 
283
330
  ```bash
284
331
  GATE=$(pp_run check ui-plan-gate "${PHASE_NUM}" --raw)
@@ -286,17 +333,19 @@ GATE=$(pp_run check ui-plan-gate "${PHASE_NUM}" --raw)
286
333
 
287
334
  Read `frontend` and `hasUiSpec` from `GATE` (in-context).
288
335
 
289
- **If `frontend` is false:** Skip silently to 3b.
336
+ **If `frontend` is false:** Skip silently to 3a.5-iii.
290
337
 
291
- **If `hasUiSpec` is true (UI-SPEC already exists):** Skip silently to 3b.
338
+ **If `hasUiSpec` is true (UI-SPEC already exists):** Skip silently to 3a.5-iii.
292
339
 
293
- **Otherwise (frontend phase + no UI-SPEC):** For each active step hook (the `kind == "step"` set from above, in array order):
340
+ **Otherwise (frontend phase + no UI-SPEC) with no active step hook — the gate-only set:** Display warning `Phase ${PHASE_NUM}: no active UI step hook can generate the missing UI-SPEC — continuing without design contract.` and proceed to 3a.5-iii. NON-BLOCKING, the pipeline-mode outcome `plan-phase.md` Branch 5 is designed for.
341
+
342
+ **Otherwise (frontend phase + no UI-SPEC) with active step hooks:** For each active step hook (the `kind == "step"` set from above, in array order):
294
343
 
295
344
  ```
296
345
  Skill(skill="pp-${ref.skill}", args="${PHASE_NUM}")
297
346
  ```
298
347
 
299
- (Prepend `pp-` to `ref.skill` — so `ui-phase` → `pp-ui-phase`. Bare `${PHASE_NUM}` args — autonomous style, same pattern as the verify:post dispatch.) Entries where `kind == "gate"` are silently ignored — autonomous is always pipeline mode, there is no blocking gate here.
348
+ (Prepend `pp-` to `ref.skill` — so `ui-phase` → `pp-ui-phase`. Bare `${PHASE_NUM}` args — autonomous style, same pattern as the verify:post dispatch.) Entries where `kind == "gate"` are not dispatched *here*; they are handled by §3a.5-iii below, which owns every gate at this point except `ui.plan-gate` — this sub-step owns that one, on every activation set that renders it.
300
349
 
301
350
  After all step hooks return, re-read:
302
351
 
@@ -304,7 +353,32 @@ After all step hooks return, re-read:
304
353
  UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
305
354
  ```
306
355
 
307
- **If `UI_SPEC_FILE` is still empty:** Display warning `Phase ${PHASE_NUM}: UI-SPEC generation did not produce output — continuing without design contract.` and proceed to 3b. NON-BLOCKING.
356
+ **If `UI_SPEC_FILE` is still empty:** Display warning `Phase ${PHASE_NUM}: UI-SPEC generation did not produce output — continuing without design contract.` and proceed to 3a.5-iii. NON-BLOCKING.
357
+
358
+ **3a.5-iii. The plan:pre gate.**
359
+
360
+ Reached on every path through §3a.5, including the ones that skipped the UI work. It runs *after*
361
+ §3a.5-i so the vault fetch has already been attempted — fetch first, then decide (GATE-03), so a
362
+ fresh clone with a reachable vault passes rather than being blocked for a cache it never had a
363
+ chance to populate.
364
+
365
+ `ui.plan-gate` is handled *before* this dispatch — §3a.5-ii ran it with its own `${PHASE_NUM}`
366
+ positional and owns its outcome on every activation set that renders it — so skip that entry; the
367
+ enforcer never sees it.
368
+
369
+ Set `GATE_POINT=plan:pre`, `GATE_HOOKS_JSON=$HOOKS_JSON` (the array already rendered above) and
370
+ leave `GATE_POSITIONAL_ARG` empty deliberately (`vault.status` reads project state, not a phase;
371
+ every plan:pre gate needing one is caller-handled — D-VH-7), then Read and follow
372
+ `pp-core/workflows/execute-phase/steps/check-query-gate.md` — the single generic `check.query` gate
373
+ enforcer (GATE-02). It owns the halt decision, the two-step contract and the message passthrough;
374
+ do not restate any of them here.
375
+
376
+ If no `kind == "gate"` entry is active, the enforcer's own inertness guard returns immediately and
377
+ prints nothing, so a project with the capability off is unaffected. When a gate does block, the
378
+ enforcer prints its named token and runs `exit 1`, stopping the unattended chain rather than
379
+ building the phase with no client standards loaded.
380
+
381
+ Then proceed to 3b.
308
382
 
309
383
  **3b. Plan**
310
384
 
@@ -406,7 +480,7 @@ Auto-invoke code review and fix chain. Autonomous mode chains both review and fi
406
480
 
407
481
  **Capability dispatch:**
408
482
  ```bash
409
- EXECUTE_POST_HOOKS_JSON=$(pp_run loop render-hooks execute:post --raw --phase "${PHASE_NUM}") || exit 1
483
+ EXECUTE_POST_HOOKS_JSON=$(pp_run loop render-hooks execute:post --raw)
410
484
  ```
411
485
 
412
486
  Resolve active step hooks from `EXECUTE_POST_HOOKS_JSON` where `kind == "step"` and `ref.skill == "code-review"`.
@@ -481,7 +555,7 @@ Resolve the active post-verification hooks and the UI-SPEC gate:
481
555
 
482
556
  ```bash
483
557
  UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
484
- HOOKS_JSON=$(pp_run loop render-hooks verify:post --raw --phase "${PHASE_NUM}") || exit 1
558
+ HOOKS_JSON=$(pp_run loop render-hooks verify:post --raw)
485
559
  ```
486
560
 
487
561
  Read the `activeHooks` array directly from the `HOOKS_JSON` value already in context (do not invoke a shell `jq` pipeline — parse as the JSON object it is). **If `activeHooks` is empty or absent:** skip silently to the iterate step.
@@ -127,11 +127,22 @@ Use AskUserQuestion:
127
127
  </step>
128
128
 
129
129
  <step name="execute_action">
130
- **Work on it now:** first Read and execute `pp-core/workflows/execute-phase/steps/vault-door-gate.md`; a stop leaves the todo pending and ends here. Then:
130
+ **Work on it now:** admission first (D-11, D-12, D-28):
131
+
132
+ ```bash
133
+ VAULT_ADMIT_PRESENT=$(pp_run query platform-bindings --pick present 2>/dev/null || echo true)
134
+ VAULT_ADMIT=inert
135
+ if [ "$VAULT_ADMIT_PRESENT" = true ]; then VAULT_ADMIT=$(pp_run vault admission 2>/dev/null || echo unrecognised); fi
136
+ case "$VAULT_ADMIT" in on) echo "VAULT_ADMIT=on" ;; off|inert) : ;; *) pp_run vault admission --explain; echo "PARETO_GATE_BLOCKED:vault.admission"; exit 1 ;; esac
137
+ ```
138
+
131
139
  ```bash
132
140
  mv ".planning/todos/pending/[filename]" ".planning/todos/completed/"
133
141
  ```
134
- Update STATE.md todo count. Present problem/solution context. Begin work or ask how to proceed.
142
+ Update STATE.md todo count. **If the admission fence printed `VAULT_ADMIT=on`:** do not do the work inline; invoke
143
+ `/pp-quick "[todo title]"` via SlashCommand, so the work carries the vault mode's record and the trace
144
+ gates (no artifact here could hold a door record; quick owns ad-hoc governed source), then continue
145
+ below. **Otherwise:** present problem/solution context and begin work or ask how to proceed, as before.
135
146
 
136
147
  **Add to phase plan:**
137
148
  Note todo reference in phase planning notes. Keep in pending. Return to list or exit.
@@ -77,7 +77,7 @@ FIX_REPORT_PATH="${PHASE_DIR}/${PADDED_PHASE}-REVIEW-FIX.md"
77
77
  Check if code review is active via the capability registry:
78
78
 
79
79
  ```bash
80
- EXECUTE_POST_HOOKS_JSON=$(pp_run loop render-hooks execute:post --raw) || exit 1
80
+ EXECUTE_POST_HOOKS_JSON=$(pp_run loop render-hooks execute:post --raw)
81
81
  ```
82
82
 
83
83
  Resolve active step hooks from `EXECUTE_POST_HOOKS_JSON` where `kind == "step"` and `ref.skill == "code-review"`.
@@ -182,9 +182,47 @@ done < <(REVIEW_PATH="${REVIEW_PATH}" node -e "
182
182
  If REVIEW.md contains a `files_reviewed_list` frontmatter field, use that as the re-review scope. If not present, fall back to re-reviewing the full phase (same behavior as initial code-review).
183
183
  </step>
184
184
 
185
- <step name="spawn_fixer">
186
- Before the first fixer spawn, Read and execute `pp-core/workflows/execute-phase/steps/vault-door-gate.md`; a stop ends this run with no fix applied.
185
+ <step name="vault_admission_gate">
186
+ Vault admission and load gate (D-11, D-12, D-28): runs before the fixer commits anything. A config the gate cannot
187
+ read halts here with the bindings present (D-05); without bindings, or with `vault.enabled` false,
188
+ nothing below prints or writes anything (D-13).
189
+
190
+ ```bash
191
+ VAULT_ADMIT_PRESENT=$(pp_run query platform-bindings --pick present 2>/dev/null || echo true)
192
+ VAULT_ADMIT=inert
193
+ if [ "$VAULT_ADMIT_PRESENT" = true ]; then VAULT_ADMIT=$(pp_run vault admission 2>/dev/null || echo unrecognised); fi
194
+ case "$VAULT_ADMIT" in on) echo "VAULT_ADMIT=on" ;; off|inert) : ;; *) pp_run vault admission --explain; echo "PARETO_GATE_BLOCKED:vault.admission"; exit 1 ;; esac
195
+ ```
196
+
197
+ **If the fence printed no `VAULT_ADMIT=on` line** (`off` and `inert` print nothing): skip the rest of this block silently. Otherwise run the load
198
+ gate on the `execute:pre` payload, filtered in the fence to the `vault` and `vault-practices`
199
+ capabilities (D-30):
200
+
201
+ ```bash
202
+ CRF_EXECUTE_PRE_HOOKS_JSON=$(pp_run loop render-hooks execute:pre --raw | node -e "const d=require('fs').readFileSync(0,'utf-8');try{const j=JSON.parse(d);j.activeHooks=(j.activeHooks||[]).filter((x)=>x&&(x.capId==='vault'||x.capId==='vault-practices'));process.stdout.write(JSON.stringify(j));}catch{process.stdout.write('');}")
203
+ printf '%s' "$CRF_EXECUTE_PRE_HOOKS_JSON" | node -e "let h=[];try{h=JSON.parse(require('fs').readFileSync(0,'utf-8')).activeHooks||[]}catch{}console.log('CRF_EXECUTE_PRE_HOOKS_JSON='+JSON.stringify({activeHooks:h}))"
204
+ ```
205
+
206
+ Set `VAULT_FETCH_HOOKS_JSON=$CRF_EXECUTE_PRE_HOOKS_JSON`, then Read and execute
207
+ `pp-core/workflows/execute-phase/steps/vault-fetch.md` (hook mode). Then, in the
208
+ `CRF_EXECUTE_PRE_HOOKS_JSON=` line that fence printed, find active entries where `kind == "gate"` and `check.query ==
209
+ "vault.status"`; if none, skip silently. Otherwise set `GATE_POINT=execute:pre` and
210
+ `GATE_HOOKS_JSON=$CRF_EXECUTE_PRE_HOOKS_JSON`, leave `GATE_POSITIONAL_ARG`, `GATE_WS` and
211
+ `GATE_STAGE` empty, then Read and follow `pp-core/workflows/execute-phase/steps/check-query-gate.md`;
212
+ it owns the halt.
187
213
 
214
+ Then record the door's START anchor, before the first `pp-code-fixer` spawn (`code-review --fix` reaches this file, so it inherits the gate and the record) (D-31):
215
+
216
+ ```bash
217
+ pp_run vault door-record --begin --door code-review-fix --raw
218
+ ```
219
+
220
+ It records HEAD in `.planning/.vault-door-starts.json`, where the record step reads it back: a shell
221
+ variable would not survive into that later step, and the agent never types a sha. It prints nothing
222
+ and writes nothing unless `vault admission` answers `on`.
223
+ </step>
224
+
225
+ <step name="spawn_fixer">
188
226
  Spawn the pp-code-fixer agent with config (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze):
189
227
 
190
228
  ```bash
@@ -388,6 +426,24 @@ fi
388
426
  This commit happens ONCE at the end of the workflow, after all iterations (if --auto) complete. Not per-iteration.
389
427
  </step>
390
428
 
429
+ <step name="vault_door_record">
430
+ One record per run, after all iterations (the fixer commits each finding itself).
431
+
432
+ Vault door record (D-12): after the door's last source commit, set `DOOR_DIR="${PHASE_DIR}"`,
433
+ `DOOR_PADDED="${PADDED_PHASE}"` and `DOOR_PHASE="${PHASE}"`, then run:
434
+
435
+ ```bash
436
+ DOOR_RUN=$(date -u +%Y%m%dT%H%M%SZ)
437
+ pp_run vault door-record --door code-review-fix --artifact "${DOOR_DIR}/${DOOR_PADDED}-door-${DOOR_RUN}-REVIEW-FIX.md" --phase "$DOOR_PHASE" --raw
438
+ ```
439
+
440
+ The verb writes a `## Vault Door Record` (mode, vault ref and sha, governing phase, the start `--begin`
441
+ recorded and the end commit; the agent never types a sha) into a fresh per-run artifact, because it refuses a second record
442
+ in one file (D-57) and a rewritten report would drop the earlier run's coverage (D-79). It prints
443
+ nothing and writes nothing unless `vault admission` answers `on`. When it wrote the file and
444
+ `commit_docs` is true, commit the `artifact` path it printed: `pp_run query commit "docs(${PADDED_PHASE}): vault door record for code-review-fix" --files "<that artifact>"`.
445
+ </step>
446
+
391
447
  <step name="present_results">
392
448
  Parse REVIEW-FIX.md frontmatter and present formatted summary to user.
393
449
 
@@ -75,10 +75,19 @@ fi
75
75
  </step>
76
76
 
77
77
  <step name="check_config_gate">
78
+ Admission first (D-28): a config the gate cannot read renders no practices contribution, so this runs before the render; without bindings it is inert.
79
+
80
+ ```bash
81
+ VAULT_ADMIT_PRESENT=$(pp_run query platform-bindings --pick present 2>/dev/null || echo true)
82
+ VAULT_ADMIT=inert
83
+ if [ "$VAULT_ADMIT_PRESENT" = true ]; then VAULT_ADMIT=$(pp_run vault admission 2>/dev/null || echo unrecognised); fi
84
+ case "$VAULT_ADMIT" in on) echo "VAULT_ADMIT=on" ;; off|inert) : ;; *) pp_run vault admission --explain; echo "PARETO_GATE_BLOCKED:vault.admission"; exit 1 ;; esac
85
+ ```
86
+
78
87
  Check if code review is active via the capability registry:
79
88
 
80
89
  ```bash
81
- EXECUTE_POST_HOOKS_JSON=$(pp_run loop render-hooks execute:post --raw) || exit 1
90
+ EXECUTE_POST_HOOKS_JSON=$(pp_run loop render-hooks execute:post --raw)
82
91
  ```
83
92
 
84
93
  Resolve active step hooks from `EXECUTE_POST_HOOKS_JSON` where `kind == "step"` and `ref.skill == "code-review"`.
@@ -5,7 +5,7 @@ Mark a shipped version (v1.0, v1.1, v2.0) as complete. Creates historical record
5
5
  </purpose>
6
6
 
7
7
  <workstream_flag_substitution>
8
- `${GSD_WS}` is a placeholder — substitute the active `--ws <name>` (drop in flat mode); never emit it literally.
8
+ `${GSD_WS}` / `{GSD_WS}` in emitted "Next:" commands and `Skill()` args and agent dispatch prompts is a placeholder, not a shell variable — nothing expands it automatically. Substitute the active `--ws <name>`, or remove it entirely in flat mode. Never print the literal token in user-facing output. See `pp-core/references/workstream-flag.md` § Substitution rule.
9
9
  </workstream_flag_substitution>
10
10
 
11
11
  <required_reading>
@@ -31,9 +31,37 @@ Read TDD mode from config:
31
31
  TDD_MODE=$(pp_run query config-get workflow.tdd_mode 2>/dev/null | jq -r 'if type == "boolean" then tostring else . end' 2>/dev/null || echo "false")
32
32
  ```
33
33
 
34
- ## 0b. Vault door gate
34
+ ## 0b. Vault admission and load gate
35
35
 
36
- A session can write a fix. Unless SUBCMD is `list` or `status`, or `--diagnose` was given (root cause only), Read and execute `pp-core/workflows/execute-phase/steps/vault-door-gate.md` before any agent is spawned; a stop ends `/pp-debug`.
36
+ Run only where a spawn site below says so (the `continue` session and a new session), never for
37
+ `list` or `status`: it decides before any session manager is spawned (D-08). With the vault on, the
38
+ fix is applied through `/pp-quick` (§ 5), so it carries the mode's record and the trace gates.
39
+
40
+ ```bash
41
+ VAULT_ADMIT_PRESENT=$(pp_run query platform-bindings --pick present 2>/dev/null || echo true)
42
+ VAULT_ADMIT=inert
43
+ if [ "$VAULT_ADMIT_PRESENT" = true ]; then VAULT_ADMIT=$(pp_run vault admission 2>/dev/null || echo unrecognised); fi
44
+ case "$VAULT_ADMIT" in on) echo "VAULT_ADMIT=on" ;; off|inert) : ;; *) pp_run vault admission --explain; echo "PARETO_GATE_BLOCKED:vault.admission"; exit 1 ;; esac
45
+ ```
46
+
47
+ **If the fence printed no `VAULT_ADMIT=on` line** (`off` and `inert` print nothing): skip the rest of this section silently. Print nothing: a
48
+ non-Pareto or `vault.enabled=false` project debugs byte-identically to how it did before (GATE-04).
49
+
50
+ **Otherwise, the load gate, in hook mode,** from the `plan:pre` payload filtered in the fence to the
51
+ `vault` and `vault-practices` capabilities (D-30):
52
+
53
+ ```bash
54
+ DEBUG_PLAN_PRE_HOOKS_JSON=$(pp_run loop render-hooks plan:pre --raw | node -e "const d=require('fs').readFileSync(0,'utf-8');try{const j=JSON.parse(d);j.activeHooks=(j.activeHooks||[]).filter((x)=>x&&(x.capId==='vault'||x.capId==='vault-practices'));process.stdout.write(JSON.stringify(j));}catch{process.stdout.write('');}")
55
+ printf '%s' "$DEBUG_PLAN_PRE_HOOKS_JSON" | node -e "let h=[];try{h=JSON.parse(require('fs').readFileSync(0,'utf-8')).activeHooks||[]}catch{}console.log('DEBUG_PLAN_PRE_HOOKS_JSON='+JSON.stringify({activeHooks:h}))"
56
+ ```
57
+
58
+ Set `VAULT_FETCH_HOOKS_JSON=$DEBUG_PLAN_PRE_HOOKS_JSON`, then Read and execute
59
+ `pp-core/workflows/execute-phase/steps/vault-fetch.md` (hook mode). Then, in the
60
+ `DEBUG_PLAN_PRE_HOOKS_JSON=` line that fence printed, find active entries where `kind == "gate"` and `check.query ==
61
+ "vault.status"`; if none, skip silently. Otherwise set `GATE_POINT=plan:pre` and
62
+ `GATE_HOOKS_JSON=$DEBUG_PLAN_PRE_HOOKS_JSON`, leave `GATE_POSITIONAL_ARG`, `GATE_WS` and
63
+ `GATE_STAGE` empty, then Read and follow `pp-core/workflows/execute-phase/steps/check-query-gate.md`.
64
+ It owns the halt: a required miss stops here with the gate's own message, before any spawn.
37
65
 
38
66
  ## 1a. LIST subcommand
39
67
 
@@ -105,6 +133,8 @@ Eliminated: {count}
105
133
 
106
134
  Surface to user. Then delegate directly to the session manager (skip Steps 2 and 3 — pass `symptoms_prefilled: true` and set the slug from SLUG variable). The existing file IS the context.
107
135
 
136
+ Run § 0b Vault admission and load gate first.
137
+
108
138
  Print before spawning (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze):
109
139
  ```
110
140
  [debug] Session: .planning/debug/{SLUG}.md
@@ -129,7 +159,8 @@ slug: {SLUG}
129
159
  debug_file_path: .planning/debug/{SLUG}.md
130
160
  symptoms_prefilled: true
131
161
  tdd_mode: {TDD_MODE}
132
- goal: find_and_fix
162
+ goal: {if § 0b printed VAULT_ADMIT=on: "find_root_cause_only", else: "find_and_fix"}
163
+ fix_route: {if § 0b printed VAULT_ADMIT=on: "quick", else: "direct"}
133
164
  specialist_dispatch_enabled: true
134
165
  </session_params>
135
166
  """,
@@ -141,6 +172,9 @@ specialist_dispatch_enabled: true
141
172
 
142
173
  Display the compact summary returned by the session manager.
143
174
 
175
+ With `fix_route` set to `quick` the session manager returns the root cause and a proposed fix instead
176
+ of applying one; `direct` keeps today's behaviour. Then, if `fix_route` was `quick`, follow § 5.
177
+
144
178
  ## 1d. Check Active Sessions (SUBCMD=debug)
145
179
 
146
180
  When SUBCMD=debug:
@@ -192,6 +226,8 @@ Create `.planning/debug/{slug}.md` with initial state using the Write tool (neve
192
226
 
193
227
  ## 4. Session Management (delegated to pp-debug-session-manager)
194
228
 
229
+ Run § 0b Vault admission and load gate first.
230
+
195
231
  After initial context setup, spawn the session manager to handle the full checkpoint/continuation loop. The session manager handles specialist_hint dispatch internally: when pp-debugger returns ROOT CAUSE FOUND it extracts the specialist_hint field and invokes the matching skill (e.g. typescript-expert, swift-concurrency) before offering fix options.
196
232
 
197
233
  Print before spawning (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze):
@@ -212,7 +248,8 @@ slug: {slug}
212
248
  debug_file_path: .planning/debug/{slug}.md
213
249
  symptoms_prefilled: true
214
250
  tdd_mode: {TDD_MODE}
215
- goal: {if diagnose_only: "find_root_cause_only", else: "find_and_fix"}
251
+ goal: {if § 0b printed VAULT_ADMIT=on or diagnose_only: "find_root_cause_only", else: "find_and_fix"}
252
+ fix_route: {if § 0b printed VAULT_ADMIT=on: "quick", else: "direct"}
216
253
  specialist_dispatch_enabled: true
217
254
  </session_params>
218
255
  """,
@@ -224,9 +261,22 @@ specialist_dispatch_enabled: true
224
261
 
225
262
  Display the compact summary returned by the session manager.
226
263
 
264
+ If `fix_route` was `quick`, follow § 5 first.
227
265
  If summary shows `DEBUG SESSION COMPLETE`: done.
228
266
  If summary shows `ABANDONED`: note session saved at `.planning/debug/{slug}.md` for later `/pp:debug continue {slug}`.
229
267
 
268
+ ## 5. Hand the fix to /pp-quick (vault on only)
269
+
270
+ **If the summary carries `FIX_ROUTE: quick` and a `PROPOSED FIX:` line** (and this is not a
271
+ `--diagnose` run): invoke `/pp-quick --from-debug {slug} "{the PROPOSED FIX line}"` via
272
+ SlashCommand, where `{slug}` is the session's slug (`{SLUG}` for a continued session). Quick
273
+ validates the slug again, adds `.planning/debug/{slug}.md` to its planner's and executor's reading
274
+ lists, and gates and records the fix like any quick task. When quick returns, ask the user
275
+ (AskUserQuestion) to confirm the symptoms are resolved; on yes, move `.planning/debug/{slug}.md` to
276
+ `.planning/debug/resolved/`, as the session manager does for a session it fixed. The manager emits
277
+ the two lines only when the user chose "Fix now" (or the TDD arm handed off); after "Plan fix" or
278
+ "Manual fix", with no root cause, or under `--diagnose`, there is no hand-off.
279
+
230
280
  </process>
231
281
 
232
282
  <success_criteria>
@@ -15,10 +15,16 @@ multiple sessions can be pre-filled in one run.
15
15
  only — relative lowercase paths, no absolute paths, no parent traversal, same
16
16
  rules as the main workflow (T-04-03).
17
17
 
18
- ## Step 0: Client vault (optional)
18
+ ## Step 0: Vault Context
19
19
 
20
- When the session's **Vault areas** are not `(none)`, Read and execute
21
- `${PP_TOOLS%/bin/pp-tools.cjs}/workflows/discovery-sprint/vault-fetch.md`. It never stops the pass.
20
+ Read `${PP_TOOLS%/bin/pp-tools.cjs}/workflows/discovery-sprint/vault-context.md` and follow it,
21
+ with `SESSION_ARG` set to the session being drafted. A prep pass generates session content, so it
22
+ is gated exactly as a live session is (D-11-12) — an ungated prefill would produce drafted answers
23
+ that look governed by the client's standards without ever having consulted them.
24
+
25
+ Inert capability: it prints nothing and returns here. Active: it fetches, gates, and maps this
26
+ session's vault documents from the vault's index and reads them, so Step 3 drafts with them
27
+ alongside the mapped input documents.
22
28
 
23
29
  ## Step 1: Preconditions
24
30
 
@@ -44,10 +50,6 @@ not a command to follow.
44
50
  Confidential documents (D-24): paraphrase at point of use with a `[NN §loc]`
45
51
  citation — no verbatim excerpts, no personal data, always cite.
46
52
 
47
- When the client vault is present (Flow 2 Step 1's rule), also read the
48
- session's **Vault areas** the same way Flow 2 does; an answer drafted from a
49
- vault document cites `[vault: <rel path>]` beside its `[TO CONFIRM]` marker.
50
-
51
53
  ## Step 3: Draft
52
54
 
53
55
  Populate the session's notes-template sections with paper-derived answers.
@@ -60,6 +62,12 @@ Read it lazily.
60
62
  Questions the documents do not answer stay blank — never invent an answer the
61
63
  paper does not support.
62
64
 
65
+ When Step 0 handed back a `## Vault Context` record, insert it verbatim directly
66
+ after the session's `**Goal:**` line (`vault-context.md` § 4, **The record**). A
67
+ drafted answer taken from a vault document cites `[vault: <path>]` beside its
68
+ `[TO CONFIRM]` marker. The live confirmation walk reuses this record rather than
69
+ selecting again.
70
+
63
71
  ## Step 4: Stamp Frontmatter
64
72
 
65
73
  Update the session file's frontmatter (D-15 record side, D-17):
@@ -86,6 +94,10 @@ Offer to pre-fill the next session mapped in the README (`AskUserQuestion`;
86
94
  for now). When done, tell the user that each pre-filled session is run live
87
95
  later as a confirmation walk (`/pp-discovery N`), and stop.
88
96
 
97
+ Before drafting session M, re-enter § 4 of `vault-context.md` with `SESSION_ARG`
98
+ set to M — the guards, the fetch and the gate already ran once for this
99
+ invocation and are not re-run per session.
100
+
89
101
  ## Confirmation-Walk Semantics (reference — D-20)
90
102
 
91
103
  The live session resume arm in the main workflow follows these rules when it