@pareto4/pp-core 1.13.0 → 1.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/.claude-plugin/plugin.json +3 -3
  2. package/README.md +26 -9
  3. package/agents/pp-code-reviewer.md +2 -1
  4. package/agents/pp-debug-session-manager.md +9 -6
  5. package/agents/pp-executor.md +10 -13
  6. package/agents/pp-plan-checker.md +4 -4
  7. package/agents/pp-planner.md +23 -9
  8. package/agents/pp-vault-reviewer.md +244 -21
  9. package/agents/pp-verifier.md +7 -3
  10. package/bin/install.js +10 -3
  11. package/commands/pp-execute-phase.md +1 -3
  12. package/commands/pp-ns-review.md +1 -1
  13. package/commands/pp-plan-phase.md +1 -2
  14. package/commands/pp-vault-review.md +5 -3
  15. package/commands/pp-verify-work.md +1 -2
  16. package/gemini-extension.json +1 -1
  17. package/hooks/dist/pp-check-update.js +0 -0
  18. package/hooks/dist/pp-cursor-session-start.js +1 -1
  19. package/hooks/dist/pp-statusline.js +1 -1
  20. package/hooks/dist/pp-update-banner.js +0 -0
  21. package/hooks/pp-check-update.js +0 -0
  22. package/hooks/pp-cursor-session-start.js +1 -1
  23. package/hooks/pp-statusline.js +1 -1
  24. package/hooks/pp-update-banner.js +0 -0
  25. package/package.json +14 -19
  26. package/pareto-skills/_shared/create-project-board.sh +0 -0
  27. package/pareto-skills/pp-project-setup/SKILL.md +1 -1
  28. package/pp-core/bin/check-latest-version.cjs +0 -0
  29. package/pp-core/bin/detect-build-commands.sh +2 -2
  30. package/pp-core/bin/lib/capability-activation.cjs +83 -9
  31. package/pp-core/bin/lib/capability-registry.cjs +396 -82
  32. package/pp-core/bin/lib/capability-state.cjs +160 -4
  33. package/pp-core/bin/lib/capability-writer.cjs +19 -5
  34. package/pp-core/bin/lib/check-command-router.cjs +3334 -946
  35. package/pp-core/bin/lib/clusters.cjs +3 -0
  36. package/pp-core/bin/lib/command-aliases.cjs +0 -35
  37. package/pp-core/bin/lib/commands.cjs +9 -4
  38. package/pp-core/bin/lib/compose-bindings.cjs +251 -88
  39. package/pp-core/bin/lib/config-loader.cjs +7 -103
  40. package/pp-core/bin/lib/config-schema.cjs +1 -1
  41. package/pp-core/bin/lib/config.cjs +154 -62
  42. package/pp-core/bin/lib/decisions.cjs +4 -4
  43. package/pp-core/bin/lib/discovery-vault-check.cjs +246 -0
  44. package/pp-core/bin/lib/frontmatter.cjs +1 -38
  45. package/pp-core/bin/lib/git-base-branch.cjs +13 -11
  46. package/pp-core/bin/lib/git-provider.cjs +4 -4
  47. package/pp-core/bin/lib/init-command-router.cjs +5 -8
  48. package/pp-core/bin/lib/init-contract.cjs +648 -738
  49. package/pp-core/bin/lib/init-interactive.cjs +76 -4
  50. package/pp-core/bin/lib/init.cjs +24 -51
  51. package/pp-core/bin/lib/install-profiles.cjs +1 -1
  52. package/pp-core/bin/lib/io.cjs +8 -0
  53. package/pp-core/bin/lib/loop-resolver.cjs +189 -129
  54. package/pp-core/bin/lib/org-profile.cjs +469 -152
  55. package/pp-core/bin/lib/package-identity.cjs +3 -3
  56. package/pp-core/bin/lib/phase-command-router.cjs +1 -29
  57. package/pp-core/bin/lib/phase-scope.cjs +1 -1
  58. package/pp-core/bin/lib/phase.cjs +11 -253
  59. package/pp-core/bin/lib/platform-bindings.cjs +40 -19
  60. package/pp-core/bin/lib/practices-review-check.cjs +15 -9
  61. package/pp-core/bin/lib/profile-output.cjs +1 -1
  62. package/pp-core/bin/lib/shell-command-projection.cjs +104 -1
  63. package/pp-core/bin/lib/state.cjs +11 -85
  64. package/pp-core/bin/lib/template.cjs +1 -1
  65. package/pp-core/bin/lib/vault-audit.cjs +650 -126
  66. package/pp-core/bin/lib/vault-command-router.cjs +2604 -367
  67. package/pp-core/bin/lib/vault-coordinates.cjs +13 -4
  68. package/pp-core/bin/lib/vault-index.cjs +699 -0
  69. package/pp-core/bin/lib/vault-mode.cjs +235 -0
  70. package/pp-core/bin/lib/vault-practices.cjs +139 -72
  71. package/pp-core/bin/lib/vault-review.cjs +194 -451
  72. package/pp-core/bin/lib/vault-seal.cjs +1936 -0
  73. package/pp-core/bin/lib/vault-standards.cjs +1505 -0
  74. package/pp-core/bin/lib/vault-trace.cjs +607 -254
  75. package/pp-core/bin/lib/vault-update.cjs +9 -33
  76. package/pp-core/bin/lib/vault-write-back.cjs +70 -42
  77. package/pp-core/bin/lib/vault.cjs +2734 -920
  78. package/pp-core/bin/lib/verify.cjs +0 -11
  79. package/pp-core/bin/portable-timeout.sh +1 -1
  80. package/pp-core/bin/pp-tools.cjs +184 -54
  81. package/pp-core/bin/shared/config-defaults.manifest.json +0 -7
  82. package/pp-core/bin/shared/config-schema.manifest.json +0 -3
  83. package/pp-core/bin/shared/model-catalog.json +2 -2
  84. package/pp-core/bin/verify-reapply-patches.cjs +0 -0
  85. package/pp-core/org-profile.json +26 -0
  86. package/pp-core/references/autonomous-best-effort.md +8 -7
  87. package/pp-core/references/loop-hook-dispatch.md +0 -25
  88. package/pp-core/references/planner-gap-closure.md +0 -1
  89. package/pp-core/references/planner-tdd.md +0 -1
  90. package/pp-core/references/planner-vault-practices.md +17 -34
  91. package/pp-core/references/planner-vault-standards.md +320 -0
  92. package/pp-core/references/planning-config.md +0 -8
  93. package/pp-core/references/practice-arithmetic-contract.md +121 -0
  94. package/pp-core/references/tdd.md +0 -1
  95. package/pp-core/references/verifier-practice-verdicts.md +263 -0
  96. package/pp-core/templates/README.md +1 -0
  97. package/pp-core/templates/VAULT-REVIEW.md +202 -0
  98. package/pp-core/templates/phase-prompt.md +2 -6
  99. package/pp-core/templates/practices/README.md +20 -23
  100. package/pp-core/workflows/_runtime-launcher.snippet.sh +0 -0
  101. package/pp-core/workflows/add-tests.md +55 -4
  102. package/pp-core/workflows/audit-fix.md +63 -2
  103. package/pp-core/workflows/audit-milestone.md +1 -1
  104. package/pp-core/workflows/autonomous.md +85 -11
  105. package/pp-core/workflows/check-todos.md +13 -2
  106. package/pp-core/workflows/code-review-fix.md +59 -3
  107. package/pp-core/workflows/code-review.md +10 -1
  108. package/pp-core/workflows/complete-milestone.md +1 -1
  109. package/pp-core/workflows/debug.md +54 -4
  110. package/pp-core/workflows/discovery-sprint/prefill.md +19 -7
  111. package/pp-core/workflows/discovery-sprint/prototype.md +46 -49
  112. package/pp-core/workflows/discovery-sprint/session-13-gates.md +2 -0
  113. package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +1 -3
  114. package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +1 -3
  115. package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +1 -3
  116. package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +20 -24
  117. package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +1 -3
  118. package/pp-core/workflows/discovery-sprint/sessions/06-security.md +1 -3
  119. package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +1 -3
  120. package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +1 -3
  121. package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +1 -3
  122. package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +1 -6
  123. package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +1 -3
  124. package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +1 -3
  125. package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +1 -3
  126. package/pp-core/workflows/discovery-sprint/templates/context-template.md +1 -1
  127. package/pp-core/workflows/discovery-sprint/vault-context.md +213 -0
  128. package/pp-core/workflows/discovery-sprint.md +7 -3
  129. package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +4 -4
  130. package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +43 -1
  131. package/pp-core/workflows/discovery-to-build.md +10 -2
  132. package/pp-core/workflows/discuss-phase.md +3 -10
  133. package/pp-core/workflows/execute-phase/steps/check-query-gate.md +329 -0
  134. package/pp-core/workflows/execute-phase/steps/client-context-gate.md +3 -2
  135. package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +1 -1
  136. package/pp-core/workflows/execute-phase/steps/execute-pre-dispatch.md +98 -0
  137. package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
  138. package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +6 -8
  139. package/pp-core/workflows/execute-phase/steps/vault-fetch.md +115 -14
  140. package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +322 -20
  141. package/pp-core/workflows/execute-phase/steps/verifier-practice-payload.md +165 -0
  142. package/pp-core/workflows/execute-phase/steps/verify-post-gate.md +163 -0
  143. package/pp-core/workflows/execute-phase.md +155 -128
  144. package/pp-core/workflows/execute-plan.md +56 -6
  145. package/pp-core/workflows/fast.md +53 -4
  146. package/pp-core/workflows/help/modes/full.md +3 -3
  147. package/pp-core/workflows/init/steps/client-context-capture.md +101 -0
  148. package/pp-core/workflows/init/steps/gitlab-provisioning.md +130 -0
  149. package/pp-core/workflows/init/steps/project-md-deferral-narration.md +66 -0
  150. package/pp-core/workflows/init/steps/reachability-outcome.md +70 -0
  151. package/pp-core/workflows/init.md +232 -272
  152. package/pp-core/workflows/new-project.md +31 -23
  153. package/pp-core/workflows/plan-phase.md +98 -45
  154. package/pp-core/workflows/quick.md +117 -3
  155. package/pp-core/workflows/secure-phase.md +1 -1
  156. package/pp-core/workflows/ship.md +57 -28
  157. package/pp-core/workflows/undo.md +1 -1
  158. package/pp-core/workflows/update.md +3 -3
  159. package/pp-core/workflows/validate-phase.md +58 -3
  160. package/pp-core/workflows/vault-review.md +282 -22
  161. package/pp-core/workflows/verify-work.md +12 -14
  162. package/scripts/changeset/cli.cjs +6 -5
  163. package/scripts/changeset/lint.cjs +0 -0
  164. package/scripts/changeset/new.cjs +33 -50
  165. package/scripts/dev-env.cjs +25 -85
  166. package/scripts/gen-capability-registry.cjs +199 -11
  167. package/scripts/gen-practice-arithmetic-contract.cjs +412 -0
  168. package/scripts/lib/allowlist-ratchet.cjs +1 -103
  169. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -2
  170. package/scripts/lint-docs-required.cjs +0 -0
  171. package/scripts/lint-pr-check-project-dir.cjs +1 -3
  172. package/scripts/lint-test-file-count.allowlist.json +12 -10
  173. package/scripts/practice-arithmetic-contract.allow.json +35 -0
  174. package/scripts/prompt-injection-scan.sh +22 -17
  175. package/scripts/record-vault-mode-golden.cjs +89 -0
  176. package/scripts/release-channel.cjs +0 -0
  177. package/scripts/sync-manifest-versions.cjs +5 -4
  178. package/scripts/sync-rulesets.sh +1 -1
  179. package/scripts/update-size-baseline.cjs +30 -274
  180. package/scripts/vault-calibrate.cjs +996 -0
  181. package/scripts/verify-opengsd-sweep.cjs +3 -5
  182. package/scripts/workflow-size.cjs +9 -61
  183. package/pp-core/bin/lib/dispatch-plan.cjs +0 -483
  184. package/pp-core/bin/lib/install-run.cjs +0 -536
  185. package/pp-core/bin/lib/loop-gates.cjs +0 -213
  186. package/pp-core/bin/lib/phase-manifest.cjs +0 -527
  187. package/pp-core/bin/lib/phase-tier.cjs +0 -480
  188. package/pp-core/bin/lib/plan-graph.cjs +0 -368
  189. package/pp-core/references/executor-vault-trace.md +0 -47
  190. package/pp-core/references/planner-plan-graph.md +0 -66
  191. package/pp-core/workflows/discovery-sprint/vault-fetch.md +0 -37
  192. package/pp-core/workflows/execute-phase/steps/checkpoint-handling.md +0 -45
  193. package/pp-core/workflows/execute-phase/steps/install-run.md +0 -29
  194. package/pp-core/workflows/execute-phase/steps/phase-close-rebaseline.md +0 -23
  195. package/pp-core/workflows/execute-phase/steps/serial-group-dispatch.md +0 -73
  196. package/pp-core/workflows/execute-phase/steps/vault-door-gate.md +0 -19
  197. package/pp-core/workflows/plan-phase/steps/phase-tier.md +0 -43
  198. package/scripts/lib/size-budget.cjs +0 -311
  199. package/scripts/lint-neutrality-names.cjs +0 -694
  200. package/scripts/lint-org-coordinates.allowlist.json +0 -35
  201. package/scripts/lint-org-coordinates.cjs +0 -426
