@pareto4/pp-core 1.17.1 → 1.19.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.
Files changed (199) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +5 -3
  3. package/agents/pp-code-fixer.md +9 -16
  4. package/agents/pp-code-reviewer.md +2 -78
  5. package/agents/pp-codebase-mapper.md +3 -0
  6. package/agents/pp-debugger.md +3 -2
  7. package/agents/pp-executor.md +25 -30
  8. package/agents/pp-integration-checker.md +1 -0
  9. package/agents/pp-phase-researcher.md +3 -1
  10. package/agents/pp-plan-checker.md +3 -0
  11. package/agents/pp-planner.md +16 -12
  12. package/agents/pp-research-synthesizer.md +1 -1
  13. package/agents/pp-ui-auditor.md +1 -0
  14. package/agents/pp-ui-researcher.md +3 -1
  15. package/agents/pp-vault-reviewer.md +43 -189
  16. package/agents/pp-verifier.md +10 -8
  17. package/commands/pp-add-tests.md +1 -1
  18. package/commands/pp-debug.md +1 -0
  19. package/commands/pp-init.md +4 -3
  20. package/commands/pp-ns-manage.md +1 -1
  21. package/commands/pp-ns-review.md +1 -1
  22. package/commands/pp-quick.md +3 -0
  23. package/commands/pp-review-backlog.md +2 -1
  24. package/commands/pp-vault-promote.md +7 -8
  25. package/commands/pp-vault-review.md +6 -7
  26. package/gemini-extension.json +1 -1
  27. package/package.json +3 -6
  28. package/pp-core/bin/lib/capability-registry.cjs +15 -357
  29. package/pp-core/bin/lib/capability-state.cjs +15 -79
  30. package/pp-core/bin/lib/capability-writer.cjs +2 -16
  31. package/pp-core/bin/lib/check-command-router.cjs +342 -2298
  32. package/pp-core/bin/lib/clusters.cjs +2 -2
  33. package/pp-core/bin/lib/commands.cjs +79 -4
  34. package/pp-core/bin/lib/compose-bindings.cjs +99 -16
  35. package/pp-core/bin/lib/config.cjs +3 -6
  36. package/pp-core/bin/lib/discovery-rollup.cjs +9 -6
  37. package/pp-core/bin/lib/discovery-screens.cjs +20 -2
  38. package/pp-core/bin/lib/discovery-sweep.cjs +12 -3
  39. package/pp-core/bin/lib/discovery-vault-check.cjs +37 -3
  40. package/pp-core/bin/lib/git-base-branch.cjs +10 -13
  41. package/pp-core/bin/lib/git-provider.cjs +5 -6
  42. package/pp-core/bin/lib/init-contract.cjs +55 -42
  43. package/pp-core/bin/lib/init-interactive.cjs +6 -20
  44. package/pp-core/bin/lib/io.cjs +2 -8
  45. package/pp-core/bin/lib/loop-resolver.cjs +8 -32
  46. package/pp-core/bin/lib/org-profile.cjs +16 -92
  47. package/pp-core/bin/lib/vault-command-router.cjs +174 -1206
  48. package/pp-core/bin/lib/vault-coordinates.cjs +4 -4
  49. package/pp-core/bin/lib/vault-index.cjs +44 -74
  50. package/pp-core/bin/lib/vault-seal.cjs +37 -122
  51. package/pp-core/bin/lib/vault-standards-promote.cjs +577 -0
  52. package/pp-core/bin/lib/vault-standards-review.cjs +293 -0
  53. package/pp-core/bin/lib/vault-standards.cjs +15 -16
  54. package/pp-core/bin/lib/vault-trace.cjs +40 -3016
  55. package/pp-core/bin/lib/vault-update.cjs +5 -26
  56. package/pp-core/bin/lib/vault.cjs +122 -316
  57. package/pp-core/bin/pp-tools.cjs +15 -36
  58. package/pp-core/org-profile.json +1 -1
  59. package/pp-core/references/autonomous-best-effort.md +4 -3
  60. package/pp-core/references/autonomous-lifecycle.md +1 -0
  61. package/pp-core/references/autonomous-smart-discuss.md +5 -1
  62. package/pp-core/references/discovery-id-conventions.md +35 -2
  63. package/pp-core/references/execute-mvp-tdd.md +3 -3
  64. package/pp-core/references/git-integration.md +57 -33
  65. package/pp-core/references/git-planning-commit.md +31 -9
  66. package/pp-core/references/phase-argument-parsing.md +1 -0
  67. package/pp-core/references/planner-gap-closure.md +1 -0
  68. package/pp-core/references/planner-revision.md +3 -1
  69. package/pp-core/references/planner-tdd.md +4 -4
  70. package/pp-core/references/planner-vault-standards.md +16 -25
  71. package/pp-core/references/planning-config.md +3 -3
  72. package/pp-core/references/shell-portability.md +28 -0
  73. package/pp-core/references/tdd.md +20 -16
  74. package/pp-core/references/worktree-path-safety.md +1 -0
  75. package/pp-core/templates/README.md +1 -1
  76. package/pp-core/templates/VAULT-REVIEW.md +24 -170
  77. package/pp-core/templates/milestone.md +5 -5
  78. package/pp-core/workflows/add-backlog.md +1 -1
  79. package/pp-core/workflows/add-tests.md +8 -5
  80. package/pp-core/workflows/add-todo.md +3 -1
  81. package/pp-core/workflows/ai-integration-phase.md +2 -1
  82. package/pp-core/workflows/audit-fix.md +7 -6
  83. package/pp-core/workflows/audit-milestone.md +1 -0
  84. package/pp-core/workflows/autonomous.md +6 -0
  85. package/pp-core/workflows/check-todos.md +2 -1
  86. package/pp-core/workflows/cleanup.md +3 -1
  87. package/pp-core/workflows/code-review-fix.md +15 -8
  88. package/pp-core/workflows/code-review.md +16 -58
  89. package/pp-core/workflows/complete-milestone.md +13 -3
  90. package/pp-core/workflows/debug.md +4 -2
  91. package/pp-core/workflows/diagnose-issues.md +1 -1
  92. package/pp-core/workflows/discovery-sprint/prototype.md +2 -2
  93. package/pp-core/workflows/discovery-sprint/session-13-gates.md +1 -1
  94. package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +51 -7
  95. package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +5 -3
  96. package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +10 -8
  97. package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +11 -2
  98. package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +5 -5
  99. package/pp-core/workflows/discovery-sprint/sessions/06-security.md +3 -3
  100. package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +2 -2
  101. package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +14 -2
  102. package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +2 -2
  103. package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +10 -10
  104. package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +11 -8
  105. package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +21 -18
  106. package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +5 -3
  107. package/pp-core/workflows/discovery-sprint/templates/context-template.md +9 -3
  108. package/pp-core/workflows/discovery-sprint/vault-context.md +17 -12
  109. package/pp-core/workflows/discovery-sprint.md +11 -8
  110. package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +1 -0
  111. package/pp-core/workflows/discuss-phase-assumptions.md +5 -3
  112. package/pp-core/workflows/discuss-phase.md +8 -3
  113. package/pp-core/workflows/docs-update.md +1 -0
  114. package/pp-core/workflows/eval-review.md +2 -1
  115. package/pp-core/workflows/execute-phase/steps/check-query-gate.md +13 -14
  116. package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +1 -0
  117. package/pp-core/workflows/execute-phase/steps/execute-pre-dispatch.md +5 -25
  118. package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -0
  119. package/pp-core/workflows/execute-phase/steps/vault-fetch.md +18 -6
  120. package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +6 -109
  121. package/pp-core/workflows/execute-phase/steps/verify-post-gate.md +1 -17
  122. package/pp-core/workflows/execute-phase.md +21 -12
  123. package/pp-core/workflows/execute-plan.md +24 -15
  124. package/pp-core/workflows/explore.md +1 -1
  125. package/pp-core/workflows/forensics.md +4 -2
  126. package/pp-core/workflows/health.md +1 -0
  127. package/pp-core/workflows/help/modes/full.md +1 -1
  128. package/pp-core/workflows/import.md +2 -1
  129. package/pp-core/workflows/ingest-docs.md +1 -0
  130. package/pp-core/workflows/init/steps/client-context-capture.md +1 -0
  131. package/pp-core/workflows/init/steps/github-provisioning.md +145 -0
  132. package/pp-core/workflows/init/steps/gitlab-provisioning.md +18 -4
  133. package/pp-core/workflows/init/steps/reachability-outcome.md +3 -1
  134. package/pp-core/workflows/init.md +56 -66
  135. package/pp-core/workflows/map-codebase.md +5 -1
  136. package/pp-core/workflows/milestone-summary.md +2 -1
  137. package/pp-core/workflows/mvp-phase.md +1 -0
  138. package/pp-core/workflows/new-milestone.md +9 -5
  139. package/pp-core/workflows/new-project.md +9 -8
  140. package/pp-core/workflows/new-workspace.md +1 -0
  141. package/pp-core/workflows/next.md +2 -1
  142. package/pp-core/workflows/pause-work.md +6 -4
  143. package/pp-core/workflows/plan-milestone-gaps.md +3 -1
  144. package/pp-core/workflows/plan-phase/steps/chunked-planning.md +1 -1
  145. package/pp-core/workflows/plan-phase.md +21 -6
  146. package/pp-core/workflows/plan-review-convergence.md +2 -0
  147. package/pp-core/workflows/plant-seed.md +4 -2
  148. package/pp-core/workflows/pr-branch.md +2 -0
  149. package/pp-core/workflows/progress.md +5 -0
  150. package/pp-core/workflows/quick.md +19 -12
  151. package/pp-core/workflows/reapply-patches.md +3 -0
  152. package/pp-core/workflows/remove-phase.md +1 -1
  153. package/pp-core/workflows/resume-project.md +3 -0
  154. package/pp-core/workflows/review.md +7 -1
  155. package/pp-core/workflows/secure-phase.md +2 -1
  156. package/pp-core/workflows/session-report.md +1 -0
  157. package/pp-core/workflows/ship.md +8 -1
  158. package/pp-core/workflows/sketch-wrap-up.md +1 -1
  159. package/pp-core/workflows/sketch.md +3 -2
  160. package/pp-core/workflows/spec-phase.md +2 -1
  161. package/pp-core/workflows/spike-wrap-up.md +1 -1
  162. package/pp-core/workflows/spike.md +4 -3
  163. package/pp-core/workflows/sync-skills.md +5 -2
  164. package/pp-core/workflows/thread.md +3 -2
  165. package/pp-core/workflows/transition.md +6 -0
  166. package/pp-core/workflows/ui-phase.md +3 -1
  167. package/pp-core/workflows/ui-review.md +2 -1
  168. package/pp-core/workflows/ultraplan-phase.md +1 -0
  169. package/pp-core/workflows/undo.md +14 -10
  170. package/pp-core/workflows/update.md +5 -0
  171. package/pp-core/workflows/validate-phase.md +9 -6
  172. package/pp-core/workflows/vault-review.md +65 -186
  173. package/pp-core/workflows/vault-write-back.md +78 -110
  174. package/pp-core/workflows/verify-phase.md +5 -0
  175. package/pp-core/workflows/verify-work.md +9 -2
  176. package/scripts/gen-capability-registry.cjs +12 -93
  177. package/scripts/lint-test-file-count.allowlist.json +1 -19
  178. package/scripts/lint-test-file-count.cjs +5 -6
  179. package/scripts/prompt-injection-scan.sh +1 -4
  180. package/scripts/sync-runtime-launcher.cjs +115 -5
  181. package/pp-core/bin/lib/practices-review-check.cjs +0 -944
  182. package/pp-core/bin/lib/vault-audit.cjs +0 -2594
  183. package/pp-core/bin/lib/vault-mode.cjs +0 -235
  184. package/pp-core/bin/lib/vault-practices.cjs +0 -2057
  185. package/pp-core/bin/lib/vault-review.cjs +0 -2102
  186. package/pp-core/bin/lib/vault-write-back.cjs +0 -905
  187. package/pp-core/references/planner-vault-practices.md +0 -95
  188. package/pp-core/references/practice-arithmetic-contract.md +0 -121
  189. package/pp-core/references/verifier-practice-verdicts.md +0 -263
  190. package/pp-core/templates/practices/README.md +0 -248
  191. package/pp-core/templates/practices/error-handling.md +0 -18
  192. package/pp-core/templates/practices/minimal-comments.md +0 -14
  193. package/pp-core/templates/practices/naming-convention.md +0 -18
  194. package/pp-core/templates/practices/practice-template.md +0 -23
  195. package/pp-core/workflows/execute-phase/steps/verifier-practice-payload.md +0 -165
  196. package/scripts/gen-practice-arithmetic-contract.cjs +0 -412
  197. package/scripts/practice-arithmetic-contract.allow.json +0 -35
  198. package/scripts/record-vault-mode-golden.cjs +0 -89
  199. package/scripts/vault-calibrate.cjs +0 -996
