@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.
Files changed (196) hide show
  1. package/.claude-plugin/plugin.json +3 -3
  2. package/README.md +26 -9
  3. package/agents/pp-code-reviewer.md +3 -2
  4. package/agents/pp-debug-session-manager.md +9 -6
  5. package/agents/pp-executor.md +7 -21
  6. package/agents/pp-plan-checker.md +4 -4
  7. package/agents/pp-planner.md +23 -9
  8. package/agents/pp-vault-reviewer.md +274 -0
  9. package/agents/pp-verifier.md +7 -3
  10. package/bin/install.js +10 -3
  11. package/commands/pp-execute-phase.md +1 -3
  12. package/commands/pp-ns-review.md +2 -1
  13. package/commands/pp-plan-phase.md +1 -2
  14. package/commands/pp-vault-review.md +35 -0
  15. package/commands/pp-verify-work.md +1 -2
  16. package/gemini-extension.json +1 -1
  17. package/hooks/dist/pp-check-update.js +0 -0
  18. package/hooks/dist/pp-cursor-session-start.js +1 -1
  19. package/hooks/dist/pp-statusline.js +1 -1
  20. package/hooks/dist/pp-update-banner.js +0 -0
  21. package/hooks/pp-check-update.js +0 -0
  22. package/hooks/pp-cursor-session-start.js +1 -1
  23. package/hooks/pp-statusline.js +1 -1
  24. package/hooks/pp-update-banner.js +0 -0
  25. package/package.json +14 -19
  26. package/pareto-skills/_shared/create-project-board.sh +0 -0
  27. package/pareto-skills/pp-project-setup/SKILL.md +1 -1
  28. package/pp-core/bin/check-latest-version.cjs +0 -0
  29. package/pp-core/bin/detect-build-commands.sh +2 -2
  30. package/pp-core/bin/lib/capability-activation.cjs +83 -9
  31. package/pp-core/bin/lib/capability-registry.cjs +526 -22
  32. package/pp-core/bin/lib/capability-state.cjs +160 -4
  33. package/pp-core/bin/lib/capability-writer.cjs +19 -5
  34. package/pp-core/bin/lib/check-command-router.cjs +3867 -274
  35. package/pp-core/bin/lib/clusters.cjs +4 -0
  36. package/pp-core/bin/lib/command-aliases.cjs +0 -28
  37. package/pp-core/bin/lib/commands.cjs +9 -4
  38. package/pp-core/bin/lib/compose-bindings.cjs +253 -76
  39. package/pp-core/bin/lib/config-loader.cjs +7 -103
  40. package/pp-core/bin/lib/config-schema.cjs +1 -1
  41. package/pp-core/bin/lib/config.cjs +154 -62
  42. package/pp-core/bin/lib/decisions.cjs +104 -27
  43. package/pp-core/bin/lib/discovery-vault-check.cjs +246 -0
  44. package/pp-core/bin/lib/frontmatter.cjs +1 -1
  45. package/pp-core/bin/lib/git-base-branch.cjs +138 -10
  46. package/pp-core/bin/lib/git-provider.cjs +4 -4
  47. package/pp-core/bin/lib/init-command-router.cjs +5 -8
  48. package/pp-core/bin/lib/init-contract.cjs +653 -629
  49. package/pp-core/bin/lib/init-interactive.cjs +77 -21
  50. package/pp-core/bin/lib/init.cjs +24 -51
  51. package/pp-core/bin/lib/install-profiles.cjs +1 -1
  52. package/pp-core/bin/lib/io.cjs +8 -0
  53. package/pp-core/bin/lib/loop-resolver.cjs +169 -47
  54. package/pp-core/bin/lib/org-profile.cjs +544 -53
  55. package/pp-core/bin/lib/package-identity.cjs +3 -3
  56. package/pp-core/bin/lib/phase-command-router.cjs +1 -23
  57. package/pp-core/bin/lib/phase-scope.cjs +1 -1
  58. package/pp-core/bin/lib/phase.cjs +11 -203
  59. package/pp-core/bin/lib/platform-bindings.cjs +40 -19
  60. package/pp-core/bin/lib/practices-review-check.cjs +15 -9
  61. package/pp-core/bin/lib/profile-output.cjs +1 -1
  62. package/pp-core/bin/lib/shell-command-projection.cjs +104 -1
  63. package/pp-core/bin/lib/state.cjs +11 -85
  64. package/pp-core/bin/lib/template.cjs +1 -1
  65. package/pp-core/bin/lib/vault-audit.cjs +2594 -0
  66. package/pp-core/bin/lib/vault-command-router.cjs +2756 -60
  67. package/pp-core/bin/lib/vault-coordinates.cjs +13 -4
  68. package/pp-core/bin/lib/vault-index.cjs +699 -0
  69. package/pp-core/bin/lib/vault-mode.cjs +235 -0
  70. package/pp-core/bin/lib/vault-practices.cjs +1194 -89
  71. package/pp-core/bin/lib/vault-review.cjs +2102 -0
  72. package/pp-core/bin/lib/vault-seal.cjs +1936 -0
  73. package/pp-core/bin/lib/vault-standards.cjs +1505 -0
  74. package/pp-core/bin/lib/vault-trace.cjs +3184 -0
  75. package/pp-core/bin/lib/vault-update.cjs +242 -0
  76. package/pp-core/bin/lib/vault-write-back.cjs +78 -31
  77. package/pp-core/bin/lib/vault.cjs +2713 -634
  78. package/pp-core/bin/lib/verify.cjs +0 -11
  79. package/pp-core/bin/portable-timeout.sh +1 -1
  80. package/pp-core/bin/pp-tools.cjs +184 -37
  81. package/pp-core/bin/shared/config-defaults.manifest.json +0 -7
  82. package/pp-core/bin/shared/config-schema.manifest.json +0 -3
  83. package/pp-core/bin/shared/model-catalog.json +1 -0
  84. package/pp-core/bin/verify-reapply-patches.cjs +0 -0
  85. package/pp-core/org-profile.json +26 -0
  86. package/pp-core/references/autonomous-best-effort.md +8 -7
  87. package/pp-core/references/loop-hook-dispatch.md +0 -14
  88. package/pp-core/references/planner-gap-closure.md +0 -1
  89. package/pp-core/references/planner-tdd.md +0 -1
  90. package/pp-core/references/planner-vault-practices.md +31 -3
  91. package/pp-core/references/planner-vault-standards.md +320 -0
  92. package/pp-core/references/planning-config.md +0 -8
  93. package/pp-core/references/practice-arithmetic-contract.md +121 -0
  94. package/pp-core/references/tdd.md +0 -1
  95. package/pp-core/references/verifier-practice-verdicts.md +263 -0
  96. package/pp-core/templates/README.md +1 -0
  97. package/pp-core/templates/VAULT-REVIEW.md +202 -0
  98. package/pp-core/templates/phase-prompt.md +2 -6
  99. package/pp-core/templates/practices/README.md +137 -11
  100. package/pp-core/templates/project-charter.md +226 -0
  101. package/pp-core/workflows/_runtime-launcher.snippet.sh +0 -0
  102. package/pp-core/workflows/add-tests.md +55 -0
  103. package/pp-core/workflows/audit-fix.md +63 -0
  104. package/pp-core/workflows/audit-milestone.md +1 -1
  105. package/pp-core/workflows/autonomous.md +85 -11
  106. package/pp-core/workflows/check-todos.md +13 -2
  107. package/pp-core/workflows/code-review-fix.md +59 -1
  108. package/pp-core/workflows/code-review.md +10 -1
  109. package/pp-core/workflows/complete-milestone.md +1 -1
  110. package/pp-core/workflows/debug.md +56 -2
  111. package/pp-core/workflows/discovery-sprint/prefill.md +21 -0
  112. package/pp-core/workflows/discovery-sprint/prototype.md +46 -48
  113. package/pp-core/workflows/discovery-sprint/session-13-gates.md +2 -0
  114. package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +1 -3
  115. package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +1 -3
  116. package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +1 -3
  117. package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +20 -24
  118. package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +1 -3
  119. package/pp-core/workflows/discovery-sprint/sessions/06-security.md +1 -3
  120. package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +1 -3
  121. package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +1 -3
  122. package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +1 -3
  123. package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +1 -6
  124. package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +1 -3
  125. package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +1 -3
  126. package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +1 -3
  127. package/pp-core/workflows/discovery-sprint/templates/context-template.md +1 -1
  128. package/pp-core/workflows/discovery-sprint/vault-context.md +213 -0
  129. package/pp-core/workflows/discovery-sprint.md +7 -3
  130. package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +4 -4
  131. package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +43 -1
  132. package/pp-core/workflows/discovery-to-build.md +9 -1
  133. package/pp-core/workflows/discuss-phase.md +3 -10
  134. package/pp-core/workflows/execute-phase/steps/check-query-gate.md +329 -0
  135. package/pp-core/workflows/execute-phase/steps/client-context-gate.md +3 -2
  136. package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +1 -1
  137. package/pp-core/workflows/execute-phase/steps/execute-pre-dispatch.md +98 -0
  138. package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
  139. package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +6 -8
  140. package/pp-core/workflows/execute-phase/steps/vault-fetch.md +115 -14
  141. package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +322 -18
  142. package/pp-core/workflows/execute-phase/steps/verifier-practice-payload.md +165 -0
  143. package/pp-core/workflows/execute-phase/steps/verify-post-gate.md +163 -0
  144. package/pp-core/workflows/execute-phase.md +154 -110
  145. package/pp-core/workflows/execute-plan.md +58 -4
  146. package/pp-core/workflows/fast.md +53 -0
  147. package/pp-core/workflows/help/modes/full.md +1 -0
  148. package/pp-core/workflows/init/steps/client-context-capture.md +101 -0
  149. package/pp-core/workflows/init/steps/gitlab-provisioning.md +130 -0
  150. package/pp-core/workflows/init/steps/project-md-deferral-narration.md +66 -0
  151. package/pp-core/workflows/init/steps/reachability-outcome.md +70 -0
  152. package/pp-core/workflows/init.md +232 -272
  153. package/pp-core/workflows/new-project.md +31 -23
  154. package/pp-core/workflows/plan-phase.md +99 -43
  155. package/pp-core/workflows/quick.md +120 -2
  156. package/pp-core/workflows/secure-phase.md +1 -1
  157. package/pp-core/workflows/ship.md +57 -24
  158. package/pp-core/workflows/update.md +3 -3
  159. package/pp-core/workflows/validate-phase.md +60 -1
  160. package/pp-core/workflows/vault-review.md +325 -0
  161. package/pp-core/workflows/verify-work.md +13 -12
  162. package/scripts/changeset/cli.cjs +6 -5
  163. package/scripts/changeset/lint.cjs +0 -0
  164. package/scripts/changeset/new.cjs +33 -50
  165. package/scripts/dev-env.cjs +25 -85
  166. package/scripts/gen-capability-registry.cjs +199 -0
  167. package/scripts/gen-practice-arithmetic-contract.cjs +412 -0
  168. package/scripts/lib/allowlist-ratchet.cjs +1 -103
  169. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -2
  170. package/scripts/lint-docs-required.cjs +0 -0
  171. package/scripts/lint-pr-check-project-dir.cjs +1 -3
  172. package/scripts/lint-test-file-count.allowlist.json +13 -2
  173. package/scripts/practice-arithmetic-contract.allow.json +35 -0
  174. package/scripts/prompt-injection-scan.sh +22 -15
  175. package/scripts/record-vault-mode-golden.cjs +89 -0
  176. package/scripts/release-channel.cjs +0 -0
  177. package/scripts/sync-manifest-versions.cjs +5 -4
  178. package/scripts/sync-rulesets.sh +1 -1
  179. package/scripts/update-size-baseline.cjs +30 -274
  180. package/scripts/vault-calibrate.cjs +996 -0
  181. package/scripts/verify-opengsd-sweep.cjs +3 -5
  182. package/scripts/workflow-size.cjs +9 -61
  183. package/pp-core/bin/lib/dispatch-plan.cjs +0 -472
  184. package/pp-core/bin/lib/install-run.cjs +0 -536
  185. package/pp-core/bin/lib/phase-tier.cjs +0 -467
  186. package/pp-core/bin/lib/plan-graph.cjs +0 -368
  187. package/pp-core/references/planner-plan-graph.md +0 -66
  188. package/pp-core/workflows/execute-phase/steps/checkpoint-handling.md +0 -45
  189. package/pp-core/workflows/execute-phase/steps/install-run.md +0 -29
  190. package/pp-core/workflows/execute-phase/steps/phase-close-rebaseline.md +0 -23
  191. package/pp-core/workflows/execute-phase/steps/serial-group-dispatch.md +0 -72
  192. package/pp-core/workflows/plan-phase/steps/phase-tier.md +0 -34
  193. package/scripts/lib/size-budget.cjs +0 -311
  194. package/scripts/lint-neutrality-names.cjs +0 -694
  195. package/scripts/lint-org-coordinates.allowlist.json +0 -35
  196. package/scripts/lint-org-coordinates.cjs +0 -426
