@pareto4/pp-core 1.12.1 → 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 (196) hide show
  1. package/.claude-plugin/plugin.json +3 -3
  2. package/README.md +26 -9
  3. package/agents/pp-code-reviewer.md +3 -2
  4. package/agents/pp-debug-session-manager.md +9 -6
  5. package/agents/pp-executor.md +7 -21
  6. package/agents/pp-plan-checker.md +4 -4
  7. package/agents/pp-planner.md +23 -9
  8. package/agents/pp-vault-reviewer.md +274 -0
  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 +2 -1
  13. package/commands/pp-plan-phase.md +1 -2
  14. package/commands/pp-vault-review.md +35 -0
  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 +526 -22
  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 +3867 -274
  35. package/pp-core/bin/lib/clusters.cjs +4 -0
  36. package/pp-core/bin/lib/command-aliases.cjs +0 -28
  37. package/pp-core/bin/lib/commands.cjs +9 -4
  38. package/pp-core/bin/lib/compose-bindings.cjs +253 -76
  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 +104 -27
  43. package/pp-core/bin/lib/discovery-vault-check.cjs +246 -0
  44. package/pp-core/bin/lib/frontmatter.cjs +1 -1
  45. package/pp-core/bin/lib/git-base-branch.cjs +138 -10
  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 +653 -629
  49. package/pp-core/bin/lib/init-interactive.cjs +77 -21
  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 +169 -47
  54. package/pp-core/bin/lib/org-profile.cjs +544 -53
  55. package/pp-core/bin/lib/package-identity.cjs +3 -3
  56. package/pp-core/bin/lib/phase-command-router.cjs +1 -23
  57. package/pp-core/bin/lib/phase-scope.cjs +1 -1
  58. package/pp-core/bin/lib/phase.cjs +11 -203
  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 +2594 -0
  66. package/pp-core/bin/lib/vault-command-router.cjs +2756 -60
  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 +1194 -89
  71. package/pp-core/bin/lib/vault-review.cjs +2102 -0
  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 +3184 -0
  75. package/pp-core/bin/lib/vault-update.cjs +242 -0
  76. package/pp-core/bin/lib/vault-write-back.cjs +78 -31
  77. package/pp-core/bin/lib/vault.cjs +2713 -634
  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 -37
  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 +1 -0
  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 -14
  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 +31 -3
  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 +137 -11
  100. package/pp-core/templates/project-charter.md +226 -0
  101. package/pp-core/workflows/_runtime-launcher.snippet.sh +0 -0
  102. package/pp-core/workflows/add-tests.md +55 -0
  103. package/pp-core/workflows/audit-fix.md +63 -0
  104. package/pp-core/workflows/audit-milestone.md +1 -1
  105. package/pp-core/workflows/autonomous.md +85 -11
  106. package/pp-core/workflows/check-todos.md +13 -2
  107. package/pp-core/workflows/code-review-fix.md +59 -1
  108. package/pp-core/workflows/code-review.md +10 -1
  109. package/pp-core/workflows/complete-milestone.md +1 -1
  110. package/pp-core/workflows/debug.md +56 -2
  111. package/pp-core/workflows/discovery-sprint/prefill.md +21 -0
  112. package/pp-core/workflows/discovery-sprint/prototype.md +46 -48
  113. package/pp-core/workflows/discovery-sprint/session-13-gates.md +2 -0
  114. package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +1 -3
  115. package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +1 -3
  116. package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +1 -3
  117. package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +20 -24
  118. package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +1 -3
  119. package/pp-core/workflows/discovery-sprint/sessions/06-security.md +1 -3
  120. package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +1 -3
  121. package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +1 -3
  122. package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +1 -3
  123. package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +1 -6
  124. package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +1 -3
  125. package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +1 -3
  126. package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +1 -3
  127. package/pp-core/workflows/discovery-sprint/templates/context-template.md +1 -1
  128. package/pp-core/workflows/discovery-sprint/vault-context.md +213 -0
  129. package/pp-core/workflows/discovery-sprint.md +7 -3
  130. package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +4 -4
  131. package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +43 -1
  132. package/pp-core/workflows/discovery-to-build.md +9 -1
  133. package/pp-core/workflows/discuss-phase.md +3 -10
  134. package/pp-core/workflows/execute-phase/steps/check-query-gate.md +329 -0
  135. package/pp-core/workflows/execute-phase/steps/client-context-gate.md +3 -2
  136. package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +1 -1
  137. package/pp-core/workflows/execute-phase/steps/execute-pre-dispatch.md +98 -0
  138. package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
  139. package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +6 -8
  140. package/pp-core/workflows/execute-phase/steps/vault-fetch.md +115 -14
  141. package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +322 -18
  142. package/pp-core/workflows/execute-phase/steps/verifier-practice-payload.md +165 -0
  143. package/pp-core/workflows/execute-phase/steps/verify-post-gate.md +163 -0
  144. package/pp-core/workflows/execute-phase.md +154 -110
  145. package/pp-core/workflows/execute-plan.md +58 -4
  146. package/pp-core/workflows/fast.md +53 -0
  147. package/pp-core/workflows/help/modes/full.md +1 -0
  148. package/pp-core/workflows/init/steps/client-context-capture.md +101 -0
  149. package/pp-core/workflows/init/steps/gitlab-provisioning.md +130 -0
  150. package/pp-core/workflows/init/steps/project-md-deferral-narration.md +66 -0
  151. package/pp-core/workflows/init/steps/reachability-outcome.md +70 -0
  152. package/pp-core/workflows/init.md +232 -272
  153. package/pp-core/workflows/new-project.md +31 -23
  154. package/pp-core/workflows/plan-phase.md +99 -43
  155. package/pp-core/workflows/quick.md +120 -2
  156. package/pp-core/workflows/secure-phase.md +1 -1
  157. package/pp-core/workflows/ship.md +57 -24
  158. package/pp-core/workflows/update.md +3 -3
  159. package/pp-core/workflows/validate-phase.md +60 -1
  160. package/pp-core/workflows/vault-review.md +325 -0
  161. package/pp-core/workflows/verify-work.md +13 -12
  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 -0
  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 +13 -2
  173. package/scripts/practice-arithmetic-contract.allow.json +35 -0
  174. package/scripts/prompt-injection-scan.sh +22 -15
  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 -472
  184. package/pp-core/bin/lib/install-run.cjs +0 -536
  185. package/pp-core/bin/lib/phase-tier.cjs +0 -467
  186. package/pp-core/bin/lib/plan-graph.cjs +0 -368
  187. package/pp-core/references/planner-plan-graph.md +0 -66
  188. package/pp-core/workflows/execute-phase/steps/checkpoint-handling.md +0 -45
  189. package/pp-core/workflows/execute-phase/steps/install-run.md +0 -29
  190. package/pp-core/workflows/execute-phase/steps/phase-close-rebaseline.md +0 -23
  191. package/pp-core/workflows/execute-phase/steps/serial-group-dispatch.md +0 -72
  192. package/pp-core/workflows/plan-phase/steps/phase-tier.md +0 -34
  193. package/scripts/lib/size-budget.cjs +0 -311
  194. package/scripts/lint-neutrality-names.cjs +0 -694
  195. package/scripts/lint-org-coordinates.allowlist.json +0 -35
  196. package/scripts/lint-org-coordinates.cjs +0 -426
