@pareto4/pp-core 1.13.0 → 1.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/.claude-plugin/plugin.json +3 -3
  2. package/README.md +26 -9
  3. package/agents/pp-code-reviewer.md +2 -1
  4. package/agents/pp-debug-session-manager.md +9 -6
  5. package/agents/pp-executor.md +10 -13
  6. package/agents/pp-plan-checker.md +4 -4
  7. package/agents/pp-planner.md +23 -9
  8. package/agents/pp-vault-reviewer.md +244 -21
  9. package/agents/pp-verifier.md +7 -3
  10. package/bin/install.js +10 -3
  11. package/commands/pp-execute-phase.md +1 -3
  12. package/commands/pp-ns-review.md +1 -1
  13. package/commands/pp-plan-phase.md +1 -2
  14. package/commands/pp-vault-review.md +5 -3
  15. package/commands/pp-verify-work.md +1 -2
  16. package/gemini-extension.json +1 -1
  17. package/hooks/dist/pp-check-update.js +0 -0
  18. package/hooks/dist/pp-cursor-session-start.js +1 -1
  19. package/hooks/dist/pp-statusline.js +1 -1
  20. package/hooks/dist/pp-update-banner.js +0 -0
  21. package/hooks/pp-check-update.js +0 -0
  22. package/hooks/pp-cursor-session-start.js +1 -1
  23. package/hooks/pp-statusline.js +1 -1
  24. package/hooks/pp-update-banner.js +0 -0
  25. package/package.json +14 -19
  26. package/pareto-skills/_shared/create-project-board.sh +0 -0
  27. package/pareto-skills/pp-project-setup/SKILL.md +1 -1
  28. package/pp-core/bin/check-latest-version.cjs +0 -0
  29. package/pp-core/bin/detect-build-commands.sh +2 -2
  30. package/pp-core/bin/lib/capability-activation.cjs +83 -9
  31. package/pp-core/bin/lib/capability-registry.cjs +396 -82
  32. package/pp-core/bin/lib/capability-state.cjs +160 -4
  33. package/pp-core/bin/lib/capability-writer.cjs +19 -5
  34. package/pp-core/bin/lib/check-command-router.cjs +3334 -946
  35. package/pp-core/bin/lib/clusters.cjs +3 -0
  36. package/pp-core/bin/lib/command-aliases.cjs +0 -35
  37. package/pp-core/bin/lib/commands.cjs +9 -4
  38. package/pp-core/bin/lib/compose-bindings.cjs +251 -88
  39. package/pp-core/bin/lib/config-loader.cjs +7 -103
  40. package/pp-core/bin/lib/config-schema.cjs +1 -1
  41. package/pp-core/bin/lib/config.cjs +154 -62
  42. package/pp-core/bin/lib/decisions.cjs +4 -4
  43. package/pp-core/bin/lib/discovery-vault-check.cjs +246 -0
  44. package/pp-core/bin/lib/frontmatter.cjs +1 -38
  45. package/pp-core/bin/lib/git-base-branch.cjs +13 -11
  46. package/pp-core/bin/lib/git-provider.cjs +4 -4
  47. package/pp-core/bin/lib/init-command-router.cjs +5 -8
  48. package/pp-core/bin/lib/init-contract.cjs +648 -738
  49. package/pp-core/bin/lib/init-interactive.cjs +76 -4
  50. package/pp-core/bin/lib/init.cjs +24 -51
  51. package/pp-core/bin/lib/install-profiles.cjs +1 -1
  52. package/pp-core/bin/lib/io.cjs +8 -0
  53. package/pp-core/bin/lib/loop-resolver.cjs +189 -129
  54. package/pp-core/bin/lib/org-profile.cjs +469 -152
  55. package/pp-core/bin/lib/package-identity.cjs +3 -3
  56. package/pp-core/bin/lib/phase-command-router.cjs +1 -29
  57. package/pp-core/bin/lib/phase-scope.cjs +1 -1
  58. package/pp-core/bin/lib/phase.cjs +11 -253
  59. package/pp-core/bin/lib/platform-bindings.cjs +40 -19
  60. package/pp-core/bin/lib/practices-review-check.cjs +15 -9
  61. package/pp-core/bin/lib/profile-output.cjs +1 -1
  62. package/pp-core/bin/lib/shell-command-projection.cjs +104 -1
  63. package/pp-core/bin/lib/state.cjs +11 -85
  64. package/pp-core/bin/lib/template.cjs +1 -1
  65. package/pp-core/bin/lib/vault-audit.cjs +650 -126
  66. package/pp-core/bin/lib/vault-command-router.cjs +2604 -367
  67. package/pp-core/bin/lib/vault-coordinates.cjs +13 -4
  68. package/pp-core/bin/lib/vault-index.cjs +699 -0
  69. package/pp-core/bin/lib/vault-mode.cjs +235 -0
  70. package/pp-core/bin/lib/vault-practices.cjs +139 -72
  71. package/pp-core/bin/lib/vault-review.cjs +194 -451
  72. package/pp-core/bin/lib/vault-seal.cjs +1936 -0
  73. package/pp-core/bin/lib/vault-standards.cjs +1505 -0
  74. package/pp-core/bin/lib/vault-trace.cjs +607 -254
  75. package/pp-core/bin/lib/vault-update.cjs +9 -33
  76. package/pp-core/bin/lib/vault-write-back.cjs +70 -42
  77. package/pp-core/bin/lib/vault.cjs +2734 -920
  78. package/pp-core/bin/lib/verify.cjs +0 -11
  79. package/pp-core/bin/portable-timeout.sh +1 -1
  80. package/pp-core/bin/pp-tools.cjs +184 -54
  81. package/pp-core/bin/shared/config-defaults.manifest.json +0 -7
  82. package/pp-core/bin/shared/config-schema.manifest.json +0 -3
  83. package/pp-core/bin/shared/model-catalog.json +2 -2
  84. package/pp-core/bin/verify-reapply-patches.cjs +0 -0
  85. package/pp-core/org-profile.json +26 -0
  86. package/pp-core/references/autonomous-best-effort.md +8 -7
  87. package/pp-core/references/loop-hook-dispatch.md +0 -25
  88. package/pp-core/references/planner-gap-closure.md +0 -1
  89. package/pp-core/references/planner-tdd.md +0 -1
  90. package/pp-core/references/planner-vault-practices.md +17 -34
  91. package/pp-core/references/planner-vault-standards.md +320 -0
  92. package/pp-core/references/planning-config.md +0 -8
  93. package/pp-core/references/practice-arithmetic-contract.md +121 -0
  94. package/pp-core/references/tdd.md +0 -1
  95. package/pp-core/references/verifier-practice-verdicts.md +263 -0
  96. package/pp-core/templates/README.md +1 -0
  97. package/pp-core/templates/VAULT-REVIEW.md +202 -0
  98. package/pp-core/templates/phase-prompt.md +2 -6
  99. package/pp-core/templates/practices/README.md +20 -23
  100. package/pp-core/workflows/_runtime-launcher.snippet.sh +0 -0
  101. package/pp-core/workflows/add-tests.md +55 -4
  102. package/pp-core/workflows/audit-fix.md +63 -2
  103. package/pp-core/workflows/audit-milestone.md +1 -1
  104. package/pp-core/workflows/autonomous.md +85 -11
  105. package/pp-core/workflows/check-todos.md +13 -2
  106. package/pp-core/workflows/code-review-fix.md +59 -3
  107. package/pp-core/workflows/code-review.md +10 -1
  108. package/pp-core/workflows/complete-milestone.md +1 -1
  109. package/pp-core/workflows/debug.md +54 -4
  110. package/pp-core/workflows/discovery-sprint/prefill.md +19 -7
  111. package/pp-core/workflows/discovery-sprint/prototype.md +46 -49
  112. package/pp-core/workflows/discovery-sprint/session-13-gates.md +2 -0
  113. package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +1 -3
  114. package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +1 -3
  115. package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +1 -3
  116. package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +20 -24
  117. package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +1 -3
  118. package/pp-core/workflows/discovery-sprint/sessions/06-security.md +1 -3
  119. package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +1 -3
  120. package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +1 -3
  121. package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +1 -3
  122. package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +1 -6
  123. package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +1 -3
  124. package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +1 -3
  125. package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +1 -3
  126. package/pp-core/workflows/discovery-sprint/templates/context-template.md +1 -1
  127. package/pp-core/workflows/discovery-sprint/vault-context.md +213 -0
  128. package/pp-core/workflows/discovery-sprint.md +7 -3
  129. package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +4 -4
  130. package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +43 -1
  131. package/pp-core/workflows/discovery-to-build.md +10 -2
  132. package/pp-core/workflows/discuss-phase.md +3 -10
  133. package/pp-core/workflows/execute-phase/steps/check-query-gate.md +329 -0
  134. package/pp-core/workflows/execute-phase/steps/client-context-gate.md +3 -2
  135. package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +1 -1
  136. package/pp-core/workflows/execute-phase/steps/execute-pre-dispatch.md +98 -0
  137. package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
  138. package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +6 -8
  139. package/pp-core/workflows/execute-phase/steps/vault-fetch.md +115 -14
  140. package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +322 -20
  141. package/pp-core/workflows/execute-phase/steps/verifier-practice-payload.md +165 -0
  142. package/pp-core/workflows/execute-phase/steps/verify-post-gate.md +163 -0
  143. package/pp-core/workflows/execute-phase.md +155 -128
  144. package/pp-core/workflows/execute-plan.md +56 -6
  145. package/pp-core/workflows/fast.md +53 -4
  146. package/pp-core/workflows/help/modes/full.md +3 -3
  147. package/pp-core/workflows/init/steps/client-context-capture.md +101 -0
  148. package/pp-core/workflows/init/steps/gitlab-provisioning.md +130 -0
  149. package/pp-core/workflows/init/steps/project-md-deferral-narration.md +66 -0
  150. package/pp-core/workflows/init/steps/reachability-outcome.md +70 -0
  151. package/pp-core/workflows/init.md +232 -272
  152. package/pp-core/workflows/new-project.md +31 -23
  153. package/pp-core/workflows/plan-phase.md +98 -45
  154. package/pp-core/workflows/quick.md +117 -3
  155. package/pp-core/workflows/secure-phase.md +1 -1
  156. package/pp-core/workflows/ship.md +57 -28
  157. package/pp-core/workflows/undo.md +1 -1
  158. package/pp-core/workflows/update.md +3 -3
  159. package/pp-core/workflows/validate-phase.md +58 -3
  160. package/pp-core/workflows/vault-review.md +282 -22
  161. package/pp-core/workflows/verify-work.md +12 -14
  162. package/scripts/changeset/cli.cjs +6 -5
  163. package/scripts/changeset/lint.cjs +0 -0
  164. package/scripts/changeset/new.cjs +33 -50
  165. package/scripts/dev-env.cjs +25 -85
  166. package/scripts/gen-capability-registry.cjs +199 -11
  167. package/scripts/gen-practice-arithmetic-contract.cjs +412 -0
  168. package/scripts/lib/allowlist-ratchet.cjs +1 -103
  169. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -2
  170. package/scripts/lint-docs-required.cjs +0 -0
  171. package/scripts/lint-pr-check-project-dir.cjs +1 -3
  172. package/scripts/lint-test-file-count.allowlist.json +12 -10
  173. package/scripts/practice-arithmetic-contract.allow.json +35 -0
  174. package/scripts/prompt-injection-scan.sh +22 -17
  175. package/scripts/record-vault-mode-golden.cjs +89 -0
  176. package/scripts/release-channel.cjs +0 -0
  177. package/scripts/sync-manifest-versions.cjs +5 -4
  178. package/scripts/sync-rulesets.sh +1 -1
  179. package/scripts/update-size-baseline.cjs +30 -274
  180. package/scripts/vault-calibrate.cjs +996 -0
  181. package/scripts/verify-opengsd-sweep.cjs +3 -5
  182. package/scripts/workflow-size.cjs +9 -61
  183. package/pp-core/bin/lib/dispatch-plan.cjs +0 -483
  184. package/pp-core/bin/lib/install-run.cjs +0 -536
  185. package/pp-core/bin/lib/loop-gates.cjs +0 -213
  186. package/pp-core/bin/lib/phase-manifest.cjs +0 -527
  187. package/pp-core/bin/lib/phase-tier.cjs +0 -480
  188. package/pp-core/bin/lib/plan-graph.cjs +0 -368
  189. package/pp-core/references/executor-vault-trace.md +0 -47
  190. package/pp-core/references/planner-plan-graph.md +0 -66
  191. package/pp-core/workflows/discovery-sprint/vault-fetch.md +0 -37
  192. package/pp-core/workflows/execute-phase/steps/checkpoint-handling.md +0 -45
  193. package/pp-core/workflows/execute-phase/steps/install-run.md +0 -29
  194. package/pp-core/workflows/execute-phase/steps/phase-close-rebaseline.md +0 -23
  195. package/pp-core/workflows/execute-phase/steps/serial-group-dispatch.md +0 -73
  196. package/pp-core/workflows/execute-phase/steps/vault-door-gate.md +0 -19
  197. package/pp-core/workflows/plan-phase/steps/phase-tier.md +0 -43
  198. package/scripts/lib/size-budget.cjs +0 -311
  199. package/scripts/lint-neutrality-names.cjs +0 -694
  200. package/scripts/lint-org-coordinates.allowlist.json +0 -35
  201. package/scripts/lint-org-coordinates.cjs +0 -426
