@pareto4/pp-core 1.13.0 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/.claude-plugin/plugin.json +3 -3
  2. package/README.md +31 -12
  3. package/agents/pp-code-fixer.md +0 -11
  4. package/agents/pp-code-reviewer.md +1 -77
  5. package/agents/pp-debug-session-manager.md +9 -6
  6. package/agents/pp-executor.md +12 -29
  7. package/agents/pp-plan-checker.md +4 -4
  8. package/agents/pp-planner.md +34 -20
  9. package/agents/pp-vault-reviewer.md +100 -23
  10. package/agents/pp-verifier.md +1 -3
  11. package/bin/install.js +10 -3
  12. package/commands/pp-execute-phase.md +1 -3
  13. package/commands/pp-ns-manage.md +1 -1
  14. package/commands/pp-ns-review.md +1 -1
  15. package/commands/pp-plan-phase.md +1 -2
  16. package/commands/pp-vault-promote.md +7 -8
  17. package/commands/pp-vault-review.md +6 -5
  18. package/commands/pp-verify-work.md +1 -2
  19. package/gemini-extension.json +1 -1
  20. package/hooks/dist/pp-check-update.js +0 -0
  21. package/hooks/dist/pp-cursor-session-start.js +1 -1
  22. package/hooks/dist/pp-statusline.js +1 -1
  23. package/hooks/dist/pp-update-banner.js +0 -0
  24. package/hooks/pp-check-update.js +0 -0
  25. package/hooks/pp-cursor-session-start.js +1 -1
  26. package/hooks/pp-statusline.js +1 -1
  27. package/hooks/pp-update-banner.js +0 -0
  28. package/package.json +10 -18
  29. package/pareto-skills/_shared/create-project-board.sh +0 -0
  30. package/pareto-skills/pp-project-setup/SKILL.md +1 -1
  31. package/pp-core/bin/check-latest-version.cjs +0 -0
  32. package/pp-core/bin/detect-build-commands.sh +2 -2
  33. package/pp-core/bin/lib/capability-activation.cjs +83 -9
  34. package/pp-core/bin/lib/capability-registry.cjs +201 -229
  35. package/pp-core/bin/lib/capability-state.cjs +93 -1
  36. package/pp-core/bin/lib/capability-writer.cjs +3 -3
  37. package/pp-core/bin/lib/check-command-router.cjs +1906 -1514
  38. package/pp-core/bin/lib/clusters.cjs +3 -0
  39. package/pp-core/bin/lib/command-aliases.cjs +0 -35
  40. package/pp-core/bin/lib/commands.cjs +9 -4
  41. package/pp-core/bin/lib/compose-bindings.cjs +251 -88
  42. package/pp-core/bin/lib/config-loader.cjs +7 -103
  43. package/pp-core/bin/lib/config-schema.cjs +1 -1
  44. package/pp-core/bin/lib/config.cjs +151 -62
  45. package/pp-core/bin/lib/decisions.cjs +4 -4
  46. package/pp-core/bin/lib/discovery-vault-check.cjs +246 -0
  47. package/pp-core/bin/lib/frontmatter.cjs +1 -38
  48. package/pp-core/bin/lib/git-base-branch.cjs +11 -12
  49. package/pp-core/bin/lib/git-provider.cjs +9 -10
  50. package/pp-core/bin/lib/init-command-router.cjs +5 -8
  51. package/pp-core/bin/lib/init-contract.cjs +620 -738
  52. package/pp-core/bin/lib/init-interactive.cjs +62 -4
  53. package/pp-core/bin/lib/init.cjs +24 -51
  54. package/pp-core/bin/lib/install-profiles.cjs +1 -1
  55. package/pp-core/bin/lib/io.cjs +4 -2
  56. package/pp-core/bin/lib/loop-resolver.cjs +165 -129
  57. package/pp-core/bin/lib/org-profile.cjs +402 -161
  58. package/pp-core/bin/lib/package-identity.cjs +3 -3
  59. package/pp-core/bin/lib/phase-command-router.cjs +1 -29
  60. package/pp-core/bin/lib/phase-scope.cjs +1 -1
  61. package/pp-core/bin/lib/phase.cjs +11 -253
  62. package/pp-core/bin/lib/platform-bindings.cjs +40 -19
  63. package/pp-core/bin/lib/profile-output.cjs +1 -1
  64. package/pp-core/bin/lib/shell-command-projection.cjs +104 -1
  65. package/pp-core/bin/lib/state.cjs +11 -85
  66. package/pp-core/bin/lib/template.cjs +1 -1
  67. package/pp-core/bin/lib/vault-command-router.cjs +1682 -477
  68. package/pp-core/bin/lib/vault-coordinates.cjs +16 -7
  69. package/pp-core/bin/lib/vault-index.cjs +656 -0
  70. package/pp-core/bin/lib/vault-seal.cjs +1851 -0
  71. package/pp-core/bin/lib/vault-standards-promote.cjs +577 -0
  72. package/pp-core/bin/lib/vault-standards-review.cjs +293 -0
  73. package/pp-core/bin/lib/vault-standards.cjs +1504 -0
  74. package/pp-core/bin/lib/vault-trace.cjs +155 -2778
  75. package/pp-core/bin/lib/vault-update.cjs +10 -59
  76. package/pp-core/bin/lib/vault.cjs +2570 -950
  77. package/pp-core/bin/lib/verify.cjs +0 -11
  78. package/pp-core/bin/portable-timeout.sh +1 -1
  79. package/pp-core/bin/pp-tools.cjs +157 -54
  80. package/pp-core/bin/shared/config-defaults.manifest.json +0 -7
  81. package/pp-core/bin/shared/config-schema.manifest.json +0 -3
  82. package/pp-core/bin/shared/model-catalog.json +2 -2
  83. package/pp-core/bin/verify-reapply-patches.cjs +0 -0
  84. package/pp-core/org-profile.json +26 -0
  85. package/pp-core/references/autonomous-best-effort.md +8 -7
  86. package/pp-core/references/loop-hook-dispatch.md +0 -25
  87. package/pp-core/references/planner-gap-closure.md +0 -1
  88. package/pp-core/references/planner-tdd.md +0 -1
  89. package/pp-core/references/planner-vault-standards.md +311 -0
  90. package/pp-core/references/planning-config.md +0 -8
  91. package/pp-core/references/tdd.md +0 -1
  92. package/pp-core/templates/README.md +1 -0
  93. package/pp-core/templates/VAULT-REVIEW.md +56 -0
  94. package/pp-core/templates/phase-prompt.md +2 -6
  95. package/pp-core/workflows/_runtime-launcher.snippet.sh +0 -0
  96. package/pp-core/workflows/add-tests.md +55 -4
  97. package/pp-core/workflows/audit-fix.md +63 -2
  98. package/pp-core/workflows/audit-milestone.md +1 -1
  99. package/pp-core/workflows/autonomous.md +85 -11
  100. package/pp-core/workflows/check-todos.md +13 -2
  101. package/pp-core/workflows/code-review-fix.md +59 -3
  102. package/pp-core/workflows/code-review.md +10 -57
  103. package/pp-core/workflows/complete-milestone.md +1 -1
  104. package/pp-core/workflows/debug.md +54 -4
  105. package/pp-core/workflows/discovery-sprint/prefill.md +19 -7
  106. package/pp-core/workflows/discovery-sprint/prototype.md +46 -49
  107. package/pp-core/workflows/discovery-sprint/session-13-gates.md +2 -0
  108. package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +1 -3
  109. package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +1 -3
  110. package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +1 -3
  111. package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +20 -24
  112. package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +1 -3
  113. package/pp-core/workflows/discovery-sprint/sessions/06-security.md +1 -3
  114. package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +1 -3
  115. package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +1 -3
  116. package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +1 -3
  117. package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +1 -6
  118. package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +1 -3
  119. package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +1 -3
  120. package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +1 -3
  121. package/pp-core/workflows/discovery-sprint/templates/context-template.md +1 -1
  122. package/pp-core/workflows/discovery-sprint/vault-context.md +207 -0
  123. package/pp-core/workflows/discovery-sprint.md +7 -3
  124. package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +4 -4
  125. package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +43 -1
  126. package/pp-core/workflows/discovery-to-build.md +10 -2
  127. package/pp-core/workflows/discuss-phase.md +3 -10
  128. package/pp-core/workflows/execute-phase/steps/check-query-gate.md +327 -0
  129. package/pp-core/workflows/execute-phase/steps/client-context-gate.md +3 -2
  130. package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +1 -1
  131. package/pp-core/workflows/execute-phase/steps/execute-pre-dispatch.md +78 -0
  132. package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
  133. package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +6 -8
  134. package/pp-core/workflows/execute-phase/steps/vault-fetch.md +115 -14
  135. package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +219 -20
  136. package/pp-core/workflows/execute-phase/steps/verify-post-gate.md +147 -0
  137. package/pp-core/workflows/execute-phase.md +152 -128
  138. package/pp-core/workflows/execute-plan.md +54 -6
  139. package/pp-core/workflows/fast.md +53 -4
  140. package/pp-core/workflows/help/modes/full.md +3 -3
  141. package/pp-core/workflows/init/steps/client-context-capture.md +101 -0
  142. package/pp-core/workflows/init/steps/gitlab-provisioning.md +130 -0
  143. package/pp-core/workflows/init/steps/project-md-deferral-narration.md +66 -0
  144. package/pp-core/workflows/init/steps/reachability-outcome.md +70 -0
  145. package/pp-core/workflows/init.md +232 -272
  146. package/pp-core/workflows/new-project.md +31 -23
  147. package/pp-core/workflows/plan-phase.md +98 -45
  148. package/pp-core/workflows/quick.md +117 -3
  149. package/pp-core/workflows/secure-phase.md +1 -1
  150. package/pp-core/workflows/ship.md +57 -28
  151. package/pp-core/workflows/undo.md +1 -1
  152. package/pp-core/workflows/update.md +3 -3
  153. package/pp-core/workflows/validate-phase.md +58 -3
  154. package/pp-core/workflows/vault-review.md +161 -23
  155. package/pp-core/workflows/vault-write-back.md +73 -110
  156. package/pp-core/workflows/verify-work.md +12 -14
  157. package/scripts/changeset/cli.cjs +6 -5
  158. package/scripts/changeset/lint.cjs +0 -0
  159. package/scripts/changeset/new.cjs +33 -50
  160. package/scripts/dev-env.cjs +25 -85
  161. package/scripts/gen-capability-registry.cjs +120 -13
  162. package/scripts/lib/allowlist-ratchet.cjs +1 -103
  163. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -2
  164. package/scripts/lint-docs-required.cjs +0 -0
  165. package/scripts/lint-pr-check-project-dir.cjs +1 -3
  166. package/scripts/lint-test-file-count.allowlist.json +3 -19
  167. package/scripts/lint-test-file-count.cjs +5 -6
  168. package/scripts/prompt-injection-scan.sh +19 -17
  169. package/scripts/release-channel.cjs +0 -0
  170. package/scripts/sync-manifest-versions.cjs +5 -4
  171. package/scripts/sync-rulesets.sh +1 -1
  172. package/scripts/update-size-baseline.cjs +30 -274
  173. package/scripts/verify-opengsd-sweep.cjs +3 -5
  174. package/scripts/workflow-size.cjs +9 -61
  175. package/pp-core/bin/lib/dispatch-plan.cjs +0 -483
  176. package/pp-core/bin/lib/install-run.cjs +0 -536
  177. package/pp-core/bin/lib/loop-gates.cjs +0 -213
  178. package/pp-core/bin/lib/phase-manifest.cjs +0 -527
  179. package/pp-core/bin/lib/phase-tier.cjs +0 -480
  180. package/pp-core/bin/lib/plan-graph.cjs +0 -368
  181. package/pp-core/bin/lib/practices-review-check.cjs +0 -938
  182. package/pp-core/bin/lib/vault-audit.cjs +0 -2070
  183. package/pp-core/bin/lib/vault-practices.cjs +0 -1990
  184. package/pp-core/bin/lib/vault-review.cjs +0 -2359
  185. package/pp-core/bin/lib/vault-write-back.cjs +0 -877
  186. package/pp-core/references/executor-vault-trace.md +0 -47
  187. package/pp-core/references/planner-plan-graph.md +0 -66
  188. package/pp-core/references/planner-vault-practices.md +0 -112
  189. package/pp-core/templates/practices/README.md +0 -251
  190. package/pp-core/templates/practices/error-handling.md +0 -18
  191. package/pp-core/templates/practices/minimal-comments.md +0 -14
  192. package/pp-core/templates/practices/naming-convention.md +0 -18
  193. package/pp-core/templates/practices/practice-template.md +0 -23
  194. package/pp-core/workflows/discovery-sprint/vault-fetch.md +0 -37
  195. package/pp-core/workflows/execute-phase/steps/checkpoint-handling.md +0 -45
  196. package/pp-core/workflows/execute-phase/steps/install-run.md +0 -29
  197. package/pp-core/workflows/execute-phase/steps/phase-close-rebaseline.md +0 -23
  198. package/pp-core/workflows/execute-phase/steps/serial-group-dispatch.md +0 -73
  199. package/pp-core/workflows/execute-phase/steps/vault-door-gate.md +0 -19
  200. package/pp-core/workflows/plan-phase/steps/phase-tier.md +0 -43
  201. package/scripts/lib/size-budget.cjs +0 -311
  202. package/scripts/lint-neutrality-names.cjs +0 -694
  203. package/scripts/lint-org-coordinates.allowlist.json +0 -35
  204. package/scripts/lint-org-coordinates.cjs +0 -426