@@ -1,11 +1,10 @@
1
1
  <purpose>
2
- Take one local practice file from preview to a confirmed, pushed promotion into the client
3
- vault — and nothing more. This workflow is the single human gate in front of an
4
- irreversible outward action: it shows the operator the complete preview, asks once whether
5
- to proceed, and only on explicit approval re-invokes the same guarded CLI arm in confirmed
6
- mode. It never authors, edits, or reformats the practice file, and it performs no direct git
7
- operation of its own — every outward action happens inside the promote verb, which this
8
- workflow only calls.
2
+ Take one local standards document from preview to a confirmed, pushed promotion into the client
3
+ vault, together with its INDEX.md entry, and nothing more. This workflow is the single human gate
4
+ in front of an irreversible outward action: it shows the operator the complete preview, asks once
5
+ whether to proceed, and only on explicit approval re-invokes the same guarded CLI verb in confirmed
6
+ mode. It never authors, edits or reformats the document, and it performs no git operation of its
7
+ own; every outward action happens inside `pp_run vault standards promote`.
9
8
  </purpose>
10
9
 
11
10
  <required_reading>
@@ -15,79 +14,67 @@ Read all files referenced by the invoking prompt's execution_context before star
15
14
  <process>
16
15
 
17
16
  <step name="text_mode">
