@pareto4/pp-core 1.12.1 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/agents/pp-code-reviewer.md +1 -1
- package/agents/pp-executor.md +2 -13
- package/agents/pp-planner.md +1 -1
- package/agents/pp-vault-reviewer.md +51 -0
- package/commands/pp-ns-review.md +2 -1
- package/commands/pp-vault-review.md +33 -0
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/pp-core/bin/lib/capability-registry.cjs +200 -10
- package/pp-core/bin/lib/check-command-router.cjs +1242 -37
- package/pp-core/bin/lib/clusters.cjs +1 -0
- package/pp-core/bin/lib/command-aliases.cjs +7 -0
- package/pp-core/bin/lib/compose-bindings.cjs +41 -27
- package/pp-core/bin/lib/decisions.cjs +104 -27
- package/pp-core/bin/lib/dispatch-plan.cjs +12 -1
- package/pp-core/bin/lib/frontmatter.cjs +37 -0
- package/pp-core/bin/lib/git-base-branch.cjs +136 -10
- package/pp-core/bin/lib/init-contract.cjs +180 -66
- package/pp-core/bin/lib/init-interactive.cjs +4 -20
- package/pp-core/bin/lib/loop-gates.cjs +213 -0
- package/pp-core/bin/lib/loop-resolver.cjs +107 -45
- package/pp-core/bin/lib/org-profile.cjs +218 -44
- package/pp-core/bin/lib/phase-command-router.cjs +7 -1
- package/pp-core/bin/lib/phase-manifest.cjs +527 -0
- package/pp-core/bin/lib/phase-tier.cjs +15 -2
- package/pp-core/bin/lib/phase.cjs +51 -1
- package/pp-core/bin/lib/vault-audit.cjs +2070 -0
- package/pp-core/bin/lib/vault-command-router.cjs +460 -1
- package/pp-core/bin/lib/vault-practices.cjs +1142 -104
- package/pp-core/bin/lib/vault-review.cjs +2359 -0
- package/pp-core/bin/lib/vault-trace.cjs +2831 -0
- package/pp-core/bin/lib/vault-update.cjs +266 -0
- package/pp-core/bin/lib/vault-write-back.cjs +19 -0
- package/pp-core/bin/lib/vault.cjs +277 -12
- package/pp-core/bin/pp-tools.cjs +30 -13
- package/pp-core/bin/shared/model-catalog.json +2 -1
- package/pp-core/references/executor-vault-trace.md +47 -0
- package/pp-core/references/loop-hook-dispatch.md +13 -2
- package/pp-core/references/planner-vault-practices.md +46 -1
- package/pp-core/references/planning-config.md +1 -1
- package/pp-core/templates/practices/README.md +140 -11
- package/pp-core/templates/project-charter.md +226 -0
- package/pp-core/workflows/add-tests.md +4 -0
- package/pp-core/workflows/audit-fix.md +2 -0
- package/pp-core/workflows/check-todos.md +1 -1
- package/pp-core/workflows/code-review-fix.md +2 -0
- package/pp-core/workflows/debug.md +4 -0
- package/pp-core/workflows/discovery-sprint/prefill.md +9 -0
- package/pp-core/workflows/discovery-sprint/prototype.md +2 -1
- package/pp-core/workflows/discovery-sprint/vault-fetch.md +37 -0
- package/pp-core/workflows/discovery-sprint.md +1 -1
- package/pp-core/workflows/discovery-to-build.md +1 -1
- package/pp-core/workflows/execute-phase/steps/serial-group-dispatch.md +1 -0
- package/pp-core/workflows/execute-phase/steps/vault-door-gate.md +19 -0
- package/pp-core/workflows/execute-phase/steps/vault-fetch.md +1 -1
- package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +10 -8
- package/pp-core/workflows/execute-phase.md +22 -5
- package/pp-core/workflows/execute-plan.md +4 -0
- package/pp-core/workflows/fast.md +4 -0
- package/pp-core/workflows/help/modes/full.md +3 -2
- package/pp-core/workflows/init.md +2 -2
- package/pp-core/workflows/new-project.md +1 -1
- package/pp-core/workflows/plan-phase/steps/phase-tier.md +9 -0
- package/pp-core/workflows/plan-phase.md +7 -4
- package/pp-core/workflows/quick.md +4 -0
- package/pp-core/workflows/ship.md +5 -1
- package/pp-core/workflows/undo.md +1 -1
- package/pp-core/workflows/validate-phase.md +4 -0
- package/pp-core/workflows/vault-review.md +65 -0
- package/pp-core/workflows/verify-work.md +3 -0
- package/scripts/gen-capability-registry.cjs +11 -0
- package/scripts/lint-test-file-count.allowlist.json +10 -1
- package/scripts/prompt-injection-scan.sh +2 -0
|
@@ -7,20 +7,22 @@ card verbatim and reads one field; it never re-derives any part of the verdict.
|
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
9
|
_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
|
|
10
|
-
VAULT_CARD=$(pp_run check vault.status --card --point "${VAULT_GATE_POINT:-plan}")
|
|
11
|
-
VAULT_ACTION=$(pp_run check vault.status --pick action)
|
|
10
|
+
VAULT_CARD=$(pp_run check vault.status --card --point "${VAULT_GATE_POINT:-plan}") || exit 1
|
|
11
|
+
VAULT_ACTION=$(pp_run check vault.status --pick action) || exit 1
|
|
12
|
+
[ -n "$VAULT_ACTION" ] || exit 1
|
|
13
|
+
[ -z "$VAULT_CARD" ] || printf '%s\n' "$VAULT_CARD"
|
|
14
|
+
echo "VAULT_ACTION=$VAULT_ACTION"
|
|
15
|
+
[ "$VAULT_ACTION" != stop ] || exit 1
|
|
12
16
|
```
|
|
13
17
|
|
|
14
|
-
**If
|
|
15
|
-
|
|
16
|
-
read that crashed is never treated as a pass.
|
|
18
|
+
**If the fence exits non-zero** (a command failed, the action is empty, or it is `stop`): STOP —
|
|
19
|
+
do not plan or build, in any mode. The gate is `onError: halt`: a read that crashed is never a pass.
|
|
17
20
|
|
|
18
|
-
**Otherwise relay
|
|
19
|
-
`VAULT_ACTION` only:
|
|
21
|
+
**Otherwise relay the printed card to the user verbatim**, then act on `VAULT_ACTION` only:
|
|
20
22
|
|
|
21
23
|
| `VAULT_ACTION` | What to do |
|
|
22
24
|
|---|---|
|
|
23
25
|
| `none` | The vault is disabled — continue silently. |
|
|
24
26
|
| `proceed` | The client vault is loaded — show the card and continue. |
|
|
25
27
|
| `confirm` | Degraded and `vault.required` is false. Interactive: ask for an explicit `confirmed` (`AskUserQuestion`; in TEXT_MODE — `--text` in `$ARGUMENTS` — a plain-text prompt accepting a typed `confirmed`), then continue. With `--auto` / `--chain` in `$ARGUMENTS`: log the card and continue without prompting. |
|
|
26
|
-
| `stop` | `vault.required` is true and the client vault is not loaded. **STOP**
|
|
28
|
+
| `stop` | The fence already exited non-zero: `vault.required` is true and the client vault is not loaded. **STOP** in every mode (including `--auto` / `--chain`); the operator fixes what the card names and re-runs. |
|
|
@@ -35,6 +35,8 @@ Orchestrator coordinates, not executes. Each subagent loads the full execute-pla
|
|
|
35
35
|
the orchestrator never receives the completion signal, treat it as successful based on spot-checks
|
|
36
36
|
and continue to the next wave/plan. Never block indefinitely waiting for a signal — always verify
|
|
37
37
|
via filesystem and git state.
|
|
38
|
+
|
|
39
|
+
**Executor contributions (execute:pre `into: executor`):** every spawn injects its dispatch-plan `contributions[].fragment` verbatim after `${AGENT_SKILLS}`. Every inline path (interactive, Copilot, no `Agent`, a stalled executor switched to inline) follows `pp_run loop render-hooks execute:pre --into executor --phase "${PHASE_NUMBER}" ${TIER_FLAG}` the same way.
|
|
38
40
|
</runtime_compatibility>
|
|
39
41
|
|
|
40
42
|
<required_reading>
|
|
@@ -348,6 +350,8 @@ Read and execute `pp-core/workflows/execute-phase/steps/client-context-gate.md`
|
|
|
348
350
|
|
|
349
351
|
<step name="execute_pre_vault">
|
|
350
352
|
Before any executor: `EXECUTE_PRE_HOOKS_JSON=$(pp_run loop render-hooks execute:pre --raw) || exit 1`. For an active `capId == "vault"` step, Read and execute `pp-core/workflows/execute-phase/steps/vault-fetch.md`; then, for an active `vault.status` gate, set `VAULT_GATE_POINT=execute` and Read and execute `pp-core/workflows/execute-phase/steps/vault-load-gate.md`. None active → skip.
|
|
353
|
+
|
|
354
|
+
Then anchor the phase for `/pp-undo --phase`: `pp_run phase manifest start "${PHASE_NUMBER}" --raw` — print its line and continue whatever it says (write-once; a refusal leaves undo on its git-log fallback).
|
|
351
355
|
</step>
|
|
352
356
|
|
|
353
357
|
<step name="discover_and_group_plans">
|
|
@@ -424,7 +428,7 @@ cd "$ORCHESTRATOR_WT" || { echo "FATAL: cannot cd to orchestrator worktree '$ORC
|
|
|
424
428
|
DISPATCH_PLAN=$(pp_run query phase.dispatch-plan "${PHASE_NUMBER}" --use-worktrees "${USE_WORKTREES:-true}" ${WAVE_FILTER:+--wave "$WAVE_FILTER"} ${GAPS_ONLY:+--gaps-only} ${TIER_FLAG})
|
|
425
429
|
```
|
|
426
430
|
|
|
427
|
-
`stages.execute.waves[]` in order, each with `spawns[]` (`kind` `serial-group`|`plan`, `plans[]` in run order with `id`, `title`, `build`, `touched_tests`; `isolation` `main-checkout`|`worktree`; `build`; `checkpoint`), `run`, `merge`, `build`, `suite_runs` (1), `tracking_commits` (1), `tracking.requirements`, `defects[]`. Report `totals` (spawns, suite runs, tracking commits). Non-empty `filters.lower_wave_incomplete` → STOP: finish the earlier waves first.
|
|
431
|
+
`stages.execute.waves[]` in order, each with `spawns[]` (`kind` `serial-group`|`plan`, `plans[]` in run order with `id`, `title`, `build`, `touched_tests`; `isolation` `main-checkout`|`worktree`; `build`; `checkpoint`; `contributions`), `run`, `merge`, `build`, `suite_runs` (1), `tracking_commits` (1), `tracking.requirements`, `defects[]`. Report `totals` (spawns, suite runs, tracking commits). Non-empty `filters.lower_wave_incomplete` → STOP: finish the earlier waves first.
|
|
428
432
|
|
|
429
433
|
**Stream-idle-timeout prevention — checkpoint heartbeats (#2410):**
|
|
430
434
|
|
|
@@ -607,6 +611,7 @@ increases monotonically across waves. `{status}` is `complete` (success),
|
|
|
607
611
|
</files_to_read>
|
|
608
612
|
|
|
609
613
|
${AGENT_SKILLS}
|
|
614
|
+
{spawn.contributions[].fragment — each verbatim, blank-line separated; nothing when empty}
|
|
610
615
|
|
|
611
616
|
<mcp_tools>
|
|
612
617
|
If CLAUDE.md or project instructions reference MCP tools (e.g. jCodeMunch, context7,
|
|
@@ -844,6 +849,7 @@ increases monotonically across waves. `{status}` is `complete` (success),
|
|
|
844
849
|
else
|
|
845
850
|
echo "⚠ Skipping tracking update — post-merge tests failed (exit ${TEST_EXIT}). Plans remain in-progress until tests pass."
|
|
846
851
|
fi
|
|
852
|
+
pp_run phase manifest record "${PHASE_NUMBER}" --raw # .phase-manifest.json for /pp-undo --phase; never stops the run
|
|
847
853
|
```
|
|
848
854
|
|
|
849
855
|
5.75. **Execute:wave:post capability dispatch:**
|
|
@@ -1067,10 +1073,16 @@ Selected wave finished successfully. This phase still has incomplete plans, so p
|
|
|
1067
1073
|
**If no incomplete plans remain after the selected wave finishes:**
|
|
1068
1074
|
- continue with the normal phase-level verification and completion flow below
|
|
1069
1075
|
- this means the selected wave happened to be the last remaining work in the phase
|
|
1076
|
+
|
|
1077
|
+
**verify:post gates** — every run that reaches this point (a partial run stopped above, so no planned plan is still waiting):
|
|
1078
|
+
```bash
|
|
1079
|
+
VERIFY_POST_GATES_JSON=$(pp_run loop run-gates verify:post --phase "${PHASE_NUMBER}" ${TIER_FLAG}) || exit 1
|
|
1080
|
+
```
|
|
1081
|
+
`loop run-gates` runs every active `verify:post` gate and owns the halt: exit 1 (a `⛔` line) stops the phase here — relay it; do not verify or mark the phase complete. `⚠` advisories: relay, continue.
|
|
1070
1082
|
</step>
|
|
1071
1083
|
|
|
1072
1084
|
<step name="code_review_gate" required="true">
|
|
1073
|
-
**This step is REQUIRED to evaluate the capability hook.** When the code-review capability is active, auto-invoke code review on the phase's source changes.
|
|
1085
|
+
**This step is REQUIRED to evaluate the capability hook.** When the code-review capability is active, auto-invoke code review on the phase's source changes. The review is advisory — it never blocks execution flow. Also dispatches the execute:post gate hooks: advisory ones (e.g. tdd.review-checkpoint) and the blocking `vault.trace` gate.
|
|
1074
1086
|
|
|
1075
1087
|
**Capability gate:**
|
|
1076
1088
|
```bash
|
|
@@ -1101,9 +1113,13 @@ Code review found issues. Consider running:
|
|
|
1101
1113
|
|
|
1102
1114
|
**Error handling:** If the Skill invocation fails or throws, catch the error, display "Code review encountered an error (non-blocking): {error}" and proceed to gate dispatch. Review failures must never block execution.
|
|
1103
1115
|
|
|
1104
|
-
**Execute:post gate hook dispatch.** After code review,
|
|
1116
|
+
**Execute:post gate hook dispatch.** After code review, run the blocking trace gate in code — exit 1 (a `⛔` line) stops the phase here; relay it and do not proceed:
|
|
1117
|
+
```bash
|
|
1118
|
+
TRACE_GATE_JSON=$(pp_run loop run-gates execute:post --phase "${PHASE_NUMBER}" ${TIER_FLAG} --query vault.trace) || exit 1
|
|
1119
|
+
```
|
|
1120
|
+
Then dispatch every other active gate hook from `EXECUTE_POST_HOOKS_JSON` where `kind == "gate"` (skip `check.query == "vault.trace"`):
|
|
1105
1121
|
|
|
1106
|
-
For each
|
|
1122
|
+
For each such gate hook:
|
|
1107
1123
|
```bash
|
|
1108
1124
|
GATE_RESULT=$(pp_run check ${hook.check.query} "${PHASE_NUMBER}" --raw)
|
|
1109
1125
|
CHECK_EXIT=$?
|
|
@@ -1243,7 +1259,7 @@ If `TEXT_MODE` is true, present as a plain-text numbered list and ask the user t
|
|
|
1243
1259
|
<step name="verify_phase_goal">
|
|
1244
1260
|
Verify phase achieved its GOAL, not just completed tasks.
|
|
1245
1261
|
|
|
1246
|
-
**Tier gate:** spawn the verifier only when `DISPATCH_PLAN.stages.verify` lists `pp-verifier` (= `verifier_enabled`). Otherwise print `Verifier skipped (tier {phase_tier.tier})`, treat the phase as `passed` and go to `update_roadmap`. When it lists `verify-install` (heavy tier), first Read and execute `pp-core/workflows/execute-phase/steps/install-run.md`: the installed-product run the verifier judges on.
|
|
1262
|
+
**Tier gate:** spawn the verifier only when `DISPATCH_PLAN.stages.verify` lists `pp-verifier` (= `verifier_enabled`). Otherwise print `Verifier skipped (tier {phase_tier.tier})`, treat the phase as `passed` and go to `update_roadmap`. When it lists `verify-install` (heavy tier), first Read and execute `pp-core/workflows/execute-phase/steps/install-run.md`: the installed-product run the verifier judges on. When it lists `verify-vault-review` (heavy tier, vault practices on), after the verifier returns, set `FORCE=1` and Read and execute `pp-core/workflows/vault-review.md` for this phase: it reports and never changes the route.
|
|
1247
1263
|
|
|
1248
1264
|
```bash
|
|
1249
1265
|
VERIFIER_SKILLS=$(pp_run query agent-skills pp-verifier)
|
|
@@ -1416,6 +1432,7 @@ These items are tracked and will appear in `/pp:progress` and `/pp:audit-uat`.
|
|
|
1416
1432
|
|
|
1417
1433
|
```bash
|
|
1418
1434
|
pp_run query commit "docs(phase-{X}): complete phase execution" --files .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md {phase_dir}/*-VERIFICATION.md
|
|
1435
|
+
pp_run phase manifest record "${PHASE_NUMBER}" --raw # the completion commit joins the /pp-undo --phase list
|
|
1419
1436
|
```
|
|
1420
1437
|
</step>
|
|
1421
1438
|
|
|
@@ -57,6 +57,10 @@ Extract from init JSON: `executor_model`, `commit_docs`, `sub_repos`, `phase_dir
|
|
|
57
57
|
If `.planning/` missing: error.
|
|
58
58
|
</step>
|
|
59
59
|
|
|
60
|
+
<step name="vault_door_gate">
|
|
61
|
+
Skipped under the `<dispatch>` tag: execute-phase gated execute:pre before spawning. Otherwise, before any task runs, Read and execute `pp-core/workflows/execute-phase/steps/vault-door-gate.md`; a stop ends this run with nothing executed.
|
|
62
|
+
</step>
|
|
63
|
+
|
|
60
64
|
<step name="identify_plan">
|
|
61
65
|
```bash
|
|
62
66
|
# Use plans/summaries from INIT JSON, or list files
|
|
@@ -41,6 +41,10 @@ This looks like it needs planning. Use /pp:quick instead:
|
|
|
41
41
|
And stop.
|
|
42
42
|
</step>
|
|
43
43
|
|
|
44
|
+
<step name="vault_door_gate">
|
|
45
|
+
Before any edit, Read and execute `pp-core/workflows/execute-phase/steps/vault-door-gate.md`. A stop ends this task: nothing is edited or committed.
|
|
46
|
+
</step>
|
|
47
|
+
|
|
44
48
|
<step name="execute_inline">
|
|
45
49
|
Do the work directly:
|
|
46
50
|
|
|
@@ -263,7 +263,7 @@ Modes:
|
|
|
263
263
|
- **default** — progress report + intelligent routing
|
|
264
264
|
- **`--next`** — auto-advance to the next logical step (use `--next --force` to bypass safety gates)
|
|
265
265
|
- **`--next --auto`** — like `--next`, but chains steps automatically until milestone completion or a blocking decision
|
|
266
|
-
- **`--next --converge`** — when the next action is planning, route it through `/pp:plan-review-convergence` instead of `/pp:plan-phase`; requires `workflow.plan_review_convergence=true`. `--cross-ai` is an alias. Reviewer flags
|
|
266
|
+
- **`--next --converge`** — when the next action is planning, route it through `/pp:plan-review-convergence` instead of `/pp:plan-phase`; requires `workflow.plan_review_convergence=true`. `--cross-ai` is an alias. Reviewer flags and `--max-cycles N` pass through to it.
|
|
267
267
|
- **`--forensic`** — append a 6-check integrity audit after the progress report
|
|
268
268
|
- **`--do "<text>"`** — smart router: dispatch freeform intent to the matching `/pp-*` command (see *Smart Router* above)
|
|
269
269
|
|
|
@@ -592,7 +592,7 @@ The commands above cover the most common day-to-day flows. Every command listed
|
|
|
592
592
|
|
|
593
593
|
- **`/pp:mvp-phase <phase-number>`** — Plan a phase as a vertical MVP slice (user story + SPIDR splitting) before handing off to plan-phase. Same end-state as `/pp:plan-phase --mvp`, with a guided MVP-shaping intro.
|
|
594
594
|
- **`/pp:ultraplan-phase [phase]`** — [BETA] Offload plan phase to Claude Code's ultraplan cloud; review in browser and import back.
|
|
595
|
-
- **`/pp:plan-review-convergence <phase> [--codex] [--gemini] [--claude] [--opencode] [--ollama] [--lm-studio] [--llama-cpp] [--all] [--text] [--ws <name>] [--max-cycles N]`** — Cross-AI plan convergence loop — replan with review feedback until no HIGH concerns remain.
|
|
595
|
+
- **`/pp:plan-review-convergence <phase> [--codex] [--gemini] [--claude] [--opencode] [--ollama] [--lm-studio] [--llama-cpp] [--all] [--text] [--ws <name>] [--max-cycles N]`** — Cross-AI plan convergence loop — replan with review feedback until no HIGH concerns remain.
|
|
596
596
|
- **`/pp:autonomous [--from N] [--to N] [--only N] [--supervised] [--interactive] [--converge]`** — Run the next incomplete phase autonomously (discuss → plan → execute), best-effort with decisions recorded; `--from`/`--to` for a range, `--supervised` restores prompts. `--converge` routes planning through plan-review convergence; `--cross-ai` is an alias.
|
|
597
597
|
|
|
598
598
|
### Quality, Review & Verification
|
|
@@ -602,6 +602,7 @@ The commands above cover the most common day-to-day flows. Every command listed
|
|
|
602
602
|
- **`/pp:validate-phase [phase]`** — Retroactively audit and fill Nyquist validation gaps for a completed phase.
|
|
603
603
|
- **`/pp:ui-review [phase]`** — Retroactive 6-pillar visual audit of implemented frontend code.
|
|
604
604
|
- **`/pp:eval-review [phase]`** — Audit an executed AI phase's evaluation coverage and produce an EVAL-REVIEW.md remediation plan.
|
|
605
|
+
- **`/pp:vault-review [phase]`** — Retroactive per-practice client-vault review of a phase; counts by verdict, never gates.
|
|
605
606
|
- **`/pp:audit-fix --source <audit-uat> [--severity medium|high|all] [--max N] [--dry-run]`** — Autonomous audit-to-fix pipeline: find issues, classify, fix, test, commit.
|
|
606
607
|
- **`/pp:add-tests <phase> [additional instructions]`** — Generate tests for a completed phase based on UAT criteria and implementation.
|
|
607
608
|
|
|
@@ -174,8 +174,8 @@ pp_run query init-contract --arm-vault
|
|
|
174
174
|
pp_run vault probe
|
|
175
175
|
```
|
|
176
176
|
|
|
177
|
-
Show both outputs verbatim. `--arm-vault` sets `vault.enabled: true
|
|
178
|
-
absent (an explicit
|
|
177
|
+
Show both outputs verbatim. `--arm-vault` sets `vault.enabled: true`, and then `vault.required:
|
|
178
|
+
true` and `vault.practices_enabled: true`, each only where the key is absent (an explicit value is kept); `ok:false` means no client vault is bound — re-run
|
|
179
179
|
`pp-tools init`. The probe line says whether the client vault is reachable at its pinned ref;
|
|
180
180
|
it prints nothing when the vault is disabled. Neither ever blocks this step.
|
|
181
181
|
|
|
@@ -107,7 +107,7 @@ hard-block initiation on an unreachable remote. For non-Pareto repos the block i
|
|
|
107
107
|
a no-op and this step is skipped entirely.
|
|
108
108
|
|
|
109
109
|
When the bindings file is present, also run `pp_run query init-contract --arm-vault`
|
|
110
|
-
(sets `vault.enabled
|
|
110
|
+
(sets `vault.enabled` and `vault.required` to true, each only where absent; `ok:false` = no client vault bound — run
|
|
111
111
|
`/pp-init`) and `pp_run vault probe` (one reachability line; empty when the vault is
|
|
112
112
|
disabled). Show both outputs verbatim; neither blocks.
|
|
113
113
|
|
|
@@ -25,6 +25,15 @@ Spawn nothing the stage does not list. The explicit flags still apply on top: `-
|
|
|
25
25
|
|
|
26
26
|
## After planning (§9, `## PLANNING COMPLETE`)
|
|
27
27
|
|
|
28
|
+
Threat-model presence gate first (active only when the tier resolves `security_enforcement` on, i.e. the step 5.55 banner showed):
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
TM_BLOCK=$(pp_run check threat-model-presence "$PHASE" $TIER_FLAG --pick block) || exit 1
|
|
32
|
+
[ "$TM_BLOCK" != "true" ] || exit 1 # stderr names each new plan without <threat_model>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
If it stops: do not go on to step 10 or 13; re-spawn the planner (step 8) requiring the block in each plan named, then return here.
|
|
36
|
+
|
|
28
37
|
Plans now exist, so re-propose from their `files_modified`:
|
|
29
38
|
|
|
30
39
|
```bash
|
|
@@ -686,6 +686,7 @@ Output this markdown directly (not as a code block):
|
|
|
686
686
|
───────────────────────────────────────────────
|
|
687
687
|
Also available:
|
|
688
688
|
- `/pp:plan-phase {N} --skip-ui ${GSD_WS}` — plan without UI-SPEC (not recommended for frontend phases)
|
|
689
|
+
- Not a UI phase? Matched: {GATE.tokens}. Add `**UI:** none` on its own line in the ROADMAP phase section.
|
|
689
690
|
```
|
|
690
691
|
|
|
691
692
|
**Exit the plan-phase workflow. Do not continue.**
|
|
@@ -815,6 +816,7 @@ Continue to step 8.
|
|
|
815
816
|
Run this Bash command to emit the branded Plan stage banner (renders on stderr, auto-degrades on non-TTY — do NOT print an inline ASCII banner, that would double-print):
|
|
816
817
|
```bash
|
|
817
818
|
pp_run banner plan "PLANNING PHASE {X}"
|
|
819
|
+
touch "${PHASE_DIR}/.planner-run-mark" # the threat-model gate examines only newer plans
|
|
818
820
|
```
|
|
819
821
|
|
|
820
822
|
Then announce the spawn:
|
|
@@ -1325,7 +1327,7 @@ After plans pass the checker (or checker is skipped), verify that all phase requ
|
|
|
1325
1327
|
**Step 1: Extract requirement IDs claimed by plans**
|
|
1326
1328
|
```bash
|
|
1327
1329
|
# Collect all requirement IDs from plan frontmatter
|
|
1328
|
-
PLAN_REQS=$(grep -h "requirements_addressed
|
|
1330
|
+
PLAN_REQS=$(grep -h "^requirements_addressed\|^requirements:" ${PHASE_DIR}/*-PLAN.md 2>/dev/null | tr -d '[]' | tr ',' '\n' | sed 's/^[[:space:]]*//' | sort -u)
|
|
1329
1331
|
```
|
|
1330
1332
|
|
|
1331
1333
|
**Step 2: Compare against phase requirements from ROADMAP**
|
|
@@ -1476,13 +1478,14 @@ Proactive, non-blocking coverage report gated on `workflow.post_planning_gaps`
|
|
|
1476
1478
|
|
|
1477
1479
|
```bash
|
|
1478
1480
|
PLAN_POST_HOOKS_JSON=$(pp_run loop render-hooks plan:post --raw --phase "$PHASE" $TIER_FLAG) || exit 1
|
|
1481
|
+
TRACE_GATE_JSON=$(pp_run loop run-gates plan:post --phase "$PHASE" $TIER_FLAG --query vault.trace) || exit 1
|
|
1479
1482
|
PHASE_REQ_IDS=$(pp_run query init.plan-phase "$PHASE" --pick phase_req_ids 2>/dev/null || echo TBD)
|
|
1480
1483
|
```
|
|
1481
1484
|
|
|
1482
|
-
Read the `activeHooks` array from `PLAN_POST_HOOKS_JSON` in-context. If the
|
|
1483
|
-
`gap-analysis` gate hook is absent (capability inactive), skip this step.
|
|
1485
|
+
The `run-gates` line runs the blocking `vault.trace` gate (vault practices on) and owns its halt: exit 1 (a `⛔` line) stops planning here — relay it verbatim and do not present the plans as ready. Read the `activeHooks` array from `PLAN_POST_HOOKS_JSON` in-context. If the
|
|
1486
|
+
`gap-analysis` gate hook is absent (capability inactive), skip the rest of this step.
|
|
1484
1487
|
|
|
1485
|
-
**For each active entry where `kind == "gate"`** (
|
|
1488
|
+
**For each active entry where `kind == "gate"`** except `check.query == "vault.trace"` (run above), in array order:
|
|
1486
1489
|
|
|
1487
1490
|
```bash
|
|
1488
1491
|
GATE_RESULT=$(pp_run check ${hook.check.query} "${PHASE_DIR}" "${PHASE_REQ_IDS}" --raw)
|
|
@@ -176,6 +176,10 @@ Quick tasks can run mid-phase - validation only checks ROADMAP.md exists, not ph
|
|
|
176
176
|
|
|
177
177
|
---
|
|
178
178
|
|
|
179
|
+
**Step 2.4: Vault door gate** — Step 2.5 is the first write. Read and execute `pp-core/workflows/execute-phase/steps/vault-door-gate.md` now; a stop ends the quick task before anything is written.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
179
183
|
**Step 2.5: Handle quick-task branching**
|
|
180
184
|
|
|
181
185
|
**If `branch_name` is empty/null:** Skip and continue on the current branch.
|
|
@@ -91,9 +91,13 @@ Verify the work is ready to ship:
|
|
|
91
91
|
|
|
92
92
|
```bash
|
|
93
93
|
SHIP_PRE_HOOKS_JSON=$(pp_run loop render-hooks ship:pre --raw --phase "${phase_number}") || exit 1
|
|
94
|
+
TRACE_GATE_JSON=$(pp_run loop run-gates ship:pre --phase "${phase_number}" --query vault.trace) || exit 1
|
|
95
|
+
AUDIT_GATE_JSON=$(pp_run loop run-gates ship:pre --phase "${phase_number}" --query vault.audit) || exit 1
|
|
94
96
|
SECURITY_FILE=$(ls "${PHASE_DIR}"/*-SECURITY.md 2>/dev/null | head -1)
|
|
95
97
|
```
|
|
96
98
|
|
|
99
|
+
The `run-gates` lines run the blocking `vault.trace` and `vault.audit` gates and own their halt: exit 1 (a `⛔` line) stops the ship — relay it verbatim; do not push or open the PR.
|
|
100
|
+
|
|
97
101
|
Read the `activeHooks` array from `SHIP_PRE_HOOKS_JSON` in-context (do NOT pipe it through a shell parser).
|
|
98
102
|
|
|
99
103
|
If an active entry exists with `kind == "gate"`, `capId == "security"`, and `blocking == true`, enforce its predicate (`SECURITY.md` frontmatter `threats_open == 0`) before shipping:
|
|
@@ -113,7 +117,7 @@ Verify the work is ready to ship:
|
|
|
113
117
|
|
|
114
118
|
**Generic `check.query` gate enforcement (capability-driven).**
|
|
115
119
|
|
|
116
|
-
For each active entry in `activeHooks` where `kind == "gate"` and a `check.query` is present (covers any capability-registered query gate, e.g. the `pareto-stack` platform-drift gates — skip entries already handled by the `security` predicate branch above):
|
|
120
|
+
For each active entry in `activeHooks` where `kind == "gate"` and a `check.query` is present (covers any capability-registered query gate, e.g. the `pareto-stack` platform-drift gates — skip entries already handled by the `security` predicate branch above, and `vault.trace` and `vault.audit`, run above):
|
|
117
121
|
|
|
118
122
|
```bash
|
|
119
123
|
GATE_RESULT=$(pp_run check ${hook.check.query} --raw)
|
|
@@ -75,7 +75,7 @@ Parse the user's selection into COMMITS list.
|
|
|
75
75
|
|
|
76
76
|
**MODE=phase:**
|
|
77
77
|
|
|
78
|
-
Read `.planning/.phase-manifest.json` if it exists.
|
|
78
|
+
Read `.planning/.phase-manifest.json` if it exists (execute-phase writes it with `pp_run phase manifest start|record`).
|
|
79
79
|
|
|
80
80
|
If the file exists and `manifest.phases?.[TARGET_PHASE]?.commits` is a non-empty array:
|
|
81
81
|
- Use `manifest.phases[TARGET_PHASE].commits` entries as COMMITS (each entry is a commit hash)
|
|
@@ -97,6 +97,10 @@ Call AskUserQuestion with gap table and options:
|
|
|
97
97
|
2. "Skip — mark manual-only" → add to Manual-Only, Step 6
|
|
98
98
|
3. "Cancel" → exit
|
|
99
99
|
|
|
100
|
+
## 4b. Vault door gate
|
|
101
|
+
|
|
102
|
+
Before the auditor writes tests, Read and execute `pp-core/workflows/execute-phase/steps/vault-door-gate.md`; a stop ends validation with no test written.
|
|
103
|
+
|
|
100
104
|
## 5. Spawn pp-nyquist-auditor
|
|
101
105
|
|
|
102
106
|
Print: `◆ Spawning nyquist auditor... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)`
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<purpose>
|
|
2
|
+
Retroactive client-vault practice review of a built phase: one agent-judged verdict (PASS / FAIL /
|
|
3
|
+
N-A / INCONCLUSIVE) per practice the vault audit cannot check, in `{padded}-VAULT-REVIEW.md`. It
|
|
4
|
+
reports and never gates: no verdict changes a status, a score, a route or a ship.
|
|
5
|
+
</purpose>
|
|
6
|
+
|
|
7
|
+
<available_agent_types>
|
|
8
|
+
Valid pp-core subagent types (use exact names — do not fall back to 'general-purpose'):
|
|
9
|
+
- pp-vault-reviewer — judges the practices the CLI could not settle
|
|
10
|
+
</available_agent_types>
|
|
11
|
+
|
|
12
|
+
<process>
|
|
13
|
+
|
|
14
|
+
## 1. Scaffold
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
_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
|
|
18
|
+
INIT=$(pp_run query init.phase-op "${PHASE_ARG}")
|
|
19
|
+
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Parse `phase_dir`, `phase_number`, `phase_name`, `padded_phase`, `text_mode`. A phase with no
|
|
23
|
+
`*-SUMMARY.md` is not built: say "Phase {N} not executed — run /pp-execute-phase {N} first." and stop.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
SCAFFOLD=$(pp_run vault review scaffold "${PHASE_DIR}" ${FORCE:+--force} --raw)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Branch on its `status`:
|
|
30
|
+
- `inert`, `usage`, `no-argument`, `unresolved` — print its `statement` and stop; nothing is written.
|
|
31
|
+
- `exists` — ask "Re-review" (set `FORCE=1`, scaffold again) or "View" (show the file, stop), with
|
|
32
|
+
AskUserQuestion, or as a numbered list when `--text` is in `$ARGUMENTS` or `text_mode` is true.
|
|
33
|
+
- `written` — continue. With `toJudge` 0 there is nothing to judge: go to step 3.
|
|
34
|
+
|
|
35
|
+
## 2. Judge
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
◆ Spawning vault reviewer... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
Agent(
|
|
43
|
+
subagent_type="pp-vault-reviewer",
|
|
44
|
+
model="{pp_run query resolve-model pp-vault-reviewer --raw}",
|
|
45
|
+
description="Vault practice review Phase {phase_number}",
|
|
46
|
+
prompt="Fill the TODO rows of {phase_dir}/{padded_phase}-VAULT-REVIEW.md (Phase {phase_number}: {phase_name})."
|
|
47
|
+
)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 3. Finalize
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pp_run vault review finalize "${PHASE_DIR}"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Print its output. If it lists repair items, send them to the reviewer once ("Fix these repair items
|
|
57
|
+
in {file}: …") and finalize again. Never clear an item by editing a computed line or a derived row.
|
|
58
|
+
|
|
59
|
+
Route the same way whatever the verdicts: `/pp-verify-work {N}` or `/pp-plan-phase {N+1}`. Commit:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pp_run query commit "docs(${PADDED_PHASE}): vault practice review" --files "${PHASE_DIR}/${PADDED_PHASE}-VAULT-REVIEW.md"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
</process>
|
|
@@ -461,9 +461,12 @@ Present summary:
|
|
|
461
461
|
|
|
462
462
|
```bash
|
|
463
463
|
VERIFY_POST_HOOKS_JSON=$(pp_run loop render-hooks verify:post --raw --phase "${phase_number}" ${GSD_WS} ${TIER_FLAG}) || exit 1
|
|
464
|
+
VERIFY_POST_GATES_JSON=$(pp_run loop run-gates verify:post --phase "${phase_number}" ${GSD_WS} ${TIER_FLAG}) || exit 1
|
|
464
465
|
SECURITY_FILE=$(ls "${PHASE_DIR}"/*-SECURITY.md 2>/dev/null | head -1)
|
|
465
466
|
```
|
|
466
467
|
|
|
468
|
+
`loop run-gates` runs every active `verify:post` gate and owns the halt: exit 1 (a `⛔` line) means a blocking gate blocked — stop, relay it, do not mark the phase complete. `⚠` lines are advisories: relay and continue.
|
|
469
|
+
|
|
467
470
|
Resolve active step hooks from `VERIFY_POST_HOOKS_JSON` where `kind == "step"` and `ref.skill == "secure-phase"`.
|
|
468
471
|
|
|
469
472
|
If an active secure-phase step hook exists AND `SECURITY_FILE` is empty, dispatch the registry-provided skill stem:
|
|
@@ -1204,6 +1204,17 @@ function validateGate(gate, prefix) {
|
|
|
1204
1204
|
if (count !== 1) {
|
|
1205
1205
|
errors.push(prefix + '.check must have exactly one of: query, predicate, agentVerdict');
|
|
1206
1206
|
}
|
|
1207
|
+
// check.args: literal tokens `loop run-gates` appends after the phase — query gates only,
|
|
1208
|
+
// at most 8, each a flag or a plain word (the runner refuses anything else at run time too).
|
|
1209
|
+
if (Object.prototype.hasOwnProperty.call(gate.check, 'args')) {
|
|
1210
|
+
const a = gate.check.args;
|
|
1211
|
+
if (!hasQuery) {
|
|
1212
|
+
errors.push(prefix + '.check.args is only valid beside check.query');
|
|
1213
|
+
} else if (!Array.isArray(a) || a.length > 8
|
|
1214
|
+
|| !a.every((t) => typeof t === 'string' && /^(?:--[a-z][a-z0-9-]*|[a-z0-9][a-z0-9._-]*)$/i.test(t))) {
|
|
1215
|
+
errors.push(prefix + '.check.args must be an array of at most 8 flags (--name) or plain words');
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1207
1218
|
// agentVerdict forces blocking: false (advisory only)
|
|
1208
1219
|
if (hasAgentVerdict && gate.blocking === true) {
|
|
1209
1220
|
errors.push(
|
|
@@ -187,13 +187,22 @@
|
|
|
187
187
|
],
|
|
188
188
|
"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."
|
|
189
189
|
},
|
|
190
|
+
"vault-review": {
|
|
191
|
+
"files": [
|
|
192
|
+
"vault-review.integration.test.cjs",
|
|
193
|
+
"vault-review.security.test.cjs",
|
|
194
|
+
"vault-review.test.cjs"
|
|
195
|
+
],
|
|
196
|
+
"issue": "Agent practice review (vault assurance R5) — three distinct concerns for pp-core/bin/lib/vault-review.cjs: vault-review.test (the pure derivations — vocabularies, both derived-N-A arms, the counts, the range, the clean-render refusal, the scaffold renderer and the line stamper — plus the require-graph barrier), vault-review.integration.test (the seams: the artifact validator and the check vault-review / vault review scaffold|finalize arms driven through the real pp-tools.cjs, where the exit-0 / no-block-key contract is proved), and vault-review.security.test (adversarial practice bodies: a spoofed fence marker, directive-shaped text, a client table inside the fence, an option-shaped base branch). The .security marker is what identifies the adversarial lane to scripts/run-tests.cjs; consolidating would blur three different questions into one file."
|
|
197
|
+
},
|
|
190
198
|
"vault-practices": {
|
|
191
199
|
"files": [
|
|
192
200
|
"vault-practices-examples.test.cjs",
|
|
193
201
|
"vault-practices-matcher.test.cjs",
|
|
202
|
+
"vault-practices.slow.test.cjs",
|
|
194
203
|
"vault-practices.test.cjs"
|
|
195
204
|
],
|
|
196
|
-
"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."
|
|
205
|
+
"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. The practice-file contract extensions added vault-practices.slow.test.cjs — the >5 s worst-case matcher corpus behind the matching cost bound, split out per the .slow suffix convention so the default suite stays fast."
|
|
197
206
|
}
|
|
198
207
|
}
|
|
199
208
|
}
|
|
@@ -89,6 +89,8 @@ ALLOWLIST=(
|
|
|
89
89
|
'tests/security.test.cjs::the adversarial suite for the library sanitisers, whose fixtures are real override and role-manipulation strings asserted to be neutralised; measured 54 pattern hits at HEAD on 2026-10-11'
|
|
90
90
|
'tests/prompt-injection-scan.security.test.cjs::drives scanForInjection in the compiled security library with crafted payloads, and carries a second allowlist of its own for that separate scanner; measured 11 pattern hits at HEAD on 2026-10-11'
|
|
91
91
|
'tests/vault-security.test.cjs::the vault fencing suite plants an instruction-override heading in a fixture vault file and asserts SUMMARY.md fences and defangs it rather than passing it to the agent as instructions; measured 1 pattern hit at HEAD on 2026-10-11'
|
|
92
|
+
'tests/vault-practices-matcher.test.cjs::the vault practices matcher suite plants an instruction-override line inside a fixture practice body and a spoofed fence end, and asserts the matcher keeps both inside the untrusted fenced region, so the fixtures must carry a real injection phrase'
|
|
93
|
+
'tests/vault-review.security.test.cjs::the adversarial suite of the agent practice review seeds a practice body with directive-shaped text aimed at the practice judge (the DIRECTIVE_TEXT constant) and asserts the rendered review entry carries it only between the fence markers, after the reference-data preamble, so the judge reads it as data; the text must be a literal for that assertion to mean anything; measured 1 pattern hit at HEAD on 2026-10-12'
|
|
92
94
|
'tests/verify.test.cjs::the verification suite plants an instruction-override line in a phase artifact and asserts it is rejected, so the line is present as a negative fixture; measured 1 pattern hit at HEAD on 2026-10-11'
|
|
93
95
|
'pp-core/bin/lib/security.cjs::gitignored build output compiled from src/security.cts per .gitignore line 117, so it is unreachable in diff mode and matters only to a local dir-mode run; it carries the library injection patterns inline; measured 8 pattern hits at HEAD on 2026-10-11'
|
|
94
96
|
'tests/read-injection-scanner.security.test.cjs::exercises the read-time injection scanner hook with the payloads a hostile file would carry, each asserted to be blocked rather than obeyed; measured 14 pattern hits at HEAD on 2026-10-11'
|