@@ -11,10 +11,11 @@
11
11
  * - VERSIONED_MANIFESTS — JSON runtime-integration manifests whose top-level
12
12
  * `version` field MUST track the package version.
13
13
  * - VERSIONED_TEXT_FILES — plain-text manifests whose ENTIRE content is the
14
- * version followed by one newline. Root `VERSION` is the repo layout's
15
- * version source for `resolveCreatedWith` (src/compose-bindings.cts), so a
16
- * repo build stamps `createdWith` from it. As a plain-text file it could not
17
- * join the JSON loop, so nothing kept it in step with package.json.
14
+ * version followed by one newline. Root `VERSION` is the repo layout's only
15
+ * version source for `resolveCreatedWithFrom` (src/compose-bindings.cts), so a
16
+ * repo build stamps `createdWith` and the practices block from it. It drifted
17
+ * from 1.11.1 through three releases because a plain-text file could not join
18
+ * the JSON loop (quick 261003-kmd closed the todo).
18
19
  *
19
20
  * Usage:
20
21
  * node scripts/sync-manifest-versions.cjs # stamp + report
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
3
 
4
- REPO="${REPO:-open-gsd/gsd-core}"
4
+ REPO="${REPO:-pareto000/pp-core}"
5
5
  ENFORCEMENT="${ENFORCEMENT:-evaluate}"