18
- Resolve `TEXT_MODE` here, before the first question, mirroring the same resolution `init.md`
19
- does before its own first prompt. Set `TEXT_MODE=true` if `--text` is present in
20
- `$ARGUMENTS`. When `TEXT_MODE` is active, replace every `AskUserQuestion` call in this
21
- workflow — including the gate in Step 4 — with a plain-text numbered list, and ask the
22
- operator to type their choice number. This is required because non-Claude runtimes have no
23
- interactive question tool, and the confirmation in Step 4 is the entire point of this
24
- workflow — it must degrade to plain text, never disappear.
25
- </step>
26
-
27
- <step name="resolve_launcher">
28
- Resolve the `pp_run` launcher shim before any other step, so every call below goes through
29
- the repo-local or installed `pp-tools` binary rather than assuming a global install:
30
-
31
- ```bash
32
- ```
17
+ Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS`. When `TEXT_MODE` is active, replace
18
+ every `AskUserQuestion` call in this workflow, including the gate below, with a plain-text numbered
19
+ list, and ask the operator to type their choice number. The confirmation is the point of this
20
+ workflow: it must degrade to plain text, never disappear.
33
21
  </step>
34
22
 
35
23
  <step name="resolve_input">
36
- Take the practice file path from `$ARGUMENTS` (the first token that is not `--text`). When
37
- none was given, ask the operator for one with a plain prompt:
24
+ Take from `$ARGUMENTS`:
38
25
 
39
- ```text
40
- Path to the local practice file to promote (e.g. practices/my-practice.md):
41
- ```
26
+ - the document path: the first token that is not a flag (required; ask for it with a plain prompt
27
+ when it is missing: `Path to the local standards document to promote (e.g. docs/standards/api-errors.md):`);
28
+ - `--path <vault-path>`: where the document goes in the vault (optional; defaults to
29
+ `standards/<file name>`);
30
+ - `--entry "<what it is>; used when <…>"`: the document's INDEX.md entry text (required when the
31
+ vault's INDEX.md does not list the document yet; replaces the existing line when given for a
32
+ listed document);
33
+ - `--section <title>`: the INDEX.md `##` section a new entry goes under (optional).
34
+
35
+ When the document is new to the vault and no `--entry` was given, ask the operator for one plain
36
+ sentence of the form `<what the document is>; used when <when a plan should follow it>`. Never
37
+ invent it.
42
38
 
43
- Do not compose, author, rewrite, or reformat the practice file at this or any later step —
44
- authoring stays outside this guarded path entirely. Whatever bytes are on disk right now at
45
- that path are exactly the bytes that will be previewed and, if approved, pushed.
39
+ Do not compose, rewrite or reformat the document at this or any later step. Whatever bytes are on
40
+ disk at that path are exactly the bytes that will be previewed and, if approved, pushed.
46
41
 
47
42
  ```bash
48
- PRACTICE_FILE="<the resolved path>"
43
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
44
+ DOC_FILE="<the resolved path>"
45
+ PROMOTE_ARGS=() # add --path, --entry and --section with their values, as given
49
46
  ```
50
47
  </step>
51
48
 
52
49
  <step name="preview">
53
- Run the promote verb with the file path and `--raw`, capturing stdout, stderr, and the exit
54
- code. This invocation performs no outward action — it only clones into a disposable
55
- directory, computes the diff, and reports.
50
+ Run the promote verb without `--confirmed`, capturing stdout, stderr and the exit code. This
51
+ invocation performs no outward action: it clones the client vault into a disposable directory,
52
+ writes the change there, and reports the diff.
56
53
 
57
54
  ```bash
58
55
  _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
56
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
59
57
  PROMOTE_PREVIEW_OUT=$(mktemp)
60
58
  PROMOTE_PREVIEW_ERR=$(mktemp)
61
- pp_run vault practices promote "$PRACTICE_FILE" --raw >"$PROMOTE_PREVIEW_OUT" 2>"$PROMOTE_PREVIEW_ERR"
59
+ pp_run vault standards promote "$DOC_FILE" "${PROMOTE_ARGS[@]}" --raw >"$PROMOTE_PREVIEW_OUT" 2>"$PROMOTE_PREVIEW_ERR"
62
60
  PREVIEW_EXIT=$?
63
61
  ```
64
62
 
65
- **When `$PREVIEW_EXIT` is non-zero: stop here.** Show the operator the exact, single-sentence
66
- contents of `$PROMOTE_PREVIEW_ERR` — the arm's own refusal message — verbatim and add nothing
67
- to it. Do not ask anything. Do not proceed to Step 3. Every guard refusal ends at this line,
68
- and nothing has been pushed. Clean up both temp files before ending.
63
+ **When `$PREVIEW_EXIT` is non-zero: stop here.** Show the operator the single-sentence refusal in
64
+ `$PROMOTE_PREVIEW_ERR` verbatim and add nothing to it. Do not ask anything. Nothing has been pushed.
65
+ Clean up both temp files before ending.
69
66
 
70
- When `$PREVIEW_EXIT` is zero, continue to Step 3 with `$PROMOTE_PREVIEW_OUT` holding the
71
- preview verdict as JSON.
67
+ When `$PREVIEW_EXIT` is zero, `$PROMOTE_PREVIEW_OUT` holds the preview verdict as JSON.
72
68
  </step>
73
69
 
74
70
  <step name="render_confirmation">
