@pareto4/pp-core 1.11.1 → 1.12.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 (257) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +31 -0
  3. package/agents/pp-code-fixer.md +1 -1
  4. package/agents/pp-debug-session-manager.md +1 -1
  5. package/agents/pp-debugger.md +1 -1
  6. package/agents/pp-executor.md +20 -10
  7. package/agents/pp-intel-updater.md +1 -1
  8. package/agents/pp-phase-researcher.md +1 -1
  9. package/agents/pp-plan-checker.md +5 -5
  10. package/agents/pp-planner.md +10 -24
  11. package/agents/pp-project-researcher.md +1 -1
  12. package/agents/pp-research-synthesizer.md +1 -1
  13. package/agents/pp-ui-researcher.md +1 -1
  14. package/agents/pp-verifier.md +8 -4
  15. package/bin/install.js +429 -82
  16. package/commands/pp-autonomous.md +8 -6
  17. package/commands/pp-discovery-to-build.md +1 -1
  18. package/commands/pp-discovery.md +3 -2
  19. package/commands/pp-discuss-phase.md +2 -2
  20. package/commands/pp-execute-phase.md +3 -1
  21. package/commands/pp-graphify.md +5 -5
  22. package/commands/pp-import.md +1 -1
  23. package/commands/pp-map-codebase.md +1 -1
  24. package/commands/pp-ns-project.md +2 -3
  25. package/commands/pp-ns-workflow.md +1 -1
  26. package/commands/pp-plan-phase.md +2 -1
  27. package/commands/pp-verify-work.md +2 -1
  28. package/gemini-extension.json +1 -1
  29. package/hooks/dist/pp-cursor-session-start.js +2 -2
  30. package/hooks/dist/pp-session-state.sh +1 -1
  31. package/hooks/dist/pp-statusline.js +21 -2
  32. package/hooks/pp-cursor-session-start.js +2 -2
  33. package/hooks/pp-session-state.sh +1 -1
  34. package/hooks/pp-statusline.js +21 -2
  35. package/package.json +18 -5
  36. package/pareto-skills/pp-project-setup/SKILL.md +5 -63
  37. package/pp-core/bin/detect-build-commands.sh +182 -0
  38. package/pp-core/bin/lib/capability-activation.cjs +19 -9
  39. package/pp-core/bin/lib/capability-registry.cjs +65 -7
  40. package/pp-core/bin/lib/check-command-router.cjs +79 -38
  41. package/pp-core/bin/lib/clusters.cjs +2 -3
  42. package/pp-core/bin/lib/command-aliases.cjs +36 -0
  43. package/pp-core/bin/lib/command-arg-projection.cjs +99 -1
  44. package/pp-core/bin/lib/commands.cjs +21 -6
  45. package/pp-core/bin/lib/compose-bindings.cjs +70 -6
  46. package/pp-core/bin/lib/config-loader.cjs +151 -8
  47. package/pp-core/bin/lib/config-schema.cjs +16 -0
  48. package/pp-core/bin/lib/config.cjs +142 -27
  49. package/pp-core/bin/lib/core-utils.cjs +43 -7
  50. package/pp-core/bin/lib/discovery-input-check.cjs +218 -0
  51. package/pp-core/bin/lib/discovery-rebaseline.cjs +1372 -0
  52. package/pp-core/bin/lib/discovery-research-track.cjs +979 -0
  53. package/pp-core/bin/lib/discovery-rollup.cjs +476 -0
  54. package/pp-core/bin/lib/discovery-screens.cjs +1995 -0
  55. package/pp-core/bin/lib/discovery-sweep.cjs +390 -0
  56. package/pp-core/bin/lib/discovery-table.cjs +98 -0
  57. package/pp-core/bin/lib/dispatch-plan.cjs +472 -0
  58. package/pp-core/bin/lib/frontmatter.cjs +17 -2
  59. package/pp-core/bin/lib/init-command-router.cjs +8 -5
  60. package/pp-core/bin/lib/init-contract.cjs +93 -4
  61. package/pp-core/bin/lib/init.cjs +124 -134
  62. package/pp-core/bin/lib/install-profiles.cjs +5 -0
  63. package/pp-core/bin/lib/install-run.cjs +536 -0
  64. package/pp-core/bin/lib/io.cjs +15 -0
  65. package/pp-core/bin/lib/loop-resolver.cjs +40 -2
  66. package/pp-core/bin/lib/milestone.cjs +345 -48
  67. package/pp-core/bin/lib/phase-command-router.cjs +23 -1
  68. package/pp-core/bin/lib/phase-locator.cjs +15 -6
  69. package/pp-core/bin/lib/phase-scope.cjs +91 -0
  70. package/pp-core/bin/lib/phase-tier.cjs +467 -0
  71. package/pp-core/bin/lib/phase.cjs +247 -21
  72. package/pp-core/bin/lib/phases-command-router.cjs +2 -0
  73. package/pp-core/bin/lib/plan-graph.cjs +368 -0
  74. package/pp-core/bin/lib/planning-workspace.cjs +53 -0
  75. package/pp-core/bin/lib/platform-bindings.cjs +32 -2
  76. package/pp-core/bin/lib/profile-output.cjs +11 -3
  77. package/pp-core/bin/lib/prohibition-enforcement.cjs +112 -0
  78. package/pp-core/bin/lib/roadmap-parser.cjs +366 -99
  79. package/pp-core/bin/lib/roadmap.cjs +141 -39
  80. package/pp-core/bin/lib/runtime-hooks-surface.cjs +1 -1
  81. package/pp-core/bin/lib/shell-command-projection.cjs +24 -0
  82. package/pp-core/bin/lib/state-command-router.cjs +82 -13
  83. package/pp-core/bin/lib/state-document.cjs +124 -7
  84. package/pp-core/bin/lib/state.cjs +784 -257
  85. package/pp-core/bin/lib/template.cjs +22 -6
  86. package/pp-core/bin/lib/vault-command-router.cjs +57 -6
  87. package/pp-core/bin/lib/vault-practices.cjs +6 -36
  88. package/pp-core/bin/lib/vault-write-back.cjs +20 -4
  89. package/pp-core/bin/lib/vault.cjs +592 -23
  90. package/pp-core/bin/lib/verify.cjs +53 -31
  91. package/pp-core/bin/lib/workstream.cjs +1 -1
  92. package/pp-core/bin/lib/worktree-safety.cjs +26 -15
  93. package/pp-core/bin/portable-timeout.sh +89 -0
  94. package/pp-core/bin/pp-tools.cjs +149 -9
  95. package/pp-core/bin/shared/config-defaults.manifest.json +7 -0
  96. package/pp-core/bin/shared/config-schema.manifest.json +3 -0
  97. package/pp-core/references/autonomous-best-effort.md +461 -0
  98. package/pp-core/references/autonomous-lifecycle.md +146 -0
  99. package/pp-core/references/autonomous-smart-discuss.md +16 -12
  100. package/pp-core/references/checkpoints.md +1 -1
  101. package/pp-core/references/discovery-id-conventions.md +202 -0
  102. package/pp-core/references/discovery-rebaseline.md +254 -0
  103. package/pp-core/references/discovery-research-track.md +283 -0
  104. package/pp-core/references/discovery-traceability.md +400 -0
  105. package/pp-core/references/loop-hook-dispatch.md +14 -0
  106. package/pp-core/references/mandatory-initial-read.md +1 -1
  107. package/pp-core/references/planner-gap-closure.md +1 -0
  108. package/pp-core/references/planner-human-verify-mode.md +1 -1
  109. package/pp-core/references/planner-load-graph-context.md +1 -1
  110. package/pp-core/references/planner-plan-graph.md +66 -0
  111. package/pp-core/references/planner-tdd.md +1 -0
  112. package/pp-core/references/planning-config.md +8 -0
  113. package/pp-core/references/sketch-tooling.md +4 -0
  114. package/pp-core/references/tdd.md +1 -0
  115. package/pp-core/templates/claude-md.md +1 -1
  116. package/pp-core/templates/phase-prompt.md +6 -2
  117. package/pp-core/templates/prototype-review/review.js +303 -0
  118. package/pp-core/templates/state.md +6 -0
  119. package/pp-core/workflows/_runtime-launcher.snippet.sh +1 -1
  120. package/pp-core/workflows/add-backlog.md +1 -1
  121. package/pp-core/workflows/add-phase.md +1 -1
  122. package/pp-core/workflows/add-tests.md +1 -1
  123. package/pp-core/workflows/add-todo.md +1 -1
  124. package/pp-core/workflows/ai-integration-phase.md +2 -2
  125. package/pp-core/workflows/audit-fix.md +12 -16
  126. package/pp-core/workflows/audit-milestone.md +2 -2
  127. package/pp-core/workflows/audit-uat.md +1 -1
  128. package/pp-core/workflows/autonomous.md +68 -306
  129. package/pp-core/workflows/check-todos.md +1 -1
  130. package/pp-core/workflows/cleanup.md +1 -1
  131. package/pp-core/workflows/code-review-fix.md +2 -2
  132. package/pp-core/workflows/code-review.md +20 -8
  133. package/pp-core/workflows/complete-milestone.md +41 -60
  134. package/pp-core/workflows/debug.md +1 -1
  135. package/pp-core/workflows/diagnose-issues.md +1 -1
  136. package/pp-core/workflows/discovery-sprint/gap-analysis.md +140 -0
  137. package/pp-core/workflows/discovery-sprint/input-package.md +140 -0
  138. package/pp-core/workflows/discovery-sprint/prefill.md +92 -0
  139. package/pp-core/workflows/discovery-sprint/prototype.md +257 -0
  140. package/pp-core/workflows/discovery-sprint/rebaseline.md +276 -0
  141. package/pp-core/workflows/discovery-sprint/session-13-gates.md +24 -0
  142. package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +92 -0
  143. package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +98 -0
  144. package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +215 -0
  145. package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +146 -0
  146. package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +98 -0
  147. package/pp-core/workflows/discovery-sprint/sessions/06-security.md +107 -0
  148. package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +98 -0
  149. package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +85 -0
  150. package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +109 -0
  151. package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +119 -0
  152. package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +92 -0
  153. package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +98 -0
  154. package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +205 -0
  155. package/pp-core/workflows/discovery-sprint/templates/context-template.md +266 -0
  156. package/pp-core/workflows/discovery-sprint.md +79 -1714
  157. package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +5 -5
  158. package/pp-core/workflows/discovery-to-build/steps/screen-inventory-gate.md +70 -0
  159. package/pp-core/workflows/discovery-to-build/templates/discovery-references.md +54 -0
  160. package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +184 -0
  161. package/pp-core/workflows/discovery-to-build.md +21 -238
  162. package/pp-core/workflows/discuss-phase/modes/advisor.md +1 -1
  163. package/pp-core/workflows/discuss-phase/modes/auto.md +1 -1
  164. package/pp-core/workflows/discuss-phase/modes/chain.md +1 -1
  165. package/pp-core/workflows/discuss-phase-assumptions.md +1 -1
  166. package/pp-core/workflows/discuss-phase.md +11 -4
  167. package/pp-core/workflows/do.md +4 -4
  168. package/pp-core/workflows/docs-update.md +1 -1
  169. package/pp-core/workflows/edit-phase.md +1 -1
  170. package/pp-core/workflows/eval-review.md +1 -1
  171. package/pp-core/workflows/execute-phase/steps/checkpoint-handling.md +45 -0
  172. package/pp-core/workflows/execute-phase/steps/client-context-gate.md +1 -1
  173. package/pp-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
  174. package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +90 -0
  175. package/pp-core/workflows/execute-phase/steps/install-run.md +29 -0
  176. package/pp-core/workflows/execute-phase/steps/phase-close-rebaseline.md +23 -0
  177. package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +76 -77
  178. package/pp-core/workflows/execute-phase/steps/serial-group-dispatch.md +72 -0
  179. package/pp-core/workflows/execute-phase/steps/vault-fetch.md +5 -5
  180. package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +19 -112
  181. package/pp-core/workflows/execute-phase.md +104 -218
  182. package/pp-core/workflows/execute-plan.md +6 -2
  183. package/pp-core/workflows/explore.md +1 -1
  184. package/pp-core/workflows/extract-learnings.md +1 -1
  185. package/pp-core/workflows/forensics.md +4 -4
  186. package/pp-core/workflows/graduation.md +1 -1
  187. package/pp-core/workflows/health.md +1 -1
  188. package/pp-core/workflows/help/modes/brief.md +1 -0
  189. package/pp-core/workflows/help/modes/default.md +2 -1
  190. package/pp-core/workflows/help/modes/full.md +12 -5
  191. package/pp-core/workflows/import.md +1 -1
  192. package/pp-core/workflows/ingest-docs.md +1 -1
  193. package/pp-core/workflows/init.md +18 -2
  194. package/pp-core/workflows/insert-phase.md +1 -1
  195. package/pp-core/workflows/list-workspaces.md +1 -1
  196. package/pp-core/workflows/manager.md +3 -3
  197. package/pp-core/workflows/map-codebase.md +2 -2
  198. package/pp-core/workflows/milestone-summary.md +4 -4
  199. package/pp-core/workflows/mvp-phase.md +1 -1
  200. package/pp-core/workflows/new-milestone.md +7 -8
  201. package/pp-core/workflows/new-project.md +6 -1
  202. package/pp-core/workflows/new-workspace.md +1 -1
  203. package/pp-core/workflows/next.md +2 -2
  204. package/pp-core/workflows/note.md +2 -2
  205. package/pp-core/workflows/pause-work.md +1 -1
  206. package/pp-core/workflows/plan-milestone-gaps.md +1 -1
  207. package/pp-core/workflows/plan-phase/steps/chunked-planning.md +111 -0
  208. package/pp-core/workflows/plan-phase/steps/phase-tier.md +34 -0
  209. package/pp-core/workflows/plan-phase.md +43 -167
  210. package/pp-core/workflows/plan-review-convergence.md +2 -2
  211. package/pp-core/workflows/plant-seed.md +1 -1
  212. package/pp-core/workflows/pr-branch.md +1 -1
  213. package/pp-core/workflows/profile-user.md +1 -1
  214. package/pp-core/workflows/progress.md +3 -3
  215. package/pp-core/workflows/quick.md +4 -2
  216. package/pp-core/workflows/remove-phase.md +1 -1
  217. package/pp-core/workflows/remove-workspace.md +1 -1
  218. package/pp-core/workflows/resume-project.md +6 -12
  219. package/pp-core/workflows/review.md +1 -1
  220. package/pp-core/workflows/scan.md +1 -1
  221. package/pp-core/workflows/secure-phase.md +2 -2
  222. package/pp-core/workflows/settings-advanced.md +1 -1
  223. package/pp-core/workflows/settings-integrations.md +1 -1
  224. package/pp-core/workflows/settings.md +1 -1
  225. package/pp-core/workflows/ship.md +26 -9
  226. package/pp-core/workflows/sketch-wrap-up.md +1 -1
  227. package/pp-core/workflows/sketch.md +2 -1
  228. package/pp-core/workflows/spec-phase.md +1 -1
  229. package/pp-core/workflows/spike-wrap-up.md +1 -1
  230. package/pp-core/workflows/spike.md +1 -1
  231. package/pp-core/workflows/stats.md +2 -2
  232. package/pp-core/workflows/thread.md +1 -1
  233. package/pp-core/workflows/transition.md +5 -9
  234. package/pp-core/workflows/ui-phase.md +23 -2
  235. package/pp-core/workflows/ui-review.md +1 -1
  236. package/pp-core/workflows/ultraplan-phase.md +2 -2
  237. package/pp-core/workflows/validate-phase.md +2 -2
  238. package/pp-core/workflows/vault-write-back.md +1 -1
  239. package/pp-core/workflows/verify-phase.md +29 -26
  240. package/pp-core/workflows/verify-work.md +14 -10
  241. package/scripts/base64-scan.sh +7 -41
  242. package/scripts/changeset/cli.cjs +25 -5
  243. package/scripts/dev-env.cjs +462 -0
  244. package/scripts/lib/allowlist-ratchet.cjs +103 -1
  245. package/scripts/lib/size-budget.cjs +311 -0
  246. package/scripts/lint-neutrality-names.cjs +694 -0
  247. package/scripts/lint-org-coordinates.allowlist.json +35 -0
  248. package/scripts/lint-org-coordinates.cjs +426 -0
  249. package/scripts/lint-skill-deps.cjs +48 -9
  250. package/scripts/lint-test-file-count.cjs +17 -2
  251. package/scripts/prompt-injection-scan.sh +41 -20
  252. package/scripts/sync-manifest-versions.cjs +41 -7
  253. package/scripts/sync-runtime-launcher.cjs +106 -1
  254. package/scripts/update-size-baseline.cjs +274 -30
  255. package/scripts/workflow-size.cjs +61 -9
  256. package/commands/pp-build-handoff.md +0 -46
  257. package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +0 -94