6
6
 
7
7
  case "$ENFORCEMENT" in
@@ -4,309 +4,65 @@
4
4
  /**
5
5
  * @file update-size-baseline.cjs
6
6
  *
7
- * Regenerates the committed per-file size baselines
8
- * (`tests/workflow-size-baseline.json`, `tests/agent-size-baseline.json`) from
9
- * the current files.
7
+ * Regenerates the committed per-file workflow size baseline
8
+ * (`tests/workflow-size-baseline.json`) from the current workflow files.
10
9
  *
11
- * Workflow size budget policy (2026-10-11; per commit since pack 9): the guards
12
- * allow each commit a small DELTA BUDGET (config size_budget.*, see
13
- * scripts/lib/size-budget.cjs), so plans never run this script. It runs once per
14
- * phase, in the phase's closing commit (`--reason "phase NN close"`, from
15
- * execute-phase), and otherwise only when one commit must grow past the budget.
16
- * Because a raise is the review record of growth, RAISING any entry requires a
17
- * reason:
10
+ * Run via `npm run size:baseline` whenever a workflow file legitimately grows
11
+ * or shrinks. Growth must still be justified in the PR; this script only
12
+ * records the new reality so the CI guard (issue #1074) can diff against it.
18
13
  *
19
- * npm run size:baseline # lower / add / remove only
20
- * npm run size:baseline -- --reason "<why>" # also allowed to raise
21
- *
22
- * Without `--reason`, a run that would raise any entry is REFUSED: it exits
23
- * non-zero and writes nothing (neither baseline). With a reason, every raise is
24
- * recorded in the baseline file's `_rebaselines` array (newest last) as
25
- * `{ date, reason, deltas: [{ file, from, to, delta }] }` — the committed
26
- * review record of a deliberate growth.
27
- *
28
- * Idempotent: running it twice with no file changes produces no diff (a run
29
- * with nothing to raise never appends a `_rebaselines` entry).
14
+ * Idempotent: running it twice with no file changes produces no diff.
30
15
  */