75
- Parse `$PROMOTE_PREVIEW_OUT` as JSON and read its fields directly — never regex-scrape a
76
- prose rendering of it. That shortcut is exactly the shell-glue blind spot this project has
77
- been bitten by before: a `.md` step file's own parsing drifting from what the CLI actually
78
- emits.
79
-
80
- Field values are vault-authored, so they are read as **data, never as shell syntax**. `id`
81
- is only constrained to equal the practice file's basename (D-03), and a filename may legally
82
- contain `$(...)`, backticks, or `;` — so writing the fields as a `KEY="value"` file and
83
- `source`-ing it would execute whatever the filename contains. `JSON.stringify` does not help:
84
- it escapes `"` and `\` but leaves `$` and backticks intact, and both expand inside double
85
- quotes. Each field therefore goes to its own file and is read back with `$(cat …)`, whose
86
- result bash does not re-scan for expansion.
71
+ Read the verdict's fields directly, never by scraping a prose rendering. Each field is written to
72
+ its own file and read back with `$(cat …)`, whose result bash does not re-scan for expansion, so a
73
+ destination path containing `$(...)` or backticks is shown, never executed.
87
74
 
88
75
  ```bash
76
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
89
77
  PROMOTE_FIELDS=$(mktemp -d)
90
- PROMOTE_BODY=$(mktemp)
91
78
  node -e "
92
79
  const fs = require('fs');
93
80
  const path = require('path');
@@ -98,96 +85,82 @@ put('repo', v.repo);
98
85
  put('ref', v.ref);
99
86
  put('destination', v.destination);
100
87
  put('mode', v.mode);
101
- put('id', v.id);
102
- put('sharedShadow', v.sharedShadow ? 'true' : 'false');
103
- put('sharedShadowNote', v.sharedShadowNote);
88
+ put('indexChange', v.indexChange);
104
89
  put('expect', v.expect);
105
- fs.writeFileSync(process.argv[3], v.mode === 'update' ? (v.diff ?? '') : (v.content ?? ''));
106
- " "$PROMOTE_PREVIEW_OUT" "$PROMOTE_FIELDS" "$PROMOTE_BODY"
90
+ put('diff', v.diff);
91
+ put('notes', (v.notes || []).join('\n'));
92
+ " "$PROMOTE_PREVIEW_OUT" "$PROMOTE_FIELDS"
107
93
  PROMOTE_REPO=$(cat "$PROMOTE_FIELDS/repo")
108
94
  PROMOTE_REF=$(cat "$PROMOTE_FIELDS/ref")
109
95
  PROMOTE_DEST=$(cat "$PROMOTE_FIELDS/destination")
110
96
  PROMOTE_MODE=$(cat "$PROMOTE_FIELDS/mode")
111
- PROMOTE_ID=$(cat "$PROMOTE_FIELDS/id")
112
- PROMOTE_SHARED=$(cat "$PROMOTE_FIELDS/sharedShadow")
113
- PROMOTE_SHARED_NOTE=$(cat "$PROMOTE_FIELDS/sharedShadowNote")
97
+ PROMOTE_INDEX=$(cat "$PROMOTE_FIELDS/indexChange")
114
98
  PROMOTE_EXPECT=$(cat "$PROMOTE_FIELDS/expect")
115
99
  ```
116
100
 
117
- Show the operator, in this exact order, reading every value from the parsed fields above —
118
- never truncated, summarized, or collapsed to "the first N lines":
101
+ Show the operator, in this order, never truncated or summarised:
119
102
 
120
103
  1. The target vault repository (`$PROMOTE_REPO`) and ref (`$PROMOTE_REF`).
121
- 2. The destination path inside that repository (`$PROMOTE_DEST`).
122
- 3. Whether this **creates** a new practice or **updates** an existing one
123
- (`$PROMOTE_MODE`, for id `$PROMOTE_ID`).
124
- 4. When `$PROMOTE_SHARED` is `true`, the shared-vault disclosure sentence verbatim from
125
- `$PROMOTE_SHARED_NOTE` — this id also exists in the shared Pareto vault, and promoting it
126
- here makes the client's version the one that applies for this client.
127
- 5. One line stating plainly that the secret scan already run inside the promote verb found
128
- nothing in this file — a file that failed that scan would have refused before a preview
129
- was ever produced, so reaching this line already proves the scan passed.
130
- 6. The complete contents of `$PROMOTE_BODY` — the full diff when `$PROMOTE_MODE` is
131
- `update`, or the full new-file content when it is `create`. Show every byte; the operator
132
- is about to approve exactly these bytes, so nothing here may be shortened.
104
+ 2. The destination path in the vault (`$PROMOTE_DEST`), and whether this **creates** a new
105
+ standards document or **updates** an existing one (`$PROMOTE_MODE`).
106
+ 3. What happens to INDEX.md (`$PROMOTE_INDEX`): `added` (a new line), `replaced` (its line gets the
107
+ new entry text) or `unchanged`.
108
+ 4. One line stating that the secret scan inside the promote verb found nothing; a document that
109
+ failed it would have been refused before any preview existed.
110
+ 5. Any notes in `$PROMOTE_FIELDS/notes`.
111
+ 6. The complete contents of `$PROMOTE_FIELDS/diff`: the document and the INDEX.md change, every
112
+ byte. The operator is about to approve exactly these bytes.
133
113
  </step>
134
114
 
135
115
  <step name="gate">
136
- Ask the operator with `AskUserQuestion` (or a numbered plain-text prompt when `TEXT_MODE` is
137
- active) whether to push this to the client vault. Offer exactly two outcomes — there is no
138
- default, no timeout, and no auto-advance, because this is an irreversible outward action and
139
- this confirmation is the whole review step:
116
+ Ask once whether to push. Exactly two outcomes, no default, no timeout, no auto-advance:
140
117
 
141
118
  ```text
142
119
  AskUserQuestion([
143
120
  {
144
121
  header: "Push to client vault",
145
- question: "Push this practice to the client vault now?",
122
+ question: "Push this standards document to the client vault now?",
146
123
  multiSelect: false,
147
124
  options: [
148
- { label: "Push it", description: "Push exactly the diff/content shown above" },
125
+ { label: "Push it", description: "Push exactly the diff shown above" },
149
126
  { label: "Stop", description: "Do not push anything — the vault stays unchanged" }
150
127
  ]
151
128
  }
152
129
  ])
153
130
  ```
154
131
 
155
- **When the operator chooses to stop:** print one sentence saying nothing was pushed and the
156
- client vault is unchanged, clean up the temp files, and end the workflow here.
132
+ **When the operator chooses to stop:** say in one sentence that nothing was pushed and the client
133
+ vault is unchanged, clean up the temp paths, and end.
157
134
 
158
- **When the operator chooses to push:** continue to Step 5.
135
+ **When the operator chooses to push:** continue.
159
136
  </step>
160
137
 
161
138
  <step name="push">
162
- Only inside this approved branch, run the promote verb again with the same file path, the
163
- confirmed flag, the expect token carried verbatim from the preview verdict, and `--raw`. This
164
- workflow never runs an outward git operation itself — the only thing it does here is invoke
165
- this verb a second time; every push, every commit, and every other outward action happens
166
- inside the verb, not in this shell block.
139
+ Only inside this approved branch, run the verb again with the same arguments, `--confirmed`, and the
140
+ expect token carried verbatim from the preview.
167
141
 
168
142
  ```bash
