@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.
- package/.claude-plugin/plugin.json +3 -3
- package/README.md +31 -12
- package/agents/pp-code-fixer.md +0 -11
- package/agents/pp-code-reviewer.md +1 -77
- package/agents/pp-debug-session-manager.md +9 -6
- package/agents/pp-executor.md +12 -29
- package/agents/pp-plan-checker.md +4 -4
- package/agents/pp-planner.md +34 -20
- package/agents/pp-vault-reviewer.md +100 -23
- package/agents/pp-verifier.md +1 -3
- package/bin/install.js +10 -3
- package/commands/pp-execute-phase.md +1 -3
- package/commands/pp-ns-manage.md +1 -1
- package/commands/pp-ns-review.md +1 -1
- package/commands/pp-plan-phase.md +1 -2
- package/commands/pp-vault-promote.md +7 -8
- package/commands/pp-vault-review.md +6 -5
- package/commands/pp-verify-work.md +1 -2
- package/gemini-extension.json +1 -1
- package/hooks/dist/pp-check-update.js +0 -0
- package/hooks/dist/pp-cursor-session-start.js +1 -1
- package/hooks/dist/pp-statusline.js +1 -1
- package/hooks/dist/pp-update-banner.js +0 -0
- package/hooks/pp-check-update.js +0 -0
- package/hooks/pp-cursor-session-start.js +1 -1
- package/hooks/pp-statusline.js +1 -1
- package/hooks/pp-update-banner.js +0 -0
- package/package.json +10 -18
- package/pareto-skills/_shared/create-project-board.sh +0 -0
- package/pareto-skills/pp-project-setup/SKILL.md +1 -1
- package/pp-core/bin/check-latest-version.cjs +0 -0
- package/pp-core/bin/detect-build-commands.sh +2 -2
- package/pp-core/bin/lib/capability-activation.cjs +83 -9
- package/pp-core/bin/lib/capability-registry.cjs +201 -229
- package/pp-core/bin/lib/capability-state.cjs +93 -1
- package/pp-core/bin/lib/capability-writer.cjs +3 -3
- package/pp-core/bin/lib/check-command-router.cjs +1906 -1514
- package/pp-core/bin/lib/clusters.cjs +3 -0
- package/pp-core/bin/lib/command-aliases.cjs +0 -35
- package/pp-core/bin/lib/commands.cjs +9 -4
- package/pp-core/bin/lib/compose-bindings.cjs +251 -88
- package/pp-core/bin/lib/config-loader.cjs +7 -103
- package/pp-core/bin/lib/config-schema.cjs +1 -1
- package/pp-core/bin/lib/config.cjs +151 -62
- package/pp-core/bin/lib/decisions.cjs +4 -4
- package/pp-core/bin/lib/discovery-vault-check.cjs +246 -0
- package/pp-core/bin/lib/frontmatter.cjs +1 -38
- package/pp-core/bin/lib/git-base-branch.cjs +11 -12
- package/pp-core/bin/lib/git-provider.cjs +9 -10
- package/pp-core/bin/lib/init-command-router.cjs +5 -8
- package/pp-core/bin/lib/init-contract.cjs +620 -738
- package/pp-core/bin/lib/init-interactive.cjs +62 -4
- package/pp-core/bin/lib/init.cjs +24 -51
- package/pp-core/bin/lib/install-profiles.cjs +1 -1
- package/pp-core/bin/lib/io.cjs +4 -2
- package/pp-core/bin/lib/loop-resolver.cjs +165 -129
- package/pp-core/bin/lib/org-profile.cjs +402 -161
- package/pp-core/bin/lib/package-identity.cjs +3 -3
- package/pp-core/bin/lib/phase-command-router.cjs +1 -29
- package/pp-core/bin/lib/phase-scope.cjs +1 -1
- package/pp-core/bin/lib/phase.cjs +11 -253
- package/pp-core/bin/lib/platform-bindings.cjs +40 -19
- package/pp-core/bin/lib/profile-output.cjs +1 -1
- package/pp-core/bin/lib/shell-command-projection.cjs +104 -1
- package/pp-core/bin/lib/state.cjs +11 -85
- package/pp-core/bin/lib/template.cjs +1 -1
- package/pp-core/bin/lib/vault-command-router.cjs +1682 -477
- package/pp-core/bin/lib/vault-coordinates.cjs +16 -7
- package/pp-core/bin/lib/vault-index.cjs +656 -0
- package/pp-core/bin/lib/vault-seal.cjs +1851 -0
- package/pp-core/bin/lib/vault-standards-promote.cjs +577 -0
- package/pp-core/bin/lib/vault-standards-review.cjs +293 -0
- package/pp-core/bin/lib/vault-standards.cjs +1504 -0
- package/pp-core/bin/lib/vault-trace.cjs +155 -2778
- package/pp-core/bin/lib/vault-update.cjs +10 -59
- package/pp-core/bin/lib/vault.cjs +2570 -950
- package/pp-core/bin/lib/verify.cjs +0 -11
- package/pp-core/bin/portable-timeout.sh +1 -1
- package/pp-core/bin/pp-tools.cjs +157 -54
- package/pp-core/bin/shared/config-defaults.manifest.json +0 -7
- package/pp-core/bin/shared/config-schema.manifest.json +0 -3
- package/pp-core/bin/shared/model-catalog.json +2 -2
- package/pp-core/bin/verify-reapply-patches.cjs +0 -0
- package/pp-core/org-profile.json +26 -0
- package/pp-core/references/autonomous-best-effort.md +8 -7
- package/pp-core/references/loop-hook-dispatch.md +0 -25
- package/pp-core/references/planner-gap-closure.md +0 -1
- package/pp-core/references/planner-tdd.md +0 -1
- package/pp-core/references/planner-vault-standards.md +311 -0
- package/pp-core/references/planning-config.md +0 -8
- package/pp-core/references/tdd.md +0 -1
- package/pp-core/templates/README.md +1 -0
- package/pp-core/templates/VAULT-REVIEW.md +56 -0
- package/pp-core/templates/phase-prompt.md +2 -6
- package/pp-core/workflows/_runtime-launcher.snippet.sh +0 -0
- package/pp-core/workflows/add-tests.md +55 -4
- package/pp-core/workflows/audit-fix.md +63 -2
- package/pp-core/workflows/audit-milestone.md +1 -1
- package/pp-core/workflows/autonomous.md +85 -11
- package/pp-core/workflows/check-todos.md +13 -2
- package/pp-core/workflows/code-review-fix.md +59 -3
- package/pp-core/workflows/code-review.md +10 -57
- package/pp-core/workflows/complete-milestone.md +1 -1
- package/pp-core/workflows/debug.md +54 -4
- package/pp-core/workflows/discovery-sprint/prefill.md +19 -7
- package/pp-core/workflows/discovery-sprint/prototype.md +46 -49
- package/pp-core/workflows/discovery-sprint/session-13-gates.md +2 -0
- package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +20 -24
- package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/06-security.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +1 -6
- package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +1 -3
- package/pp-core/workflows/discovery-sprint/templates/context-template.md +1 -1
- package/pp-core/workflows/discovery-sprint/vault-context.md +207 -0
- package/pp-core/workflows/discovery-sprint.md +7 -3
- package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +4 -4
- package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +43 -1
- package/pp-core/workflows/discovery-to-build.md +10 -2
- package/pp-core/workflows/discuss-phase.md +3 -10
- package/pp-core/workflows/execute-phase/steps/check-query-gate.md +327 -0
- package/pp-core/workflows/execute-phase/steps/client-context-gate.md +3 -2
- package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +1 -1
- package/pp-core/workflows/execute-phase/steps/execute-pre-dispatch.md +78 -0
- package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
- package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +6 -8
- package/pp-core/workflows/execute-phase/steps/vault-fetch.md +115 -14
- package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +219 -20
- package/pp-core/workflows/execute-phase/steps/verify-post-gate.md +147 -0
- package/pp-core/workflows/execute-phase.md +152 -128
- package/pp-core/workflows/execute-plan.md +54 -6
- package/pp-core/workflows/fast.md +53 -4
- package/pp-core/workflows/help/modes/full.md +3 -3
- package/pp-core/workflows/init/steps/client-context-capture.md +101 -0
- package/pp-core/workflows/init/steps/gitlab-provisioning.md +130 -0
- package/pp-core/workflows/init/steps/project-md-deferral-narration.md +66 -0
- package/pp-core/workflows/init/steps/reachability-outcome.md +70 -0
- package/pp-core/workflows/init.md +232 -272
- package/pp-core/workflows/new-project.md +31 -23
- package/pp-core/workflows/plan-phase.md +98 -45
- package/pp-core/workflows/quick.md +117 -3
- package/pp-core/workflows/secure-phase.md +1 -1
- package/pp-core/workflows/ship.md +57 -28
- package/pp-core/workflows/undo.md +1 -1
- package/pp-core/workflows/update.md +3 -3
- package/pp-core/workflows/validate-phase.md +58 -3
- package/pp-core/workflows/vault-review.md +161 -23
- package/pp-core/workflows/vault-write-back.md +73 -110
- package/pp-core/workflows/verify-work.md +12 -14
- package/scripts/changeset/cli.cjs +6 -5
- package/scripts/changeset/lint.cjs +0 -0
- package/scripts/changeset/new.cjs +33 -50
- package/scripts/dev-env.cjs +25 -85
- package/scripts/gen-capability-registry.cjs +120 -13
- package/scripts/lib/allowlist-ratchet.cjs +1 -103
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -2
- package/scripts/lint-docs-required.cjs +0 -0
- package/scripts/lint-pr-check-project-dir.cjs +1 -3
- package/scripts/lint-test-file-count.allowlist.json +3 -19
- package/scripts/lint-test-file-count.cjs +5 -6
- package/scripts/prompt-injection-scan.sh +19 -17
- package/scripts/release-channel.cjs +0 -0
- package/scripts/sync-manifest-versions.cjs +5 -4
- package/scripts/sync-rulesets.sh +1 -1
- package/scripts/update-size-baseline.cjs +30 -274
- package/scripts/verify-opengsd-sweep.cjs +3 -5
- package/scripts/workflow-size.cjs +9 -61
- package/pp-core/bin/lib/dispatch-plan.cjs +0 -483
- package/pp-core/bin/lib/install-run.cjs +0 -536
- package/pp-core/bin/lib/loop-gates.cjs +0 -213
- package/pp-core/bin/lib/phase-manifest.cjs +0 -527
- package/pp-core/bin/lib/phase-tier.cjs +0 -480
- package/pp-core/bin/lib/plan-graph.cjs +0 -368
- package/pp-core/bin/lib/practices-review-check.cjs +0 -938
- package/pp-core/bin/lib/vault-audit.cjs +0 -2070
- package/pp-core/bin/lib/vault-practices.cjs +0 -1990
- package/pp-core/bin/lib/vault-review.cjs +0 -2359
- package/pp-core/bin/lib/vault-write-back.cjs +0 -877
- package/pp-core/references/executor-vault-trace.md +0 -47
- package/pp-core/references/planner-plan-graph.md +0 -66
- package/pp-core/references/planner-vault-practices.md +0 -112
- package/pp-core/templates/practices/README.md +0 -251
- package/pp-core/templates/practices/error-handling.md +0 -18
- package/pp-core/templates/practices/minimal-comments.md +0 -14
- package/pp-core/templates/practices/naming-convention.md +0 -18
- package/pp-core/templates/practices/practice-template.md +0 -23
- package/pp-core/workflows/discovery-sprint/vault-fetch.md +0 -37
- package/pp-core/workflows/execute-phase/steps/checkpoint-handling.md +0 -45
- package/pp-core/workflows/execute-phase/steps/install-run.md +0 -29
- package/pp-core/workflows/execute-phase/steps/phase-close-rebaseline.md +0 -23
- package/pp-core/workflows/execute-phase/steps/serial-group-dispatch.md +0 -73
- package/pp-core/workflows/execute-phase/steps/vault-door-gate.md +0 -19
- package/pp-core/workflows/plan-phase/steps/phase-tier.md +0 -43
- package/scripts/lib/size-budget.cjs +0 -311
- package/scripts/lint-neutrality-names.cjs +0 -694
- package/scripts/lint-org-coordinates.allowlist.json +0 -35
- 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 `
|
|
16
|
-
* repo build stamps `createdWith`
|
|
17
|
-
*
|
|
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
|
package/scripts/sync-rulesets.sh
CHANGED
|
@@ -4,309 +4,65 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* @file update-size-baseline.cjs
|
|
6
6
|
*
|
|
7
|
-
* Regenerates the committed per-file size
|
|
8
|
-
* (`tests/workflow-size-baseline.json
|
|
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
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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
|
-
*
|
|
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,
|
|
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
|
|
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:
|
|
83
|
-
*
|
|
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
|
|
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
|
-
*
|
|
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
|
|
175
|
-
* @param {function(string): boolean} [opts.predicate] -
|
|
176
|
-
* @
|
|
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
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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/
|
|
14
|
-
* docs/
|
|
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
|
-
*
|
|
40
|
-
*
|
|
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
|
|
58
|
-
* (`
|
|
59
|
-
* baseline generator share one measurement path (#1074).
|
|
60
|
-
* Non-recursive by design
|
|
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
|
-
*
|
|
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
|
|
78
|
+
* @returns {Object<string, number>} Map of `<stem>.md` → LF byte size, sorted.
|
|
129
79
|
*/
|
|
130
80
|
function measureWorkflows(dir = WORKFLOWS_DIR) {
|
|
131
|
-
return
|
|
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
|
};
|