@@ -5,9 +5,9 @@
5
5
 
6
6
  const packageName = "@pareto4/pp-core";
7
7
  const binName = "pp-core";
8
- const repoSlug = "pareto4/pp-core";
9
- const repoUrl = "https://gitlab.com/pareto4/pp-core";
10
- const changelogRawUrl = "https://gitlab.com/pareto4/pp-core/-/raw/main/CHANGELOG.md";
8
+ const repoSlug = "pareto000/pp-core";
9
+ const repoUrl = "https://github.com/pareto000/pp-core";
10
+ const changelogRawUrl = "https://raw.githubusercontent.com/pareto000/pp-core/main/CHANGELOG.md";
11
11
  const cacheSlug = "pareto4-pp-core";
12
12
  const updateCacheFileName = "pp-update-check-pareto4-pp-core.json";
13
13
 
@@ -6,7 +6,7 @@
6
6
  * Unsupported in this router:
7
7
  * - scaffold: routed through top-level scaffold command.
8
8
  *
9
- * CJS-only subcommands: mvp-mode, dispatch-plan, install-run, tier, tier-propose (dispatched directly, before hub).
9
+ * CJS-only subcommands: mvp-mode (dispatched directly, before hub).
10
10
  *
11
11
  * #3788: dispatch is mediated by CommandRoutingHub. The public entry point