143
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
169
144
  PROMOTE_PUSH_OUT=$(mktemp)
170
145
  PROMOTE_PUSH_ERR=$(mktemp)
171
- pp_run vault practices promote "$PRACTICE_FILE" --confirmed --expect "$PROMOTE_EXPECT" --raw >"$PROMOTE_PUSH_OUT" 2>"$PROMOTE_PUSH_ERR"
146
+ pp_run vault standards promote "$DOC_FILE" "${PROMOTE_ARGS[@]}" --confirmed --expect "$PROMOTE_EXPECT" --raw >"$PROMOTE_PUSH_OUT" 2>"$PROMOTE_PUSH_ERR"
172
147
  PUSH_EXIT=$?
173
148
  ```
174
149
 
175
- **When `$PUSH_EXIT` is non-zero:** show the arm's own single-sentence refusal from
176
- `$PROMOTE_PUSH_ERR` verbatim and end — the arm has already guaranteed nothing was pushed on a
177
- refusal. A concurrent-edit refusal here is expected, ordinary behavior: it means someone else
178
- changed this same practice in the client vault between the preview in Step 2 and this push.
179
- The remedy is simply to run this workflow again from the top — the new preview will diff
180
- against their current version. Clean up all temp files before ending.
181
-
182
- When `$PUSH_EXIT` is zero, continue to Step 6 with `$PROMOTE_PUSH_OUT` holding the push
183
- verdict as JSON.
150
+ **When `$PUSH_EXIT` is non-zero:** show the verb's single-sentence refusal from `$PROMOTE_PUSH_ERR`
151
+ verbatim and end; a refusal guarantees nothing was pushed. A concurrent-edit refusal is ordinary:
152
+ someone changed the document or INDEX.md in the vault since the preview. The remedy is to run this
153
+ workflow again from the top. Clean up all temp paths before ending.
184
154
  </step>
185
155
 
186
156
  <step name="report">
187
- Parse `$PROMOTE_PUSH_OUT` the same way Step 3 parsed the preview verdict — direct field
188
- reads, never a prose scrape:
157
+ Read the push verdict's fields the same way, then show the operator the repository, the destination,
158
+ the new commit, what happened to INDEX.md, and whether the local vault cache was refreshed. When the
159
+ refresh did not succeed, say so plainly: the next vault fetch picks the change up; the push itself
160
+ succeeded.
189
161
 
190
162
  ```bash
163
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
191
164
  node -e "
192
165
  const fs = require('fs');
193
166
  const v = JSON.parse(fs.readFileSync(process.argv[1], 'utf8'));
@@ -195,19 +168,14 @@ process.stdout.write(
195
168
  'repo=' + (v.repo ?? '') + '\n' +
196
169
  'destination=' + (v.destination ?? '') + '\n' +
197
170
  'commit=' + (v.commit ?? '') + '\n' +
171
+ 'indexChange=' + (v.indexChange ?? '') + '\n' +
198
172
  'cacheRefreshed=' + String(!!v.cacheRefreshed) + '\n'
199
173
  );
200
174
  " "$PROMOTE_PUSH_OUT"
201
175
  ```
202
176
 
203
- Show the operator: the repository, the destination path, the new commit the push produced,
204
- and whether the local read cache of the vault was refreshed. When the local cache refresh did
205
- not succeed, say so plainly and note that the next vault fetch will pick up the change — this
206
- is a cosmetic staleness in the local cache only, not a sign the push failed.
207
-
208
- Clean up every temp path created in this workflow (`$PROMOTE_PREVIEW_OUT`,
209
- `$PROMOTE_PREVIEW_ERR`, `$PROMOTE_BODY`, `$PROMOTE_PUSH_OUT`, `$PROMOTE_PUSH_ERR`, and the
210
- `$PROMOTE_FIELDS` **directory**, which needs `rm -rf`) before ending.
177
+ Clean up every temp path created in this workflow (`$PROMOTE_PREVIEW_OUT`, `$PROMOTE_PREVIEW_ERR`,
178
+ `$PROMOTE_PUSH_OUT`, `$PROMOTE_PUSH_ERR`, and the `$PROMOTE_FIELDS` directory with `rm -rf`).
211
179
  </step>
212
180
 
213
181
  </process>
@@ -38,6 +38,7 @@ Extract from init JSON: `phase_dir`, `phase_number`, `phase_name`, `has_plans`,
38
38
 
39
39
  Then load phase details and list plans/summaries:
40
40
  ```bash
41
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
41
42
  pp_run query roadmap.get-phase "${phase_number}"
42
43
  grep -E "^| ${phase_number}" .planning/REQUIREMENTS.md 2>/dev/null || true