31
16
 
32
17
  const fs = require('fs');
33
18
  const path = require('path');
34
- const { measureMdFiles, measureMdTree, WORKFLOWS_DIR } = require('./workflow-size.cjs');
19
+ const { measureMdFiles, WORKFLOWS_DIR } = require('./workflow-size.cjs');
35
20
 
36
21
  const BASELINE_PATH = path.join(__dirname, '..', 'tests', 'workflow-size-baseline.json');
37
22
  const AGENTS_DIR = path.join(__dirname, '..', 'agents');
38
23
  const AGENT_BASELINE_PATH = path.join(__dirname, '..', 'tests', 'agent-size-baseline.json');
39
- const isPpAgent = (f) => f.startsWith('pp-');
40
-
41
- /** Key of the raise log inside a baseline file. Never a measured filename. */
42
- const REBASELINES_KEY = '_rebaselines';
43
-
44
- /**
45
- * Split a parsed baseline file into its size entries and its raise log.
46
- * Size entries are the numeric values; metadata keys (`_rebaselines`) are not
47
- * sizes and must never reach a guard as one.
48
- *
49
- * @param {object|null} raw - Parsed baseline JSON (null when absent).
50
- * @returns {{ sizes: Object<string, number>, rebaselines: Array<object> }}
51
- */
52
- function splitBaseline(raw) {
53
- const sizes = {};
54
- let rebaselines = [];
55
- if (raw && typeof raw === 'object') {
56
- for (const [key, value] of Object.entries(raw)) {
57
- if (key === REBASELINES_KEY) rebaselines = value;
58
- else sizes[key] = value;
59
- }
60
- }
61
- return { sizes, rebaselines };
62
- }
63
-
64
- /**
65
- * Read and parse a baseline file; `null` when it does not exist.
66
- *
67
- * @param {string} filePath
68
- * @returns {object|null}
69
- */
70
- function readBaseline(filePath) {
71
- let text;
72
- try {
73
- text = fs.readFileSync(filePath, 'utf-8');
74
- } catch (err) {
75
- if (err.code === 'ENOENT') return null;
76
- throw err;
77
- }
78
- return JSON.parse(text);
79
- }
24
+ const isGsdAgent = (f) => f.startsWith('pp-');
80
25
 