12
12
  * and observable CLI behaviour are unchanged.
@@ -51,28 +51,6 @@ function routePhaseCommand({ phase, args, cwd, raw, error }) {
51
51
  phase.cmdPhaseMvpMode(cwd, args.slice(2), raw);
52
52
  return;
53
53
  }
54
- // `dispatch-plan` (pack 9): read-only; the executor spawns, suite runs and
55
- // tracking commits execute-phase obeys (src/dispatch-plan.cts).
56
- if (subcommand === 'dispatch-plan') {
57
- phase.cmdPhaseDispatchPlan(cwd, args.slice(2), raw);
58
- return;
59
- }
60
- // `install-run` (pack 9): heavy-tier verification of the installed product —
61
- // pack, install, init, run the phase's install checks (src/install-run.cts).
62
- if (subcommand === 'install-run') {
63
- phase.cmdPhaseInstallRun(cwd, args.slice(2), raw);
64
- return;
65
- }
66
- // `tier` / `tier-propose` (pack 9): the phase's ceremony tier and its
67
- // proposal (src/phase-tier.cts); `tier-propose --record` writes ROADMAP.
68
- if (subcommand === 'tier') {
69
- phase.cmdPhaseTier(cwd, args.slice(2), raw);
70
- return;
71
- }
72
- if (subcommand === 'tier-propose') {
73
- phase.cmdPhaseTierPropose(cwd, args.slice(2), raw);
74
- return;
75
- }
76
54
  // ── Build the CJS registry ──────────────────────────────────────────────────
77
55
  // Each handler receives a ctx object from the hub and must return a HubResult.