@@ -19,8 +19,8 @@ const { output, error, ERROR_REASON } = io;
19
19
  const planningWorkspaceMod = require("./planning-workspace.cjs");
20
20
  const { planningDir } = planningWorkspaceMod;
21
21
  // eslint-disable-next-line @typescript-eslint/no-require-imports
22
- const phaseLocatorMod = require("./phase-locator.cjs");
23
- const { findPhaseInternal } = phaseLocatorMod;
22
+ const phaseScopeMod = require("./phase-scope.cjs");
23
+ const { resolvePhaseForRead } = phaseScopeMod;
24
24
  const decisions_cjs_1 = require("./decisions.cjs");
25
25
  const ui_safety_gate_cjs_1 = require("./ui-safety-gate.cjs");
26
26
  const semver_compare_cjs_1 = require("./semver-compare.cjs");
@@ -39,7 +39,7 @@ const { runGapAnalysis } = gapCheckerModule;
39
39
  const prohibition_enforcement_cjs_1 = require("./prohibition-enforcement.cjs");
40
40
  // eslint-disable-next-line @typescript-eslint/no-require-imports
41
41
  const vaultModule = require("./vault.cjs");
42
- const { vaultStatus, readManifest } = vaultModule;
42
+ const { vaultStatus, readManifest, computeVaultVerdict, verdictFields, renderVaultCard, VAULT_GATE_POINTS } = vaultModule;
43
43
  const git_base_branch_cjs_1 = require("./git-base-branch.cjs");