81
26
  /**
82
- * Serialize to the on-disk baseline format: size keys sorted, then the
83
- * `_rebaselines` log last; 2-space indent, trailing newline (a stable,
84
- * minimal-diff artifact).
27
+ * Serialize a size map to the on-disk baseline format: keys sorted, 2-space
28
+ * indent, trailing newline (so the file is a stable, minimal-diff artifact).
85
29
  *
86
30
  * @param {Object<string, number>} sizes
87
- * @param {Array<object>} [rebaselines]
88
31
  * @returns {string}
89
32
  */
90
- function serializeBaseline(sizes, rebaselines = []) {
33
+ function serializeBaseline(sizes) {
91
34
  const sorted = {};
92
35
  for (const key of Object.keys(sizes).sort()) sorted[key] = sizes[key];
93
- sorted[REBASELINES_KEY] = rebaselines;
94
36
  return JSON.stringify(sorted, null, 2) + '\n';
95
37
  }
96
38
 
97
39
  /**
98
- * Classify the difference between an old and a new size map.
99
- *
100
- * @param {Object<string, number>} oldSizes
101
- * @param {Object<string, number>} newSizes
102
- * @returns {{ raised: Array<{file:string,from:number,to:number,delta:number}>,
103
- * lowered: Array<{file:string,from:number,to:number,delta:number}>,
104
- * added: string[], removed: string[] }}
105
- */
106
- function diffSizes(oldSizes, newSizes) {
107
- const raised = [];
108
- const lowered = [];
109
- const added = [];
110
- for (const file of Object.keys(newSizes).sort()) {
111
- if (!(file in oldSizes)) {
112
- added.push(file);
113
- continue;
114
- }
115
- const from = oldSizes[file];
116
- const to = newSizes[file];
117
- if (to > from) raised.push({ file, from, to, delta: to - from });
118
- else if (to < from) lowered.push({ file, from, to, delta: from - to });
119
- }
120
- const removed = Object.keys(oldSizes).filter((f) => !(f in newSizes)).sort();
121
- return { raised, lowered, added, removed };
122
- }
123
-
124
- /**
125
- * Validate a `_rebaselines` log. Returns a list of problems (empty = valid).
126
- * Every entry must carry an ISO date, a non-empty reason and a non-empty list
127
- * of positive deltas whose arithmetic holds; dates must be non-decreasing
128
- * (newest last).
129
- *
130
- * @param {unknown} log
131
- * @returns {string[]}
132
- */
133
- function validateRebaselineLog(log) {
134
- if (!Array.isArray(log)) return [`${REBASELINES_KEY} must be an array, got ${JSON.stringify(log)}`];
135
- const problems = [];
136
- const isSize = (n) => Number.isInteger(n) && n >= 0;
137
- let prevDate = '';
138
- log.forEach((entry, i) => {
139
- const at = `${REBASELINES_KEY}[${i}]`;
140
- if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
141
- problems.push(`${at} must be an object`);
142
- return;
143
- }
144
- if (typeof entry.date !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(entry.date)) {
145
- problems.push(`${at}.date must be YYYY-MM-DD, got ${JSON.stringify(entry.date)}`);
146
- } else {
147
- if (entry.date < prevDate) problems.push(`${at}.date ${entry.date} is older than the entry before it (newest must be last)`);
148
- prevDate = entry.date;
149
- }
150
- if (typeof entry.reason !== 'string' || entry.reason.trim() === '') {
151
- problems.push(`${at}.reason must be a non-empty string`);
152
- }
153
- if (!Array.isArray(entry.deltas) || entry.deltas.length === 0) {
154
- problems.push(`${at}.deltas must be a non-empty array`);
155
- return;
156
- }
157
- entry.deltas.forEach((d, j) => {
158
- const dat = `${at}.deltas[${j}]`;
159
- if (!d || typeof d.file !== 'string' || d.file === '') problems.push(`${dat}.file must be a non-empty string`);
160
- if (!d || !isSize(d.from) || !isSize(d.to)) problems.push(`${dat}.from/.to must be non-negative integers`);
161
- else if (!Number.isInteger(d.delta) || d.delta <= 0 || d.delta !== d.to - d.from) {
162
- problems.push(`${dat}.delta must be the positive difference to - from (${d.to - d.from}), got ${JSON.stringify(d.delta)}`);
163
- }
164
- });
165
- });
166
- return problems;
167
- }
168
-
169
- /**
170
- * Compute (but do not write) a regenerated baseline.
40
+ * Write a baseline file from the measured `.md` sizes in a directory.
171
41
  *
172
42
  * @param {object} [opts]
173
43
  * @param {string} [opts.dir] - Directory to measure (default: workflows).
174
- * @param {string} [opts.outPath] - Baseline file (default: workflow baseline).
175
- * @param {function(string): boolean} [opts.predicate] - File filter.
176
- * @param {function(string, function): Object<string, number>} [opts.measure] -
177
- * Measurer (default: recursive `measureMdTree`; agents pass `measureMdFiles`).
178
- * @param {string} [opts.reason] - Justification; required to raise any entry.
179
- * @param {string} [opts.today] - Date stamp for a raise (default: today, UTC).
180
- * @returns {{ outPath: string, refused: boolean, count: number, content: string|null,
181
- * change: ReturnType<typeof diffSizes> }}
44
+ * @param {string} [opts.outPath] - Baseline file to write (default: workflow).
45
+ * @param {function(string): boolean} [opts.predicate] - Filename filter.
46
+ * @returns {{ outPath: string, count: number, content: string }}
182
47
  */