43
44
  ls "$phase_dir"/*-SUMMARY.md "$phase_dir"/*-PLAN.md 2>/dev/null || true
@@ -57,6 +58,7 @@ Extract **phase goal** from ROADMAP.md (the outcome to verify, not tasks), **req
57
58
  Use `pp-tools.cjs query` verify handlers (or legacy pp-tools) to extract must_haves from each PLAN:
58
59
 
59
60
  ```bash
61
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
60
62
  for plan in "$PHASE_DIR"/*-PLAN.md; do
61
63
  MUST_HAVES=$(pp_run query frontmatter.get "$plan" --field must_haves)
62
64
  echo "=== $plan ===" && echo "$MUST_HAVES"
@@ -124,6 +126,7 @@ For each truth: identify supporting artifacts → check artifact status → chec
124
126
  Use `pp-tools.cjs query verify.artifacts` (or legacy pp-tools) for artifact verification against must_haves in each PLAN:
125
127
 
126
128
  ```bash
129
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
127
130
  for plan in "$PHASE_DIR"/*-PLAN.md; do
128
131
  ARTIFACT_RESULT=$(pp_run query verify.artifacts "$plan")
129
132
  echo "=== $plan ===" && echo "$ARTIFACT_RESULT"
@@ -167,6 +170,7 @@ wiring or leftover code from plan revisions.
167
170
  Use `pp-tools.cjs query verify.key-links` (or legacy pp-tools) for key link verification against must_haves in each PLAN:
168
171
 
169
172
  ```bash
173
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
170
174
  for plan in "$PHASE_DIR"/*-PLAN.md; do
171
175
  LINKS_RESULT=$(pp_run query verify.key-links "$plan")
172
176
  echo "=== $plan ===" && echo "$LINKS_RESULT"
@@ -223,6 +227,7 @@ phase.
223
227
  no `<decisions>` block.
224
228
 
225
229
  ```bash
230
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
226
231
  GATE_CFG=$(pp_run query config-get workflow.context_coverage_gate 2>/dev/null || echo "true")
227
232
  if [ "$GATE_CFG" != "false" ]; then
228
233
  # Discover the phase CONTEXT.md via glob expansion rather than `ls | head`
@@ -38,6 +38,7 @@ If $ARGUMENTS contains a phase number, load context:
38
38
 
39
39
  ```bash
40
40
  _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
41
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
41
42
  GSD_WS=""
42
43
  echo "$ARGUMENTS" | grep -qE -- '--ws[[:space:]]+[^[:space:]]+' && GSD_WS=$(echo "$ARGUMENTS" | grep -oE -- '--ws[[:space:]]+[^[:space:]]+')
43
44
  PHASE_ARG=$(echo "$ARGUMENTS" | sed -E 's/--ws[[:space:]]+[^[:space:]]+//g' | xargs)
@@ -51,6 +52,7 @@ AGENT_SKILLS_CHECKER=$(pp_run query agent-skills pp-plan-checker)
51
52
  Parse JSON for: `planner_model`, `checker_model`, `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `has_verification`, `uat_path`.
52
53
 
53
54
  ```bash
55
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
54
56
  # MVP mode detection via the centralized phase.mvp-mode resolver.
55
57
  # verify-work has no --mvp CLI flag (mode is inherited from the planned phase),
56
58
  # so we omit --cli-flag — the verb falls through roadmap → config → false.
@@ -111,6 +113,7 @@ Continue to `create_uat_file`.
111
113
  Before UAT, check UI capability activation and whether Playwright/Puppeteer MCP tools are available.
112
114
 
113
115
  ```bash
116
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
114
117
  PLAN_HOOKS_JSON=$(pp_run loop render-hooks plan:pre --raw)
115
118
  UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
116
119
  ```
@@ -148,6 +151,7 @@ UI checkpoints: {N} auto-verified, {M} queued for manual review
148
151
  Use `phase_dir` from init (or run init if not already done).
149
152
 
150
153
  ```bash
154
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
151
155
  ls "$phase_dir"/*-SUMMARY.md 2>/dev/null || true
152
156
  ```
153
157
 
@@ -167,6 +171,7 @@ When `MVP_MODE=false` (mode is null, absent, or the phase has no `**Mode:**` lin
167
171
  **User-story format guard.** When `MVP_MODE=true`, also verify the phase's goal is in User Story format via the centralized validator:
168
172
 
169
173
  ```bash
174
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
170
175
  PHASE_GOAL=$(pp_run query roadmap.get-phase "${phase_number}" ${GSD_WS} --pick goal)
171
176
  USER_STORY_VALID=$(pp_run query user-story.validate --story "$PHASE_GOAL" --pick valid)
172
177
  if [ "$USER_STORY_VALID" != "true" ]; then
@@ -434,7 +439,7 @@ Clear Current Test section:
434
439
 
435
440
  Commit the UAT file:
436
441
  ```bash
437
- pp_run query commit "test({phase_num}): complete UAT - {passed} passed, {issues} issues" --files ".planning/phases/XX-name/{phase_num}-UAT.md"
442
+ pp_run query commit "docs(planning): complete UAT - {passed} passed, {issues} issues" --phase "{phase_num}" --files ".planning/phases/XX-name/{phase_num}-UAT.md"
438
443
  ```
439
444
 
440
445
  Present summary:
@@ -458,12 +463,13 @@ Present summary:
458
463
  **If issues == 0:**
459
464
 
460
465
  ```bash
466
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
461
467
  VERIFY_POST_HOOKS_JSON=$(pp_run loop render-hooks verify:post --raw)
462
468
  printf '%s' "$VERIFY_POST_HOOKS_JSON" | node -e "let h=[];try{h=JSON.parse(require('fs').readFileSync(0,'utf-8')).activeHooks||[]}catch{}console.log('VERIFY_POST_HOOKS_JSON='+JSON.stringify({activeHooks:h}))"
463
469
  SECURITY_FILE=$(ls "${PHASE_DIR}"/*-SECURITY.md 2>/dev/null | head -1)
464
470
  ```
465
471
 
466
- **Vault practice gates.** Read `pp-core/workflows/execute-phase/steps/verify-post-gate.md` — it
472
+ **Vault gates.** Read `pp-core/workflows/execute-phase/steps/verify-post-gate.md` — it
467
473
  dispatches this point's blocking gates to the generic enforcer, which owns the halt. Rendering the
468
474
  payload above is not enforcing it, and the branch below marks the phase complete.
469
475
 
@@ -478,6 +484,7 @@ Skill(skill="pp-${ref.skill}", args="{phase}")
478
484
  After the skill returns, refresh `SECURITY_FILE`:
479
485
 
480
486
  ```bash
487
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
481
488
  SECURITY_FILE=$(ls "${PHASE_DIR}"/*-SECURITY.md 2>/dev/null | head -1)
482
489
  ```
483
490
 
@@ -1247,8 +1247,7 @@ function validateGate(gate, prefix) {
1247
1247
  * - contribution.into ∈ step's agentRoles, OR contribution.into names an agent
1248
1248
  * already registered by some capability's own `agents` array (a non-pipeline
1249
1249
  * consumer of the same loop point, e.g. code-review.md reading execute:post
1250
- * alongside execute-phase.md — see capabilities/vault-practices's
1251
- * execute:post -> pp-code-reviewer contribution)
1250
+ * alongside execute-phase.md)
1252
1251
  * - when references a config key in cap.config
1253
1252
  *
1254
1253
  * NOTE: step.consumes satisfiability is NOT checked here — it requires the full
@@ -1331,78 +1330,9 @@ function validateAgainstContract(cap, capId, knownAgentIds) {
1331
1330
  return errors;
1332
1331
  }
1333
1332
 
1334
- /**
1335
- * Phase 15 D-02: validate the per-hook `vaultMode` filter field.
1336
- *
1337
- * The pure loop resolver evaluates `vaultMode` against an injected mode value
1338
- * and never emits it, so an unknown or misplaced value must be refused HERE,
1339
- * at generation, never interpreted at runtime. Three rules, each reported on
1340
- * its own:
1341
- * (a) the field is allowed on contributions and gates only (a step is
1342
- * refused);
1343
- * (b) the value must be a string in the vault capability's declared
1344
- * `config["vault.mode"].values`. That enum declaration is the ONE owner of
1345
- * the closed set; it is read here, never restated. With no vault
1346
- * capability, or no such values array, there is no closed set and every
1347
- * `vaultMode` is refused (fail closed);
1348
- * (c) the declaring capability must be `vault` or list `vault` in `requires`,
1349
- * so a filtered hook can only exist where the vault gate's closure holds.
1350
- *
1351
- * Presence is own-property presence, so a JSON `null` is refused under (b).
1352
- *
1353
- * @param {Map<string, object>} capMap
1354
- * @returns {string[]} error strings; empty = all pass
1355
- */
1356
- function validateVaultModeField(capMap) {
1357
- const errors = [];
1358
- const vaultCap = capMap.get('vault');
1359
- const modeDecl = vaultCap && vaultCap.config && typeof vaultCap.config === 'object'
1360
- ? vaultCap.config['vault.mode']
1361
- : undefined;
1362
- const allowed = modeDecl && Array.isArray(modeDecl.values)
1363
- ? modeDecl.values.filter((v) => typeof v === 'string')
1364
- : null;
1365
- const KINDS = [['steps', 'step'], ['contributions', 'contribution'], ['gates', 'gate']];
1366
- for (const [capId, cap] of capMap) {
1367
- if (!cap || typeof cap !== 'object') continue;
1368
- const prefix = 'capability "' + capId + '"';
1369
- const requiresVault = Array.isArray(cap.requires) && cap.requires.includes('vault');
1370
- for (const [arrKey, kind] of KINDS) {
1371
- const arr = Array.isArray(cap[arrKey]) ? cap[arrKey] : [];
1372
- for (const hook of arr) {
1373
- if (!hook || typeof hook !== 'object') continue;
1374
- if (!Object.prototype.hasOwnProperty.call(hook, 'vaultMode')) continue;
1375
- const where = prefix + ' ' + kind + ' at point "' + String(hook.point) + '"';
1376
- const value = hook.vaultMode;
1377
- if (kind === 'step') {
1378
- errors.push(where + ': vaultMode is allowed on contributions and gates only');
1379
- }
1380
- if (allowed === null) {
1381
- errors.push(
1382
- where + ': vaultMode ' + JSON.stringify(value) + ' cannot be checked: the vault capability ' +
1383
- 'declares no "vault.mode" values, which own the closed set',
1384
- );
1385
- } else if (typeof value !== 'string' || !allowed.includes(value)) {
1386
- errors.push(
1387
- where + ': vaultMode ' + JSON.stringify(value) + ' is not one of [' + allowed.join(', ') +
1388
- '] (the closed set owned by the vault capability\'s "vault.mode" values)',
1389
- );
1390
- }
1391
- if (capId !== 'vault' && !requiresVault) {
1392
- errors.push(
1393
- where + ': vaultMode is allowed only on the "vault" capability or a capability that ' +
1394
- 'lists "vault" in requires',
1395
- );
1396
- }
1397
- }
1398
- }
1399
- }
1400
- return errors;
1401
- }
1402
-
1403
1333
  /**
1404
1334
  * Quick 261015-ect (QD-13): the vault-family rule for `requiresPlatformBindings`,
1405
- * the mirror of the vaultMode rule (c) above. A capability that is `vault` or
1335
+ * A capability that is `vault` or
1406
1336
  * lists `vault` in `requires` MUST declare the field, so a vault-family
1407
1337
  * capability added later cannot render hooks on a project the vault does not
1408
1338
  * govern by forgetting it. A capability outside the family may declare it too.
@@ -1441,18 +1371,15 @@ const SEAL_FIELD_NAME = 'onRecordedArtifacts';
1441
1371
  * A gate carrying the field renders for an enabled, config-inactive capability
1442
1372
  * over recorded vault artifacts, bypassing the capability's active check and
1443
1373
  * its `when` (D-18). That is a widening of render behaviour, so the field is
1444
- * closed HERE, at generation, to exactly the shape the two shipped seal gates
1445
- * have. Six rules, each reported on its own:
1374
+ * closed HERE, at generation, to exactly the shape the shipped seal gate has.
1375
+ * Five rules, each reported on its own:
1446
1376
  * (a) gates only (a step or contribution carrying it is refused);
1447
1377
  * (b) the value is literal `true` (`"true"`, `1`, `null`, `false` refused);
1448
- * (c) the declaring capability lists `vault` in `requires` and is not `vault`
1449
- * itself (D-26: with the vault off the injected mode is null, so both
1450
- * modes' filtered gates pass and sealing the vault capability too would
1451
- * dispatch `check vault.trace` twice);
1452
- * (d) the gate also carries `vaultMode` (the resolver filters by mode BEFORE
1453
- * the predicate, research C-1);
1454
- * (e) the gate is `blocking: true`;
1455
- * (f) the point is `ship:pre` (D-21: the requirement's block point; a seal at
1378
+ * (c) the declaring capability is `vault` or lists `vault` in `requires`.
1379
+ * A seal gate never dispatches twice: the resolver drops it whenever its
1380
+ * capability is active, so it renders only with the vault switched off;
1381
+ * (d) the gate is `blocking: true`;
1382
+ * (e) the point is `ship:pre` (D-21: the requirement's block point; a seal at
1456
1383
  * verify:post would halt execute-phase on a switch set on purpose).
1457
1384
  *
1458
1385
  * Presence is own-property presence.
@@ -1481,15 +1408,12 @@ function validateSealField(capMap) {
1481
1408
  if (value !== true) {
1482
1409
  errors.push(where + ': ' + SEAL_FIELD_NAME + ' must be literal true (got ' + JSON.stringify(value) + ')');
1483
1410
  }
1484
- if (capId === 'vault' || !requiresVault) {
1411
+ if (capId !== 'vault' && !requiresVault) {
1485
1412
  errors.push(
1486
- where + ': ' + SEAL_FIELD_NAME + ' is allowed only on a capability that lists "vault" in requires ' +
1487
- 'and is not "vault" itself',
1413
+ where + ': ' + SEAL_FIELD_NAME + ' is allowed only on the "vault" capability or a capability that ' +
1414
+ 'lists "vault" in requires',
1488
1415
  );
1489
1416
  }
1490
- if (!Object.prototype.hasOwnProperty.call(hook, 'vaultMode')) {
1491
- errors.push(where + ': a gate carrying ' + SEAL_FIELD_NAME + ' must also carry vaultMode');
1492
- }
1493
1417
  if (hook.blocking !== true) {
1494
1418
  errors.push(where + ': a gate carrying ' + SEAL_FIELD_NAME + ' must be blocking: true');
1495
1419
  }
@@ -1662,10 +1586,6 @@ const TIER_RANK = { core: 0, standard: 1, full: 2 };
1662
1586
  function validateCrossCapability(capMap, centralKeys) {
1663
1587
  const errors = [];
1664
1588
 
1665
- // Phase 15 D-02: the per-hook vaultMode filter field (needs capMap to read the
1666
- // vault capability's closed value set and every capability's `requires`).
1667
- errors.push(...validateVaultModeField(capMap));
1668
-
1669
1589
  // Phase 16 D-19: the per-hook seal field (needs capMap for every capability's
1670
1590
  // `requires`).
1671
1591
  errors.push(...validateSealField(capMap));
@@ -2841,7 +2761,6 @@ module.exports = {
2841
2761
  validateAgainstContract,
2842
2762
  validateConsumesGlobal,
2843
2763
  validateCrossCapability,
2844
- validateVaultModeField,
2845
2764
  validateSealField,
2846
2765
  validatePlatformBindingsField,
2847
2766
  SEAL_FIELD_NAME,
@@ -114,14 +114,6 @@
114
114
  ],
115
115
  "issue": "180"
116
116
  },
117
- "vault-review": {
118
- "files": [
119
- "vault-review.integration.test.cjs",
120
- "vault-review.security.test.cjs",
121
- "vault-review.test.cjs"
122
- ],
123
- "issue": "71924 — Vault Assurance phase 06 (06-03/06-04), three distinct concerns for pp-core/bin/lib/vault-review.cjs: vault-review.test (06-03 unit tests over every pure derivation — the verdict vocabularies, both derived-N-A arms, the counts arithmetic, the range resolver and the clean-render refusal predicate), vault-review.integration.test (06-04 SEAM tests — the deterministic validator of the agent-written artifact, plus the `check vault-review` arm driven through the real pp-core/bin/pp-tools.cjs, where the exit-0/no-block-key contract of D-06-01 and VERIFY-03 is proved at the boundary a gate enforcer actually reads, with RED control 2 proving that guard non-vacuous), and vault-review.security.test (06-04 ADVERSARIAL tests — a spoofed external-content fence-end marker, directive-shaped text and a forged tool-call block planted in real practice BODIES, proving 05-REVIEW's open Critical CR-02 is not repeated on this surface, plus the CR-03 class: no absolute filesystem path in any emitted byte). The split mirrors the `vault` cluster's own reasoning: the unit lane, the CLI seam and the security marker are independently load-bearing verification gates, and the `.security.test.cjs` marker is what makes the adversarial lane identifiable to a reader and to scripts/run-tests.cjs. Consolidating would blur three different questions into one file."
124
- },
125
117
  "verify": {
126
118
  "files": [
127
119
  "bug-2969-verify-reapply-patches.test.cjs",
@@ -189,22 +181,12 @@
189
181
  "files": [
190
182
  "feat-71924-vault-required-gate.test.cjs",
191
183
  "vault-doors.integration.test.cjs",
192
- "vault-practices.property.test.cjs",
193
184
  "vault-read-path.test.cjs",
194
185
  "vault-security.test.cjs",
195
186
  "vault-status-check.test.cjs",
196
187
  "vault.test.cjs"
197
188
  ],
198
- "issue": "v1.7.0 Client Vault Load — four distinct concerns: vault.test (fetch/status/summary unit), vault-security (VSEC-02/03 write-guard + secret/fence), vault-read-path (VGATE-03 SUMMARY pointer), vault-status-check (VGATE-02 gate-query handler). Phase 24-02 added vault-practices.property.test.cjs (fast-check fuzz of the practices glob-fallback compiler + a practices-frontmatter round-trip, PRAC-02) — it is grouped under this bucket rather than \"vault-practices\" because the test-file-count lint's hyphen-prefix matcher treats \"vault-practices.property\" (period before \"property\") as starting with \"vault-\" before it can match the more specific \"vault-practices-\" prefix; the module it actually tests is pp-core/bin/lib/vault-practices.cjs, not vault.cjs. Vault Assurance phase 02 (#71924) added feat-71924-vault-required-gate.test.cjs as the phase's ONE consolidated source-grep feature file — it owns the source-text assertions for GATE-02, GATE-03, GATE-06, GATE-08, GATE-09 and GATE-10 together, deliberately in a single file so the phase's six requirements do not each spawn a sibling; later plans in the phase extend it rather than adding files. Vault Assurance phase 16 plan 10 added vault-doors.integration.test.cjs as the phase's ONE door fence-proof owner (DOOR-01..05, D-09): every door's shipped fence (execute-plan, quick, fast, debug, the secondary doors) composed by tests/helpers/gate-fence-composer.cjs and run in bash and zsh, one describe per door plan, so 16-12..16-15 extend it rather than each adding a sibling. The hyphen-prefix matcher attributes it here because no vault-doors module exists; it tests workflow fences, not vault.cjs."
199
- },
200
- "vault-practices": {
201
- "files": [
202
- "vault-practices-examples.test.cjs",
203
- "vault-practices-matcher.test.cjs",
204
- "vault-practices.slow.test.cjs",
205
- "vault-practices.test.cjs"
206
- ],
207
- "issue": "Phase 24 (24-01/24-02/24-04) — three distinct concerns for pp-core/bin/lib/vault-practices.cjs: vault-practices.test (24-01 parser/validation/discovery unit tests), vault-practices-matcher.test (24-02 glob matcher + two-bucket result + report unit tests), vault-practices-examples.test (24-04 shipped-example drift guard + minimal-comments global-bucket smoke case, PRAC-03 Success Criterion 3). All three test the same production module and are each independently load-bearing; consolidating would blur distinct plan-level verification gates. Vault Assurance phase 05 (#71924) added vault-practices.slow.test.cjs — the >5 s six-axis worst-case matcher corpus for the same production module (pp-core/bin/lib/vault-practices.cjs), split out per the .slow suffix convention so the default suite stays fast; attributed here once the count lint learned to strip the .slow suffix."
189
+ "issue": "v1.7.0 Client Vault Load — four distinct concerns: vault.test (fetch/status/summary unit), vault-security (VSEC-02/03 write-guard + secret/fence), vault-read-path (VGATE-03 SUMMARY pointer), vault-status-check (VGATE-02 gate-query handler). Vault Assurance phase 02 (#71924) added feat-71924-vault-required-gate.test.cjs as the phase's ONE consolidated source-grep feature file — it owns the source-text assertions for GATE-02, GATE-03, GATE-06, GATE-08, GATE-09 and GATE-10 together, deliberately in a single file so the phase's six requirements do not each spawn a sibling; later plans in the phase extend it rather than adding files. Vault Assurance phase 16 plan 10 added vault-doors.integration.test.cjs as the phase's ONE door fence-proof owner (DOOR-01..05, D-09): every door's shipped fence (execute-plan, quick, fast, debug, the secondary doors) composed by tests/helpers/gate-fence-composer.cjs and run in bash and zsh, one describe per door plan, so 16-12..16-15 extend it rather than each adding a sibling. The hyphen-prefix matcher attributes it here because no vault-doors module exists; it tests workflow fences, not vault.cjs."
208
190
  }
209
191
  }
210
192
  }