@@ -1,368 +0,0 @@
1
- "use strict";
2
- /**
3
- * Plan graph — the single owner of how a phase's plans are ordered.
4
- *
5
- * Computes, from each PLAN.md's frontmatter (`depends_on`, `files_modified`,
6
- * `serial_group`, `chain_reason`, task shapes):
7
- *
8
- * - the wave of every plan;
9
- * - the serial groups (plans that share a wave but must run one after
10
- * another because they touch the same non-source file);
11
- * - the chain metrics (plan count, wave count, longest chain, and how many
12
- * links of that chain exist only because two plans touch the same
13
- * non-source file);
14
- * - the chain check the plan checker enforces (a longest chain longer than
15
- * half the plan count FAILS unless every link of it carries a recorded
16
- * `chain_reason`);
17
- * - same-shape merge candidates for the planner.
18
- *
19
- * Callers: `phase-plan-index` (src/phase.cts) adds this to the plan inventory.
20
- * The planner and plan-checker agents read that output; they never compute a
21
- * wave or a chain themselves.
22
- *
23
- * ## The wave rule
24
- *
25
- * A plan's wave is its longest-path level over two kinds of edges:
26
- * 1. declared `depends_on` edges;
27
- * 2. implicit edges between two plans that share a SOURCE file (see
28
- * `classifyPlanFile`) and are not already ordered by (1). The implicit
29
- * edge runs from the earlier plan (file order) to the later one.
30
- * Two plans that share only NON-source files (a test file, a size baseline, a
31
- * changeset, a doc, a planning file) get no edge: they stay in the same wave
32
- * and are put in one serial group instead.
33
- */
34
- Object.defineProperty(exports, "__esModule", { value: true });
35
- exports.PLAN_TASK_CAP = exports.PLAN_TITLE_MAX = void 0;
36
- exports.classifyPlanFile = classifyPlanFile;
37
- exports.isInstructionFile = isInstructionFile;
38
- exports.isSourceFile = isSourceFile;
39
- exports.planTitleProblem = planTitleProblem;
40
- exports.readPlanTitle = readPlanTitle;
41
- exports.computePlanGraph = computePlanGraph;
42
- const SOURCE_DIRS = ['src/', 'bin/', 'pp-core/bin/', 'hooks/', 'scripts/'];
43
- const DOC_EXTENSIONS = /\.(md|mdx|markdown|rst|txt|adoc)$/i;
44
- const DOC_BASENAMES = /^(LICENSE|NOTICE|CHANGELOG|AUTHORS)(\.[a-z]+)?$/i;
45
- function normalizePath(file) {
46
- return String(file).trim().replace(/\\/g, '/').replace(/^\.\//, '');
47
- }
48
- /**
49
- * Classify one `files_modified` entry. This is THE source/non-source predicate
50
- * for plan ordering; nothing else decides it.
51
- *
52
- * Checked in this order (first match wins):
53
- * planning — under `.planning/`
54
- * changeset — under `.changeset/` or `.changesets/`
55
- * baseline — a `*baseline*.json`/`*.baseline.*` file, `__snapshots__/`, `*.snap`
56
- * test — under a `test/`, `tests/` or `__tests__/` directory or a
57
- * `*.Tests/` project; or named `*.test.*`, `*.spec.*`,
58
- * `*_test.go|py`, `test_*.py`
59
- * source — under `src/`, `bin/`, `pp-core/bin/`, `hooks/` or `scripts/`
60
- * (pp-core ships executable code from these)
61
- * doc — a `.md/.mdx/.markdown/.rst/.txt/.adoc` file, anything under
62
- * `docs/` or `doc/`, or LICENSE/NOTICE/CHANGELOG/AUTHORS. In
63
- * pp-core this includes workflow, agent, reference and template
64
- * markdown.
65
- * source — anything else. An unrecognised file is treated as source, so
66
- * it keeps the strict "shared file = later wave" rule.
67
- */
68
- function classifyPlanFile(file) {
69
- const p = normalizePath(file);
70
- const base = p.split('/').pop() ?? p;
71
- if (p.startsWith('.planning/'))
72
- return 'planning';
73
- if (/^\.changesets?\//.test(p))
74
- return 'changeset';
75
- if (/baseline[^/]*\.json$/i.test(base) || /\.baseline\./i.test(base) || /(^|\/)__snapshots__\//.test(p) || /\.snap$/i.test(base)) {
76
- return 'baseline';
77
- }
78
- if (/(^|\/)(tests?|__tests__)\//.test(p) ||
79
- /(^|\/)[^/]+\.Tests?\//.test(p) ||
80
- /\.(test|spec)\.[^/]+$/i.test(base) ||
81
- /_test\.(go|py)$/i.test(base) ||
82
- /^test_[^/]*\.py$/i.test(base)) {
83
- return 'test';
84
- }
85
- if (SOURCE_DIRS.some((dir) => p.startsWith(dir)))
86
- return 'source';
87
- if (DOC_EXTENSIONS.test(base) || /^docs?\//.test(p) || DOC_BASENAMES.test(base))
88
- return 'doc';
89
- return 'source';
90
- }
91
- /** A workflow or agent markdown file (the unit same-shape plans are made of). */
92
- function isInstructionFile(file) {
93
- const p = normalizePath(file);
94
- return /\.md$/i.test(p) && (/(^|\/)workflows\//.test(p) || /(^|\/)agents\//.test(p));
95
- }
96
- /** True when `file` is a source file for plan ordering (see `classifyPlanFile`). */
97
- function isSourceFile(file) {
98
- return classifyPlanFile(file) === 'source';
99
- }
100
- /** Longest a plan title may be before it is reported (still valid). */
101
- exports.PLAN_TITLE_MAX = 100;
102
- /**
103
- * Problem with a plan's `title:` frontmatter value, or null when it is a valid
104
- * one-line title. `undefined` means the key is absent.
105
- */
106
- function planTitleProblem(value) {
107
- if (value === undefined)
108
- return 'Missing required frontmatter field: title';
109
- if (typeof value !== 'string' || value.trim() === '') {
110
- return 'Frontmatter field title must be a non-empty one-line string';
111
- }
112
- if (/[\r\n]/.test(value))
113
- return 'Frontmatter field title must be one line';
114
- return null;
115
- }
116
- /** Normalised title, or null when absent/invalid. */
117
- function readPlanTitle(value) {
118
- return planTitleProblem(value) === null ? value.trim() : null;
119
- }
120
- /** Default task cap per plan (agents/pp-planner.md: "Each plan: 2-3 tasks maximum."). */
121
- exports.PLAN_TASK_CAP = 3;
122
- function slug(text) {
123
- return text.toLowerCase().replace(/\.[a-z0-9]+$/i, '').replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'group';
124
- }
125
- function intersect(a, b) {
126
- return [...new Set(a.filter((f) => b.has(f)))].sort();
127
- }
128
- /**
129
- * Compute the plan graph. `nodes` must be in plan file order (that order
130
- * breaks ties and orients implicit edges).
131
- */
132
- function computePlanGraph(nodes, opts = {}) {
133
- const taskCap = opts.taskCap ?? exports.PLAN_TASK_CAP;
134
- const ids = nodes.map((n) => n.id);
135
- const index = new Map(ids.map((id, i) => [id, i]));
136
- const files = nodes.map((n) => n.filesModified.map(normalizePath).filter((f) => f !== ''));
137
- const fileSets = files.map((f) => new Set(f));
138
- const warnings = [];
139
- // Adjacency over indices: edges[i] = successors of i.
140
- const succ = nodes.map(() => new Set());
141
- nodes.forEach((n, j) => {
142
- for (const dep of n.dependsOn) {
143
- const i = index.get(dep);
144
- if (i === undefined || i === j)
145
- continue;
146
- succ[i].add(j);
147
- }
148
- });
149
- const levelsOf = () => {
150
- const inDeg = nodes.map(() => 0);
151
- succ.forEach((s) => s.forEach((j) => { inDeg[j]++; }));
152
- const level = nodes.map(() => 0);
153
- const queue = [];
154
- inDeg.forEach((d, i) => { if (d === 0)
155
- queue.push(i); });
156
- let head = 0;
157
- while (head < queue.length) {
158
- const cur = queue[head++];
159
- for (const nxt of [...succ[cur]].sort((a, b) => a - b)) {
160
- level[nxt] = Math.max(level[nxt], level[cur] + 1);
161
- if (--inDeg[nxt] === 0)
162
- queue.push(nxt);
163
- }
164
- }
165
- return { level, done: new Set(queue) };
166
- };
167
- // A declared cycle has no waves; nodes Kahn never dequeued are on or behind it.
168
- const declaredLevels = levelsOf();
169
- if (declaredLevels.done.size < nodes.length) {
170
- return emptyGraph(nodes, ids.filter((_, i) => !declaredLevels.done.has(i)));
171
- }
172
- const reaches = (from, to) => {
173
- const stack = [from];
174
- const seen = new Set([from]);
175
- while (stack.length > 0) {
176
- const cur = stack.pop();
177
- if (cur === to)
178
- return true;
179
- for (const nxt of succ[cur]) {
180
- if (!seen.has(nxt)) {
181
- seen.add(nxt);
182
- stack.push(nxt);
183
- }
184
- }
185
- }
186
- return false;
187
- };
188
- // Implicit edges: a shared SOURCE file orders two otherwise-unordered plans.
189
- const implicitEdges = [];
190
- for (let i = 0; i < nodes.length; i++) {
191
- for (let j = i + 1; j < nodes.length; j++) {
192
- const sharedSource = intersect(files[j], fileSets[i]).filter(isSourceFile);
193
- if (sharedSource.length === 0)
194
- continue;
195
- if (reaches(i, j) || reaches(j, i))
196
- continue;
197
- succ[i].add(j);
198
- implicitEdges.push({ from: ids[i], to: ids[j], shared_source_files: sharedSource });
199
- }
200
- }
201
- const { level } = levelsOf();
202
- // Same-shape merge candidates: no source file, exactly one workflow/agent file
203
- // (any other files are tests, baselines, changesets or planning), same task shape.
204
- const shapeGroups = new Map();
205
- nodes.forEach((n, i) => {
206
- if (n.taskTypes.length === 0)
207
- return;
208
- const kinds = files[i].map(classifyPlanFile);
209
- if (kinds.includes('source'))
210
- return;
211
- if (files[i].filter(isInstructionFile).length !== 1 || kinds.filter((k) => k === 'doc').length !== 1)
212
- return;
213
- const shape = n.taskTypes.join('+');
214
- if (!shapeGroups.has(shape))
215
- shapeGroups.set(shape, []);
216
- shapeGroups.get(shape).push(i);
217
- });
218
- const mergeCandidates = [];
219
- for (const [shape, members] of shapeGroups) {
220
- if (members.length < 2)
221
- continue;
222
- const totalTasks = members.reduce((sum, i) => sum + nodes[i].taskTypes.length, 0);
223
- mergeCandidates.push({
224
- shape,
225
- plans: members.map((i) => ids[i]),
226
- total_tasks: totalTasks,
227
- task_cap: taskCap,
228
- action: totalTasks <= taskCap ? 'merge' : 'serial_group',
229
- });
230
- }
231
- // Serial groups: union-find within each level.
232
- const parent = nodes.map((_, i) => i);
233
- const find = (x) => (parent[x] === x ? x : (parent[x] = find(parent[x])));
234
- const union = (a, b) => {
235
- const ra = find(a);
236
- const rb = find(b);
237
- if (ra !== rb)
238
- parent[Math.max(ra, rb)] = Math.min(ra, rb);
239
- };
240
- for (let i = 0; i < nodes.length; i++) {
241
- for (let j = i + 1; j < nodes.length; j++) {
242
- if (level[i] !== level[j])
243
- continue;
244
- const shared = intersect(files[j], fileSets[i]);
245
- const sameDeclared = nodes[i].serialGroup !== null && nodes[i].serialGroup === nodes[j].serialGroup;
246
- if (shared.length > 0 || sameDeclared)
247
- union(i, j);
248
- }
249
- }
250
- for (const cand of mergeCandidates) {
251
- if (cand.action !== 'serial_group')
252
- continue;
253
- const members = cand.plans.map((id) => index.get(id));
254
- for (const a of members)
255
- for (const b of members)
256
- if (a < b && level[a] === level[b])
257
- union(a, b);
258
- }
259
- const groupsByRoot = new Map();
260
- nodes.forEach((_, i) => {
261
- const r = find(i);
262
- if (!groupsByRoot.has(r))
263
- groupsByRoot.set(r, []);
264
- groupsByRoot.get(r).push(i);
265
- });
266
- const serialGroupOf = Object.fromEntries(ids.map((id) => [id, null]));
267
- const serialGroups = [];
268
- const usedNames = new Set();
269
- const roots = [...groupsByRoot.keys()].sort((a, b) => a - b);
270
- for (const root of roots) {
271
- const members = groupsByRoot.get(root).sort((a, b) => a - b);
272
- if (members.length < 2)
273
- continue;
274
- const sharedFiles = new Set();
275
- for (const a of members)
276
- for (const b of members)
277
- if (a < b)
278
- intersect(files[b], fileSets[a]).forEach((f) => sharedFiles.add(f));
279
- const shared = [...sharedFiles].sort();
280
- const declaredName = members.map((i) => nodes[i].serialGroup).find((g) => !!g);
281
- let name = declaredName ?? `w${level[root] + 1}-${shared.length > 0 ? slug(shared[0].split('/').pop()) : 'same-shape'}`;
282
- if (usedNames.has(name)) {
283
- let k = 2;
284
- while (usedNames.has(`${name}-${k}`))
285
- k++;
286
- name = `${name}-${k}`;
287
- }
288
- usedNames.add(name);
289
- for (const i of members)
290
- serialGroupOf[ids[i]] = name;
291
- serialGroups.push({ name, level: level[root], plans: members.map((i) => ids[i]), shared_files: shared });
292
- }
293
- nodes.forEach((n, i) => {
294
- if (n.serialGroup && serialGroupOf[ids[i]] !== n.serialGroup) {
295
- warnings.push(`Plan ${ids[i]}: declares serial_group: ${n.serialGroup} but the plan graph puts it in ` +
296
- `${serialGroupOf[ids[i]] ?? 'no serial group'} (a serial group only holds plans of one wave).`);
297
- }
298
- });
299
- // Longest chain (by plan count) over the full DAG.
300
- const order = ids.map((_, i) => i).sort((a, b) => level[a] - level[b] || a - b);
301
- const best = nodes.map(() => 1);
302
- const prev = nodes.map(() => null);
303
- const preds = nodes.map(() => []);
304
- succ.forEach((s, i) => s.forEach((j) => preds[j].push(i)));
305
- for (const j of order) {
306
- for (const i of preds[j].sort((a, b) => a - b)) {
307
- if (best[i] + 1 > best[j]) {
308
- best[j] = best[i] + 1;
309
- prev[j] = i;
310
- }
311
- }
312
- }
313
- let end = -1;
314
- nodes.forEach((_, i) => { if (end === -1 || best[i] > best[end])
315
- end = i; });
316
- const chain = [];
317
- for (let cur = end; cur !== null && cur !== -1; cur = prev[cur])
318
- chain.unshift(cur);
319
- const links = [];
320
- for (let k = 1; k < chain.length; k++) {
321
- const a = chain[k - 1];
322
- const b = chain[k];
323
- const shared = intersect(files[b], fileSets[a]);
324
- const kind = shared.some(isSourceFile) ? 'source-file' : shared.length > 0 ? 'non-source-file' : 'declared';
325
- links.push({ from: ids[a], to: ids[b], kind, shared_files: shared, reason: nodes[b].chainReason });
326
- }
327
- const nonSourceLinks = links.filter((l) => l.kind === 'non-source-file').length;
328
- const limit = nodes.length / 2;
329
- const unreasoned = links.filter((l) => !l.reason).map((l) => ({ from: l.from, to: l.to, kind: l.kind }));
330
- const passed = chain.length <= limit || unreasoned.length === 0;
331
- const waveCount = nodes.length === 0 ? 0 : Math.max(...level) + 1;
332
- const message = passed
333
- ? `longest chain ${chain.length} of ${nodes.length} plans (limit ${limit})` +
334
- (chain.length > limit ? ' — every link has a chain_reason' : '')
335
- : `longest chain is ${chain.length} plans, over half the plan count (${nodes.length} plans, limit ${limit}), ` +
336
- `and ${unreasoned.length} link(s) have no chain_reason: ${unreasoned.map((l) => `${l.from}->${l.to} (${l.kind})`).join(', ')}. ` +
337
- `Drop depends_on edges that exist only because two plans touch the same non-source file ` +
338
- `(they share a wave in a serial group instead), merge same-shape plans, or record a ` +
339
- `chain_reason on each later plan saying why it must wait.`;
340
- return {
341
- cycle: null,
342
- level: Object.fromEntries(ids.map((id, i) => [id, level[i]])),
343
- serialGroupOf,
344
- serialGroups,
345
- implicitEdges,
346
- plan_count: nodes.length,
347
- wave_count: waveCount,
348
- longest_chain: { length: chain.length, plans: chain.map((i) => ids[i]), links, non_source_links: nonSourceLinks },
349
- chain_check: { passed, limit, unreasoned_links: passed ? [] : unreasoned, message },
350
- merge_candidates: mergeCandidates,
351
- warnings,
352
- };
353
- }
354
- function emptyGraph(nodes, cycle) {
355
- return {
356
- cycle,
357
- level: {},
358
- serialGroupOf: Object.fromEntries(nodes.map((n) => [n.id, null])),
359
- serialGroups: [],
360
- implicitEdges: [],
361
- plan_count: nodes.length,
362
- wave_count: 0,
363
- longest_chain: { length: 0, plans: [], links: [], non_source_links: 0 },
364
- chain_check: { passed: false, limit: nodes.length / 2, unreasoned_links: [], message: `depends_on cycle: ${cycle.join(', ')}` },
365
- merge_candidates: [],
366
- warnings: [],
367
- };
368
- }
@@ -1,47 +0,0 @@
1
- # Executor — the practice record in SUMMARY.md
2
-
3
- Load this when your brief carries the vault-practices executor fragment AND the plan you are executing
4
- carries a `<vault_practices>` block (degraded or not). A plan without that block gets no `vault:` ledger
5
- and no `## Practices Applied` section — that is how a vault-disabled project's SUMMARY stays unchanged.
6
-
7
- It applies to every plan you execute, one SUMMARY each: an isolated executor runs one plan; a
8
- serial-group executor runs this once per plan, in plan order, before that plan's SUMMARY Write.
9
-
10
- ## 1. Scaffold — after the last task commit, before the SUMMARY Write
11
-
12
- ```bash
13
- pp_run vault trace scaffold --stage execute --plan <phase>-<plan> <the source files your tasks committed...>
14
- ```
15
-
16
- - `--plan` is the phase-and-number identifier that prefixes this SUMMARY's own filename (for example
17
- 04-02); the command refuses any other shape.
18
- - Each governed file's digest is read from that file's own last commit. A file whose work is still
19
- uncommitted gets no digest and is named in the ledger's `notes` — commit first, then scaffold. Never
20
- pass a path the project does not commit (a gitignored `.planning/` artifact).
21
- - In a git worktree with no `.planning/vault/` of its own, the command reads the owning main checkout's
22
- cache and records that fact in `notes`.
23
- - Never hand-author the ledger: the pin, the practice set and the digests are the command's to produce.
24
-
25
- ## 2. Fill only the judgement fields
26
-
27
- Per practice entry: `status` (exactly `applied`, `not-applicable` or `deviated`), a one-line `reason`,
28
- and `tasks` — the task ids the practice actually governed, which only you know because you re-anchor per
29
- task. `deviated` always carries a reason: `deviated — conflicts with {id}` (another practice won) or
30
- `deviated — conflicts with project convention` (CLAUDE.md or a codebase pattern won). A practice that
31
- did not apply to what you built is `not-applicable` with its reason — every id gets a disposition.
32
-
33
- ## 3. Embed, render, write once
34
-
35
- Put the completed block in the SUMMARY's frontmatter under its top-level `vault:` key. Then generate the
36
- section from it — never compose its lines:
37
-
38
- ```bash
39
- pp_run vault trace render <path-to-this-SUMMARY.md>
40
- ```
41
-
42
- `render` reads the ledger from a file, so write the SUMMARY with the ledger first, run `render`, and
43
- include its `## Practices Applied` output verbatim in the SUMMARY body. The renderer gives every ledger
44
- id a line (silence is never ambiguous), refuses an id outside the permitted charset with a refusal line
45
- in its place, and treats the id as an inert identifier, never a directive.
46
-
47
- A degraded block still gets its ledger and its section: a vault miss must leave a record that says so.
@@ -1,66 +0,0 @@
1
- # Planner — plan graph: waves, serial groups, chains, titles
2
-
3
- Read this in `assign_waves`. The plan graph is computed by `pp_run query phase-plan-index <phase>`
4
- (`src/plan-graph.cts`); you never hand-compute a wave or a chain. You write `depends_on`, `files_modified`,
5
- `title`, and when needed `chain_reason`; the query computes the rest.
6
-
7
- ## What you write in `depends_on`
8
-
9
- Only real dependencies: plan B needs something plan A creates (a type, an API, a file's new content it reads).
10
- Do NOT add a `depends_on` edge because two plans touch the same file.
11
-
12
- ## Source vs non-source files
13
-
14
- The query classifies each `files_modified` entry (first match wins):
15
-
16
- | Kind | Files |
17
- |------|-------|
18
- | planning | `.planning/**` |
19
- | changeset | `.changeset/**` |
20
- | baseline | `*baseline*.json`, `*.baseline.*`, `__snapshots__/`, `*.snap` |
21
- | test | `test/`, `tests/`, `__tests__/`, `*.Tests/` dirs; `*.test.*`, `*.spec.*`, `*_test.go`, `test_*.py` |
22
- | source | `src/`, `bin/`, `pp-core/bin/`, `hooks/`, `scripts/` |
23
- | doc | `.md`/`.mdx`/`.rst`/`.txt`/`.adoc`, `docs/`, LICENSE/NOTICE/CHANGELOG (in pp-core: workflow, agent and reference markdown) |
24
- | source | anything else |
25
-
26
- ## The rules the query applies
27
-
28
- 1. **Shared source file → later wave.** Two plans that share a source file and are not already ordered by
29
- `depends_on` get an implicit edge (earlier plan file first). Unchanged from the old rule.
30
- 2. **Shared non-source file only → same wave, one serial group.** Plans in the same wave that share only
31
- tests, baselines, changesets, docs or planning files stay in that wave with a computed `serial_group`.
32
- One executor runs a serial group's plans one after another, in plan order.
33
- 3. **Same-shape plans** (same task types, each on exactly one workflow/agent file, no source file) are listed
34
- in `graph.merge_candidates`. `action: merge` → fold them into one plan (they fit the 3-task cap).
35
- `action: serial_group` → keep them as plans; the query puts those in one wave in one serial group.
36
- 4. **Chain check.** `graph.chain_check.passed` is false when the longest chain is longer than half the plan
37
- count and a link of it has no `chain_reason`. Fix it by dropping `depends_on` edges that exist only for a
38
- shared non-source file, merging same-shape plans, or — when the order is real — writing on the later plan
39
- `chain_reason: "<one line: what it needs from the plan before it>"`.
40
-
41
- ## Frontmatter you write back
42
-
43
- After the query: set each plan's `wave:` to its computed `wave`, and `serial_group:` to its computed
44
- `serial_group` (omit the key when null). Use `pp_run query frontmatter.set <plan> wave <N>` /
45
- `... serial_group <name>`.
46
-
47
- ## Print before you return
48
-
49
- ```
50
- Plan graph: {plan_count} plans · {wave_count} waves · longest chain {longest_chain.length}
51
- ({longest_chain.non_source_links} linked only by a non-source file) · chain check {passed|FAILED}
52
- ```
53
-
54
- ## Size baselines
55
-
56
- Never list `tests/*-size-baseline.json` in `files_modified` or put a `size:baseline` step in a plan. Each commit
57
- is checked against a per-commit budget (`size_budget.*` in config); execute-phase re-baselines once, in the
58
- phase's closing commit (`--reason "phase NN close"`). A plan whose design needs more growth than the budget says
59
- so in its task, and that task records it with `npm run size:baseline -- --reason "<why>"`.
60
-
61
- ## Titles
62
-
63
- Every PLAN.md has `title:` — one line, what the plan delivers (e.g. `title: "Plan graph in phase-plan-index"`).
64
- `verify.plan-structure` and `frontmatter.validate --schema plan` fail a plan without one. Plans that already
65
- have a SUMMARY only warn. The inventory query prints the title; execute-phase's wave table and executor
66
- brief use it.
@@ -1,37 +0,0 @@
1
- # Client vault fetch — optional (Flow 2 Step 1, prefill Step 0, prototype Step 0)
2
-
3
- > **Lazy-loaded.** Read this file from `workflows/discovery-sprint.md` Flow 2 Step 1 when the
4
- > session has vault areas, from `prefill.md` Step 0 and from `prototype.md` Step 0. It carries its own
5
- > launcher preamble because each caller Reads it directly.
6
-
7
- The client vault is optional in the sprint. This step brings it in when the project has one, so a
8
- session reads the client's current documents rather than whatever an earlier planning run left in
9
- `.planning/vault/`. It never stops discovery: a project without a bound and enabled vault prints
10
- nothing, and a fetch that fails is reported once, after which the session runs as if there were no
11
- vault. It writes only the vault cache under `.planning/vault/` (kept out of git) and its index, and
12
- removes a stale index after a failed fetch.
13
-
14
- ```bash
15
- _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
16
- DISC_PRESENT=$(pp_run query platform-bindings --pick present 2>/dev/null || echo false)
17
- if [ "$DISC_PRESENT" = true ]; then
18
- if ! DISC_ENABLED=$(pp_run query config-get vault.enabled --raw --default false 2>/dev/null); then
19
- DISC_ENABLED=false
20
- echo "vault: .planning/config.json cannot be read, so this session runs without the client vault."
21
- fi
22
- if [ "$DISC_ENABLED" = true ]; then
23
- pp_run vault fetch >/dev/null 2>&1
24
- DISC_FILES=$(pp_run vault status --pick fileCount 2>/dev/null || echo 0)
25
- if [ "${DISC_FILES:-0}" -gt 0 ] 2>/dev/null && pp_run vault summary >/dev/null 2>&1; then
26
- echo "vault: client vault ready (${DISC_FILES} files, index .planning/vault/SUMMARY.md)."
27
- else
28
- rm -f .planning/vault/SUMMARY.md
29
- echo "vault: the client vault could not be fetched, so this session runs without it. \`pp-tools vault probe\` checks access."
30
- fi
31
- fi
32
- fi
33
- ```
34
-
35
- Show the printed line, if any, once, then return to the caller. Whether the vault is present for the
36
- session is then decided exactly as Flow 2 Step 1 says (bindings, `vault.enabled`, and
37
- `.planning/vault/SUMMARY.md`); the line above is the only mention of a failed fetch.
@@ -1,45 +0,0 @@
1
- # Checkpoint handling (execute-phase `checkpoint_handling` step)
2
-
3
- Read by `execute-phase.md` when an executor returns a checkpoint (plans with
4
- `autonomous: false`, or an auth gate). Extracted verbatim so the host loop stays
5
- under its size ceiling; nothing here runs unless a checkpoint is returned.
6
-
7
- **Auto-mode checkpoint handling:**
8
- Read auto-advance config (chain flag OR user preference — same boolean as `check.auto-mode`):
9
- ```bash
10
- _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
11
- AUTO_MODE=$(pp_run query check auto-mode --pick active 2>/dev/null || echo "false")
12
- ```
13
-
14
- When executor returns a checkpoint AND (`UNATTENDED` or `AUTO_MODE`) is true (U-E1):
15
- - **human-action**, or package legitimacy — ANY of `**Gate:** blocking-human` in the return (the executor copies the task's `gate` attribute there), `gate="blocking-human"`, or `what-built` begins `Package`; the last two are the fallback for a return that predates the `Gate:` line → hard stop: present to user (below).
16
- - **human-verify** (other) → continuation with `{user_response}` = `"DEFERRED — not verified by a human (unattended run); record this checkpoint as not done in the SUMMARY and continue"`; log `⏭ Checkpoint deferred (recorded, not approved)`.
17
- - **decision** → continuation with the option marked recommended, else the first; log `⚡ Auto-selected: [option]`.
18
-
19
- **Standard flow (neither flag, human-action, or blocking-human):**
20
-
21
- 1. Spawn agent for checkpoint plan
22
- 2. Agent runs until checkpoint task or auth gate → returns structured state
23
- 3. Agent return includes: completed tasks table, current task + blocker, checkpoint type/details, what's awaited
24
- 4. **Present to user:**
25
- ```
26
- ## Checkpoint: [Type]
27
-
28
- **Plan:** 03-03 Dashboard Layout
29
- **Progress:** 2/3 tasks complete
30
-
31
- [Checkpoint Details from agent return]
32
- [Awaiting section from agent return]
33
- ```
34
- 5. User responds: "approved"/"done" | issue description | decision selection
35
- 6. **Spawn continuation agent (NOT resume)** using continuation-prompt.md template:
36
- - `{completed_tasks_table}`: From checkpoint return
37
- - `{resume_task_number}` + `{resume_task_name}`: Current task
38
- - `{user_response}`: What user provided
39
- - `{resume_instructions}`: Based on checkpoint type
40
- 7. Continuation agent verifies previous commits, continues from resume point
41
- 8. Repeat until plan completes or user stops
42
-
43
- **Fresh agent, not resume:** resume breaks with parallel tool calls.
44
-
45
- **Checkpoints in parallel waves:** Agent pauses and returns while other parallel agents may complete. Present checkpoint, spawn continuation, wait for all before next wave.
@@ -1,29 +0,0 @@
1
- # Install run — execute-phase step (heavy tier)
2
-
3
- Runs only when `DISPATCH_PLAN.stages.verify` lists `verify-install` (heavy tier). Verification of a heavy
4
- phase measures the installed product, not the repository: `phase.install-run` builds and packs the
5
- package, installs the tarball into a temp project with the installer the user runs
6
- (`pp-core --claude --local`, temp HOME), runs init there, then runs the phase's install checks against
7
- the INSTALLED `pp-tools` — never the repository's. The checks are the phase's ROADMAP
8
- `**Install check:**` line (backticked `pp-tools …` commands); without it, a default smoke set runs.
9
-
10
- ```bash
11
- _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
12
- INSTALL_RUN=$(pp_run query phase.install-run "${PHASE_NUMBER}")
13
- INSTALL_VERDICT=$(printf '%s' "$INSTALL_RUN" | jq -r '.verdict')
14
- INSTALL_RUN_FILE=$(printf '%s' "$INSTALL_RUN" | jq -r '.results_file')
15
- echo "install run: ${INSTALL_VERDICT} — $(printf '%s' "$INSTALL_RUN" | jq -r '.reason')"
16
- ```
17
-
18
- The results file (`{phase_dir}/{phase_num}-INSTALL-RUN.json`) holds the tarball name and version, every
19
- command with its exit code and truncated output, `failures[]` and the verdict. Do not re-run the commands
20
- yourself and do not edit the file.
21
-
22
- - `pass` / `not_applicable` (not a pp-core package): continue to the verifier.
23
- - `fail`: the phase cannot pass verification. Print each `failures[]` entry (stage, command, exit code).
24
-
25
- When the verifier is spawned, add this line to its `<files_to_read>` block:
26
- `- {INSTALL_RUN_FILE} (installed-product run — judge user-facing behaviour on it, not on the repo)`.
27
-
28
- When the verifier is NOT listed and the verdict is `fail`: do not mark the phase passed. Present the
29
- failures as the gaps and offer `/pp-plan-phase {phase} --gaps`.
@@ -1,23 +0,0 @@
1
- # Phase-close size re-baseline — execute-phase step
2
-
3
- Plans never run `size:baseline`: each commit is checked against a per-commit size budget
4
- (`size_budget.per_file_bytes` / `size_budget.per_commit_bytes`). The phase's growth is recorded once,
5
- here, in the phase's closing commit, after every wave, the code-review fixes and the regression gate.
6
-
7
- Applies only when the project defines a `size:baseline` npm script (pp-core's own repository). Elsewhere
8
- this step does nothing.
9
-
10
- ```bash
11
- if node -e "process.exit((require('./package.json').scripts || {})['size:baseline'] ? 0 : 1)" 2>/dev/null; then
12
- npm run size:baseline -- --reason "phase ${PHASE_NUMBER} close" || {
13
- echo "size:baseline failed — fix before completing phase ${PHASE_NUMBER}" >&2; exit 1; }
14
- CHANGED_BASELINES=$(git diff --name-only -- 'tests/*-size-baseline.json')
15
- if [ -n "$CHANGED_BASELINES" ]; then
16
- git add -- $CHANGED_BASELINES
17
- git commit -m "chore(phase-${PHASE_NUMBER}): re-baseline workflow and agent sizes at phase close"
18
- fi
19
- fi
20
- ```
21
-
22
- The `_rebaselines` entry this writes lists every file the phase grew, with its delta — the review record for
23
- the phase's growth. Nothing to commit means the phase grew nothing past the baseline.