183
- function planBaseline({
184
- dir = WORKFLOWS_DIR,
185
- outPath = BASELINE_PATH,
186
- predicate,
187
- measure = measureMdTree,
188
- reason,
189
- today = new Date().toISOString().slice(0, 10),
190
- } = {}) {
191
- const sizes = measure(dir, predicate);
192
- const { sizes: oldSizes, rebaselines: oldLog } = splitBaseline(readBaseline(outPath));
193
- const change = diffSizes(oldSizes, sizes);
194
- const count = Object.keys(sizes).length;
195
- const hasReason = typeof reason === 'string' && reason.trim() !== '';
196
- if (change.raised.length > 0 && !hasReason) {
197
- return { outPath, refused: true, count, content: null, change };
198
- }
199
- const log = Array.isArray(oldLog) ? [...oldLog] : [];
200
- if (change.raised.length > 0) {
201
- log.push({ date: today, reason: reason.trim(), deltas: change.raised });
202
- }
203
- return { outPath, refused: false, count, content: serializeBaseline(sizes, log), change };
204
- }
205
-
206
- /**
207
- * Regenerate one baseline file. A refused plan (a raise without a reason)
208
- * writes nothing.
209
- *
210
- * @param {Parameters<typeof planBaseline>[0]} [opts]
211
- * @returns {ReturnType<typeof planBaseline>}
212
- */
213
- function generateBaseline(opts = {}) {
214
- const plan = planBaseline(opts);
215
- if (!plan.refused) fs.writeFileSync(plan.outPath, plan.content);
216
- return plan;
217
- }
218
-
219
- /**
220
- * Parse CLI arguments: only `--reason <text>` / `--reason=<text>`.
221
- *
222
- * @param {string[]} argv
223
- * @returns {{ reason?: string }}
224
- */
225
- function parseArgs(argv) {
226
- const out = {};
227
- for (let i = 0; i < argv.length; i++) {
228
- const arg = argv[i];
229
- if (arg === '--reason') {
230
- out.reason = argv[++i];
231
- if (out.reason === undefined) throw new Error('--reason needs a value');
232
- } else if (arg.startsWith('--reason=')) {
233
- out.reason = arg.slice('--reason='.length);
234
- } else {
235
- throw new Error(`unknown argument: ${arg} (only --reason "<text>" is accepted)`);
236
- }
237
- }
238
- if (out.reason !== undefined && out.reason.trim() === '') throw new Error('--reason must not be empty');
239
- return out;
48
+ function generateBaseline({ dir = WORKFLOWS_DIR, outPath = BASELINE_PATH, predicate } = {}) {
49
+ const sizes = measureMdFiles(dir, predicate);
50
+ const content = serializeBaseline(sizes);
51
+ fs.writeFileSync(outPath, content);
52
+ return { outPath, count: Object.keys(sizes).length, content };
240
53
  }
241
54
 
242
- const TARGETS = [
243
- { label: 'workflow', dir: WORKFLOWS_DIR, outPath: BASELINE_PATH, measure: measureMdTree },
244
- { label: 'agent', dir: AGENTS_DIR, outPath: AGENT_BASELINE_PATH, predicate: isPpAgent, measure: measureMdFiles },
245
- ];
246
-
247
- /**
248
- * CLI entry. Plans every target first and writes only when none is refused,
249
- * so a refusal leaves BOTH baselines untouched.
250
- *
251
- * @param {string[]} argv
252
- * @param {object} [io]
253
- * @param {Array<object>} [io.targets]
254
- * @param {string} [io.today]
255
- * @param {NodeJS.WritableStream} [io.stdout]
256
- * @param {NodeJS.WritableStream} [io.stderr]
257
- * @returns {number} exit code
258
- */
259
- function main(argv, { targets = TARGETS, today, stdout = process.stdout, stderr = process.stderr } = {}) {
260
- let args;
261
- try {
262
- args = parseArgs(argv);
263
- } catch (err) {
264
- stderr.write(`size:baseline: ${err.message}\n`);
265
- return 2;
266
- }
267
- const plans = targets.map((t) => ({ t, plan: planBaseline({ ...t, reason: args.reason, today }) }));
268
- const refused = plans.filter((p) => p.plan.refused);
269
- if (refused.length > 0) {
270
- const lines = refused.flatMap(({ t, plan }) =>
271
- plan.change.raised.map((r) => ` - ${t.label}: ${r.file}: ${r.from} → ${r.to} (+${r.delta})`)
272
- );
273
- stderr.write(
274
- 'size:baseline: REFUSED — this run would RAISE baseline entries, which needs a reason:\n' +
275
- `${lines.join('\n')}\n` +
276
- 'Shrink the file(s), or re-run with: npm run size:baseline -- --reason "<why this growth is needed>"\n' +
277
- 'Nothing was written.\n'
278
- );
279
- return 1;
280
- }
281
- for (const { t, plan } of plans) {
282
- fs.writeFileSync(plan.outPath, plan.content);
283
- const c = plan.change;
284
- stdout.write(
285
- `Wrote ${plan.count} ${t.label} sizes to ${path.relative(process.cwd(), plan.outPath)} ` +
286
- `(raised ${c.raised.length}, lowered ${c.lowered.length}, added ${c.added.length}, removed ${c.removed.length})\n`
55
+ if (require.main === module) {
56
+ const targets = [
57
+ { label: 'workflow', dir: WORKFLOWS_DIR, outPath: BASELINE_PATH },
58
+ { label: 'agent', dir: AGENTS_DIR, outPath: AGENT_BASELINE_PATH, predicate: isGsdAgent },
59
+ ];
60
+ for (const t of targets) {
61
+ const { outPath, count } = generateBaseline(t);
62
+ process.stdout.write(
63
+ `Wrote ${count} ${t.label} sizes to ${path.relative(process.cwd(), outPath)}\n`
287
64
  );
288
65
  }
289
- if (args.reason !== undefined && plans.every((p) => p.plan.change.raised.length === 0)) {
290
- stdout.write('Note: nothing was raised, so --reason was not recorded.\n');
291
- }
292
- return 0;
293
- }
294
-
295
- if (require.main === module) {
296
- process.exitCode = main(process.argv.slice(2));
297
66
  }
298
67
 
299
- module.exports = {
300
- generateBaseline,
301
- planBaseline,
302
- serializeBaseline,
303
- splitBaseline,
304
- readBaseline,
305
- diffSizes,
306
- validateRebaselineLog,
307
- parseArgs,
308
- main,
309
- REBASELINES_KEY,
310
- BASELINE_PATH,
311
- AGENT_BASELINE_PATH,
312
- };
68
+ module.exports = { generateBaseline, serializeBaseline, BASELINE_PATH, AGENT_BASELINE_PATH };
@@ -10,8 +10,9 @@
10
10
  *
11
11
  * Explicit carve-outs (allowlist — intentional OpenGSD references):
12
12
  * D-07 provenance files: LICENSE, NOTICE, UPSTREAM_SYNC.md
13
- * D-06 historical corpus: docs/adr/, docs/proposals/, docs/discussions/,
14
- * docs/issueevidence/, docs/prd/, docs/research/
13
+ * D-06 historical corpus: docs/adr/, docs/prd/, docs/research/
14
+ * (docs/proposals, docs/discussions and docs/issueevidence
15
+ * were upstream working notes, removed from the fork)
15
16
  * D-06 notice file: docs/adr/README.md (contains the history-notice header)
16
17
  * Migration provenance: pp-core/bin/lib/installer-migrations/005-*.cjs
17
18
  * (comment accurately names the upstream source package)
@@ -79,9 +80,6 @@ const PROVENANCE_FILES = new Set([
79
80
  */
80
81
  const HISTORICAL_CORPUS_DIRS = [
81
82
  path.join(ROOT, 'docs', 'adr'),
82
- path.join(ROOT, 'docs', 'proposals'),
83
- path.join(ROOT, 'docs', 'discussions'),
84
- path.join(ROOT, 'docs', 'issueevidence'),
85
83
  path.join(ROOT, 'docs', 'prd'),
86
84
  path.join(ROOT, 'docs', 'research'),
87
85
  ];
@@ -36,10 +36,8 @@ function lfByteCount(filePath) {
36
36
 
37
37
  /**
38
38
  * List top-level workflow stems (filenames without the `.md` extension), sorted.
39
- * Top-level only on purpose: a stem is what the tier tables (XL/LARGE/DEFAULT)
40
- * classify, and only top-level files have a tier. Nested files under
41
- * `workflows/<name>/{steps,modes,templates}/` are still SIZE-guarded — see
42
- * `measureMdTree` / `measureWorkflows`, which recurse.
39
+ * Non-recursive by design: per-mode bodies under `workflows/<name>/modes/` and
40
+ * templates are NOT measured — only the always-loaded top-level workflows.
43
41
  *
44
42
  * @param {string} [dir] - Workflows directory (defaults to the canonical one).
45
43
  * @returns {string[]} Sorted stems, e.g. `['autonomous', 'plan-phase', ...]`.
@@ -54,10 +52,10 @@ function listWorkflowStems(dir = WORKFLOWS_DIR) {
54
52
 
55
53
  /**
56
54
  * Measure every top-level `.md` file in `dir`, keyed by filename, byte sizes.
57
- * Generic over directory and an optional filename predicate — used for agents
58
- * (`agents/pp-*.md`), which are a flat directory, so the agent guard and the
59
- * baseline generator share one measurement path (#1074).
60
- * Non-recursive by design; use `measureMdTree` for a nested tree.
55
+ * Generic over directory and an optional filename predicate — used for both
56
+ * workflows (`pp-core/workflows/*.md`) and agents (`agents/gsd-*.md`) so the
57
+ * size guards and the baseline generator share one measurement path (#1074).
58
+ * Non-recursive by design.
61
59
  *
62
60
  * @param {string} dir - Directory to scan.
63
61
  * @param {function(string): boolean} [predicate] - Filename filter (default: all `.md`).
@@ -74,69 +72,19 @@ function measureMdFiles(dir, predicate = () => true) {
74
72
  }
75
73
 
76
74
  /**
77
- * List every `.md` file under `dir`, recursively, as POSIX relative paths
78
- * (`plan-phase.md`, `execute-phase/steps/vault-fetch.md`), sorted.
79
- *
80
- * Keys are POSIX on every platform so the committed baseline is identical on a
81
- * Windows checkout. Top-level files keep their bare filename, exactly as the
82
- * non-recursive `measureMdFiles` keys them, so existing consumers that look up
83
- * `baseline['plan-phase.md']` keep working.
84
- *
85
- * @param {string} dir - Directory to walk.
86
- * @param {function(string): boolean} [predicate] - Filter on the POSIX relative path.
87
- * @returns {string[]} Sorted POSIX relative paths.
88
- */
89
- function listMdTree(dir, predicate = () => true) {
90
- const out = [];
91
- const walk = (abs, rel) => {
92
- for (const entry of fs.readdirSync(abs, { withFileTypes: true })) {
93
- const childRel = rel ? `${rel}/${entry.name}` : entry.name;
94
- const childAbs = path.join(abs, entry.name);
95
- if (entry.isDirectory()) walk(childAbs, childRel);
96
- else if (entry.isFile() && entry.name.endsWith('.md') && predicate(childRel)) {
97
- out.push(childRel);
98
- }
99
- }
100
- };
101
- walk(dir, '');
102
- return out.sort();
103
- }
104
-
105
- /**
106
- * Measure every `.md` file under `dir`, recursively, keyed by POSIX relative
107
- * path (see `listMdTree`), LF byte sizes.
108
- *
109
- * @param {string} dir - Directory to walk.
110
- * @param {function(string): boolean} [predicate] - Filter on the POSIX relative path.
111
- * @returns {Object<string, number>} Map of relative path → LF byte size, keys sorted.
112
- */
113
- function measureMdTree(dir, predicate = () => true) {
114
- const out = {};
115
- for (const rel of listMdTree(dir, predicate)) {
116
- out[rel] = lfByteCount(path.join(dir, ...rel.split('/')));
117
- }
118
- return out;
119
- }
120
-
121
- /**
122
- * Measure every workflow `.md` file — top-level AND nested (`steps/`, `modes/`,
123
- * `templates/`, ...) — keyed by POSIX relative path. Recursive since the
124
- * workflow size budget policy of 2026-10-11: a nested step file is loaded into
125
- * context just like its parent, so growth there must be visible too.
75
+ * Measure every top-level workflow file, keyed by filename (`<stem>.md`).
126
76
  *
127
77
  * @param {string} [dir] - Workflows directory (defaults to the canonical one).
128
- * @returns {Object<string, number>} Map of relative path → LF byte size, sorted.
78
+ * @returns {Object<string, number>} Map of `<stem>.md` → LF byte size, sorted.
129
79
  */
130
80
  function measureWorkflows(dir = WORKFLOWS_DIR) {
131
- return measureMdTree(dir);
81
+ return measureMdFiles(dir);
132
82
  }
133
83
 
134
84
  module.exports = {
135
85
  WORKFLOWS_DIR,
136
86
  lfByteCount,
137
87
  listWorkflowStems,
138
- listMdTree,
139
88
  measureMdFiles,
140
- measureMdTree,
141
89
  measureWorkflows,
142
90
  };