78
56
  const cjsRegistry = {
@@ -4,7 +4,7 @@
4
4
  * this caller use it?
5
5
  *
6
6
  * THE owner of that decision for every consumer of the raw phase locator
7
- *. The raw locator in phase-locator.cts answers a
7
+ * (PBI 75323, quick 261009-gvf). The raw locator in phase-locator.cts answers a
8
8
  * phase token from the live `phases/` tree first and then falls through to the
9
9
  * closed milestones' `milestones/<v>-phases/` archives, marking such a match
10
10
  * with the archive's version. It applies no milestone compare, and it must not:
@@ -56,10 +56,6 @@ const clock_cjs_1 = require("./clock.cjs");
56
56
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- uat-predicate.cjs is an export= CommonJS module
57
57
  const uatPredicate = require("./uat-predicate.cjs");
58
58
  const { evaluateUatPassed } = uatPredicate;
59
- const plan_graph_cjs_1 = require("./plan-graph.cjs");
60
- const dispatch_plan_cjs_1 = require("./dispatch-plan.cjs");
61
- const phase_tier_cjs_1 = require("./phase-tier.cjs");
62
- const install_run_cjs_1 = require("./install-run.cjs");
63
59
  const { planningDir, withPlanningLock, milestonePathsFor } = planningWorkspace;
64
60
  const { extractFrontmatter } = frontmatterMod;
65
61
  const { readModifyWriteStateMd, stateExtractField, stateReplaceField, stateReplaceFieldWithFallback, syncStateFrontmatter, withStateLock, updatePerformanceMetricsSection, } = stateMod;
@@ -408,140 +404,6 @@ function cmdPhasePlanIndex(cwd, phase, raw) {
408
404
  if (!phase) {
409
405
  error('phase required for phase-plan-index');
410
406
  }
411
- const built = buildPhasePlanIndex(cwd, phase);
412
- if (built.fatal !== undefined) {
413
- error(built.fatal);
414
- return;
415
- }
416
- output(built.result, raw);
417
- }
418
- /**
419
- * `phase.dispatch-plan <phase> [--wave N] [--gaps-only] [--use-worktrees true|false]`:
420
- * the executor spawns, builds, suite runs and tracking commits for the phase's
421
- * incomplete plans (computed by src/dispatch-plan.cts from the plan inventory).
422
- */
423
- function cmdPhaseDispatchPlan(cwd, args, raw) {
424
- const parsed = (0, dispatch_plan_cjs_1.parseDispatchPlanArgs)(args);
425
- if ('error' in parsed) {
426
- error(parsed.error);
427
- return;
428
- }
429
- const built = buildPhasePlanIndex(cwd, parsed.phase);
430
- if (built.fatal !== undefined) {
431
- error(built.fatal);
432
- return;
433
- }
434
- if (built.phaseDir === null) {
435
- output({ phase: built.result['phase'], error: 'Phase not found' }, raw);
436
- return;
437
- }
438
- const policy = (0, dispatch_plan_cjs_1.resolveDispatchPolicy)(cwd, parsed);
439
- const plans = built.result['plans'].map((p) => {
440
- const extra = built.extras.get(p['id']);
441
- return {
442
- id: p['id'],
443
- title: p['title'] ?? null,
444
- wave: p['wave'],
445
- serial_group: p['serial_group'] ?? null,
446
- files_modified: p['files_modified'],
447
- autonomous: p['autonomous'],
448
- has_summary: p['has_summary'],
449
- requirements: extra?.requirements ?? [],
450
- gap_closure: extra?.gapClosure ?? false,
451
- };
452
- });
453
- const phaseDirRel = node_path_1.default.relative(cwd, built.phaseDir).split(node_path_1.default.sep).join('/');
454
- const tier = (0, phase_tier_cjs_1.resolvePhaseTierForProject)(cwd, parsed.phase, parsed.tier);
455
- output((0, dispatch_plan_cjs_1.computeDispatchPlan)(built.result['phase'], phaseDirRel, plans, policy, tier, { planBounceScript: tier.plan_bounce_script }), raw);
456
- }
457
- /**
458
- * `phase.install-run <phase> [--tarball <tgz>] [--package-dir <dir>] [--no-build]`:
459
- * pack, install into a temp project, init and run the phase's install checks
460
- * against the installed copy; writes `<NN>-INSTALL-RUN.json` (src/install-run.cts).
461
- */
462
- function cmdPhaseInstallRun(cwd, args, raw) {
463
- const parsed = (0, install_run_cjs_1.parseInstallRunArgs)(args);
464
- if ('error' in parsed) {
465
- error(parsed.error, ERROR_REASON.USAGE);
466
- return;
467
- }
468
- let out;
469
- try {
470
- out = (0, install_run_cjs_1.runInstallRun)({ cwd, phase: parsed.phase, tarball: parsed.tarball, packageDir: parsed.packageDir, build: parsed.build });
471
- }
472
- catch (e) {
473
- error(e.message);
474
- return;
475
- }
476
- output(out, raw);
477
- }
478
- /**
479
- * `phase.tier <phase> [--tier light|standard|heavy]`: the phase's resolved
480
- * ceremony tier — tier, source, gate set, suppressed gates (src/phase-tier.cts).
481
- */
482
- function cmdPhaseTier(cwd, args, raw) {
483
- const usage = 'Usage: phase tier <phase> [--tier light|standard|heavy]';
484
- const parsed = (0, phase_tier_cjs_1.parseTierFlag)(args.filter((a) => a !== '--raw'));
485
- if ('error' in parsed) {
486
- error(`${parsed.error}. ${usage}`, ERROR_REASON.USAGE);
487
- return;
488
- }
489
- if (parsed.rest.length !== 1 || parsed.rest[0].startsWith('--')) {
490
- error(parsed.rest.length === 0 ? `phase required for phase tier. ${usage}` : `phase tier takes one phase and --tier. ${usage}`, ERROR_REASON.USAGE);
491
- return;
492
- }
493
- const phaseArg = parsed.rest[0];
494
- output({ phase: phaseArg, ...(0, phase_tier_cjs_1.resolvePhaseTierForProject)(cwd, phaseArg, parsed.tier) }, raw);
495
- }
496
- /**
497
- * `phase.tier-propose <phase> [--record] [--tier X]`: propose a tier from the
498
- * phase's plans (`files_modified`) or, before planning, its requirement tags
499
- * and ROADMAP text. `--record` writes it to the phase's ROADMAP `**Tier:**`
500
- * field, marked `(proposed: …)`, unless the operator already chose one;
501
- * `--record --tier X` records X as the operator's choice.
502
- */
503
- function cmdPhaseTierPropose(cwd, args, raw) {
504
- const usage = 'Usage: phase tier-propose <phase> [--record] [--tier light|standard|heavy]';
505
- const parsed = (0, phase_tier_cjs_1.parseTierFlag)(args.filter((a) => a !== '--raw'));
506
- if ('error' in parsed) {
507
- error(`${parsed.error}. ${usage}`, ERROR_REASON.USAGE);
508
- return;
509
- }
510
- const doRecord = parsed.rest.includes('--record');
511
- const positional = parsed.rest.filter((a) => a !== '--record');
512
- if (positional.length !== 1 || positional[0].startsWith('--')) {
513
- error(positional.length === 0 ? `phase required for phase tier-propose. ${usage}` : `phase tier-propose does not support ${positional.find((a) => a.startsWith('--')) ?? positional.join(' ')}. ${usage}`, ERROR_REASON.USAGE);
514
- return;
515
- }
516
- const phaseArg = positional[0];
517
- const record = (0, phase_tier_cjs_1.readPhaseRecord)(cwd, phaseArg);
518
- if (record.section === null) {
519
- output({ phase: phaseArg, error: 'Phase not found in ROADMAP.md' }, raw);
520
- return;
521
- }
522
- const built = buildPhasePlanIndex(cwd, phaseArg);
523
- const planRows = built.fatal === undefined && built.phaseDir !== null
524
- ? built.result['plans'].map((p) => ({ id: p['id'], files_modified: p['files_modified'] ?? [] }))
525
- : [];
526
- const reqMatch = record.section.match(/^\*\*Requirements:?\*\*[^\S\n]*:?[^\S\n]*([^\n]*)$/m);
527
- const requirements = reqMatch ? reqMatch[1].replace(/[[\]]/g, '').split(',').map((s) => s.trim()).filter((s) => s !== '' && s !== 'TBD') : [];
528
- const proposal = (0, phase_tier_cjs_1.proposePhaseTier)({ plans: planRows, requirements, roadmapText: record.section });
529
- let recorded = null;
530
- if (doRecord) {
531
- recorded = parsed.tier !== null
532
- ? (0, phase_tier_cjs_1.recordPhaseTier)(cwd, phaseArg, parsed.tier, { proposed: false })
533
- : (0, phase_tier_cjs_1.recordPhaseTier)(cwd, phaseArg, proposal.tier, { proposed: true, reason: proposal.reason });
534
- }
535
- const after = (0, phase_tier_cjs_1.resolvePhaseTierForProject)(cwd, phaseArg, parsed.tier);
536
- output({
537
- phase: phaseArg,
538
- proposal,
539
- phase_record: record.tier !== null ? { tier: record.tier, proposed: record.proposed } : null,
540
- recorded,
541
- effective: { tier: after.tier, source: after.source, suppressed: after.suppressed },
542
- }, raw);
543
- }
544
- function buildPhasePlanIndex(cwd, phase) {
545
407
  const phasesDir = node_path_1.default.join(planningDir(cwd), 'phases');
546
408
  const normalized = normalizePhaseName(phase);
547
409
  let phaseDir = null;
@@ -562,11 +424,8 @@ function buildPhasePlanIndex(cwd, phase) {
562
424
  // phases dir doesn't exist
563
425
  }
564
426
  if (!phaseDir) {
565
- return {
566
- result: { phase: normalized, error: 'Phase not found', plans: [], waves: {}, incomplete: [], has_checkpoints: false },
567
- phaseDir: null,
568
- extras: new Map(),
569
- };
427
+ output({ phase: normalized, error: 'Phase not found', plans: [], waves: {}, incomplete: [], has_checkpoints: false }, raw);
428
+ return;
570
429
  }
571
430
  void phaseDirName; // used only to set phaseDir above
572
431
  const phaseFiles = node_fs_1.default.readdirSync(phaseDir);
@@ -588,13 +447,6 @@ function buildPhasePlanIndex(cwd, phase) {
588
447
  const xmlTasks = content.match(/<task[\s>]/gi) || [];
589
448
  const mdTasks = content.match(/##\s*Task\s*\d+/gi) || [];
590
449
  const taskCount = xmlTasks.length || mdTasks.length;
591
- const taskTypes = xmlTasks.length > 0
592
- ? [...content.matchAll(/<task(\s[^>]*)?>/gi)].map((m) => /type=["']?([\w:-]+)/i.exec(m[1] ?? '')?.[1] ?? 'auto')
593
- : mdTasks.map(() => 'auto');
594
- const fmString = (key) => {
595
- const value = fm[key];
596
- return typeof value === 'string' && value.trim() !== '' ? value.trim() : null;
597
- };
598
450
  const parsedWave = parseInt(fm['wave'], 10);
599
451
  const declaredWave = Number.isNaN(parsedWave) ? null : parsedWave;
600
452
  let dependsOn = [];
@@ -617,10 +469,6 @@ function buildPhasePlanIndex(cwd, phase) {
617
469
  filesModified = Array.isArray(fmFiles) ? fmFiles.map(String) : [String(fmFiles)];
618
470
  }
619
471
  const hasSummary = completedPlanIds.has(planId) || completedPlanIds.has(extractCanonicalPlanId(planFile));
620
- const fmReqs = fm['requirements'];
621
- const requirements = (Array.isArray(fmReqs) ? fmReqs.map(String) : typeof fmReqs === 'string' && fmReqs.trim() !== '' ? fmReqs.split(/[\s,]+/) : [])
622
- .map((r) => r.trim())
623
- .filter((r) => r !== '' && r !== '[]');
624
472
  rawPlans.push({
625
473
  id: planId,
626
474
  declaredWave,
@@ -630,13 +478,6 @@ function buildPhasePlanIndex(cwd, phase) {
630
478
  filesModified,
631
479
  taskCount,
632
480
  hasSummary,
633
- title: (0, plan_graph_cjs_1.readPlanTitle)(fm['title']),
634
- serialGroup: fmString('serial_group'),
635
- chainReason: fmString('chain_reason'),
636
- taskTypes,
637
- requirements,
638
- // eslint-disable-next-line @typescript-eslint/no-base-to-string -- FrontmatterValue scalar-to-string
639
- gapClosure: String(fm['gap_closure'] ?? '').trim() === 'true',
640
481
  });
641
482
  }
642
483
  // ── Pass 2: topological level assignment via depends_on DAG ──────────────
@@ -645,31 +486,18 @@ function buildPhasePlanIndex(cwd, phase) {
645
486
  const lower = p.id.toLowerCase();
646
487
  const existing = seenLower.get(lower);
647
488
  if (existing !== undefined) {
648
- return {
649
- fatal: `depends_on index collision in phase ${normalized}: plan IDs '${existing}' and '${p.id}' are identical when case-folded. Rename one file to avoid ambiguous dependency resolution.`,
650
- };
489
+ error(`depends_on index collision in phase ${normalized}: plan IDs '${existing}' and '${p.id}' are identical when case-folded. Rename one file to avoid ambiguous dependency resolution.`);
490
+ return;
651
491
  }
652
492
  seenLower.set(lower, p.id);
653
493
  }
654
494
  const planMap = new Map(rawPlans.map((p) => [p.id.toLowerCase(), p]));
655
495
  const canonicalToId = new Map(rawPlans.map((p) => [extractCanonicalPlanId(p.id).toLowerCase(), p.id]));
656
- // Wave + serial-group assignment and chain metrics: single owner is plan-graph.cts.
657
- const resolveDep = (dep) => {
658
- const lower = String(dep).toLowerCase();
659
- return planMap.has(lower) ? planMap.get(lower).id : canonicalToId.get(lower);
660
- };
661
- const graph = (0, plan_graph_cjs_1.computePlanGraph)(rawPlans.map((p) => ({
662
- id: p.id,
663
- title: p.title,
664
- dependsOn: p.dependsOn.map(resolveDep).filter((d) => d !== undefined),
665
- filesModified: p.filesModified,
666
- serialGroup: p.serialGroup,
667
- chainReason: p.chainReason,
668
- taskTypes: p.taskTypes,
669
- })));
670
- if (graph.cycle) {
671
- const cycleNodes = graph.cycle;
672
- return { fatal: `depends_on cycle detected in phase ${normalized} — cycle involves: ${cycleNodes.join(', ')}` };
496
+ const { level, visited } = computeDependencyLevels(rawPlans, planMap, canonicalToId);
497
+ if (visited < rawPlans.length) {
498
+ const cycleNodes = rawPlans.filter((p) => !level.has(p.id)).map((p) => p.id);
499
+ error(`depends_on cycle detected in phase ${normalized} — cycle involves: ${cycleNodes.join(', ')}`);
500
+ return;
673
501
  }
674
502
  // ── Pass 3: determine lowest bucket key and build output ─────────────────
675
503
  const anyWaveZero = rawPlans.some((p) => p.declaredWave === 0);
@@ -686,16 +514,14 @@ function buildPhasePlanIndex(cwd, phase) {
686
514
  if (!rawPlan.hasSummary) {
687
515
  incomplete.push(rawPlan.id);
688
516
  }
689
- const computedWave = (graph.level[rawPlan.id] ?? 0) + levelOffset;
517
+ const computedWave = (level.get(rawPlan.id) ?? 0) + levelOffset;
690
518
  const effectiveWave = computedWave;
691
519
  if (rawPlan.declaredWave !== null && rawPlan.declaredWave !== computedWave) {
692
520
  warnings.push(`Plan ${rawPlan.id}: declared wave: ${rawPlan.declaredWave} but depends_on DAG places it in wave ${computedWave}`);
693
521
  }
694
522
  const plan = {
695
523
  id: rawPlan.id,
696
- title: rawPlan.title,
697
524
  wave: effectiveWave,
698
- serial_group: graph.serialGroupOf[rawPlan.id] ?? null,
699
525
  depends_on: rawPlan.dependsOn.map((dep) => {
700
526
  const lower = String(dep).toLowerCase();
701
527
  return planMap.has(lower) ? planMap.get(lower).id : dep;
@@ -717,28 +543,14 @@ function buildPhasePlanIndex(cwd, phase) {
717
543
  phase: normalized,
718
544
  plans,
719
545
  waves,
720
- serial_groups: Object.fromEntries(graph.serialGroups.map((g) => [g.name, { wave: g.level + levelOffset, plans: g.plans, shared_files: g.shared_files }])),
721
- graph: {
722
- plan_count: graph.plan_count,
723
- wave_count: graph.wave_count,
724
- longest_chain: graph.longest_chain,
725
- chain_check: graph.chain_check,
726
- implicit_edges: graph.implicitEdges,
727
- merge_candidates: graph.merge_candidates,
728
- },
729
546
  incomplete,
730
547
  has_checkpoints: hasCheckpoints,
731
548
  };
732
- warnings.push(...graph.warnings);
733
549
  if (planNamingWarning)
734
550
  result['warning'] = planNamingWarning;
735
551
  if (warnings.length > 0)
736
552
  result['warnings'] = warnings;
737
- return {
738
- result,
739
- phaseDir,
740
- extras: new Map(rawPlans.map((p) => [p.id, { requirements: p.requirements, gapClosure: p.gapClosure }])),
741
- };
553
+ output(result, raw);
742
554
  }
743
555
  function cmdPhaseAdd(cwd, description, raw, customId) {
744
556
  if (!description) {
@@ -1657,10 +1469,6 @@ module.exports = {
1657
1469
  cmdPhaseNextDecimal,
1658
1470
  cmdFindPhase,
1659
1471
  cmdPhasePlanIndex,
1660
- cmdPhaseDispatchPlan,
1661
- cmdPhaseInstallRun,
1662
- cmdPhaseTier,
1663
- cmdPhaseTierPropose,
1664
1472
  cmdPhaseAdd,
1665
1473
  cmdPhaseAddBatch,
1666
1474
  cmdPhaseMvpMode,
@@ -34,8 +34,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
34
34
  return (mod && mod.__esModule) ? mod : { "default": mod };
35
35
  };
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
- exports.BINDINGS_COORDINATE_FIELDS = exports.MACHINE_SPECIFIC_CLONE_TARGET_PATTERNS = exports.VAULT_REPO_PATTERN_SOURCE = exports.PLATFORM_REPO_PATTERN_SOURCE = exports.CONTRACT_SCHEMA_VERSION = void 0;
37
+ exports.BINDINGS_COORDINATE_FIELDS = exports.MACHINE_SPECIFIC_CLONE_TARGET_PATTERNS = exports.VAULT_REPO_PATTERN_SOURCE = exports.PLATFORM_REPO_PATTERN_SOURCE = exports.SLUG_RE = exports.CONTRACT_SCHEMA_VERSION = void 0;
38
38
  exports.validateBindings = validateBindings;
39
+ exports.redactCoordinateForEmission = redactCoordinateForEmission;
40
+ exports.redactBindingsField = redactBindingsField;
39
41
  exports.redactBindingsForEmission = redactBindingsForEmission;
40
42
  exports.cmdPlatformBindings = cmdPlatformBindings;
41
43
  exports.cmdValidateBindings = cmdValidateBindings;
@@ -65,7 +67,7 @@ const SCHEMA_VERSION_RE = /^\d+\.\d+\.\d+$/;
65
67
  exports.CONTRACT_SCHEMA_VERSION = '1.1.0';
66
68
  const PLATFORM_TAG_RE = /^v\d+\.\d+\.\d+/;
67
69
  // Provisional namespace pattern (lenient): only enforced if client.slug is present.
68
- const SLUG_RE = /^[a-z0-9][a-z0-9-]*$/;
70
+ exports.SLUG_RE = /^[a-z0-9][a-z0-9-]*$/;
69
71
  // A host-relative path of two or more slash-separated segments built from
70
72
  // word/dot/hyphen characters — e.g. `pareto4/pareto-platform-gcp` or Azure's
71
73
  // four-segment `org/project/_git/repo`. This is the pre-v1.1.0 vault-repo
@@ -77,8 +79,8 @@ const HOST_RELATIVE_SLUG_SOURCE = '^[\\w.-]+(?:\\/[\\w.-]+)+$';
77
79
  * A host-relative path of two or more segments. Compiled into
78
80
  * `platform.repo.pattern` by `scripts/gen-platform-bindings-schema.cjs` so
79
81
  * the runtime regex and the schema pattern cannot drift (D-06). Deliberately
80
- * accepts no URL and no SSH form: `resolveLatestTagViaGitRemote` composes the
81
- * host from `platform.host` itself (Phase 31 D-09), so a full URL here would
82
+ * accepts no URL and no SSH form: `resolveLatestPlatformTag` (compose-bindings)
83
+ * composes the host from `platform.host` itself (Phase 31 D-09), so a full URL here would
82
84
  * give the host two competing encodings — `platform.host` is authoritative.
83
85
  */
84
86
  exports.PLATFORM_REPO_PATTERN_SOURCE = HOST_RELATIVE_SLUG_SOURCE;
@@ -183,7 +185,7 @@ function validateBindings(o) {
183
185
  // Provisional namespaces — lenient. A missing/unknown namespace is fine; only
184
186
  // a PRESENT slug field that is malformed is flagged (it never gates on absence).
185
187
  if (isObject(o.client) && o.client.slug !== undefined) {
186
- if (typeof o.client.slug !== 'string' || !SLUG_RE.test(o.client.slug)) {
188
+ if (typeof o.client.slug !== 'string' || !exports.SLUG_RE.test(o.client.slug)) {
187
189
  errors.push('client.slug: present but not a url-safe lowercase slug');
188
190
  }
189
191
  }
@@ -203,35 +205,53 @@ function validateBindings(o) {
203
205
  // IN-03: project.slug — symmetric lenient check matching client.slug behaviour.
204
206
  // The schema defines the same ^[a-z0-9][a-z0-9-]*$ pattern for both fields.
205
207
  if (isObject(o.project) && o.project.slug !== undefined) {
206
- if (typeof o.project.slug !== 'string' || !SLUG_RE.test(o.project.slug)) {
208
+ if (typeof o.project.slug !== 'string' || !exports.SLUG_RE.test(o.project.slug)) {
207
209
  errors.push('project.slug: present but not a url-safe lowercase slug');
208
210
  }
209
211
  }
210
212
  return { valid: errors.length === 0, errors, warnings };
211
213
  }
212
214
  /**
213
- * Pack 6 D-07: the `.platform-bindings.json` fields that carry a remote repo
214
- * coordinate (and so the fields a credential can ride in). Hosts are not
215
- * listed: a host is a bare host by contract and is read for real use.
215
+ * VAULT-EGRESS-1 (quick 261005-8py gap closure): the `.platform-bindings.json`
216
+ * fields that carry a remote repo coordinate, and therefore the fields a
217
+ * credential (`https://user:TOKEN@host/…`, `user:TOKEN@host:path`) can ride in.
218
+ * Hosts are deliberately NOT listed: `platform.host` is read for real use by
219
+ * /pp-init's provisioning step (`--pick platform.host`), and a host is a bare
220
+ * host by contract.
216
221
  */
217
222
  exports.BINDINGS_COORDINATE_FIELDS = Object.freeze(['platform.repo', 'client.vaultRepo']);
218
223
  /**
219
- * Emission-only: a copy of a bindings-shaped object with every coordinate field
220
- * routed through the one owner (`redactCloneTarget`, src/vault.cts — lazily
221
- * required so this module's load graph is unchanged). Never use the result for
222
- * a write, a comparison, or a clone.
224
+ * Emission-only: the display-safe form of one coordinate, through the ONE
225
+ * owner (`redactCloneTarget` in `src/vault.cts`) — never a second redactor.
226
+ * Lazily required, the idiom `src/vault-command-router.cts` uses, so this
227
+ * module's load graph is unchanged. Never call this on a value that is written
228
+ * to disk or handed to git: those keep the RAW coordinate.
229
+ */
230
+ function redactCoordinateForEmission(value) {
231
+ if (typeof value !== 'string' || value === '')
232
+ return value;
233
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
234
+ const { redactCloneTarget } = require('./vault.cjs');
235
+ return redactCloneTarget(value) ?? value;
236
+ }
237
+ /** Emission-only: `value` redacted when `field` (dotted) is a coordinate field. */
238
+ function redactBindingsField(field, value) {
239
+ return exports.BINDINGS_COORDINATE_FIELDS.includes(field) ? redactCoordinateForEmission(value) : value;
240
+ }
241
+ /**
242
+ * Emission-only: a copy of a bindings-shaped object with every coordinate
243
+ * field routed through the owner. The input is never mutated — callers that
244
+ * compare (drift, `action`) or write keep the raw object.
223
245
  */
224
246
  function redactBindingsForEmission(obj) {
225
247
  if (!isObject(obj))
226
248
  return obj;
227
- // eslint-disable-next-line @typescript-eslint/no-require-imports
228
- const { redactCloneTarget } = require('./vault.cjs');
229
249
  const copy = JSON.parse(JSON.stringify(obj));
230
250
  for (const field of exports.BINDINGS_COORDINATE_FIELDS) {
231
251
  const [ns, key] = field.split('.');
232
252
  const parent = copy[ns];
233
253
  if (isObject(parent) && typeof parent[key] === 'string') {
234
- parent[key] = redactCloneTarget(parent[key]);
254
+ parent[key] = redactCoordinateForEmission(parent[key]);
235
255
  }
236
256
  }
237
257
  return copy;
@@ -281,9 +301,10 @@ function cmdPlatformBindings(cwd, args, raw) {
281
301
  output(validateBindings(parsed), raw);
282
302
  return;
283
303
  }
284
- // present + valid → echo the bindings under a present flag.
285
- // Pack 6 D-07: the remote coordinate fields are emitted display-safe; every
286
- // other field is verbatim. The file on disk keeps the raw value.
304
+ // present + valid → echo the bindings under a present flag. The coordinate
305
+ // fields are display-safe (VAULT-EGRESS-1); every other field is verbatim,
306
+ // and the one consumer of the whole dump (vault-load-gate.md) reads only
307
+ // `client.vaultRepo`'s presence and the raw `client.vaultRef`.
287
308
  output(Object.assign({ present: true }, redactBindingsForEmission(parsed)), raw);
288
309
  }
289
310
  // Anchored regex for `?ref=` values in module source lines (T-07-04: untrusted text,
@@ -46,9 +46,9 @@
46
46
  *
47
47
  * Every function here treats its input as UNTRUSTED TEXT. The review file
48
48
  * this module parses is authored partly by an untrusted client vault
49
- * (`REFERENCE_DATA_PREAMBLE`, `FENCE_START_MARKER`/`FENCE_END_MARKER` and
50
- * heading-shaped lines can all legitimately appear inside client body
51
- * content) and partly by an LLM reviewer whose output is not guaranteed
49
+ * (`REFERENCE_DATA_PREAMBLE`, `FENCE_START_MARKER`/`FENCE_END_MARKER` — read
50
+ * from the vault-index leaf, their one owner — and heading-shaped lines can
51
+ * all legitimately appear inside client body content) and partly by an LLM reviewer whose output is not guaranteed
52
52
  * well-formed. The module performs zero I/O: no environment-variable read, no
53
53
  * stdout write, no early process termination, no filesystem access, no
54
54
  * dynamic `require`, no `eval`. It is pure text in, structure out — the CLI arm
@@ -163,15 +163,19 @@
163
163
  *
164
164
  * ## Reuses (no new runtime dependency)
165
165
  *
166
- * None. This module needs no imports at all — it is pure string processing
167
- * over already-materialized text, mirroring `src/vault-practices.cts`'s
168
- * `defangFenceMarkers`/`boundedFencedExcerpt` marker literals and the
169
- * zero-width-space defanged form, but reimplementing no logic from that file.
166
+ * One import, and no runtime dependency: the dependency-free vault-index leaf
167
+ * (`src/vault-index.cts`), the single owner of the external-content marker
168
+ * literals (D-17/D-26, 14-04). Everything else is pure string processing over
169
+ * already-materialized text, recognising the markers and the zero-width-space
170
+ * defanged form `defangFenceMarkers` produces, but reimplementing no logic
171
+ * from `src/vault-practices.cts`.
170
172
  *
171
173
  * ADR-457 build-at-publish: this TypeScript file is the SOURCE OF TRUTH. It
172
174
  * compiles to the git-ignored `pp-core/bin/lib/practices-review-check.cjs`;
173
175
  * do NOT hand-edit the compiled output.
174
176
  */
177
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
178
+ const vaultIndexLeaf = require("./vault-index.cjs");
175
179
  // ─── Literals ────────────────────────────────────────────────────────────────
176
180
  //
177
181
  // Copied character-for-character from pp-core/workflows/code-review.md's
@@ -182,8 +186,10 @@ const SECTION_HEADING = '## Practices Findings (client vault)';
182
186
  const FINDING_HEADING = '### PR-';
183
187
  const ASKS_LABEL = '**Asks:**';
184
188
  const FILE_LABEL = '**File:**';
185
- const FENCE_START_MARKER = '<!-- vault:external-content-start -->';
186
- const FENCE_END_MARKER = '<!-- vault:external-content-end -->';
189
+ // D-17/D-26 (14-04): the marker values come from the vault-index leaf, their
190
+ // one owner; the local names stay so every call site below reads unchanged.
191
+ const FENCE_START_MARKER = vaultIndexLeaf.EXTERNAL_CONTENT_FENCE_START;
192
+ const FENCE_END_MARKER = vaultIndexLeaf.EXTERNAL_CONTENT_FENCE_END;
187
193
  // The fixed reference-data disclaimer sentences `boundedFencedExcerpt`
188
194
  // (src/vault-practices.cts) prepends to EVERY excerpt. `clientBodyOf` strips
189
195
  // these before an excerpt enters the unfenced-quote comparison corpus, so a
@@ -28,7 +28,7 @@ const { loadConfig } = configLoader;
28
28
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
29
29
  const runtime_homes_cjs_1 = require("./runtime-homes.cjs");
30
30
  const runtime_slash_cjs_1 = require("./runtime-slash.cjs");
31
- // The start-command fallback uses the pp-named alias (same function).
31
+ // PBI 75232: the start-command fallback uses the pp-named alias (same function).
32
32
  const runtime_slash_cjs_2 = require("./runtime-slash.cjs");
33
33
  const runtime_name_policy_cjs_1 = require("./runtime-name-policy.cjs");
34
34
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- planning-workspace.cjs is an export= CommonJS module