@pareto4/pp-core 1.12.1 → 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.
- package/.claude-plugin/plugin.json +3 -3
- package/README.md +26 -9
- package/agents/pp-code-reviewer.md +3 -2
- package/agents/pp-debug-session-manager.md +9 -6
- package/agents/pp-executor.md +7 -21
- package/agents/pp-plan-checker.md +4 -4
- package/agents/pp-planner.md +23 -9
- package/agents/pp-vault-reviewer.md +274 -0
- package/agents/pp-verifier.md +7 -3
- package/bin/install.js +10 -3
- package/commands/pp-execute-phase.md +1 -3
- package/commands/pp-ns-review.md +2 -1
- package/commands/pp-plan-phase.md +1 -2
- package/commands/pp-vault-review.md +35 -0
- 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 +14 -19
- 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 +526 -22
- package/pp-core/bin/lib/capability-state.cjs +160 -4
- package/pp-core/bin/lib/capability-writer.cjs +19 -5
- package/pp-core/bin/lib/check-command-router.cjs +3867 -274
- package/pp-core/bin/lib/clusters.cjs +4 -0
- package/pp-core/bin/lib/command-aliases.cjs +0 -28
- package/pp-core/bin/lib/commands.cjs +9 -4
- package/pp-core/bin/lib/compose-bindings.cjs +253 -76
- 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 +154 -62
- package/pp-core/bin/lib/decisions.cjs +104 -27
- package/pp-core/bin/lib/discovery-vault-check.cjs +246 -0
- package/pp-core/bin/lib/frontmatter.cjs +1 -1
- package/pp-core/bin/lib/git-base-branch.cjs +138 -10
- package/pp-core/bin/lib/git-provider.cjs +4 -4
- package/pp-core/bin/lib/init-command-router.cjs +5 -8
- package/pp-core/bin/lib/init-contract.cjs +653 -629
- package/pp-core/bin/lib/init-interactive.cjs +77 -21
- 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 +8 -0
- package/pp-core/bin/lib/loop-resolver.cjs +169 -47
- package/pp-core/bin/lib/org-profile.cjs +544 -53
- package/pp-core/bin/lib/package-identity.cjs +3 -3
- package/pp-core/bin/lib/phase-command-router.cjs +1 -23
- package/pp-core/bin/lib/phase-scope.cjs +1 -1
- package/pp-core/bin/lib/phase.cjs +11 -203
- package/pp-core/bin/lib/platform-bindings.cjs +40 -19
- package/pp-core/bin/lib/practices-review-check.cjs +15 -9
- 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-audit.cjs +2594 -0
- package/pp-core/bin/lib/vault-command-router.cjs +2756 -60
- package/pp-core/bin/lib/vault-coordinates.cjs +13 -4
- package/pp-core/bin/lib/vault-index.cjs +699 -0
- package/pp-core/bin/lib/vault-mode.cjs +235 -0
- package/pp-core/bin/lib/vault-practices.cjs +1194 -89
- package/pp-core/bin/lib/vault-review.cjs +2102 -0
- package/pp-core/bin/lib/vault-seal.cjs +1936 -0
- package/pp-core/bin/lib/vault-standards.cjs +1505 -0
- package/pp-core/bin/lib/vault-trace.cjs +3184 -0
- package/pp-core/bin/lib/vault-update.cjs +242 -0
- package/pp-core/bin/lib/vault-write-back.cjs +78 -31
- package/pp-core/bin/lib/vault.cjs +2713 -634
- 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 +184 -37
- 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 +1 -0
- 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 -14
- 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-practices.md +31 -3
- package/pp-core/references/planner-vault-standards.md +320 -0
- package/pp-core/references/planning-config.md +0 -8
- package/pp-core/references/practice-arithmetic-contract.md +121 -0
- package/pp-core/references/tdd.md +0 -1
- package/pp-core/references/verifier-practice-verdicts.md +263 -0
- package/pp-core/templates/README.md +1 -0
- package/pp-core/templates/VAULT-REVIEW.md +202 -0
- package/pp-core/templates/phase-prompt.md +2 -6
- package/pp-core/templates/practices/README.md +137 -11
- package/pp-core/templates/project-charter.md +226 -0
- package/pp-core/workflows/_runtime-launcher.snippet.sh +0 -0
- package/pp-core/workflows/add-tests.md +55 -0
- package/pp-core/workflows/audit-fix.md +63 -0
- 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 -1
- package/pp-core/workflows/code-review.md +10 -1
- package/pp-core/workflows/complete-milestone.md +1 -1
- package/pp-core/workflows/debug.md +56 -2
- package/pp-core/workflows/discovery-sprint/prefill.md +21 -0
- package/pp-core/workflows/discovery-sprint/prototype.md +46 -48
- 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 +213 -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 +9 -1
- package/pp-core/workflows/discuss-phase.md +3 -10
- package/pp-core/workflows/execute-phase/steps/check-query-gate.md +329 -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 +98 -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 +322 -18
- package/pp-core/workflows/execute-phase/steps/verifier-practice-payload.md +165 -0
- package/pp-core/workflows/execute-phase/steps/verify-post-gate.md +163 -0
- package/pp-core/workflows/execute-phase.md +154 -110
- package/pp-core/workflows/execute-plan.md +58 -4
- package/pp-core/workflows/fast.md +53 -0
- package/pp-core/workflows/help/modes/full.md +1 -0
- 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 +99 -43
- package/pp-core/workflows/quick.md +120 -2
- package/pp-core/workflows/secure-phase.md +1 -1
- package/pp-core/workflows/ship.md +57 -24
- package/pp-core/workflows/update.md +3 -3
- package/pp-core/workflows/validate-phase.md +60 -1
- package/pp-core/workflows/vault-review.md +325 -0
- package/pp-core/workflows/verify-work.md +13 -12
- 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 +199 -0
- package/scripts/gen-practice-arithmetic-contract.cjs +412 -0
- 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 +13 -2
- package/scripts/practice-arithmetic-contract.allow.json +35 -0
- package/scripts/prompt-injection-scan.sh +22 -15
- package/scripts/record-vault-mode-golden.cjs +89 -0
- 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/vault-calibrate.cjs +996 -0
- package/scripts/verify-opengsd-sweep.cjs +3 -5
- package/scripts/workflow-size.cjs +9 -61
- package/pp-core/bin/lib/dispatch-plan.cjs +0 -472
- package/pp-core/bin/lib/install-run.cjs +0 -536
- package/pp-core/bin/lib/phase-tier.cjs +0 -467
- package/pp-core/bin/lib/plan-graph.cjs +0 -368
- package/pp-core/references/planner-plan-graph.md +0 -66
- 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 -72
- package/pp-core/workflows/plan-phase/steps/phase-tier.md +0 -34
- 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
|
@@ -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
|
};
|
|
@@ -1,472 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Dispatch plan — the single owner of what execute-phase spawns and runs.
|
|
4
|
-
*
|
|
5
|
-
* From the plan inventory (`phase-plan-index`: waves and serial groups from
|
|
6
|
-
* src/plan-graph.cts) and the project's isolation settings, it computes, per
|
|
7
|
-
* wave and in order:
|
|
8
|
-
*
|
|
9
|
-
* - the executor spawns: ONE spawn per serial group, on the main checkout
|
|
10
|
-
* (no worktree), listing its plans in run order; one spawn per other plan,
|
|
11
|
-
* isolated in a worktree unless worktrees are off or the plan touches a
|
|
12
|
-
* git submodule path (the rule execute-phase applied per plan before);
|
|
13
|
-
* - whether each spawn needs a build: only when one of its plans touches a
|
|
14
|
-
* source file (`isSourceFile`, the plan-graph predicate);
|
|
15
|
-
* - the merge step, then exactly ONE full-suite run by the orchestrator after
|
|
16
|
-
* the merge, then exactly ONE STATE/ROADMAP/REQUIREMENTS tracking commit by
|
|
17
|
-
* the orchestrator;
|
|
18
|
-
* - planning defects: a source file shared by two plans of one wave, or any
|
|
19
|
-
* file shared by two plans in different spawns. A wave with a defect runs
|
|
20
|
-
* its spawns one at a time.
|
|
21
|
-
*
|
|
22
|
-
* Totals: spawn count, suite-run count, tracking-commit count, build count and
|
|
23
|
-
* the SUMMARY.md files the phase is expected to produce.
|
|
24
|
-
*
|
|
25
|
-
* There is no spawn trace: workflows are markdown a model follows. This plan
|
|
26
|
-
* is what execute-phase spawns ("spawn exactly the listed spawns"), so it is
|
|
27
|
-
* the dry run that tests count.
|
|
28
|
-
*
|
|
29
|
-
* ## Stages
|
|
30
|
-
*
|
|
31
|
-
* Output is shaped as `stages: { plan, execute, verify }`. `stages.plan` and
|
|
32
|
-
* `stages.verify` come from the phase's resolved ceremony tier
|
|
33
|
-
* (`resolvePhaseTier` in src/phase-tier.cts, laid out by `computeTierStages`
|
|
34
|
-
* here): researcher, pattern mapper,
|
|
35
|
-
* planner (always) and plan checker passes; then code reviewer and fixer,
|
|
36
|
-
* the heavy-tier install run (`phase.install-run`, src/install-run.cts),
|
|
37
|
-
* verifier, security auditor and nyquist auditor. `totals.spawns` counts every agent spawn of every stage, as an upper
|
|
38
|
-
* bound: entries marked `conditional` run only when the step before them asks
|
|
39
|
-
* for it (a revision after a failed check, a fix after review findings).
|
|
40
|
-
*/
|
|
41
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
42
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
43
|
-
};
|
|
44
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
-
exports.STAGE_AGENTS = void 0;
|
|
46
|
-
exports.computeTierStages = computeTierStages;
|
|
47
|
-
exports.submoduleIntersection = submoduleIntersection;
|
|
48
|
-
exports.parseGitmodulesPaths = parseGitmodulesPaths;
|
|
49
|
-
exports.computeDispatchPlan = computeDispatchPlan;
|
|
50
|
-
exports.parseDispatchPlanArgs = parseDispatchPlanArgs;
|
|
51
|
-
exports.resolveDispatchPolicy = resolveDispatchPolicy;
|
|
52
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
53
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
54
|
-
const plan_graph_cjs_1 = require("./plan-graph.cjs");
|
|
55
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports -- planning-workspace.cjs is an export= CommonJS module
|
|
56
|
-
const planningWorkspace = require("./planning-workspace.cjs");
|
|
57
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports -- config-loader.cjs is an export= CommonJS module
|
|
58
|
-
const configLoaderMod = require("./config-loader.cjs");
|
|
59
|
-
const { planningDir } = planningWorkspace;
|
|
60
|
-
const { loadConfig } = configLoaderMod;
|
|
61
|
-
/** Agent type names the stages spawn (each is a file under agents/). */
|
|
62
|
-
exports.STAGE_AGENTS = Object.freeze({
|
|
63
|
-
researcher: 'pp-phase-researcher',
|
|
64
|
-
patternMapper: 'pp-pattern-mapper',
|
|
65
|
-
planner: 'pp-planner',
|
|
66
|
-
planChecker: 'pp-plan-checker',
|
|
67
|
-
verifier: 'pp-verifier',
|
|
68
|
-
codeReviewer: 'pp-code-reviewer',
|
|
69
|
-
codeFixer: 'pp-code-fixer',
|
|
70
|
-
nyquistAuditor: 'pp-nyquist-auditor',
|
|
71
|
-
securityAuditor: 'pp-security-auditor',
|
|
72
|
-
});
|
|
73
|
-
function spawn(id, agent, gate, reason, conditional = false) {
|
|
74
|
-
return { id, kind: 'spawn', agent, gate, reason, conditional };
|
|
75
|
-
}
|
|
76
|
-
function step(id, gate, reason) {
|
|
77
|
-
return { id, kind: 'step', agent: null, gate, reason, conditional: false };
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* The planning and verification stages of a phase, from its resolved tier.
|
|
81
|
-
* `planBounceScript`: bounce runs only with a configured script.
|
|
82
|
-
*/
|
|
83
|
-
function computeTierStages(tier, opts = {}) {
|
|
84
|
-
const g = tier.gates;
|
|
85
|
-
const plan = [];
|
|
86
|
-
if (g.research)
|
|
87
|
-
plan.push(spawn('plan-research', exports.STAGE_AGENTS.researcher, 'research', 'workflow.research is on for this tier'));
|
|
88
|
-
if (g.pattern_mapper)
|
|
89
|
-
plan.push(spawn('plan-patterns', exports.STAGE_AGENTS.patternMapper, 'pattern_mapper', 'workflow.pattern_mapper is on for this tier'));
|
|
90
|
-
plan.push(spawn('plan-planner', exports.STAGE_AGENTS.planner, 'planner', 'the planner always runs'));
|
|
91
|
-
if (g.plan_check) {
|
|
92
|
-
for (let pass = 1; pass <= g.plan_check_passes; pass++) {
|
|
93
|
-
if (pass > 1) {
|
|
94
|
-
plan.push(spawn(`plan-revise-${pass - 1}`, exports.STAGE_AGENTS.planner, 'plan_check', `revision ${pass - 1}: only when check pass ${pass - 1} found issues`, true));
|
|
95
|
-
}
|
|
96
|
-
plan.push(spawn(`plan-check-${pass}`, exports.STAGE_AGENTS.planChecker, 'plan_check', pass === 1 ? `check pass 1 of at most ${g.plan_check_passes}` : `check pass ${pass} of at most ${g.plan_check_passes}: after revision ${pass - 1}`, pass > 1));
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
if (g.plan_bounce && opts.planBounceScript) {
|
|
100
|
-
plan.push(step('plan-bounce', 'plan_bounce', `refine plans through workflow.plan_bounce_script, ${g.plan_bounce_passes} pass(es)`));
|
|
101
|
-
if (g.plan_check)
|
|
102
|
-
plan.push(spawn('plan-bounce-check', exports.STAGE_AGENTS.planChecker, 'plan_bounce', 're-check bounced plans; a plan that fails is restored', true));
|
|
103
|
-
}
|
|
104
|
-
if (g.post_planning_gaps)
|
|
105
|
-
plan.push(step('plan-gaps', 'post_planning_gaps', 'post-planning gap report (plan:post), non-blocking'));
|
|
106
|
-
const verify = [];
|
|
107
|
-
if (g.code_review) {
|
|
108
|
-
verify.push(spawn('verify-code-review', exports.STAGE_AGENTS.codeReviewer, 'code_review', 'code review of the phase source changes'));
|
|
109
|
-
verify.push(spawn('verify-code-fix', exports.STAGE_AGENTS.codeFixer, 'code_review', 'fix loop: only when the review finds issues', true));
|
|
110
|
-
}
|
|
111
|
-
// The install run goes first: the verifier judges the phase on its results file.
|
|
112
|
-
if (g.install_verification)
|
|
113
|
-
verify.push(step('verify-install', 'install_verification', 'pack, install into a temp project, init and run the phase install checks against the installed copy (phase.install-run → NN-INSTALL-RUN.json)'));
|
|
114
|
-
if (g.verifier)
|
|
115
|
-
verify.push(spawn('verify-goal', exports.STAGE_AGENTS.verifier, 'verifier', 'goal-backward phase verification (VERIFICATION.md)'));
|
|
116
|
-
if (g.security_enforcement)
|
|
117
|
-
verify.push(spawn('verify-security', exports.STAGE_AGENTS.securityAuditor, 'security_enforcement', 'threat-model verification (SECURITY.md)'));
|
|
118
|
-
if (g.nyquist_validation)
|
|
119
|
-
verify.push(spawn('verify-nyquist', exports.STAGE_AGENTS.nyquistAuditor, 'nyquist_validation', 'validation-gap audit (VALIDATION.md)'));
|
|
120
|
-
return { plan, verify };
|
|
121
|
-
}
|
|
122
|
-
function normalizePath(file) {
|
|
123
|
-
return String(file).trim().replace(/\\/g, '/').replace(/^\.\//, '');
|
|
124
|
-
}
|
|
125
|
-
function stripEnds(p) {
|
|
126
|
-
return p.replace(/^\.\//, '').replace(/\/$/, '');
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Planned paths that touch a submodule (the per-plan worktree rule, #2772):
|
|
130
|
-
* either side normalised (leading `./`, one trailing `/`), matched both ways
|
|
131
|
-
* (planned path inside the submodule, or submodule inside the planned path),
|
|
132
|
-
* and a globbed planned path matched by its literal prefix before the first
|
|
133
|
-
* `*`, `?` or `[`.
|
|
134
|
-
*/
|
|
135
|
-
function submoduleIntersection(planFiles, submodulePaths) {
|
|
136
|
-
const hits = new Set();
|
|
137
|
-
const inside = (a, b) => a === b || a.startsWith(`${b}/`);
|
|
138
|
-
for (const smRaw of submodulePaths) {
|
|
139
|
-
const sm = stripEnds(smRaw.trim());
|
|
140
|
-
if (sm === '')
|
|
141
|
-
continue;
|
|
142
|
-
for (const pfRaw of planFiles) {
|
|
143
|
-
const pf = stripEnds(String(pfRaw).trim());
|
|
144
|
-
if (pf === '')
|
|
145
|
-
continue;
|
|
146
|
-
let matched = inside(pf, sm) || inside(sm, pf);
|
|
147
|
-
if (!matched && /[*?[]/.test(pf)) {
|
|
148
|
-
const prefix = pf.slice(0, pf.search(/[*?[]/)).replace(/\/$/, '');
|
|
149
|
-
matched = prefix !== '' && (inside(sm, prefix) || inside(prefix, sm));
|
|
150
|
-
}
|
|
151
|
-
if (matched)
|
|
152
|
-
hits.add(String(pfRaw).trim());
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
return [...hits];
|
|
156
|
-
}
|
|
157
|
-
/** Submodule paths declared in a `.gitmodules` file's text. */
|
|
158
|
-
function parseGitmodulesPaths(text) {
|
|
159
|
-
const paths = [];
|
|
160
|
-
let inSubmodule = false;
|
|
161
|
-
for (const line of text.split(/\r?\n/)) {
|
|
162
|
-
const section = /^\s*\[\s*([^\s\]]+)/.exec(line);
|
|
163
|
-
if (section) {
|
|
164
|
-
inSubmodule = section[1].toLowerCase() === 'submodule';
|
|
165
|
-
continue;
|
|
166
|
-
}
|
|
167
|
-
const kv = /^\s*path\s*=\s*(.+?)\s*$/i.exec(line);
|
|
168
|
-
if (inSubmodule && kv)
|
|
169
|
-
paths.push(kv[1].replace(/^"(.*)"$/, '$1'));
|
|
170
|
-
}
|
|
171
|
-
return paths;
|
|
172
|
-
}
|
|
173
|
-
function isolationFor(plan, policy) {
|
|
174
|
-
if (!policy.useWorktrees) {
|
|
175
|
-
return { isolation: 'main-checkout', reason: `worktrees off (workflow.use_worktrees=false, from ${policy.useWorktreesSource})` };
|
|
176
|
-
}
|
|
177
|
-
if (policy.submodulePaths.length > 0) {
|
|
178
|
-
const files = plan.files_modified.map((f) => String(f).trim()).filter((f) => f !== '');
|
|
179
|
-
if (files.length === 0) {
|
|
180
|
-
return { isolation: 'main-checkout', reason: 'files_modified missing/unparseable in a submodule project — no worktree, as a safety fallback' };
|
|
181
|
-
}
|
|
182
|
-
const hits = submoduleIntersection(files, policy.submodulePaths);
|
|
183
|
-
if (hits.length > 0) {
|
|
184
|
-
return { isolation: 'main-checkout', reason: `planned paths intersect submodule paths (${hits.join(' ')})` };
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
return { isolation: 'worktree', reason: 'independent plan: its own worktree' };
|
|
188
|
-
}
|
|
189
|
-
function summaryPath(phaseDir, id) {
|
|
190
|
-
return `${phaseDir}/${id === '' ? '' : `${id}-`}SUMMARY.md`;
|
|
191
|
-
}
|
|
192
|
-
function planEntry(plan, phaseDir) {
|
|
193
|
-
const files = plan.files_modified.map(normalizePath).filter((f) => f !== '');
|
|
194
|
-
return {
|
|
195
|
-
id: plan.id,
|
|
196
|
-
title: plan.title,
|
|
197
|
-
build: files.length === 0 || files.some(plan_graph_cjs_1.isSourceFile),
|
|
198
|
-
touched_tests: files.filter((f) => (0, plan_graph_cjs_1.classifyPlanFile)(f) === 'test'),
|
|
199
|
-
summary: summaryPath(phaseDir, plan.id),
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
function buildReason(plans) {
|
|
203
|
-
const reasons = [];
|
|
204
|
-
for (const p of plans) {
|
|
205
|
-
const files = p.files_modified.map(normalizePath).filter((f) => f !== '');
|
|
206
|
-
if (files.length === 0)
|
|
207
|
-
reasons.push(`${p.id}: files_modified not declared`);
|
|
208
|
-
else {
|
|
209
|
-
const src = files.filter(plan_graph_cjs_1.isSourceFile);
|
|
210
|
-
if (src.length > 0)
|
|
211
|
-
reasons.push(`${p.id} touches ${src.join(', ')}`);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
return reasons.length > 0 ? reasons.join('; ') : 'no plan touches a source file';
|
|
215
|
-
}
|
|
216
|
-
function waveDefects(spawns, byId) {
|
|
217
|
-
const members = [];
|
|
218
|
-
for (const s of spawns) {
|
|
219
|
-
for (const p of s.plans) {
|
|
220
|
-
const plan = byId.get(p.id);
|
|
221
|
-
members.push({ id: p.id, spawn: s.id, files: new Set(plan.files_modified.map(normalizePath).filter((f) => f !== '')) });
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
const defects = [];
|
|
225
|
-
for (let i = 0; i < members.length; i++) {
|
|
226
|
-
for (let j = i + 1; j < members.length; j++) {
|
|
227
|
-
const a = members[i];
|
|
228
|
-
const b = members[j];
|
|
229
|
-
const shared = [...a.files].filter((f) => b.files.has(f)).sort();
|
|
230
|
-
if (shared.length === 0)
|
|
231
|
-
continue;
|
|
232
|
-
const source = shared.filter(plan_graph_cjs_1.isSourceFile);
|
|
233
|
-
if (source.length > 0) {
|
|
234
|
-
defects.push({
|
|
235
|
-
kind: 'source-file-overlap',
|
|
236
|
-
plans: [a.id, b.id],
|
|
237
|
-
files: source,
|
|
238
|
-
message: `Plan ${a.id} and Plan ${b.id} both modify source file(s) ${source.join(', ')} in one wave — a shared source file must order the plans into separate waves (planning defect).`,
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
else if (a.spawn !== b.spawn) {
|
|
242
|
-
defects.push({
|
|
243
|
-
kind: 'cross-spawn-overlap',
|
|
244
|
-
plans: [a.id, b.id],
|
|
245
|
-
files: shared,
|
|
246
|
-
message: `Plan ${a.id} and Plan ${b.id} both modify ${shared.join(', ')} but run in different spawns — they belong in one serial group (planning defect).`,
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
return defects;
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Compute the dispatch plan. `plans` are `phase-plan-index` rows in plan file
|
|
255
|
-
* order; `phaseDir` is the phase directory relative to the project root.
|
|
256
|
-
*/
|
|
257
|
-
function computeDispatchPlan(phase, phaseDir, plans, policy, tier = null, tierOpts = {}) {
|
|
258
|
-
const byId = new Map(plans.map((p) => [p.id, p]));
|
|
259
|
-
const skippedComplete = plans.filter((p) => p.has_summary).map((p) => p.id);
|
|
260
|
-
const incomplete = plans.filter((p) => !p.has_summary && (!policy.gapsOnly || p.gap_closure === true));
|
|
261
|
-
const selected = incomplete.filter((p) => policy.waveFilter === null || p.wave === policy.waveFilter);
|
|
262
|
-
const selectedIds = new Set(selected.map((p) => p.id));
|
|
263
|
-
const skippedByFilter = plans.filter((p) => !p.has_summary && !selectedIds.has(p.id)).map((p) => p.id);
|
|
264
|
-
const lowerWaveIncomplete = policy.waveFilter === null
|
|
265
|
-
? []
|
|
266
|
-
: incomplete.filter((p) => p.wave < policy.waveFilter).map((p) => p.id);
|
|
267
|
-
const waveNumbers = [...new Set(selected.map((p) => p.wave))].sort((a, b) => a - b);
|
|
268
|
-
const waves = [];
|
|
269
|
-
for (const n of waveNumbers) {
|
|
270
|
-
const inWave = selected.filter((p) => p.wave === n);
|
|
271
|
-
const groupCounts = new Map();
|
|
272
|
-
for (const p of inWave)
|
|
273
|
-
if (p.serial_group)
|
|
274
|
-
groupCounts.set(p.serial_group, (groupCounts.get(p.serial_group) ?? 0) + 1);
|
|
275
|
-
const spawns = [];
|
|
276
|
-
const placedGroups = new Set();
|
|
277
|
-
for (const p of inWave) {
|
|
278
|
-
const group = p.serial_group && (groupCounts.get(p.serial_group) ?? 0) >= 2 ? p.serial_group : null;
|
|
279
|
-
if (group !== null) {
|
|
280
|
-
if (placedGroups.has(group))
|
|
281
|
-
continue;
|
|
282
|
-
placedGroups.add(group);
|
|
283
|
-
const members = inWave.filter((q) => q.serial_group === group);
|
|
284
|
-
const entries = members.map((q) => planEntry(q, phaseDir));
|
|
285
|
-
spawns.push({
|
|
286
|
-
id: `w${n}-s${spawns.length + 1}`,
|
|
287
|
-
agent: 'pp-executor',
|
|
288
|
-
kind: 'serial-group',
|
|
289
|
-
serial_group: group,
|
|
290
|
-
plans: entries,
|
|
291
|
-
isolation: 'main-checkout',
|
|
292
|
-
isolation_reason: `serial group ${group}: one executor runs its ${members.length} plans in order on the main checkout`,
|
|
293
|
-
build: entries.some((e) => e.build),
|
|
294
|
-
build_reason: buildReason(members),
|
|
295
|
-
checkpoint: members.some((q) => !q.autonomous),
|
|
296
|
-
expected_summaries: entries.map((e) => e.summary),
|
|
297
|
-
});
|
|
298
|
-
continue;
|
|
299
|
-
}
|
|
300
|
-
const entry = planEntry(p, phaseDir);
|
|
301
|
-
const iso = isolationFor(p, policy);
|
|
302
|
-
spawns.push({
|
|
303
|
-
id: `w${n}-s${spawns.length + 1}`,
|
|
304
|
-
agent: 'pp-executor',
|
|
305
|
-
kind: 'plan',
|
|
306
|
-
serial_group: null,
|
|
307
|
-
plans: [entry],
|
|
308
|
-
isolation: iso.isolation,
|
|
309
|
-
isolation_reason: iso.reason,
|
|
310
|
-
build: entry.build,
|
|
311
|
-
build_reason: buildReason([p]),
|
|
312
|
-
checkpoint: !p.autonomous,
|
|
313
|
-
expected_summaries: [entry.summary],
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
const defects = waveDefects(spawns, byId);
|
|
317
|
-
const worktreeSpawns = spawns.filter((s) => s.isolation === 'worktree').length;
|
|
318
|
-
const run = defects.length > 0 || !policy.parallelization || spawns.length < 2 ? 'sequential' : 'parallel';
|
|
319
|
-
const runReason = defects.length > 0
|
|
320
|
-
? 'planning defect: overlapping files_modified in this wave — run every spawn one at a time'
|
|
321
|
-
: !policy.parallelization
|
|
322
|
-
? 'parallelization is off'
|
|
323
|
-
: spawns.length < 2
|
|
324
|
-
? 'one spawn'
|
|
325
|
-
: 'worktree spawns run alongside each other; main-checkout spawns run one at a time';
|
|
326
|
-
const requirements = [...new Set(inWave.flatMap((p) => p.requirements ?? []))];
|
|
327
|
-
const expected = spawns.flatMap((s) => s.expected_summaries);
|
|
328
|
-
waves.push({
|
|
329
|
-
wave: n,
|
|
330
|
-
run,
|
|
331
|
-
run_reason: runReason,
|
|
332
|
-
spawns,
|
|
333
|
-
merge: { worktree_spawns: worktreeSpawns, cleanup: worktreeSpawns > 0 },
|
|
334
|
-
build: spawns.some((s) => s.build),
|
|
335
|
-
suite_runs: 1,
|
|
336
|
-
suite: { by: 'orchestrator', after: 'merge' },
|
|
337
|
-
tracking_commits: 1,
|
|
338
|
-
tracking: { by: 'orchestrator', plans: inWave.map((p) => p.id), requirements },
|
|
339
|
-
expected_summaries: expected,
|
|
340
|
-
defects,
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
const stages = tier ? computeTierStages(tier, tierOpts) : { plan: [], verify: [] };
|
|
344
|
-
const stagePlan = stages.plan;
|
|
345
|
-
const stageVerify = stages.verify;
|
|
346
|
-
const planSpawns = stagePlan.filter((e) => e.kind === 'spawn').length;
|
|
347
|
-
const verifySpawns = stageVerify.filter((e) => e.kind === 'spawn').length;
|
|
348
|
-
const executorSpawns = waves.flatMap((w) => w.spawns);
|
|
349
|
-
return {
|
|
350
|
-
phase,
|
|
351
|
-
phase_dir: phaseDir,
|
|
352
|
-
tier: tier ? { tier: tier.tier, source: tier.source, suppressed: tier.suppressed, gates: tier.gates } : null,
|
|
353
|
-
policy: {
|
|
354
|
-
use_worktrees: policy.useWorktrees,
|
|
355
|
-
use_worktrees_source: policy.useWorktreesSource,
|
|
356
|
-
parallelization: policy.parallelization,
|
|
357
|
-
submodule_paths: policy.submodulePaths,
|
|
358
|
-
},
|
|
359
|
-
filters: {
|
|
360
|
-
wave: policy.waveFilter,
|
|
361
|
-
gaps_only: policy.gapsOnly,
|
|
362
|
-
skipped_complete: skippedComplete,
|
|
363
|
-
skipped_by_filter: skippedByFilter,
|
|
364
|
-
lower_wave_incomplete: lowerWaveIncomplete,
|
|
365
|
-
},
|
|
366
|
-
stages: { plan: stagePlan, execute: { waves }, verify: stageVerify },
|
|
367
|
-
totals: {
|
|
368
|
-
waves: waves.length,
|
|
369
|
-
spawns: planSpawns + executorSpawns.length + verifySpawns,
|
|
370
|
-
plan_spawns: planSpawns,
|
|
371
|
-
verify_spawns: verifySpawns,
|
|
372
|
-
executor_spawns: executorSpawns.length,
|
|
373
|
-
main_checkout_spawns: executorSpawns.filter((s) => s.isolation === 'main-checkout').length,
|
|
374
|
-
isolated_spawns: executorSpawns.filter((s) => s.isolation === 'worktree').length,
|
|
375
|
-
builds: executorSpawns.filter((s) => s.build).length,
|
|
376
|
-
suite_runs: waves.reduce((sum, w) => sum + w.suite_runs, 0),
|
|
377
|
-
tracking_commits: waves.reduce((sum, w) => sum + w.tracking_commits, 0),
|
|
378
|
-
expected_summaries: waves.flatMap((w) => w.expected_summaries),
|
|
379
|
-
defects: waves.reduce((sum, w) => sum + w.defects.length, 0),
|
|
380
|
-
},
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
/** `<phase> [--wave N] [--gaps-only] [--use-worktrees true|false] [--tier light|standard|heavy]`. */
|
|
384
|
-
function parseDispatchPlanArgs(args) {
|
|
385
|
-
const usage = 'Usage: phase dispatch-plan <phase> [--wave N] [--gaps-only] [--use-worktrees true|false] [--tier light|standard|heavy]';
|
|
386
|
-
let phase = null;
|
|
387
|
-
let waveFilter = null;
|
|
388
|
-
let gapsOnly = false;
|
|
389
|
-
let useWorktrees = null;
|
|
390
|
-
let tier = null;
|
|
391
|
-
for (let i = 0; i < args.length; i++) {
|
|
392
|
-
const a = args[i];
|
|
393
|
-
if (a === '--raw')
|
|
394
|
-
continue;
|
|
395
|
-
if (a === '--gaps-only') {
|
|
396
|
-
gapsOnly = true;
|
|
397
|
-
continue;
|
|
398
|
-
}
|
|
399
|
-
if (a === '--wave') {
|
|
400
|
-
const v = args[++i];
|
|
401
|
-
if (v === undefined || !/^\d+$/.test(v))
|
|
402
|
-
return { error: `--wave needs a wave number. ${usage}` };
|
|
403
|
-
waveFilter = parseInt(v, 10);
|
|
404
|
-
continue;
|
|
405
|
-
}
|
|
406
|
-
if (a === '--use-worktrees') {
|
|
407
|
-
const v = args[++i];
|
|
408
|
-
if (v !== 'true' && v !== 'false')
|
|
409
|
-
return { error: `--use-worktrees needs true or false. ${usage}` };
|
|
410
|
-
useWorktrees = v === 'true';
|
|
411
|
-
continue;
|
|
412
|
-
}
|
|
413
|
-
if (a === '--tier') {
|
|
414
|
-
const v = args[++i];
|
|
415
|
-
if (v !== 'light' && v !== 'standard' && v !== 'heavy')
|
|
416
|
-
return { error: `--tier needs light, standard or heavy. ${usage}` };
|
|
417
|
-
tier = v;
|
|
418
|
-
continue;
|
|
419
|
-
}
|
|
420
|
-
if (a.startsWith('--'))
|
|
421
|
-
return { error: `phase dispatch-plan does not support ${a}. ${usage}` };
|
|
422
|
-
if (phase !== null)
|
|
423
|
-
return { error: `phase dispatch-plan takes one phase. ${usage}` };
|
|
424
|
-
phase = a;
|
|
425
|
-
}
|
|
426
|
-
if (!phase)
|
|
427
|
-
return { error: `phase required for phase dispatch-plan. ${usage}` };
|
|
428
|
-
return { phase, waveFilter, gapsOnly, useWorktrees, tier };
|
|
429
|
-
}
|
|
430
|
-
/** Isolation and concurrency settings: CLI flag, then `.planning/config.json`, then defaults. */
|
|
431
|
-
function resolveDispatchPolicy(cwd, args) {
|
|
432
|
-
let raw = {};
|
|
433
|
-
try {
|
|
434
|
-
raw = JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.join(planningDir(cwd), 'config.json'), 'utf-8'));
|
|
435
|
-
}
|
|
436
|
-
catch {
|
|
437
|
-
raw = {};
|
|
438
|
-
}
|
|
439
|
-
const workflow = (raw['workflow'] && typeof raw['workflow'] === 'object' ? raw['workflow'] : {});
|
|
440
|
-
let useWorktrees = true;
|
|
441
|
-
let useWorktreesSource = 'default';
|
|
442
|
-
if (args.useWorktrees !== null) {
|
|
443
|
-
useWorktrees = args.useWorktrees;
|
|
444
|
-
useWorktreesSource = 'cli';
|
|
445
|
-
}
|
|
446
|
-
else if (workflow['use_worktrees'] !== undefined) {
|
|
447
|
-
useWorktrees = workflow['use_worktrees'] !== false && workflow['use_worktrees'] !== 'false';
|
|
448
|
-
useWorktreesSource = 'config';
|
|
449
|
-
}
|
|
450
|
-
let parallelization = true;
|
|
451
|
-
try {
|
|
452
|
-
parallelization = loadConfig(cwd)['parallelization'] !== false;
|
|
453
|
-
}
|
|
454
|
-
catch {
|
|
455
|
-
parallelization = true;
|
|
456
|
-
}
|
|
457
|
-
let submodulePaths = [];
|
|
458
|
-
try {
|
|
459
|
-
submodulePaths = parseGitmodulesPaths(node_fs_1.default.readFileSync(node_path_1.default.join(cwd, '.gitmodules'), 'utf-8'));
|
|
460
|
-
}
|
|
461
|
-
catch {
|
|
462
|
-
submodulePaths = [];
|
|
463
|
-
}
|
|
464
|
-
return {
|
|
465
|
-
useWorktrees,
|
|
466
|
-
useWorktreesSource,
|
|
467
|
-
parallelization,
|
|
468
|
-
submodulePaths,
|
|
469
|
-
waveFilter: args.waveFilter,
|
|
470
|
-
gapsOnly: args.gapsOnly,
|
|
471
|
-
};
|
|
472
|
-
}
|