44
44
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
45
45
  // eslint-disable-next-line @typescript-eslint/no-require-imports
@@ -371,7 +371,7 @@ function cmdDecisionCoverageVerify(projectDir, args, raw) {
371
371
  * Invocable as: pp_run check ui-plan-gate <phase>
372
372
  *
373
373
  * Uses checkUiPresence from ui-safety-gate.cjs — does NOT reimplement frontend detection.
374
- * Uses getRoadmapPhaseWithFallback + findPhaseInternal from leaf modules for phase data.
374
+ * Uses getRoadmapPhaseWithFallback + the phase-scope owner's reader answer for phase data.
375
375
  */
376
376
  function findUiSpecInDir(phaseDir) {
377
377
  if (!phaseDir || !node_fs_1.default.existsSync(phaseDir))
@@ -398,7 +398,7 @@ function findUiSpecInDir(phaseDir) {
398
398
  * surface the miss — we do NOT silently degrade to frontend:false if the roadmap
399
399
  * exists but the phase header is absent.
400
400
  * (b) Runs checkUiPresence (frontend detection) — no reimplementation.
401
- * (c) Resolves the phase directory via findPhaseInternal (phase-locator.cjs); checks for *-UI-SPEC.md.
401
+ * (c) Resolves the phase directory via resolvePhaseForRead (phase-scope.cjs); checks for *-UI-SPEC.md.
402
402
  *
403
403
  * Returns: { frontend, hasUiSpec, block, uiSpecPath, phaseLookupFailed }
404
404
  * block = frontend && !hasUiSpec
@@ -432,12 +432,12 @@ function computeUiPlanGate(projectDir, phase) {
432
432
  // (b) Run checkUiPresence (frontend detection) — reuse existing helper; no reimplementation
433
433
  const presenceResult = (0, ui_safety_gate_cjs_1.checkUiPresence)(phaseSection);
434
434
  const frontend = presenceResult.hasUI;
435
- // (c) Resolve phase directory via findPhaseInternal and check for *-UI-SPEC.md
435
+ // (c) Resolve phase directory via the phase-scope owner and check for *-UI-SPEC.md
436
436
  let phaseDir = '';
437
437
  try {
438
- const result = findPhaseInternal(projectDir, phase);
438
+ const result = resolvePhaseForRead(projectDir, phase);
439
439
  if (result && typeof result === 'object') {
440
- // findPhaseInternal returns { directory: '<relative-posix-path>', ... }
440
+ // the owner returns the raw locator's { directory: '<relative-posix-path>', ... }
441
441
  // directory is relative to cwd — resolve it to absolute.
442
442
  const relDir = typeof result['directory'] === 'string' ? result['directory'] : '';
443
443
  if (relDir) {
@@ -499,7 +499,7 @@ const UI_PATH_PATTERNS_RE = /\/(components|pages|views|screens|layouts|ui|fronte
499
499
  * same lookup as computeUiPlanGate — to determine if this is a frontend phase.
500
500
  * (b) Runs checkUiPresence (frontend detection) — no reimplementation.
501
501
  * (c) Checks git diff HEAD~1..HEAD for UI file changes in the current worktree.
502
- * (d) Resolves the phase directory via findPhaseInternal (phase-locator.cjs); checks for *-UI-SPEC.md.
502
+ * (d) Resolves the phase directory via resolvePhaseForRead (phase-scope.cjs); checks for *-UI-SPEC.md.
503
503
  *
504
504
  * Returns: { frontend, hasUiFiles, hasUiSpec, block, message?, phaseLookupFailed? }
505
505
  * block = frontend && hasUiFiles && !hasUiSpec
@@ -543,7 +543,7 @@ function computeUiSafetyGate(projectDir, phase) {
543
543
  // (d) Resolve phase directory and check for *-UI-SPEC.md (same as computeUiPlanGate)
544
544
  let phaseDir = '';
545
545
  try {
546
- const result = findPhaseInternal(projectDir, phase);
546
+ const result = resolvePhaseForRead(projectDir, phase);
547
547
  if (result && typeof result === 'object') {
548
548
  const relDir = typeof result['directory'] === 'string' ? result['directory'] : '';
549
549
  if (relDir) {
@@ -587,7 +587,7 @@ function cmdTddReviewCheckpoint(projectDir, args, raw) {
587
587
  // Resolve phase directory
588
588
  let phaseDir = '';
589
589
  try {
590
- const result = findPhaseInternal(projectDir, phase);
590
+ const result = resolvePhaseForRead(projectDir, phase);
591
591
  if (result && typeof result === 'object') {
592
592
  const relDir = typeof result['directory'] === 'string' ? result['directory'] : '';
593
593
  if (relDir)
@@ -921,23 +921,28 @@ function cmdShipPlatformDrift(cwd, args, raw) {
921
921
  output({ status, block: status === 'block', severity, pinned, latest, present: true }, raw, undefined);
922
922
  }
923
923
  /**
924
- * cmdVaultStatus — advisory gate-query handler for `check vault.status` / `check vault-status`.
924
+ * cmdVaultStatus — gate-query handler for `check vault.status` / `check vault-status`.
925
925
  *
926
- * Contract (D-01/D-02 from 13-CONTEXT.md):
927
- * - PURE manifest read: never fetches, never spawns, never throws, always exits 0.
928
- * - `block` is ALWAYS `false` (advisory only — never computes block:true).
929
- * - Inert when `.platform-bindings.json` is absent (same boundary as cmdShipPlatformDrift).
930
- * - Reuses `vaultStatus` + `readManifest` (no bespoke manifest parsing).
926
+ * Contract:
927
+ * - PURE read: never fetches, never spawns, never throws, exits 0 on every
928
+ * verdict (a usage error — an unknown `--point` — is the only non-zero exit).
929
+ * - Pack 6 D-01: the verdict is decided by ONE owner, `computeVaultVerdict`
930
+ * (src/vault.cts). This handler spreads the owner's `VAULT_VERDICT_FIELDS`
931
+ * over the legacy envelope; it decides nothing itself.
932
+ * - Legacy envelope (§8 of v1.7.0-DESIGN), kept key for key:
933
+ * { status, block, present, tier, source, fallbackReason, repo, ref,
934
+ * resolvedSha, fileCount, stale, fetchedAt }
935
+ * `block` now comes from the owner: `enabled && required && verdict !==
936
+ * 'loaded'` (D-03) — false whenever `vault.required` is false, as before.
937
+ * `repo` is display-safe (D-07).
938
+ * - `--card --point plan|execute` (D-04) prints the human card the workflow
939
+ * relays verbatim; it prints nothing when the vault is inert (D-06).
931
940
  *
932
- * Verdict envelope (§8 of v1.7.0-DESIGN):
933
- * { status, block: false, present, tier, source, fallbackReason, repo, ref,
934
- * resolvedSha, fileCount, stale }
935
- *
936
- * Invocable as: pp_run check vault.status [--raw]
941
+ * Invocable as: pp_run check vault.status [--raw] [--card --point plan|execute]
937
942
  * The dot→hyphen normalization in routeCheckCommand maps "vault.status" to
938
943
  * the "vault-status" branch.
939
944
  */
940
- function cmdVaultStatus(cwd, raw) {
945
+ function cmdVaultStatus(cwd, args, raw) {
941
946
  // Step 1: Resolve project root (same approach as cmdShipPlatformDrift).
942
947
  let root;
943
948
  try {
@@ -946,31 +951,42 @@ function cmdVaultStatus(cwd, raw) {
946
951
  catch {
947
952
  root = cwd;
948
953
  }
949
- // Step 2: Inert guard — absent .platform-bindings.json → inert (opt-in default).
950
- // This mirrors the cmdShipPlatformDrift 939–943 inert path exactly.
954
+ const verdict = computeVaultVerdict(root);
955
+ if (args.includes('--card')) {
956
+ const pointIdx = args.indexOf('--point');
957
+ const point = pointIdx === -1 ? undefined : args[pointIdx + 1];
958
+ if (point === undefined || !VAULT_GATE_POINTS.includes(point)) {
959
+ error(`check vault.status --card needs --point ${VAULT_GATE_POINTS.join('|')} (got ${point === undefined ? 'none' : JSON.stringify(point)}).`, ERROR_REASON.SDK_MISSING_ARG);
960
+ return;
961
+ }
962
+ const card = renderVaultCard(verdict, point);
963
+ output({ ...verdictFields(verdict), card }, true, card);
964
+ return;
965
+ }
966
+ const fields = verdictFields(verdict);
967
+ // Step 2: Legacy inert envelope — absent .platform-bindings.json (unchanged keys and values).
951
968
  const bindingsPath = node_path_1.default.join(root, '.platform-bindings.json');
952
969
  if (!node_fs_1.default.existsSync(bindingsPath)) {
953
- output({ status: 'inert', block: false, present: false }, raw, undefined);
970
+ output({ status: 'inert', block: verdict.block, present: false, ...fields }, raw, undefined);
954
971
  return;
955
972
  }
956
- // Step 3: Call vaultStatus(cwd). present:false → miss, advisory, no error.
957
- const vs = vaultStatus(cwd);
973
+ // Step 3: Call vaultStatus(root). present:false → miss.
974
+ const vs = vaultStatus(root);
958
975
  if (!vs.present) {
959
- output({ status: 'miss', block: false, present: false }, raw, undefined);
976
+ output({ status: 'miss', block: verdict.block, present: false, ...fields }, raw, undefined);
960
977
  return;
961
978
  }
962
- // Step 4: Resolve fallbackReason + repo via readManifest (fills the vaultStatus gap).
963
- const vaultDirPath = node_path_1.default.join(cwd, '.planning', 'vault');
964
- const manifest = readManifest(vaultDirPath);
979
+ // Step 4: fallbackReason + repo via readManifest (display-safe since Pack 6 D-07).
980
+ const manifest = readManifest(node_path_1.default.join(root, '.planning', 'vault'));
965
981
  const fallbackReason = manifest?.fallbackReason ?? null;
966
982
  const repo = manifest?.repo ?? null;
967
983
  // Step 5: Derive status from manifest source.
968
984
  const src = vs.source ?? 'miss';
969
985
  const status = (src === 'fetched' || src === 'cache' || src === 'miss') ? src : 'miss';
970
- // Step 6: Emit uniform advisory verdict. block is ALWAYS false (advisory only, D-02).
986
+ // Step 6: Legacy envelope + the owner's verdict fields (block among them).
971
987
  output({
972
988
  status,
973
- block: false,
989
+ block: verdict.block,
974
990
  present: true,
975
991
  tier: vs.tier ?? null,
976
992
  source: vs.source ?? null,
@@ -981,6 +997,7 @@ function cmdVaultStatus(cwd, raw) {
981
997
  fileCount: vs.fileCount ?? 0,
982
998
  stale: vs.stale ?? true,
983
999
  fetchedAt: vs.fetchedAt ?? null,
1000
+ ...fields,
984
1001
  }, raw, undefined);
985
1002
  }
986
1003
  // ─── ship.practices query handler (INJ-03, Phase 28) ──────────────────────────
@@ -1084,7 +1101,7 @@ function chooseShipReviewFile(dir) {
1084
1101
  /**
1085
1102
  * Build the trailing "what did the code review record" pointer line
1086
1103
  * (D-08/D-09). Reads `.planning/STATE.md`'s `current_phase`, resolves the
1087
- * phase directory via `findPhaseInternal` (never a hand-built
1104
+ * phase directory via `resolvePhaseForRead` (never a hand-built
1088
1105
  * `.planning/phases/` path, never a directory glob outside the chosen phase
1089
1106
  * directory), chooses the right REVIEW.md file via `chooseShipReviewFile`,
1090
1107
  * and reads its `practices.total` count. Every step degrades to the soft
@@ -1102,7 +1119,7 @@ function buildShipReviewPointerLine(root) {
1102
1119
  if (currentPhase === undefined || currentPhase === null || currentPhase === '') {
1103
1120
  return SHIP_REVIEW_SOFT_NOTE;
1104
1121
  }
1105
- const phaseResult = findPhaseInternal(root, currentPhase);
1122
+ const phaseResult = resolvePhaseForRead(root, currentPhase);
1106
1123
  if (!phaseResult)
1107
1124
  return SHIP_REVIEW_SOFT_NOTE;
1108
1125
  const phaseDir = node_path_1.default.join(root, phaseResult.directory);
@@ -1415,6 +1432,29 @@ function cmdCheckPracticesReview({ args }, _check, _core) {
1415
1432
  const text = renderedLines.length > 0 ? renderedLines.join('\n') + '\n' : '';
1416
1433
  c.output(text, true, text);
1417
1434
  }
1435
+ /**
1436
+ * Pack 6 D-01: the check router's subcommand table — every subcommand
1437
+ * `routeCheckCommand` dispatches, in its hyphen form. `readsVerdict` marks the
1438
+ * ones that report the vault load verdict; each must spread the owner's
1439
+ * `VAULT_VERDICT_FIELDS` verbatim (tests/vault-status-check.test.cjs
1440
+ * enumerates this table and compares every reader against the owner).
1441
+ */
1442
+ const CHECK_SUBCOMMANDS = Object.freeze([
1443
+ { name: 'auto-mode', readsVerdict: false },
1444
+ { name: 'decision-coverage-plan', readsVerdict: false },
1445
+ { name: 'decision-coverage-verify', readsVerdict: false },
1446
+ { name: 'gap-analysis-plan-post', readsVerdict: false },
1447
+ { name: 'practices-review', readsVerdict: false },
1448
+ { name: 'prohibition-enforcement', readsVerdict: false },
1449
+ { name: 'ship-platform-drift', readsVerdict: false },
1450
+ { name: 'ship-practices', readsVerdict: false },
1451
+ { name: 'tdd-review-checkpoint', readsVerdict: false },
1452
+ { name: 'ui-plan-gate', readsVerdict: false },
1453
+ { name: 'ui-safety-gate', readsVerdict: false },
1454
+ { name: 'vault-status', readsVerdict: true },
1455
+ { name: 'verify-schema-drift', readsVerdict: false },
1456
+ { name: 'verify-codebase-drift', readsVerdict: false },
1457
+ ]);
1418
1458
  function routeCheckCommand({ args, cwd, raw, _check, _core }) {
1419
1459
  // Normalize dots to hyphens in the subcommand so both forms are accepted.
1420
1460
  // This makes `check.query = "ui.plan-gate"` (dotted form in capability.json gates)
@@ -1469,7 +1509,7 @@ function routeCheckCommand({ args, cwd, raw, _check, _core }) {
1469
1509
  // vault.status — advisory vault cache state query (Phase 13, D-01/D-02).
1470
1510
  // Dot-to-hyphen normalization means query "vault.status" routes here.
1471
1511
  // Pure manifest read; block is ALWAYS false; inert when bindings absent.
1472
- cmdVaultStatus(cwd, raw);
1512
+ cmdVaultStatus(cwd, args, raw);
1473
1513
  return;
1474
1514
  }
1475
1515
  if (subcommand === 'verify-schema-drift') {
@@ -1509,7 +1549,7 @@ function routeCheckCommand({ args, cwd, raw, _check, _core }) {
1509
1549
  (0, prohibition_enforcement_cjs_1.routeProhibitionEnforcement)(args, raw);
1510
1550
  return;
1511
1551
  }
1512
- error('Unknown check subcommand. Available: auto-mode, decision-coverage-plan, decision-coverage-verify, gap-analysis-plan-post, practices-review, prohibition-enforcement, ship-platform-drift, ship-practices, tdd-review-checkpoint, ui-plan-gate, ui-safety-gate, vault-status, verify-schema-drift, verify-codebase-drift', ERROR_REASON.SDK_UNKNOWN_COMMAND);
1552
+ error(`Unknown check subcommand. Available: ${CHECK_SUBCOMMANDS.map((c) => c.name).join(', ')}`, ERROR_REASON.SDK_UNKNOWN_COMMAND);
1513
1553
  }
1514
1554
  module.exports = {
1515
1555
  routeCheckCommand,
@@ -1523,4 +1563,5 @@ module.exports = {
1523
1563
  cmdShipPractices,
1524
1564
  cmdVaultStatus,
1525
1565
  cmdCheckPracticesReview,
1566
+ CHECK_SUBCOMMANDS,
1526
1567
  };
@@ -23,10 +23,9 @@ exports.CLUSTERS = Object.freeze({
23
23
  'init',
24
24
  'new-project',
25
25
  'discovery',
26
- // Discovery→build handoff: canonical + retained alias, grouped with the
27
- // other project-lifecycle entrypoints (routed via pp-ns-project).
26
+ // Discovery→build handoff, grouped with the other project-lifecycle
27
+ // entrypoints (routed via pp-ns-project).
28
28
  'discovery-to-build',
29
- 'build-handoff',
30
29
  'discuss-phase',
31
30
  'plan-phase',
32
31
  'execute-phase',
@@ -470,6 +470,14 @@ exports.PHASES_COMMAND_ALIASES = [
470
470
  ],
471
471
  "subcommand": "archive",
472
472
  "mutation": true
473
+ },
474
+ {
475
+ "canonical": "phases.archive-closed",
476
+ "aliases": [
477
+ "phases archive-closed"
478
+ ],
479
+ "subcommand": "archive-closed",
480
+ "mutation": true
473
481
  }
474
482
  ];
475
483
  exports.VALIDATE_COMMAND_ALIASES = [
@@ -690,6 +698,20 @@ exports.NON_FAMILY_COMMAND_ALIASES = [
690
698
  ],
691
699
  "mutation": true
692
700
  },
701
+ {
702
+ "canonical": "phase.dispatch-plan",
703
+ "aliases": [
704
+ "phase dispatch-plan"
705
+ ],
706
+ "mutation": false
707
+ },
708
+ {
709
+ "canonical": "phase.install-run",
710
+ "aliases": [
711
+ "phase install-run"
712
+ ],
713
+ "mutation": true
714
+ },
693
715
  {
694
716
  "canonical": "phase.mvp-mode",
695
717
  "aliases": [
@@ -697,6 +719,20 @@ exports.NON_FAMILY_COMMAND_ALIASES = [
697
719
  ],
698
720
  "mutation": false
699
721
  },
722
+ {
723
+ "canonical": "phase.tier",
724
+ "aliases": [
725
+ "phase tier"
726
+ ],
727
+ "mutation": false
728
+ },
729
+ {
730
+ "canonical": "phase.tier-propose",
731
+ "aliases": [
732
+ "phase tier-propose"
733
+ ],
734
+ "mutation": true
735
+ },
700
736
  {
701
737
  "canonical": "progress.bar",
702
738
  "aliases": [
@@ -6,11 +6,13 @@
6
6
  * hand-written .cjs; only types are added.
7
7
  *
8
8
  * Shared helpers for command-family adapters to project argv tokens into
9
- * typed named values and multi-word segments.
9
+ * typed named values and multi-word segments, and (261008-mwk) to refuse the
10
+ * tokens a verb would not read — `validateVerbArgs`, the one owner of that rule.
10
11
  */
11
12
  Object.defineProperty(exports, "__esModule", { value: true });
12
13
  exports.parseNamedArgs = parseNamedArgs;
13
14
  exports.parseMultiwordArg = parseMultiwordArg;
15
+ exports.validateVerbArgs = validateVerbArgs;
14
16
  /**
15
17
  * Extract named --flag <value> pairs from an args array.
16
18
  * Returns an object mapping flag names to their values (null if absent).
@@ -53,3 +55,99 @@ function parseMultiwordArg(args, flag) {
53
55
  }
54
56
  return tokens.length > 0 ? tokens.join(' ') : null;
55
57
  }
58
+ // T-mwk-02: refused tokens are echoed to stderr; cap each one.
59
+ const ECHO_LIMIT = 80;
60
+ function echoToken(token) {
61
+ const cut = token.length > ECHO_LIMIT ? token.slice(0, ECHO_LIMIT) : token;
62
+ return cut === '' || /\s/.test(cut) ? JSON.stringify(cut) : cut;
63
+ }
64
+ /**
65
+ * Refuse every argument a verb would not read (261008-mwk D-1, D-5, D-7).
66
+ *
67
+ * `parseNamedArgs` indexes argv for whole `--flag` tokens and looks at nothing
68
+ * else, so before this owner existed a stray positional, an unknown flag, a
69
+ * `--flag=value` token, a value flag with no value after it, and the second of
70
+ * a repeated flag were all dropped while the verb reported success. This walk
71
+ * reads the tokens exactly as the parser does and names each one it would drop:
72
+ *
73
+ * - `positional` — a non-flag token beyond the verb's positional allowance;
74
+ * - `unknown flag` — a `--name` the spec does not declare;
75
+ * - `use --name <value>` (or `use --name`) — a `--name=value` token;
76
+ * - `no value follows` — a value flag that is last or followed by a `--` token;
77
+ * - `given twice; only the first is read` — a repeated declared flag.
78
+ *
79
+ * Pure: no I/O, no environment. The caller turns a refusal into its own error
80
+ * (the state router uses `ERROR_REASON.USAGE`).
81
+ *
82
+ * @param tokens argv after the verb (global flags already stripped by pp-tools main()).
83
+ * @param spec the verb's declared shape.
84
+ * @param verb optional label for the message, e.g. `state record-session`.
85
+ * @returns `{ ok: true }`, or the refused tokens with kinds and a D-7 message.
86
+ */
87
+ function validateVerbArgs(tokens, spec, verb) {
88
+ if (spec.free)
89
+ return { ok: true };
90
+ const valueFlags = spec.valueFlags ?? [];
91
+ const booleanFlags = spec.booleanFlags ?? [];
92
+ const refused = [];
93
+ const positionals = [];
94
+ const seen = new Set();
95
+ for (let i = 0; i < tokens.length; i++) {
96
+ const token = tokens[i];
97
+ if (spec.verbatimPositionals || !token.startsWith('--')) {
98
+ positionals.push(token);
99
+ continue;
100
+ }
101
+ const name = token.slice(2);
102
+ const eq = name.indexOf('=');
103
+ if (eq !== -1) {
104
+ const bare = name.slice(0, eq);
105
+ if (valueFlags.includes(bare))
106
+ refused.push({ token, kind: `use --${bare} <value>` });
107
+ else if (booleanFlags.includes(bare))
108
+ refused.push({ token, kind: `use --${bare}` });
109
+ else
110
+ refused.push({ token, kind: 'unknown flag' });
111
+ continue;
112
+ }
113
+ const isValue = valueFlags.includes(name);
114
+ if (!isValue && !booleanFlags.includes(name)) {
115
+ refused.push({ token, kind: 'unknown flag' });
116
+ continue;
117
+ }
118
+ const hasValue = isValue && i + 1 < tokens.length && !tokens[i + 1].startsWith('--');
119
+ if (seen.has(name)) {
120
+ refused.push({ token, kind: 'given twice; only the first is read' });
121
+ }
122
+ else {
123
+ seen.add(name);
124
+ if (isValue && !hasValue)
125
+ refused.push({ token, kind: 'no value follows' });
126
+ }
127
+ if (hasValue)
128
+ i++;
129
+ }
130
+ const allowance = spec.positionalExcludedBy && seen.has(spec.positionalExcludedBy)
131
+ ? 0
132
+ : (spec.maxPositionals ?? 0);
133
+ for (const token of positionals.slice(allowance))
134
+ refused.push({ token, kind: 'positional' });
135
+ if (refused.length === 0)
136
+ return { ok: true };
137
+ return { ok: false, refused, reason: refusalReason(refused, spec, verb) };
138
+ }
139
+ function refusalReason(refused, spec, verb) {
140
+ const prefix = verb ? `${verb} does not read:` : 'does not read:';
141
+ const list = refused.map((r) => `${echoToken(r.token)} (${r.kind})`).join(', ');
142
+ const accepts = spec.positionalLabel ?? acceptedShape(spec);
143
+ return `${prefix} ${list}. It accepts: ${accepts}. Nothing was written.`;
144
+ }
145
+ function acceptedShape(spec) {
146
+ const max = spec.maxPositionals ?? 0;
147
+ const parts = [
148
+ ...(spec.valueFlags ?? []).map((f) => `--${f} <value>`),
149
+ ...(spec.booleanFlags ?? []).map((f) => `--${f}`),
150
+ ...(max > 0 ? [`at most ${max} positional${max === 1 ? '' : 's'}`] : []),
151
+ ];
152
+ return parts.length ? parts.join(', ') : 'no arguments';
153
+ }
@@ -26,7 +26,10 @@ const phaseIdMod = require("./phase-id.cjs");
26
26
  const { normalizePhaseName, comparePhaseNum, extractPhaseToken } = phaseIdMod;
27
27
  // eslint-disable-next-line @typescript-eslint/no-require-imports
28
28
  const phaseLocatorMod = require("./phase-locator.cjs");
29
- const { getArchivedPhaseDirs, findPhaseInternal } = phaseLocatorMod;
29
+ const { getArchivedPhaseDirs } = phaseLocatorMod;
30
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
31
+ const phaseScopeMod = require("./phase-scope.cjs");
32
+ const { resolvePhaseForRead, resolvePhaseForWrite } = phaseScopeMod;
30
33
  // eslint-disable-next-line @typescript-eslint/no-require-imports
31
34
  const roadmapParserMod = require("./roadmap-parser.cjs");
32
35
  const { extractCurrentMilestone, stripShippedMilestones: _stripShippedMilestones, getMilestoneInfo, getMilestonePhaseFilter, getRoadmapPhaseInternal } = roadmapParserMod;
@@ -455,7 +458,9 @@ function cmdCommit(cwd, message, files, raw, amend, noVerify) {
455
458
  const phaseMatch = (files || []).join(' ').match(/(\d+(?:\.\d+)*)-/);
456
459
  if (phaseMatch) {
457
460
  const phaseNum = phaseMatch[1];
458
- const phaseInfo = findPhaseInternal(cwd, phaseNum);
461
+ // D-03: a prior milestone's archived phase whose number the current
462
+ // ROADMAP reuses names no branch (the phase-scope owner drops it).
463
+ const phaseInfo = resolvePhaseForRead(cwd, phaseNum);
459
464
  if (phaseInfo) {
460
465
  branchName = config['phase_branch_template']
461
466
  .replace('{phase}', normalizePhaseName(phaseInfo['phase_number']))
@@ -902,8 +907,9 @@ function cmdTodoMatchPhase(cwd, phase, raw) {
902
907
  const phaseKeywords = new Set(phaseText.split(/[\s\-_/.,;:()\[\]{}|]+/)
903
908
  .map(w => w.replace(/[^a-z0-9]/g, ''))
904
909
  .filter(w => w.length > 2 && !stopWords.has(w)));
905
- // Find phase directory to get expected file paths
906
- const phaseInfoDisk = findPhaseInternal(cwd, phase);
910
+ // Find phase directory to get expected file paths (D-03: through the
911
+ // phase-scope owner, so a prior milestone's archive never lends this phase its plans)
912
+ const phaseInfoDisk = resolvePhaseForRead(cwd, phase);
907
913
  const phasePlans = [];
908
914
  if (phaseInfoDisk && phaseInfoDisk['found']) {
909
915
  try {
@@ -987,10 +993,19 @@ function cmdScaffold(cwd, type, options, raw) {
987
993
  const { phase, name } = options;
988
994
  const padded = phase ? normalizePhaseName(phase) : '00';
989
995
  const today = new Date().toISOString().split('T')[0];
990
- // Find phase directory
991
- const phaseInfo = phase ? findPhaseInternal(cwd, phase) : null;
996
+ // Find phase directory. D-02 (todo "template-fill-writes-
997
+ // into-an-archived-phase-directory"): context / uat / verification never write into a
998
+ // closed milestone's archive. The phase-scope owner hands back a live match only; an
999
+ // archive this phase still names is refused naming it, and a prior milestone's archive
1000
+ // whose number the current ROADMAP reuses takes the not-found line. phase-dir never reads
1001
+ // the match (it always creates phases/<padded>-<slug of --name>), so it is unchanged.
1002
+ const writeVerdict = phase ? resolvePhaseForWrite(cwd, phase) : null;
1003
+ const phaseInfo = writeVerdict && writeVerdict.ok ? writeVerdict.match : null;
992
1004
  const phaseDir = phaseInfo ? node_path_1.default.join(cwd, phaseInfo['directory']) : null;
993
1005
  if (phase && !phaseDir && type !== 'phase-dir') {
1006
+ if (writeVerdict && !writeVerdict.ok && writeVerdict.reason === 'archived') {
1007
+ error(`Phase ${phase} is archived in ${writeVerdict.directory} (milestone ${writeVerdict.version}): refusing to write into a closed milestone's record`);
1008
+ }
994
1009
  error(`Phase ${phase} directory not found`);
995
1010
  }
996
1011
  let filePath, content;
@@ -47,7 +47,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
47
47
  Object.defineProperty(exports, "__esModule", { value: true });
48
48
  exports.COMPOSE_BINDINGS_FLAGS = exports.SEED_VALUE_MAX_LENGTH = void 0;
49
49
  exports._resetPlatformTagMemoForTests = _resetPlatformTagMemoForTests;
50
+ exports.resolveCreatedWithFrom = resolveCreatedWithFrom;
50
51
  exports.composeBindings = composeBindings;
52
+ exports.redactComposeResultForEmission = redactComposeResultForEmission;
51
53
  exports.cmdComposeBindings = cmdComposeBindings;
52
54
  const node_fs_1 = __importDefault(require("node:fs"));
53
55
  const node_path_1 = __importDefault(require("node:path"));
@@ -436,12 +438,34 @@ function resolveLatestPlatformTag(cwd, repo, provider, opts) {
436
438
  return result;
437
439
  }
438
440
  /**
439
- * Read the first readable, semver-shaped `VERSION` file among the installed
440
- * `pp-core/VERSION` location and this repo's own root `VERSION`, returning
441
- * `<binName>@<version>`; when neither is readable, `binName` alone.
441
+ * Read the first readable, semver-shaped `VERSION` file among the candidate
442
+ * locations resolved against `baseDir`, returning `<binName>@<version>`.
443
+ *
444
+ * The candidate list is `baseDir`-relative rather than `__dirname`-relative so
445
+ * the layout under test is CHOSEN by the caller instead of inherited from
446
+ * wherever the compiled module happens to sit — an installed tree and this
447
+ * repo's own tree put `VERSION` in different places, and a test that cannot
448
+ * pick a layout cannot tell those two cases apart.
449
+ *
450
+ * Three locations are probed, in order: the `VERSION` sitting beside the bin
451
+ * directory; then the installed `pp-core/VERSION` that the installer writes
452
+ * into a project's runtime home; then this repo's own root `VERSION`. The
453
+ * installed location is consulted BEFORE the repo root so an installed tree
454
+ * reports the version it was actually installed from. In this repo that middle
455
+ * candidate is absent, so resolution falls through to the root file.
456
+ *
457
+ * @param baseDir - directory the candidates are resolved against. Production
458
+ * passes this module's own `__dirname`; a test passes a fixture directory
459
+ * shaped like `pp-core/bin/lib`.
460
+ * @returns `<binName>@<version>` for the first readable, semver-shaped
461
+ * candidate; when no candidate is readable, `binName` alone.
442
462
  */
443
- function resolveCreatedWith() {
444
- const candidates = [node_path_1.default.join(__dirname, '..', 'VERSION'), node_path_1.default.join(__dirname, '..', '..', '..', 'VERSION')];
463
+ function resolveCreatedWithFrom(baseDir) {
464
+ const candidates = [
465
+ node_path_1.default.join(baseDir, '..', 'VERSION'),
466
+ node_path_1.default.join(baseDir, '..', '..', 'VERSION'),
467
+ node_path_1.default.join(baseDir, '..', '..', '..', 'VERSION'),
468
+ ];
445
469
  for (const candidate of candidates) {
446
470
  try {
447
471
  const raw = node_fs_1.default.readFileSync(candidate, 'utf-8').trim();
@@ -455,6 +479,13 @@ function resolveCreatedWith() {
455
479
  }
456
480
  return package_identity_cjs_1.binName;
457
481
  }
482
+ /**
483
+ * Resolve `project.createdWith` for the running installation by probing the
484
+ * candidate layout against this module's own directory.
485
+ */
486
+ function resolveCreatedWith() {
487
+ return resolveCreatedWithFrom(__dirname);
488
+ }
458
489
  /**
459
490
  * Build the composed candidate object from the seed, the project answers
460
491
  * `/pp-init` already captured, the resolved platform host, the D-04 tag pin,
@@ -772,6 +803,39 @@ function composeBindings(cwd, opts = {}) {
772
803
  * does, then calls `composeBindings` and emits the result as data — a
773
804
  * verdict is never a process error.
774
805
  */
806
+ /**
807
+ * Pack 6 D-07: the display-safe copy of a compose result, for EMISSION only.
808
+ * Every remote coordinate field (`platform.repo`, `client.vaultRepo`) in
809
+ * `composed`, `seed`, `provenance` and `drift` goes through the one owner
810
+ * (`redactCloneTarget`, src/vault.cts); warnings go through the prose
811
+ * redactor. `composeBindings` itself, its drift/action comparison and every
812
+ * write keep the RAW values — never write or compare the result of this.
813
+ */
814
+ function redactComposeResultForEmission(result) {
815
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
816
+ const v = require('./vault.cjs');
817
+ const COORD = new Set(['platform.repo', 'client.vaultRepo']);
818
+ const safe = (x) => (typeof x === 'string' ? v.redactCloneTarget(x) : x);
819
+ const out = JSON.parse(JSON.stringify(result));
820
+ if (out.composed && typeof out.composed === 'object') {
821
+ for (const field of COORD) {
822
+ const [ns, key] = field.split('.');
823
+ const parent = out.composed[ns];
824
+ if (parent && typeof parent === 'object' && typeof parent[key] === 'string')
825
+ parent[key] = safe(parent[key]);
826
+ }
827
+ }
828
+ if (out.seed) {
829
+ if (typeof out.seed.platformRepo === 'string')
830
+ out.seed.platformRepo = safe(out.seed.platformRepo);
831
+ if (typeof out.seed.clientVaultRepo === 'string')
832
+ out.seed.clientVaultRepo = safe(out.seed.clientVaultRepo);
833
+ }
834
+ out.provenance = (out.provenance ?? []).map((p) => (COORD.has(p.field) ? { ...p, value: safe(p.value) } : p));
835
+ out.drift = (out.drift ?? []).map((d) => COORD.has(d.key) ? { ...d, profileValue: safe(d.profileValue), effectiveValue: safe(d.effectiveValue) } : d);
836
+ out.warnings = (out.warnings ?? []).map((w) => (typeof w === 'string' ? v.redactRemoteText(w) : w));
837
+ return out;
838
+ }
775
839
  function cmdComposeBindings(cwd, options, raw) {
776
840
  let root;
777
841
  try {
@@ -781,5 +845,5 @@ function cmdComposeBindings(cwd, options, raw) {
781
845
  root = cwd;
782
846
  }
783
847
  const result = composeBindings(root, { write: options.write === true, offline: options.offline === true });
784
- output(result, raw);
848
+ output(redactComposeResultForEmission(result), raw);
785
849
  }