@@ -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, manifest (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,34 +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
- // `manifest start|record` (pack 10): write `.phase-manifest.json`, which
77
- // `/pp-undo --phase` reads (src/phase-manifest.cts).
78
- if (subcommand === 'manifest') {
79
- phase.cmdPhaseManifest(cwd, args.slice(2), raw);
80
- return;
81
- }
82
54
  // ── Build the CJS registry ──────────────────────────────────────────────────
83
55
  // Each handler receives a ctx object from the hub and must return a HubResult.
84
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,14 +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
- // eslint-disable-next-line @typescript-eslint/no-require-imports -- loop-resolver.cjs is an export= CommonJS module
64
- const loopResolver = require("./loop-resolver.cjs");
65
- // eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-manifest.cjs is an export= CommonJS module
66
- const phaseManifest = require("./phase-manifest.cjs");
67
59
  const { planningDir, withPlanningLock, milestonePathsFor } = planningWorkspace;
68
60
  const { extractFrontmatter } = frontmatterMod;
69
61
  const { readModifyWriteStateMd, stateExtractField, stateReplaceField, stateReplaceFieldWithFallback, syncStateFrontmatter, withStateLock, updatePerformanceMetricsSection, } = stateMod;
@@ -412,185 +404,6 @@ function cmdPhasePlanIndex(cwd, phase, raw) {
412
404
  if (!phase) {
413
405
  error('phase required for phase-plan-index');
414
406
  }
415
- const built = buildPhasePlanIndex(cwd, phase);
416
- if (built.fatal !== undefined) {
417
- error(built.fatal);
418
- return;
419
- }
420
- output(built.result, raw);
421
- }
422
- /**
423
- * `phase.dispatch-plan <phase> [--wave N] [--gaps-only] [--use-worktrees true|false]`:
424
- * the executor spawns, builds, suite runs and tracking commits for the phase's
425
- * incomplete plans (computed by src/dispatch-plan.cts from the plan inventory).
426
- */
427
- function cmdPhaseDispatchPlan(cwd, args, raw) {
428
- const parsed = (0, dispatch_plan_cjs_1.parseDispatchPlanArgs)(args);
429
- if ('error' in parsed) {
430
- error(parsed.error);
431
- return;
432
- }
433
- const built = buildPhasePlanIndex(cwd, parsed.phase);
434
- if (built.fatal !== undefined) {
435
- error(built.fatal);
436
- return;
437
- }
438
- if (built.phaseDir === null) {
439
- output({ phase: built.result['phase'], error: 'Phase not found' }, raw);
440
- return;
441
- }
442
- const policy = (0, dispatch_plan_cjs_1.resolveDispatchPolicy)(cwd, parsed);
443
- const plans = built.result['plans'].map((p) => {
444
- const extra = built.extras.get(p['id']);
445
- return {
446
- id: p['id'],
447
- title: p['title'] ?? null,
448
- wave: p['wave'],
449
- serial_group: p['serial_group'] ?? null,
450
- files_modified: p['files_modified'],
451
- autonomous: p['autonomous'],
452
- has_summary: p['has_summary'],
453
- requirements: extra?.requirements ?? [],
454
- gap_closure: extra?.gapClosure ?? false,
455
- };
456
- });
457
- const phaseDirRel = node_path_1.default.relative(cwd, built.phaseDir).split(node_path_1.default.sep).join('/');
458
- const tier = (0, phase_tier_cjs_1.resolvePhaseTierForProject)(cwd, parsed.phase, parsed.tier);
459
- // The executor brief: active execute:pre `into: executor` contributions,
460
- // resolved by the loop resolver under this phase's tier. An unreadable config
461
- // refuses here exactly as `loop render-hooks` does — a brief computed from
462
- // swallowed defaults would silently drop a capability's executor contract.
463
- const hooks = loopResolver.resolveProjectLoopHooks(cwd, 'execute:pre', { phaseTier: tier, what: 'phase.dispatch-plan' });
464
- if (!hooks.ok) {
465
- error(hooks.message, hooks.reason === 'config_unreadable' ? ERROR_REASON.CONFIG_PARSE_FAILED : undefined);
466
- return;
467
- }
468
- const executorContributions = loopResolver.contributionsInto(hooks.resolved, 'executor');
469
- // The agent practice review's one applicability predicate (src/vault.cts),
470
- // required lazily: only this command needs the vault module here.
471
- // eslint-disable-next-line @typescript-eslint/no-require-imports
472
- const vaultMod = require('./vault.cjs');
473
- const vaultReview = vaultMod.vaultReviewApplies(cwd);
474
- output((0, dispatch_plan_cjs_1.computeDispatchPlan)(built.result['phase'], phaseDirRel, plans, policy, tier, { planBounceScript: tier.plan_bounce_script, vaultReview, executorContributions }), raw);
475
- }
476
- /**
477
- * `phase.install-run <phase> [--tarball <tgz>] [--package-dir <dir>] [--no-build]`:
478
- * pack, install into a temp project, init and run the phase's install checks
479
- * against the installed copy; writes `<NN>-INSTALL-RUN.json` (src/install-run.cts).
480
- */
481
- function cmdPhaseInstallRun(cwd, args, raw) {
482
- const parsed = (0, install_run_cjs_1.parseInstallRunArgs)(args);
483
- if ('error' in parsed) {
484
- error(parsed.error, ERROR_REASON.USAGE);
485
- return;
486
- }
487
- let out;
488
- try {
489
- out = (0, install_run_cjs_1.runInstallRun)({ cwd, phase: parsed.phase, tarball: parsed.tarball, packageDir: parsed.packageDir, build: parsed.build });
490
- }
491
- catch (e) {
492
- error(e.message);
493
- return;
494
- }
495
- output(out, raw);
496
- }
497
- /**
498
- * `phase.tier <phase> [--tier light|standard|heavy]`: the phase's resolved
499
- * ceremony tier — tier, source, gate set, suppressed gates (src/phase-tier.cts).
500
- */
501
- /**
502
- * `phase.manifest start|record <phase>`: write `.phase-manifest.json`, the
503
- * file `/pp-undo --phase` reads (src/phase-manifest.cts). `start` anchors the
504
- * phase at HEAD (write-once); `record` lists `<startCommit>..HEAD`. Always
505
- * exits 0: a refusal is reported (`ok: false`, `reason`) and the caller
506
- * continues — `/pp-undo` then falls back to its git-log search.
507
- */
508
- function cmdPhaseManifest(cwd, args, raw) {
509
- const usage = 'Usage: phase manifest start|record <phase>';
510
- const positional = args.filter((a) => a !== '--raw');
511
- const action = positional[0];
512
- const phaseArg = positional[1];
513
- if ((action !== 'start' && action !== 'record') || !phaseArg || positional.length > 2) {
514
- error(usage, ERROR_REASON.USAGE);
515
- return;
516
- }
517
- const built = buildPhasePlanIndex(cwd, phaseArg);
518
- if (built.fatal !== undefined) {
519
- error(built.fatal);
520
- return;
521
- }
522
- if (built.phaseDir === null) {
523
- const result = { ok: false, action, reason: phaseManifest.PHASE_MANIFEST_WRITE_REASON.PHASE_DIR_UNRESOLVABLE };
524
- output(result, raw, phaseManifest.renderManifestResult(result));
525
- return;
526
- }
527
- const opts = { root: cwd, phaseDir: built.phaseDir, cwd };
528
- const result = action === 'start' ? phaseManifest.recordPhaseStart(opts) : phaseManifest.recordPhaseCommits(opts);
529
- output(result, raw, phaseManifest.renderManifestResult(result));
530
- }
531
- function cmdPhaseTier(cwd, args, raw) {
532
- const usage = 'Usage: phase tier <phase> [--tier light|standard|heavy]';
533
- const parsed = (0, phase_tier_cjs_1.parseTierFlag)(args.filter((a) => a !== '--raw'));
534
- if ('error' in parsed) {
535
- error(`${parsed.error}. ${usage}`, ERROR_REASON.USAGE);
536
- return;
537
- }
538
- if (parsed.rest.length !== 1 || parsed.rest[0].startsWith('--')) {
539
- error(parsed.rest.length === 0 ? `phase required for phase tier. ${usage}` : `phase tier takes one phase and --tier. ${usage}`, ERROR_REASON.USAGE);
540
- return;
541
- }
542
- const phaseArg = parsed.rest[0];
543
- output({ phase: phaseArg, ...(0, phase_tier_cjs_1.resolvePhaseTierForProject)(cwd, phaseArg, parsed.tier) }, raw);
544
- }
545
- /**
546
- * `phase.tier-propose <phase> [--record] [--tier X]`: propose a tier from the
547
- * phase's plans (`files_modified`) or, before planning, its requirement tags
548
- * and ROADMAP text. `--record` writes it to the phase's ROADMAP `**Tier:**`
549
- * field, marked `(proposed: …)`, unless the operator already chose one;
550
- * `--record --tier X` records X as the operator's choice.
551
- */
552
- function cmdPhaseTierPropose(cwd, args, raw) {
553
- const usage = 'Usage: phase tier-propose <phase> [--record] [--tier light|standard|heavy]';
554
- const parsed = (0, phase_tier_cjs_1.parseTierFlag)(args.filter((a) => a !== '--raw'));
555
- if ('error' in parsed) {
556
- error(`${parsed.error}. ${usage}`, ERROR_REASON.USAGE);
557
- return;
558
- }
559
- const doRecord = parsed.rest.includes('--record');
560
- const positional = parsed.rest.filter((a) => a !== '--record');
561
- if (positional.length !== 1 || positional[0].startsWith('--')) {
562
- 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);
563
- return;
564
- }
565
- const phaseArg = positional[0];
566
- const record = (0, phase_tier_cjs_1.readPhaseRecord)(cwd, phaseArg);
567
- if (record.section === null) {
568
- output({ phase: phaseArg, error: 'Phase not found in ROADMAP.md' }, raw);
569
- return;
570
- }
571
- const built = buildPhasePlanIndex(cwd, phaseArg);
572
- const planRows = built.fatal === undefined && built.phaseDir !== null
573
- ? built.result['plans'].map((p) => ({ id: p['id'], files_modified: p['files_modified'] ?? [] }))
574
- : [];
575
- const reqMatch = record.section.match(/^\*\*Requirements:?\*\*[^\S\n]*:?[^\S\n]*([^\n]*)$/m);
576
- const requirements = reqMatch ? reqMatch[1].replace(/[[\]]/g, '').split(',').map((s) => s.trim()).filter((s) => s !== '' && s !== 'TBD') : [];
577
- const proposal = (0, phase_tier_cjs_1.proposePhaseTier)({ plans: planRows, requirements, roadmapText: record.section });
578
- let recorded = null;
579
- if (doRecord) {
580
- recorded = parsed.tier !== null
581
- ? (0, phase_tier_cjs_1.recordPhaseTier)(cwd, phaseArg, parsed.tier, { proposed: false })
582
- : (0, phase_tier_cjs_1.recordPhaseTier)(cwd, phaseArg, proposal.tier, { proposed: true, reason: proposal.reason });
583
- }
584
- const after = (0, phase_tier_cjs_1.resolvePhaseTierForProject)(cwd, phaseArg, parsed.tier);
585
- output({
586
- phase: phaseArg,
587
- proposal,
588
- phase_record: record.tier !== null ? { tier: record.tier, proposed: record.proposed } : null,
589
- recorded,
590
- effective: { tier: after.tier, source: after.source, suppressed: after.suppressed },
591
- }, raw);
592
- }
593
- function buildPhasePlanIndex(cwd, phase) {
594
407
  const phasesDir = node_path_1.default.join(planningDir(cwd), 'phases');
595
408
  const normalized = normalizePhaseName(phase);
596
409
  let phaseDir = null;
@@ -611,11 +424,8 @@ function buildPhasePlanIndex(cwd, phase) {
611
424
  // phases dir doesn't exist
612
425
  }
613
426
  if (!phaseDir) {
614
- return {
615
- result: { phase: normalized, error: 'Phase not found', plans: [], waves: {}, incomplete: [], has_checkpoints: false },
616
- phaseDir: null,
617
- extras: new Map(),
618
- };
427
+ output({ phase: normalized, error: 'Phase not found', plans: [], waves: {}, incomplete: [], has_checkpoints: false }, raw);
428
+ return;
619
429
  }
620
430
  void phaseDirName; // used only to set phaseDir above
621
431
  const phaseFiles = node_fs_1.default.readdirSync(phaseDir);
@@ -637,13 +447,6 @@ function buildPhasePlanIndex(cwd, phase) {
637
447
  const xmlTasks = content.match(/<task[\s>]/gi) || [];
638
448
  const mdTasks = content.match(/##\s*Task\s*\d+/gi) || [];
639
449
  const taskCount = xmlTasks.length || mdTasks.length;
640
- const taskTypes = xmlTasks.length > 0
641
- ? [...content.matchAll(/<task(\s[^>]*)?>/gi)].map((m) => /type=["']?([\w:-]+)/i.exec(m[1] ?? '')?.[1] ?? 'auto')
642
- : mdTasks.map(() => 'auto');
643
- const fmString = (key) => {
644
- const value = fm[key];
645
- return typeof value === 'string' && value.trim() !== '' ? value.trim() : null;
646
- };
647
450
  const parsedWave = parseInt(fm['wave'], 10);
648
451
  const declaredWave = Number.isNaN(parsedWave) ? null : parsedWave;
649
452
  let dependsOn = [];
@@ -666,10 +469,6 @@ function buildPhasePlanIndex(cwd, phase) {
666
469
  filesModified = Array.isArray(fmFiles) ? fmFiles.map(String) : [String(fmFiles)];
667
470
  }
668
471
  const hasSummary = completedPlanIds.has(planId) || completedPlanIds.has(extractCanonicalPlanId(planFile));
669
- const fmReqs = fm['requirements'];
670
- const requirements = (Array.isArray(fmReqs) ? fmReqs.map(String) : typeof fmReqs === 'string' && fmReqs.trim() !== '' ? fmReqs.split(/[\s,]+/) : [])
671
- .map((r) => r.trim())
672
- .filter((r) => r !== '' && r !== '[]');
673
472
  rawPlans.push({
674
473
  id: planId,
675
474
  declaredWave,
@@ -679,13 +478,6 @@ function buildPhasePlanIndex(cwd, phase) {
679
478
  filesModified,
680
479
  taskCount,
681
480
  hasSummary,
682
- title: (0, plan_graph_cjs_1.readPlanTitle)(fm['title']),
683
- serialGroup: fmString('serial_group'),
684
- chainReason: fmString('chain_reason'),
685
- taskTypes,
686
- requirements,
687
- // eslint-disable-next-line @typescript-eslint/no-base-to-string -- FrontmatterValue scalar-to-string
688
- gapClosure: String(fm['gap_closure'] ?? '').trim() === 'true',
689
481
  });
690
482
  }
691
483
  // ── Pass 2: topological level assignment via depends_on DAG ──────────────
@@ -694,31 +486,18 @@ function buildPhasePlanIndex(cwd, phase) {
694
486
  const lower = p.id.toLowerCase();
695
487
  const existing = seenLower.get(lower);
696
488
  if (existing !== undefined) {
697
- return {
698
- 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.`,
699
- };
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;
700
491
  }
701
492
  seenLower.set(lower, p.id);
702
493
  }
703
494
  const planMap = new Map(rawPlans.map((p) => [p.id.toLowerCase(), p]));
704
495
  const canonicalToId = new Map(rawPlans.map((p) => [extractCanonicalPlanId(p.id).toLowerCase(), p.id]));
705
- // Wave + serial-group assignment and chain metrics: single owner is plan-graph.cts.
706
- const resolveDep = (dep) => {
707
- const lower = String(dep).toLowerCase();
708
- return planMap.has(lower) ? planMap.get(lower).id : canonicalToId.get(lower);
709
- };
710
- const graph = (0, plan_graph_cjs_1.computePlanGraph)(rawPlans.map((p) => ({
711
- id: p.id,
712
- title: p.title,
713
- dependsOn: p.dependsOn.map(resolveDep).filter((d) => d !== undefined),
714
- filesModified: p.filesModified,
715
- serialGroup: p.serialGroup,
716
- chainReason: p.chainReason,
717
- taskTypes: p.taskTypes,
718
- })));
719
- if (graph.cycle) {
720
- const cycleNodes = graph.cycle;
721
- 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;
722
501
  }
723
502
  // ── Pass 3: determine lowest bucket key and build output ─────────────────
724
503
  const anyWaveZero = rawPlans.some((p) => p.declaredWave === 0);
@@ -735,16 +514,14 @@ function buildPhasePlanIndex(cwd, phase) {
735
514
  if (!rawPlan.hasSummary) {
736
515
  incomplete.push(rawPlan.id);
737
516
  }
738
- const computedWave = (graph.level[rawPlan.id] ?? 0) + levelOffset;
517
+ const computedWave = (level.get(rawPlan.id) ?? 0) + levelOffset;
739
518
  const effectiveWave = computedWave;
740
519
  if (rawPlan.declaredWave !== null && rawPlan.declaredWave !== computedWave) {
741
520
  warnings.push(`Plan ${rawPlan.id}: declared wave: ${rawPlan.declaredWave} but depends_on DAG places it in wave ${computedWave}`);
742
521
  }
743
522
  const plan = {
744
523
  id: rawPlan.id,
745
- title: rawPlan.title,
746
524
  wave: effectiveWave,
747
- serial_group: graph.serialGroupOf[rawPlan.id] ?? null,
748
525
  depends_on: rawPlan.dependsOn.map((dep) => {
749
526
  const lower = String(dep).toLowerCase();
750
527
  return planMap.has(lower) ? planMap.get(lower).id : dep;
@@ -766,28 +543,14 @@ function buildPhasePlanIndex(cwd, phase) {
766
543
  phase: normalized,
767
544
  plans,
768
545
  waves,
769
- serial_groups: Object.fromEntries(graph.serialGroups.map((g) => [g.name, { wave: g.level + levelOffset, plans: g.plans, shared_files: g.shared_files }])),
770
- graph: {
771
- plan_count: graph.plan_count,
772
- wave_count: graph.wave_count,
773
- longest_chain: graph.longest_chain,
774
- chain_check: graph.chain_check,
775
- implicit_edges: graph.implicitEdges,
776
- merge_candidates: graph.merge_candidates,
777
- },
778
546
  incomplete,
779
547
  has_checkpoints: hasCheckpoints,
780
548
  };
781
- warnings.push(...graph.warnings);
782
549
  if (planNamingWarning)
783
550
  result['warning'] = planNamingWarning;
784
551
  if (warnings.length > 0)
785
552
  result['warnings'] = warnings;
786
- return {
787
- result,
788
- phaseDir,
789
- extras: new Map(rawPlans.map((p) => [p.id, { requirements: p.requirements, gapClosure: p.gapClosure }])),
790
- };
553
+ output(result, raw);
791
554
  }
792
555
  function cmdPhaseAdd(cwd, description, raw, customId) {
793
556
  if (!description) {
@@ -1706,11 +1469,6 @@ module.exports = {
1706
1469
  cmdPhaseNextDecimal,
1707
1470
  cmdFindPhase,
1708
1471
  cmdPhasePlanIndex,
1709
- cmdPhaseDispatchPlan,
1710
- cmdPhaseInstallRun,
1711
- cmdPhaseTier,
1712
- cmdPhaseTierPropose,
1713
- cmdPhaseManifest,
1714
1472
  cmdPhaseAdd,
1715
1473
  cmdPhaseAddBatch,
1716
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