@pareto4/pp-core 1.21.1 → 1.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/bin/install.js +205 -28
- package/commands/pp-help.md +2 -1
- package/gemini-extension.json +1 -1
- package/hooks/dist/pp-check-update-worker.js +1 -1
- package/hooks/dist/pp-context-monitor.js +3 -3
- package/hooks/dist/pp-statusline.js +88 -14
- package/hooks/dist/pp-update-banner.js +44 -4
- package/hooks/dist/pp-workflow-guard.js +3 -3
- package/hooks/pp-check-update-worker.js +1 -1
- package/hooks/pp-context-monitor.js +3 -3
- package/hooks/pp-statusline.js +88 -14
- package/hooks/pp-update-banner.js +44 -4
- package/hooks/pp-workflow-guard.js +3 -3
- package/package.json +2 -2
- package/pp-core/bin/lib/audience-command.cjs +271 -0
- package/pp-core/bin/lib/audience.cjs +293 -0
- package/pp-core/bin/lib/commands.cjs +26 -17
- package/pp-core/bin/lib/discovery-charter.cjs +13 -1
- package/pp-core/bin/lib/discovery-flow-page.cjs +191 -0
- package/pp-core/bin/lib/discovery-for-phase.cjs +33 -5
- package/pp-core/bin/lib/discovery-input-check.cjs +11 -11
- package/pp-core/bin/lib/discovery-processes.cjs +97 -4
- package/pp-core/bin/lib/discovery-session-check.cjs +7 -7
- package/pp-core/bin/lib/discovery-status.cjs +419 -0
- package/pp-core/bin/lib/discovery-stories.cjs +36 -5
- package/pp-core/bin/lib/discovery-tests.cjs +398 -0
- package/pp-core/bin/lib/explain-command.cjs +48 -0
- package/pp-core/bin/lib/gate-card.cjs +97 -0
- package/pp-core/bin/lib/hook-messages.cjs +132 -0
- package/pp-core/bin/lib/init-contract.cjs +116 -76
- package/pp-core/bin/lib/init-interactive.cjs +110 -49
- package/pp-core/bin/lib/init.cjs +44 -3
- package/pp-core/bin/lib/io.cjs +38 -0
- package/pp-core/bin/lib/message-labels.cjs +75 -0
- package/pp-core/bin/lib/messages/core.cjs +20 -0
- package/pp-core/bin/lib/messages/discovery.cjs +267 -0
- package/pp-core/bin/lib/messages/gate.cjs +257 -0
- package/pp-core/bin/lib/messages/hook.cjs +114 -0
- package/pp-core/bin/lib/messages/init.cjs +513 -0
- package/pp-core/bin/lib/messages/progress.cjs +110 -0
- package/pp-core/bin/lib/messages.cjs +208 -0
- package/pp-core/bin/lib/vault-command-router.cjs +16 -3
- package/pp-core/bin/lib/violation-render.cjs +78 -0
- package/pp-core/bin/pp-tools.cjs +92 -23
- package/pp-core/bin/shared/config-schema.manifest.json +1 -0
- package/pp-core/references/audience-moments.md +119 -0
- package/pp-core/references/audience.md +114 -0
- package/pp-core/references/discovery-build-consumption.md +5 -2
- package/pp-core/references/discovery-charter.md +2 -2
- package/pp-core/references/discovery-id-conventions.md +1 -1
- package/pp-core/references/discovery-processes.md +31 -7
- package/pp-core/references/discovery-session-output.md +1 -1
- package/pp-core/references/discovery-tests.md +186 -0
- package/pp-core/templates/project-charter.md +3 -4
- package/pp-core/workflows/add-phase.md +2 -0
- package/pp-core/workflows/add-tests.md +2 -0
- package/pp-core/workflows/add-todo.md +2 -0
- package/pp-core/workflows/ai-integration-phase.md +2 -0
- package/pp-core/workflows/audit-milestone.md +2 -0
- package/pp-core/workflows/autonomous.md +2 -0
- package/pp-core/workflows/check-todos.md +2 -0
- package/pp-core/workflows/code-review-fix.md +2 -0
- package/pp-core/workflows/code-review.md +2 -0
- package/pp-core/workflows/complete-milestone.md +10 -1
- package/pp-core/workflows/discovery-sprint/charter.md +3 -2
- package/pp-core/workflows/discovery-sprint/prefill.md +3 -0
- package/pp-core/workflows/discovery-sprint/prototype.md +14 -0
- package/pp-core/workflows/discovery-sprint/session-13-gates.md +2 -0
- package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +4 -1
- package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +135 -76
- package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +94 -64
- package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +3 -2
- package/pp-core/workflows/discovery-sprint/vault-context.md +15 -0
- package/pp-core/workflows/discovery-sprint.md +16 -2
- package/pp-core/workflows/discovery-to-build/steps/handoff-gates.md +16 -9
- package/pp-core/workflows/discovery-to-build/templates/discovery-references.md +1 -1
- package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +9 -1
- package/pp-core/workflows/discovery-to-build.md +10 -4
- package/pp-core/workflows/discuss-phase/discovery.md +2 -2
- package/pp-core/workflows/discuss-phase-assumptions.md +2 -0
- package/pp-core/workflows/discuss-phase.md +1 -1
- package/pp-core/workflows/edit-phase.md +2 -0
- package/pp-core/workflows/eval-review.md +2 -0
- package/pp-core/workflows/execute-phase/steps/check-query-gate.md +25 -1
- package/pp-core/workflows/execute-phase/steps/vault-fetch.md +1 -0
- package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +2 -0
- package/pp-core/workflows/execute-phase.md +1 -1
- package/pp-core/workflows/execute-plan.md +2 -0
- package/pp-core/workflows/extract-learnings.md +2 -0
- package/pp-core/workflows/help/modes/business.md +56 -0
- package/pp-core/workflows/help.md +12 -2
- package/pp-core/workflows/import.md +10 -1
- package/pp-core/workflows/ingest-docs.md +2 -0
- package/pp-core/workflows/init.md +17 -1
- package/pp-core/workflows/insert-phase.md +2 -0
- package/pp-core/workflows/list-workspaces.md +2 -0
- package/pp-core/workflows/manager.md +2 -0
- package/pp-core/workflows/map-codebase.md +2 -0
- package/pp-core/workflows/milestone-summary.md +2 -0
- package/pp-core/workflows/new-milestone.md +10 -1
- package/pp-core/workflows/new-project.md +12 -2
- package/pp-core/workflows/new-workspace.md +2 -0
- package/pp-core/workflows/next.md +8 -0
- package/pp-core/workflows/plan-milestone-gaps.md +10 -1
- package/pp-core/workflows/plan-phase.md +2 -1
- package/pp-core/workflows/plan-review-convergence.md +1 -1
- package/pp-core/workflows/progress.md +10 -0
- package/pp-core/workflows/quick.md +11 -1
- package/pp-core/workflows/remove-phase.md +2 -0
- package/pp-core/workflows/remove-workspace.md +2 -0
- package/pp-core/workflows/resume-project.md +25 -0
- package/pp-core/workflows/review.md +2 -0
- package/pp-core/workflows/scan.md +2 -0
- package/pp-core/workflows/secure-phase.md +2 -0
- package/pp-core/workflows/settings.md +41 -0
- package/pp-core/workflows/ship.md +2 -0
- package/pp-core/workflows/spec-phase.md +1 -1
- package/pp-core/workflows/ui-phase.md +2 -0
- package/pp-core/workflows/ui-review.md +2 -0
- package/pp-core/workflows/ultraplan-phase.md +2 -0
- package/pp-core/workflows/validate-phase.md +2 -0
- package/pp-core/workflows/vault-review.md +2 -0
- package/pp-core/workflows/verify-phase.md +2 -0
- package/pp-core/workflows/verify-work.md +2 -0
- package/scripts/affected-tests-lib.cjs +26 -12
- package/scripts/ci-release-only.cjs +158 -0
- package/scripts/live-runs/driver.cjs +326 -0
- package/scripts/live-runs/run.cjs +906 -0
- package/scripts/live-runs/scenarios.cjs +326 -0
- package/scripts/live-runs/scratch-project.cjs +132 -0
- package/scripts/live-runs/scrub.cjs +291 -0
- package/scripts/live-runs/turn-guard.cjs +107 -0
- package/scripts/live-runs/walk-setup.cjs +198 -0
- package/scripts/run-tests.cjs +151 -132
- package/scripts/test-env.cjs +64 -0
|
@@ -8,6 +8,13 @@ Read all files referenced by the invoking prompt's execution_context before star
|
|
|
8
8
|
|
|
9
9
|
<process>
|
|
10
10
|
|
|
11
|
+
```bash
|
|
12
|
+
_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
|
|
13
|
+
pp_run query audience fields
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**Audience, before any question:** run the block above. If it prints `pp-tools.cjs not found`, show that error and stop. If it exits non-zero for any other reason, tell the person the audience setting could not be read (show the error) and continue; if it prints JSON, apply the **Audience** line below to it now.
|
|
17
|
+
|
|
11
18
|
## 1. Load Audit Results
|
|
12
19
|
|
|
13
20
|
```bash
|
|
@@ -65,7 +72,6 @@ Gap: Flow "View dashboard" broken at data fetch
|
|
|
65
72
|
|
|
66
73
|
Find highest existing phase:
|
|
67
74
|
```bash
|
|
68
|
-
_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
|
|
69
75
|
[ -z "${ZSH_VERSION:-}" ] || emulate -R sh
|
|
70
76
|
# Get sorted phase list, extract last one
|
|
71
77
|
HIGHEST=$(pp_run query phases.list --pick directories[-1])
|
|
@@ -145,12 +151,15 @@ grep -c "Pending" .planning/REQUIREMENTS.md
|
|
|
145
151
|
For each new phase (N, N+1, …), resolve the directory name via `init.phase-op` so the `project_code` prefix is honoured:
|
|
146
152
|
|
|
147
153
|
```bash
|
|
154
|
+
command -v pp_run >/dev/null 2>&1 || { echo "ERROR: pp_run is not on PATH; prepend the resolver line from the top block to this block and run it again" >&2; exit 1; }
|
|
148
155
|
INIT=$(pp_run query init.phase-op "{NN}")
|
|
149
156
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
150
157
|
expected_phase_dir=$(echo "$INIT" | node -e "process.stdout.write(JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')).expected_phase_dir)")
|
|
151
158
|
mkdir -p "${expected_phase_dir}"
|
|
152
159
|
```
|
|
153
160
|
|
|
161
|
+
**Audience:** if `audience` is set, Read and follow `{audience_ref}`. Reply in any `response_language` and pass it to subagents; code and paths stay as is.
|
|
162
|
+
|
|
154
163
|
Repeat for each gap-closure phase number. This produces `{CODE}-{NN}-{slug}/` when `project_code` is set in `.planning/config.json`, and `{NN}-{slug}/` otherwise — consistent with all other phase-creation paths.
|
|
155
164
|
|
|
156
165
|
## 9. Commit Roadmap and Requirements Update
|
|
@@ -88,7 +88,7 @@ When `CONTEXT_WINDOW >= 500000`, the planner prompt includes the 3 most recent p
|
|
|
88
88
|
|
|
89
89
|
Parse JSON for: `researcher_model`, `planner_model`, `checker_model`, `research_enabled`, `plan_checker_enabled`, `nyquist_validation_enabled`, `commit_docs`, `text_mode`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `has_research`, `has_context`, `has_reviews`, `has_plans`, `plan_count`, `phase_status` (#3569), `planning_exists`, `roadmap_exists`, `phase_req_ids`, `response_language`, `granularity`.
|
|
90
90
|
|
|
91
|
-
**
|
|
91
|
+
**Audience:** if `audience` is set, Read and follow `{audience_ref}`. Reply in any `response_language` and pass it to subagents; code and paths stay as is.
|
|
92
92
|
|
|
93
93
|
**File paths (for <files_to_read> blocks):** `state_path`, `roadmap_path`, `requirements_path`, `context_path`, `research_path`, `verification_path`, `uat_path`, `reviews_path`. These are null if files don't exist.
|
|
94
94
|
|
|
@@ -1404,6 +1404,7 @@ if [ "$GATE_CFG" != "false" ]; then
|
|
|
1404
1404
|
# no trackable decisions). Verify-phase counterpart deliberately omits this exit-1 — that
|
|
1405
1405
|
# gate is non-blocking by design (review finding F15).
|
|
1406
1406
|
echo "$GATE_RESULT" | jq -e '(.passed // .data.passed) == true' >/dev/null || {
|
|
1407
|
+
pp_run gate-card decision-coverage --state blocked --details 2>/dev/null || true
|
|
1407
1408
|
echo "$GATE_RESULT" | jq -r '(.message // .data.message // "Decision coverage gate failed.")'
|
|
1408
1409
|
exit 1
|
|
1409
1410
|
}
|
|
@@ -73,7 +73,7 @@ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
|
73
73
|
|
|
74
74
|
Parse JSON for: `phase_dir`, `phase_number`, `padded_phase`, `phase_name`, `has_plans`, `plan_count`, `commit_docs`, `text_mode`, `response_language`.
|
|
75
75
|
|
|
76
|
-
**
|
|
76
|
+
**Audience:** if `audience` is set, Read and follow `{audience_ref}`. Reply in any `response_language` and pass it to subagents; code and paths stay as is.
|
|
77
77
|
|
|
78
78
|
Set `TEXT_MODE=true` if `--text` is present in $ARGUMENTS OR `text_mode` from init JSON is `true`. When `TEXT_MODE` is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number.
|
|
79
79
|
|
|
@@ -21,8 +21,18 @@ INIT=$(pp_run query init.progress)
|
|
|
21
21
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
**Audience:** if `audience` is set, Read and follow `{audience_ref}`. Reply in any `response_language` and pass it to subagents; code and paths stay as is.
|
|
25
|
+
|
|
24
26
|
Extract from init JSON: `project_exists`, `roadmap_exists`, `state_exists`, `phases`, `current_phase`, `next_phase`, `milestone_version`, `completed_count`, `phase_count`, `paused_at`, `state_path`, `roadmap_path`, `project_path`, `config_path`.
|
|
25
27
|
|
|
28
|
+
**Discovery Sprint only:** if `project_exists` and `roadmap_exists` are both false, run:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pp_run query discovery.status --raw
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
If it prints JSON with `"discovery_only": true`, run `pp_run query discovery.status` and show its output exactly as printed, with nothing before or after it. Then stop: skip the rest of this workflow (the report, the routing and the Route 0 scan) whatever the arguments; `--forensic` adds nothing here. If it prints `"discovery_only": false`, or the command fails, continue below exactly as before.
|
|
35
|
+
|
|
26
36
|
```bash
|
|
27
37
|
DISCUSS_MODE=$(pp_run query config-get workflow.discuss_mode --raw 2>/dev/null || echo "discuss")
|
|
28
38
|
```
|
|
@@ -31,6 +31,14 @@ Valid pp-core subagent types (use exact names — do not fall back to 'general-p
|
|
|
31
31
|
</available_agent_types>
|
|
32
32
|
|
|
33
33
|
<process>
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
_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
|
|
37
|
+
pp_run query audience fields
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Audience, before any question:** run the block above. If it prints `pp-tools.cjs not found`, show that error and stop. If it exits non-zero for any other reason, tell the person the audience setting could not be read (show the error) and continue; if it prints JSON, apply the **Audience** line below to it now.
|
|
41
|
+
|
|
34
42
|
**Step 1: Parse arguments and get task description**
|
|
35
43
|
|
|
36
44
|
Parse `$ARGUMENTS` for:
|
|
@@ -130,7 +138,7 @@ If `$VALIDATE_MODE` only:
|
|
|
130
138
|
**Step 2: Initialize**
|
|
131
139
|
|
|
132
140
|
```bash
|
|
133
|
-
|
|
141
|
+
command -v pp_run >/dev/null 2>&1 || { echo "ERROR: pp_run is not on PATH; prepend the resolver line from the top block to this block and run it again" >&2; exit 1; }
|
|
134
142
|
INIT=$(pp_run query init.quick "$DESCRIPTION")
|
|
135
143
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
136
144
|
AGENT_SKILLS_PLANNER=$(pp_run query agent-skills pp-planner)
|
|
@@ -139,6 +147,8 @@ AGENT_SKILLS_CHECKER=$(pp_run query agent-skills pp-plan-checker)
|
|
|
139
147
|
AGENT_SKILLS_VERIFIER=$(pp_run query agent-skills pp-verifier)
|
|
140
148
|
```
|
|
141
149
|
|
|
150
|
+
**Audience:** if `audience` is set, Read and follow `{audience_ref}`. Reply in any `response_language` and pass it to subagents; code and paths stay as is.
|
|
151
|
+
|
|
142
152
|
Parse JSON for: `planner_model`, `executor_model`, `checker_model`, `verifier_model`, `commit_docs`, `branch_name`, `quick_id`, `slug`, `date`, `timestamp`, `quick_dir`, `task_dir`, `roadmap_exists`, `planning_exists`.
|
|
143
153
|
|
|
144
154
|
```bash
|
|
@@ -34,6 +34,8 @@ INIT=$(pp_run query init.phase-op "${target}")
|
|
|
34
34
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
**Audience:** if `audience` is set, Read and follow `{audience_ref}`. Reply in any `response_language` and pass it to subagents; code and paths stay as is.
|
|
38
|
+
|
|
37
39
|
Extract: `phase_found`, `phase_dir`, `phase_number`, `commit_docs`, `roadmap_exists`.
|
|
38
40
|
|
|
39
41
|
Also read STATE.md and ROADMAP.md content for parsing current position.
|
|
@@ -18,6 +18,8 @@ INIT=$(pp_run query init.remove-workspace "$WORKSPACE_NAME")
|
|
|
18
18
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
**Audience:** if `audience` is set, Read and follow `{audience_ref}`. Reply in any `response_language` and pass it to subagents; code and paths stay as is.
|
|
22
|
+
|
|
21
23
|
Parse JSON for: `workspace_name`, `workspace_path`, `has_manifest`, `strategy`, `repos`, `repo_count`, `dirty_repos`, `has_dirty_repos`.
|
|
22
24
|
|
|
23
25
|
**If no workspace name provided:**
|
|
@@ -29,8 +29,33 @@ INIT=$(pp_run query init.resume)
|
|
|
29
29
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
**Audience:** if `audience` is set, Read and follow `{audience_ref}`. Reply in any `response_language` and pass it to subagents; code and paths stay as is.
|
|
33
|
+
|
|
32
34
|
Parse JSON for: `state_exists`, `roadmap_exists`, `project_exists`, `planning_exists`, `has_interrupted_agent`, `interrupted_agent_id`, `commit_docs`.
|
|
33
35
|
|
|
36
|
+
**Discovery Sprint only (check this first):** if `project_exists` and `roadmap_exists` are both false, whatever `state_exists` says, run:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pp_run query discovery.status --raw
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
If it prints JSON with `"discovery_only": true`:
|
|
43
|
+
|
|
44
|
+
1. Run `pp_run query discovery.status` and show its output exactly as printed; it already says where the sprint stopped.
|
|
45
|
+
2. Run the block below (check_incomplete_work's scan); if it lists `HANDOFF.json` or a `.continue-here` file, add one line naming it as a note left by an earlier pause. The next step stays the sprint's. Leave the files in place: `HANDOFF.json` is not deleted here, it is left for a later build-mode resume.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
find .planning -maxdepth 1 -name HANDOFF.json -print 2>/dev/null || true
|
|
49
|
+
find .planning -maxdepth 3 -name '.continue-here*.md' -print 2>/dev/null || true
|
|
50
|
+
find . -maxdepth 1 -name '.continue-here*.md' -print 2>/dev/null || true
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
3. Offer three numbered choices in the person's wording: the next step (the JSON's `next.command`, with the reason the view gives); the whole sprint (`/pp-discovery`); Something else. When `next.kind` is `open_sprint`, offer only the last two. Wait for the person's choice.
|
|
54
|
+
4. For the first two, show the chosen command directly, without `/clear` (route_to_workflow's rule). For Something else, ask what they need.
|
|
55
|
+
5. End the workflow there: skip load_state, check_incomplete_work, present_status, determine_next_action, offer_options, route_to_workflow and update_session. On a discovery-only project never rebuild STATE.md and never run `state.record-session`.
|
|
56
|
+
|
|
57
|
+
If it prints `"discovery_only": false`, or the command fails, continue below exactly as before.
|
|
58
|
+
|
|
34
59
|
**If `state_exists` is true:** Proceed to load_state
|
|
35
60
|
**If `state_exists` is false but `roadmap_exists` or `project_exists` is true:** Offer to reconstruct STATE.md
|
|
36
61
|
**If `planning_exists` is false:** This is a new project - route to /pp:init
|
|
@@ -116,6 +116,8 @@ INIT=$(pp_run query init.phase-op "${PHASE_ARG}")
|
|
|
116
116
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
+
**Audience:** if `audience` is set, Read and follow `{audience_ref}`. Reply in any `response_language` and pass it to subagents; code and paths stay as is.
|
|
120
|
+
|
|
119
121
|
Read from init: `phase_dir`, `phase_number`, `padded_phase`.
|
|
120
122
|
|
|
121
123
|
Then read:
|
|
@@ -44,6 +44,8 @@ INIT=$(pp_run query init.map-codebase 2>/dev/null || echo "{}")
|
|
|
44
44
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
+
**Audience:** if `audience` is set, Read and follow `{audience_ref}`. Reply in any `response_language` and pass it to subagents; code and paths stay as is.
|
|
48
|
+
|
|
47
49
|
Look up which documents would be produced for the selected focus (from the mapping table above).
|
|
48
50
|
|
|
49
51
|
For each target document, check if it already exists in `.planning/codebase/`:
|
|
@@ -22,6 +22,8 @@ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
|
22
22
|
AGENT_SKILLS_AUDITOR=$(pp_run query agent-skills pp-security-auditor)
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
**Audience:** if `audience` is set, Read and follow `{audience_ref}`. Reply in any `response_language` and pass it to subagents; code and paths stay as is.
|
|
26
|
+
|
|
25
27
|
Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`.
|
|
26
28
|
|
|
27
29
|
```bash
|
|
@@ -535,6 +535,45 @@ Write `~/.gsd/defaults.json` with:
|
|
|
535
535
|
```
|
|
536
536
|
</step>
|
|
537
537
|
|
|
538
|
+
<step name="audience">
|
|
539
|
+
This question is about **you**, not this project: it decides how pp-core talks to you in every project. It is saved in your own per-person file `~/.gsd/person.json` — never in this project's config file and never in `~/.gsd/defaults.json`.
|
|
540
|
+
|
|
541
|
+
Run `pp_run query audience status --raw` and read `audience`, `audience_source`, `language_source` and `language_name`.
|
|
542
|
+
|
|
543
|
+
If `language_source` is `person_file`, the person chose a language explicitly and every pick below keeps it. Show it before the question, so it is never silent:
|
|
544
|
+
|
|
545
|
+
```
|
|
546
|
+
Language: {language_name} — your own choice, kept.
|
|
547
|
+
Clear: pp-tools audience set --clear-language
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
The clear command names no audience on purpose: it removes only the language and never saves `PP_AUDIENCE` or a default as the person's audience (D-12).
|
|
551
|
+
|
|
552
|
+
```
|
|
553
|
+
AskUserQuestion([
|
|
554
|
+
{
|
|
555
|
+
question: "Who are you? This is about you, not this project.",
|
|
556
|
+
header: "Audience",
|
|
557
|
+
multiSelect: false,
|
|
558
|
+
options: [
|
|
559
|
+
{ label: "Business user", description: "Plain words, no technical terms." },
|
|
560
|
+
{ label: "Business engineer", description: "Plain English; you know the technology." },
|
|
561
|
+
{ label: "Developer", description: "As today." },
|
|
562
|
+
{ label: "Keep current", description: "{audience}" }
|
|
563
|
+
]
|
|
564
|
+
}
|
|
565
|
+
])
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
The labels are the first-run prompt's own (D-09; a test pins them). Mark the option matching the current value with `(current)`.
|
|
569
|
+
|
|
570
|
+
- `Keep current`: change nothing.
|
|
571
|
+
- Any other answer: run `pp_run query audience set business`, `pp_run query audience set engineer` or `pp_run query audience set developer` (the matching value) and show its output. An explicit pick is saved even when it equals the default, so the first-run question is never asked again.
|
|
572
|
+
- If `audience_source` is `environment`, tell the user that `PP_AUDIENCE` overrides the saved value in this terminal.
|
|
573
|
+
- A change affects only what pp-core shows from now on; files already written (discovery sessions, registers) are never rewritten.
|
|
574
|
+
- Never add `audience` or `language` to the project config write or to the defaults file.
|
|
575
|
+
</step>
|
|
576
|
+
|
|
538
577
|
<step name="confirm">
|
|
539
578
|
Display:
|
|
540
579
|
|
|
@@ -568,6 +607,7 @@ Display:
|
|
|
568
607
|
| Skip Discuss | {On/Off} |
|
|
569
608
|
| Context Warnings | {On/Off} |
|
|
570
609
|
| Saved as Defaults | {Yes/No} |
|
|
610
|
+
| Audience (you, not this project) | {business/engineer/developer} |
|
|
571
611
|
|
|
572
612
|
These settings apply to future /pp:plan-phase and /pp:execute-phase runs.
|
|
573
613
|
|
|
@@ -588,5 +628,6 @@ Quick commands:
|
|
|
588
628
|
- [ ] User presented with 24 settings (profile + workflow toggles + features + git branching + git tagging + ctx warnings), grouped into six sections: Planning, Execution, Docs & Output, Features, Model & Pipeline, Misc. `code_review_depth` is conditional on `code_review=on`. Model profile uses a two-question split (Q1: Adaptive / Standard tier / Inherit; Q2: Quality / Balanced / Budget — only when Standard tier chosen) to stay within the 4-option AskUserQuestion cap while exposing all 5 valid profiles (#3784). Drift Guard (`plan_review.source_grounding`) is in the Planning section.
|
|
589
629
|
- [ ] Config updated with model_profile, workflow, and git sections
|
|
590
630
|
- [ ] User offered to save as global defaults (~/.gsd/defaults.json)
|
|
631
|
+
- [ ] Audience asked once and saved with pp_run query audience set — never into the project config or the defaults file
|
|
591
632
|
- [ ] Changes confirmed to user
|
|
592
633
|
</success_criteria>
|
|
@@ -33,6 +33,8 @@ INIT=$(pp_run query init.phase-op "${PHASE_ARG}")
|
|
|
33
33
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
**Audience:** if `audience` is set, Read and follow `{audience_ref}`. Reply in any `response_language` and pass it to subagents; code and paths stay as is.
|
|
37
|
+
|
|
36
38
|
Parse from init JSON: `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `padded_phase`, `commit_docs`.
|
|
37
39
|
|
|
38
40
|
Also load config for branching strategy:
|
|
@@ -63,7 +63,7 @@ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
|
63
63
|
|
|
64
64
|
Parse JSON for: `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `state_path`, `requirements_path`, `roadmap_path`, `planning_path`, `response_language`, `commit_docs`.
|
|
65
65
|
|
|
66
|
-
**
|
|
66
|
+
**Audience:** if `audience` is set, Read and follow `{audience_ref}`. Reply in any `response_language` and pass it to subagents; code and paths stay as is.
|
|
67
67
|
|
|
68
68
|
**If `phase_found` is false:**
|
|
69
69
|
```
|
|
@@ -26,6 +26,8 @@ AGENT_SKILLS_UI=$(pp_run query agent-skills pp-ui-researcher)
|
|
|
26
26
|
AGENT_SKILLS_UI_CHECKER=$(pp_run query agent-skills pp-ui-checker)
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
**Audience:** if `audience` is set, Read and follow `{audience_ref}`. Reply in any `response_language` and pass it to subagents; code and paths stay as is.
|
|
30
|
+
|
|
29
31
|
Parse JSON for: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `has_context`, `has_research`, `commit_docs`.
|
|
30
32
|
|
|
31
33
|
**File paths:** `state_path`, `roadmap_path`, `requirements_path`, `context_path`, `research_path`.
|
|
@@ -22,6 +22,8 @@ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
|
22
22
|
AGENT_SKILLS_UI_REVIEWER=$(pp_run query agent-skills pp-ui-auditor)
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
**Audience:** if `audience` is set, Read and follow `{audience_ref}`. Reply in any `response_language` and pass it to subagents; code and paths stay as is.
|
|
26
|
+
|
|
25
27
|
Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `commit_docs`.
|
|
26
28
|
|
|
27
29
|
```bash
|
|
@@ -72,6 +72,8 @@ INIT=$(pp_run query init.plan-phase "$PHASE")
|
|
|
72
72
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
+
**Audience:** if `audience` is set, Read and follow `{audience_ref}`. Reply in any `response_language` and pass it to subagents; code and paths stay as is.
|
|
76
|
+
|
|
75
77
|
Parse JSON for: `phase_found`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`,
|
|
76
78
|
`phase_dir`, `roadmap_path`, `requirements_path`, `research_path`, `planning_exists`.
|
|
77
79
|
|
|
@@ -26,6 +26,8 @@ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
|
26
26
|
AGENT_SKILLS_AUDITOR=$(pp_run query agent-skills pp-nyquist-auditor)
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
+
**Audience:** if `audience` is set, Read and follow `{audience_ref}`. Reply in any `response_language` and pass it to subagents; code and paths stay as is.
|
|
30
|
+
|
|
29
31
|
Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`.
|
|
30
32
|
|
|
31
33
|
```bash
|
|
@@ -21,6 +21,8 @@ if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
|
21
21
|
AGENT_SKILLS_VAULT_REVIEWER=$(pp_run query agent-skills pp-vault-reviewer)
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
**Audience:** if `audience` is set, Read and follow `{audience_ref}`. Reply in any `response_language` and pass it to subagents; code and paths stay as is.
|
|
25
|
+
|
|
24
26
|
Parse: `phase_dir`, `phase_number`, `phase_name`, `padded_phase`, `commit_docs`.
|
|
25
27
|
|
|
26
28
|
### 0a. Derive first, and stop when there is nothing to review
|
|
@@ -34,6 +34,8 @@ INIT=$(pp_run query init.phase-op "${PHASE_ARG}")
|
|
|
34
34
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
**Audience:** if `audience` is set, Read and follow `{audience_ref}`. Reply in any `response_language` and pass it to subagents; code and paths stay as is.
|
|
38
|
+
|
|
37
39
|
Extract from init JSON: `phase_dir`, `phase_number`, `phase_name`, `has_plans`, `plan_count`.
|
|
38
40
|
|
|
39
41
|
Then load phase details and list plans/summaries:
|
|
@@ -49,6 +49,8 @@ AGENT_SKILLS_PLANNER=$(pp_run query agent-skills pp-planner)
|
|
|
49
49
|
AGENT_SKILLS_CHECKER=$(pp_run query agent-skills pp-plan-checker)
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
**Audience:** if `audience` is set, Read and follow `{audience_ref}`. Reply in any `response_language` and pass it to subagents; code and paths stay as is.
|
|
53
|
+
|
|
52
54
|
Parse JSON for: `planner_model`, `checker_model`, `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `has_verification`, `uat_path`.
|
|
53
55
|
|
|
54
56
|
```bash
|
|
@@ -5,6 +5,7 @@ const { readdirSync, readFileSync, existsSync } = require('node:fs');
|
|
|
5
5
|
const path = require('node:path');
|
|
6
6
|
|
|
7
7
|
const { ExitError } = require('./lib/cli-exit.cjs');
|
|
8
|
+
const { prepareSuiteEnv } = require('./test-env.cjs');
|
|
8
9
|
const { suiteOf } = require('./run-tests.cjs');
|
|
9
10
|
|
|
10
11
|
const CRITICAL_PATHS = [
|
|
@@ -405,21 +406,33 @@ function runNodeTestFiles(repoRoot, files) {
|
|
|
405
406
|
}
|
|
406
407
|
if (current.length > 0) chunks.push(current);
|
|
407
408
|
|
|
409
|
+
// D-16 / WR-04: the same isolation `npm test` gets — session variables and
|
|
410
|
+
// PP_AUDIENCE stripped, GSD_HOME on a throwaway suite home, leak tripwire.
|
|
411
|
+
const suiteEnv = prepareSuiteEnv({ ...process.env });
|
|
408
412
|
let firstFailure = 0;
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
413
|
+
try {
|
|
414
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
415
|
+
if (chunks.length > 1) {
|
|
416
|
+
console.error(`affected-tests: chunk ${i + 1}/${chunks.length} (${chunks[i].length} files)`);
|
|
417
|
+
}
|
|
418
|
+
try {
|
|
419
|
+
execFileSync(process.execPath, ['--test', concurrency, ...chunks[i]], {
|
|
420
|
+
cwd: repoRoot,
|
|
421
|
+
stdio: 'inherit',
|
|
422
|
+
env: suiteEnv.env,
|
|
423
|
+
});
|
|
424
|
+
} catch (error) {
|
|
425
|
+
const code = error.status || 1;
|
|
426
|
+
if (firstFailure === 0) firstFailure = code;
|
|
427
|
+
}
|
|
412
428
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
env: { ...process.env },
|
|
418
|
-
});
|
|
419
|
-
} catch (error) {
|
|
420
|
-
const code = error.status || 1;
|
|
421
|
-
if (firstFailure === 0) firstFailure = code;
|
|
429
|
+
const leak = suiteEnv.leakMessage();
|
|
430
|
+
if (leak) {
|
|
431
|
+
console.error(`affected-tests: ${leak}`);
|
|
432
|
+
if (firstFailure === 0) firstFailure = 1;
|
|
422
433
|
}
|
|
434
|
+
} finally {
|
|
435
|
+
suiteEnv.cleanup();
|
|
423
436
|
}
|
|
424
437
|
if (firstFailure !== 0) throw new ExitError(firstFailure);
|
|
425
438
|
}
|
|
@@ -548,6 +561,7 @@ module.exports = {
|
|
|
548
561
|
resolveBaseRef,
|
|
549
562
|
resolveRelativeDependency,
|
|
550
563
|
resolveRunPlan,
|
|
564
|
+
runNodeTestFiles,
|
|
551
565
|
shouldRunFullSuite,
|
|
552
566
|
toPosixPath,
|
|
553
567
|
runAffectedTests,
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ci-release-only.cjs — is this PR diff only a release (version bump + CHANGELOG
|
|
6
|
+
* render)? If so the CI `test` and `smoke` jobs run the release checks instead
|
|
7
|
+
* of the full suite: the code in a release PR is the code `main` already holds,
|
|
8
|
+
* and the v* tag run executes the full suite again before `pack` and `publish`,
|
|
9
|
+
* so a second full run on the release PR proves nothing new (2026-11-10: ~20
|
|
10
|
+
* billed minutes per release).
|
|
11
|
+
*
|
|
12
|
+
* Release-only means EVERY changed path is one of:
|
|
13
|
+
* - VERSION or CHANGELOG.md (modified);
|
|
14
|
+
* - a `.changeset/*.md` fragment that was DELETED (consumed by the render);
|
|
15
|
+
* - a versioned JSON manifest whose content is identical once its version
|
|
16
|
+
* field(s) are removed — so a dependency, script or override change in
|
|
17
|
+
* package.json / package-lock.json can never pass as a release.
|
|
18
|
+
* Anything else — a source file, an added or edited fragment, a rename, a new
|
|
19
|
+
* file — makes the diff a normal one and the full suite runs.
|
|
20
|
+
*
|
|
21
|
+
* Usage:
|
|
22
|
+
* node scripts/ci-release-only.cjs --base <rev> --head <rev>
|
|
23
|
+
* prints {"releaseOnly":bool,"reasons":[...]} and, under GitHub Actions,
|
|
24
|
+
* appends `release_only=true|false` to $GITHUB_OUTPUT. Exit 0 either way.
|
|
25
|
+
* node scripts/ci-release-only.cjs --list-tests
|
|
26
|
+
* prints the release-check test files, one per line.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const { execFileSync } = require('child_process');
|
|
30
|
+
const fs = require('fs');
|
|
31
|
+
|
|
32
|
+
const PLAIN_RELEASE_FILES = new Set(['VERSION', 'CHANGELOG.md']);
|
|
33
|
+
|
|
34
|
+
// Each versioned manifest and the key paths of the version fields a release
|
|
35
|
+
// bump rewrites (scripts/sync-manifest-versions.cjs + `npm version`).
|
|
36
|
+
const VERSIONED_JSON = new Map([
|
|
37
|
+
['package.json', [['version']]],
|
|
38
|
+
['package-lock.json', [['version'], ['packages', '', 'version']]],
|
|
39
|
+
['.claude-plugin/plugin.json', [['version']]],
|
|
40
|
+
['gemini-extension.json', [['version']]],
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
// The tests that read what a release PR changes: the fragments, the rendered
|
|
44
|
+
// CHANGELOG and the versioned manifests. tests/ci-release-only.test.cjs fails
|
|
45
|
+
// if one is missing, and if a test file that reads CHANGELOG.md or the
|
|
46
|
+
// .changeset directory is not listed here.
|
|
47
|
+
const RELEASE_CHECK_TESTS = [
|
|
48
|
+
'tests/changeset-claims.test.cjs',
|
|
49
|
+
'tests/changeset-cli.test.cjs',
|
|
50
|
+
'tests/changeset-github-release-notes.test.cjs',
|
|
51
|
+
'tests/changeset-lint.test.cjs',
|
|
52
|
+
'tests/changeset-new.test.cjs',
|
|
53
|
+
'tests/changeset-parse.test.cjs',
|
|
54
|
+
'tests/changeset-render.test.cjs',
|
|
55
|
+
'tests/changeset-serialize.test.cjs',
|
|
56
|
+
'tests/docs-parity-live-registry.test.cjs',
|
|
57
|
+
'tests/feat-71924-shipped-claims.test.cjs',
|
|
58
|
+
'tests/feat-71924-tag-gate-steps.test.cjs',
|
|
59
|
+
'tests/feat-71924-vault-required-gate.test.cjs',
|
|
60
|
+
'tests/issue-766-plugin-manifest.test.cjs',
|
|
61
|
+
'tests/issue-775-gemini-extension.test.cjs',
|
|
62
|
+
'tests/issue-844-manifest-version-sync.test.cjs',
|
|
63
|
+
'tests/package-manifest.test.cjs',
|
|
64
|
+
'tests/product-name-purity.test.cjs',
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
// Test files that mention CHANGELOG.md or .changeset without reading the live
|
|
68
|
+
// release content a release PR rewrites — each with the reason it is safe to
|
|
69
|
+
// leave out of the release checks. The ratchet in tests/ci-release-only.test.cjs
|
|
70
|
+
// makes every other mention a listed release check.
|
|
71
|
+
const RELEASE_CHECK_EXEMPT = new Map([
|
|
72
|
+
['tests/bug-3258-no-stale-gsd-intel-references.test.cjs', 'excludes CHANGELOG.md from its scan'], // gsd-allow-legacy-name: names an existing test file
|
|
73
|
+
['tests/bug-3810-no-gsd-sdk-runtime-refs.test.cjs', 'excludes .changeset/ from its scan'], // gsd-allow-legacy-name: names an existing test file
|
|
74
|
+
['tests/cline-install.test.cjs', 'copies CHANGELOG.md as an install artifact, content unread'],
|
|
75
|
+
['tests/copilot-install.test.cjs', 'checks CHANGELOG.md is installed, content unread'],
|
|
76
|
+
['tests/install.test.cjs', 'excludes CHANGELOG.md from its tree scan'],
|
|
77
|
+
['tests/issue-498-package-identity.test.cjs', 'a CHANGELOG.md URL in a fixture string'],
|
|
78
|
+
['tests/issue-607-legacy-cleanup.test.cjs', 'writes its own CHANGELOG.md fixture in a temp dir'],
|
|
79
|
+
['tests/lint-docs-required.test.cjs', 'synthetic .changeset paths, no repo read'],
|
|
80
|
+
['tests/vault-seal.integration.test.cjs', 'synthetic .changeset paths in a temp repo'],
|
|
81
|
+
['tests/vault-seal.test.cjs', 'synthetic CHANGELOG.md / .changeset paths'],
|
|
82
|
+
['tests/vault-trace.integration.test.cjs', 'sweeps pp-core/commands/agents; deleting fragments cannot add a dispatcher'],
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
function stripVersions(text, keyPaths) {
|
|
86
|
+
const doc = JSON.parse(text);
|
|
87
|
+
for (const keyPath of keyPaths) {
|
|
88
|
+
let node = doc;
|
|
89
|
+
for (const key of keyPath.slice(0, -1)) {
|
|
90
|
+
node = node && typeof node === 'object' ? node[key] : undefined;
|
|
91
|
+
}
|
|
92
|
+
if (node && typeof node === 'object') delete node[keyPath[keyPath.length - 1]];
|
|
93
|
+
}
|
|
94
|
+
return JSON.stringify(doc);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @param {{status: string, path: string}[]} changes `git diff --name-status` rows
|
|
99
|
+
* @param {(rev: 'base'|'head', file: string) => string} read file content at a side
|
|
100
|
+
* @returns {{releaseOnly: boolean, reasons: string[]}}
|
|
101
|
+
*/
|
|
102
|
+
function classify(changes, read) {
|
|
103
|
+
const reasons = [];
|
|
104
|
+
if (changes.length === 0) reasons.push('empty diff');
|
|
105
|
+
for (const { status, path: file } of changes) {
|
|
106
|
+
if (PLAIN_RELEASE_FILES.has(file) && status === 'M') continue;
|
|
107
|
+
if (/^\.changeset\/[^/]+\.md$/.test(file) && file !== '.changeset/README.md' && status === 'D') continue;
|
|
108
|
+
if (VERSIONED_JSON.has(file) && status === 'M') {
|
|
109
|
+
let same = false;
|
|
110
|
+
try {
|
|
111
|
+
same = stripVersions(read('base', file), VERSIONED_JSON.get(file)) ===
|
|
112
|
+
stripVersions(read('head', file), VERSIONED_JSON.get(file));
|
|
113
|
+
} catch (err) {
|
|
114
|
+
reasons.push(`${file}: unreadable (${err.message})`);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (same) continue;
|
|
118
|
+
reasons.push(`${file}: changes beyond the version field`);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
reasons.push(`${status} ${file}: not a release file`);
|
|
122
|
+
}
|
|
123
|
+
return { releaseOnly: reasons.length === 0, reasons };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function gitDiff(base, head, cwd) {
|
|
127
|
+
const out = execFileSync('git', ['diff', '--name-status', '--no-renames', base, head], { cwd, encoding: 'utf8' });
|
|
128
|
+
return out.split('\n').filter(Boolean).map(line => {
|
|
129
|
+
const [status, file] = line.split('\t');
|
|
130
|
+
return { status: status[0], path: file };
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function main(argv) {
|
|
135
|
+
if (argv.includes('--list-tests')) {
|
|
136
|
+
process.stdout.write(RELEASE_CHECK_TESTS.join('\n') + '\n');
|
|
137
|
+
return 0;
|
|
138
|
+
}
|
|
139
|
+
const base = argv[argv.indexOf('--base') + 1];
|
|
140
|
+
const head = argv[argv.indexOf('--head') + 1];
|
|
141
|
+
if (!argv.includes('--base') || !argv.includes('--head') || !base || !head) {
|
|
142
|
+
process.stderr.write('usage: ci-release-only.cjs --base <rev> --head <rev> | --list-tests\n');
|
|
143
|
+
return 2;
|
|
144
|
+
}
|
|
145
|
+
const cwd = process.cwd();
|
|
146
|
+
const revs = { base, head };
|
|
147
|
+
const verdict = classify(gitDiff(base, head, cwd), (side, file) =>
|
|
148
|
+
execFileSync('git', ['show', `${revs[side]}:${file}`], { cwd, encoding: 'utf8' }));
|
|
149
|
+
process.stdout.write(JSON.stringify(verdict) + '\n');
|
|
150
|
+
if (process.env.GITHUB_OUTPUT) {
|
|
151
|
+
fs.appendFileSync(process.env.GITHUB_OUTPUT, `release_only=${verdict.releaseOnly}\n`);
|
|
152
|
+
}
|
|
153
|
+
return 0;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
module.exports = { classify, stripVersions, RELEASE_CHECK_TESTS, RELEASE_CHECK_EXEMPT, VERSIONED_JSON };
|
|
157
|
+
|
|
158
|
+
if (require.main === module) process.exitCode = main(process.argv.slice(2));
|