@@ -0,0 +1,163 @@
1
+ # `verify:post` capability gate dispatch — enforce, do not decorate (TGATE-02, D-05)
2
+
3
+ Extracted from `execute-phase.md` so the host loop body keeps shrinking, per ADR-857 Phase 6.
4
+ `steps/` files are outside `tests/workflow-size-baseline.json` by design; the host is not, and the
5
+ phase-6 ratchet on the host is a ratchet-DOWN that is never re-baselined. This is the second
6
+ application of that pattern in this workflow — `steps/execute-pre-dispatch.md` is the first — and it
7
+ is the mechanism rather than a workaround: every inline candidate for this body measured over the
8
+ host's remaining headroom, while the labelled Read the host gained costs under a hundred bytes.
9
+
10
+ This step is dispatched from two callers: `execute-phase.md`'s end-of-phase verification step, and
11
+ `verify-work.md`'s issues-are-zero branch. Each Reads it immediately after it has
12
+ `VERIFY_POST_HOOKS_JSON` in scope. It exists because **rendering is not enforcing**: every
13
+ `verify:post` call site in this repository resolved STEP-kind hooks only, so a gate declared at this
14
+ point would have rendered and been silently ignored — the decorative-dispatch defect already found
15
+ once at the execute-side point. This file is what makes the declaration enforce.
16
+
17
+ Read the `activeHooks` array from the printed `VERIFY_POST_HOOKS_JSON=` line, in-context (do NOT pipe it through a
18
+ shell parser).
19
+
20
+ **Admission first (D-28, D-05).** Both callers Read this file unconditionally right after rendering
21
+ `verify:post`, so this is where the strict point refuses a config the gate could not read. It
22
+ precedes the inert branch below because a broken config renders no gate and would take that branch.
23
+ Without `.platform-bindings.json` it is inert and prints nothing.
24
+
25
+ ```bash
26
+ _PP_SHIM_NAME="pp-tools.cjs"; _pp_root() { ( for _v in $(env | sed -n 's/^\(GIT_[A-Za-z0-9_]*\)=.*/\1/p'); do unset "$_v"; done; _c=$(git rev-parse --git-common-dir 2>/dev/null); if [ "${_c##*/}" = .git ] && cd "$_c/.." 2>/dev/null; then pwd -P; else git rev-parse --show-toplevel 2>/dev/null || pwd; fi ); }; pp_run() { case "$PP_TOOLS" in *.cjs) node "$PP_TOOLS" "$@";; *) "$PP_TOOLS" "$@";; esac; }; _PP_RUNTIME_ROOT="${RUNTIME_DIR:-$(_pp_root)}"; PP_TOOLS="${_PP_RUNTIME_ROOT}/pp-core/bin/${_PP_SHIM_NAME}"; if [ -f "$PP_TOOLS" ]; then :; elif [ -f "${_PP_RUNTIME_ROOT}/.claude/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${_PP_RUNTIME_ROOT}/.claude/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${_PP_RUNTIME_ROOT}/.codex/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${_PP_RUNTIME_ROOT}/.codex/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "$HOME/.claude/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="$HOME/.claude/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${HERMES_HOME:-$HOME/.hermes}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${CODEX_HOME:-$HOME/.codex}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CODEX_HOME:-$HOME/.codex}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/pp-core/bin/${_PP_SHIM_NAME}"; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/pp-core/bin/${_PP_SHIM_NAME}"; elif command -v pp-tools >/dev/null 2>&1; then PP_TOOLS="$(command -v pp-tools)"; else echo "ERROR: pp-tools.cjs not found at $PP_TOOLS and pp-tools is not on PATH. Run: npx -y @pareto4/pp-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${PP_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${PP_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
27
+ VAULT_ADMIT_PRESENT=$(pp_run query platform-bindings --pick present 2>/dev/null || echo true)
28
+ VAULT_ADMIT=inert
29
+ if [ "$VAULT_ADMIT_PRESENT" = true ]; then VAULT_ADMIT=$(pp_run vault admission 2>/dev/null || echo unrecognised); fi
30
+ 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
31
+ ```
32
+
33
+ **If `activeHooks` is empty or absent, or contains no entry where `kind == "gate"` and a
34
+ `check.query` is present:** skip the rest of this file silently. Print nothing. This branch comes
35
+ first deliberately — it is what keeps a project with no active `verify:post` capability
36
+ structurally inert, and a gate paragraph reached before it would fire on a project that never
37
+ enabled the capability at all.
38
+
39
+ The predicate tests BOTH conditions, which is the predicate `ship.md`'s generic loop tests. Until
40
+ 04-12 it tested only the first, and that looser form was **harmless rather than correct**: an entry
41
+ carrying no `check.query` would have reached the enforcer, whose own loop filters it out — so the
42
+ right behaviour came from another file's guard rather than from this one's, and no predicate gate
43
+ is declared at this point today that would have exposed the difference. A guard that is correct by
44
+ another file's behaviour is one edit away from being wrong.
45
+
46
+ **Otherwise, dispatch the gate.** Set:
47
+
48
+ - `GATE_POINT=verify:post` — the loop point label, message text only.
49
+ - `GATE_HOOKS_JSON=$VERIFY_POST_HOOKS_JSON` — the payload the host already rendered above; this
50
+ file never renders hooks itself.
51
+ - `GATE_POSITIONAL_ARG="${PHASE_DIR}"` — the phase directory, which the host already has in scope
52
+ on the line after the render.
53
+ - `GATE_UNPAIRED=strict` — **but only on a FULL-PHASE run: when the host's `WAVE_FILTER` is unset
54
+ AND this invocation carried no `--gaps-only`.** On a full-phase run this is one of the two points
55
+ at which every plan must already have an executed counterpart, so a
56
+ plan artifact with no summary counterpart is a real defect here rather than the normal
57
+ mid-lifecycle state it is at `plan:post` and `execute:wave:post`. When **either** of those
58
+ partial-execution modes is active, do not set `GATE_UNPAIRED` at all.
59
+
60
+ Four facts a future editor needs before touching this condition (04-12, CR-04; 04-18, WR-01).
61
+ The condition is on **partial execution**, not on one flag — read all four before adding a fifth
62
+ mode, because two of these were written when the condition named `WAVE_FILTER` alone and the
63
+ omission of the other mode shipped a halt onto a supported command. **One:** a
64
+ `--wave N` run legitimately leaves every later-wave plan holding a PLAN.md and no SUMMARY.md —
65
+ that is what the mode is *for*, and the host sets `WAVE_FILTER` from `--wave N` during argument
66
+ parsing, filters the plan list on it, and runs a wave-safety check on it, all before this point.
67
+ **Two:** this step runs **before** the host's `handle_partial_wave_execution` step, which is the
68
+ step that exists precisely to handle an incomplete phase — so an unconditional strict verdict
69
+ here is a false halt on a supported execution mode reached *ahead of* the code that would have
70
+ explained it, and the gate is declared `blocking: true` / `onError: halt`, so the user cannot
71
+ advance to the next wave at all. **Three:** the `ship:pre` caller is deliberately **not**
72
+ conditioned the same way and must not be "harmonised" with this one. Its preflight requires
73
+ `verification.status == passed` before its gate loop is reachable, so a ship run is by
74
+ construction a full-phase run: it has no partial mode and therefore no false halt to fix. The
75
+ asymmetry between the two strict points is the whole finding.
76
+
77
+ **Four:** `--gaps-only` is a partial-execution mode in exactly the sense `--wave N` is, and the
78
+ original condition did not cover it — the prose here even asserted `--wave N` was "the third
79
+ mid-lifecycle case", which made a complete-sounding enumeration out of an incomplete one. The
80
+ host filters the plan list on `--gaps-only` on the same line it filters on `WAVE_FILTER`, so a
81
+ gaps-only run legitimately leaves every **non**-gap plan holding a PLAN.md and no SUMMARY.md.
82
+ With the condition keyed to `WAVE_FILTER` alone, such a run reached this gate with `WAVE_FILTER`
83
+ unset, set `GATE_UNPAIRED=strict`, and drew `coverage_pair_missing` / `block: true` on those
84
+ untouched plans — a hard halt, on a `blocking: true` / `onError: halt` hook, that the user cannot
85
+ advance past. And `handle_partial_wave_execution` does not rescue it twice over: it guards on
86
+ `WAVE_FILTER` only, and it runs *after* this point regardless. Worst of all, the host documents
87
+ `/pp:execute-phase {X} --gaps-only` (with the workstream flag) as the gap-closure cycle's **own**
88
+ command — so the halt landed on the documented remedy for a failed verification, i.e. on the next
89
+ pass of the very loop this gate serves. The workstream placeholder is deliberately NOT written out
90
+ here: this file emits no command, and the token carries a substitution obligation that only a file
91
+ which does emit one should take on.
92
+
93
+ **Why this is keyed to the flag and not to a plan count.** `--wave N` is bound to a named host
94
+ variable; `--gaps-only` is not — the host's argument parsing says it "keeps its current meaning"
95
+ and every later reference is to the flag by name. So the flag is the only form available at this
96
+ point, and using it matches how the host itself tests this mode. Keying instead on "no incomplete
97
+ plans remain" would be a better invariant and would cover the *next* mode without an edit, but
98
+ that count does not exist yet here: the post-execution plan index is computed inside
99
+ `handle_partial_wave_execution`, one step **later**. Moving the count earlier is a host edit, and
100
+ the host is under a ratchet-down byte budget this file exists to protect. If a future change puts
101
+ an incomplete-plan count in scope at this point, prefer it to both flags and delete this fact.
102
+
103
+ The tolerant path is reached by leaving `GATE_UNPAIRED` **unset**, never by setting it to the
104
+ literal `tolerant`. The enforcer emits this parameter as a two-expansion flag pair specifically so
105
+ an unset variable contributes no word at all and the command line stays byte-identical to the one
106
+ that shipped before the parameter existed. An editor who writes the tolerant literal instead gets
107
+ a command line that works for a different reason — its equivalence to the unset case is the
108
+ router's tolerance of an explicit value, not the enforcer's contract — and the next parameter
109
+ added to that line inherits the confusion.
110
+
111
+ Set **no** `GATE_WS`: a phase **directory** already carries the workstream partition — the
112
+ enforcer's own parameter table says so — so the flag is neither needed nor set. Set **no**
113
+ `GATE_STAGE`: this point validates BOTH stages, exactly as the `execute:post` caller deliberately
114
+ omits it, and that omission is the documented invariant rather than an oversight to tidy up later.
115
+
116
+ **A caller exposing neither partial-execution mode is a full-phase run by construction, and therefore
117
+ strict.** The condition above is written for the host, which binds `WAVE_FILTER` during argument
118
+ parsing and parses `--gaps-only`. `/pp-verify-work` binds neither, so both of its tests are vacuously
119
+ satisfied and the strict value applies on every one of its runs. That is the right verdict rather
120
+ than an accident of the wording: a UAT pass is a whole-phase judgement, and it is the point at which
121
+ that workflow marks the phase complete in ROADMAP.md and STATE.md, so a plan artifact with no
122
+ executed counterpart is exactly the defect the strict value exists to catch — not the normal
123
+ mid-lifecycle state it is at `plan:post` and `execute:wave:post`. The obligation that places on the
124
+ next caller: if one ever grows a partial-execution mode, it owes this condition the same two tests
125
+ the host already answers, written here rather than assumed at the call site.
126
+
127
+ **Before the enforcer runs, close the phase's range.** Run
128
+ `pp_run vault manifest record "${PHASE_DIR}" --raw`, print its one-line result, and continue
129
+ regardless. It writes `phases[<phase>].commits` = `git rev-list --reverse --no-merges
130
+ <startCommit>..HEAD` beside the anchor item 4 of `execute-pre-dispatch.md` recorded, so the
131
+ `vault.audit` gate dispatched next and `/pp-undo` read one list. A phase with no anchor (built
132
+ before the anchor existed) answers `start-not-recorded`, which is exactly the case the audit's
133
+ trace-digest fallback covers. `/pp-verify-work` reaches this file with `${PHASE_DIR}` bound too.
134
+
135
+ **Then commit the recorded manifest.** Only when that result's `ok` is `true`, run
136
+ `pp_run query commit "docs(phase-<phase>): record phase manifest" --files "<manifestPath>"`, with
137
+ `<phase>` and `<manifestPath>` taken from the same result (`manifestPath` is root-relative). Print the
138
+ outcome and continue on every path. Without this, a project that commits `.planning/` keeps the
139
+ manifest untracked and `/pp-ship`'s clean-tree preflight asks about it every time. The commit verb
140
+ itself skips on `commit_docs: false` and on a gitignored `.planning/`, so no guard is added here. A
141
+ `nothing_to_commit` or failed commit is printed, never retried with raw `git`, and never halts.
142
+
143
+ Then Read and follow `pp-core/workflows/execute-phase/steps/check-query-gate.md` — the single
144
+ generic `check.query` gate enforcer (GATE-02, D-VH-7). **It owns the halt decision**, the two-step
145
+ contract and the `message` passthrough. Do not restate its loop here, and do not interpret its
146
+ verdict here: a third enforcement loop is precisely what that enforcer was created to prevent, and
147
+ two were already collapsed into it.
148
+
149
+ **After the enforcer returns, certify the phase's end anchors — on a full-phase run only.** The
150
+ enforcer halts on a blocking verdict, so this paragraph is reached only when it let the run go on;
151
+ nothing here assumes the phase is clean from that. On a full-phase run (the same two tests that set
152
+ `GATE_UNPAIRED=strict` above: `WAVE_FILTER` unset and no `--gaps-only`), run
153
+ `pp_run vault anchor certify "${PHASE_DIR}" --raw`, print its one-line result, and continue
154
+ regardless. On a `--wave N` or `--gaps-only` run, skip it: the verb's own strict check would block on
155
+ the plans not yet run, so it could only refuse `not-clean`. The verb re-runs
156
+ `check vault.trace "${PHASE_DIR}" --unpaired strict` itself and records each SUMMARY's end anchor in
157
+ the directory's `.vault-certificates.json` only when that check answers clean with no override, so
158
+ this step can never certify what the gate did not pass (D-107, D-108). A skipped or failed step
159
+ certifies nothing, which only widens the next phase's SEAL-01 reach and never hides a commit (D-110).
160
+ The verb prints nothing unless `vault admission` answers `on` (no `.platform-bindings.json`, or the
161
+ vault off), so print nothing yourself then either. Every result (a certificate, `kept`, or a
162
+ refusal such as `not-clean` or `overridden`) is printed, never retried, and never halts. No bash fence: this file carries one
163
+ launcher preamble, in the admission fence, and this step runs as the manifest-record step above runs.