@pareto4/pp-core 1.18.0 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/agents/pp-code-fixer.md +9 -5
  3. package/agents/pp-code-reviewer.md +1 -0
  4. package/agents/pp-codebase-mapper.md +3 -0
  5. package/agents/pp-debugger.md +3 -2
  6. package/agents/pp-executor.md +16 -7
  7. package/agents/pp-integration-checker.md +1 -0
  8. package/agents/pp-phase-researcher.md +3 -1
  9. package/agents/pp-plan-checker.md +3 -0
  10. package/agents/pp-planner.md +5 -1
  11. package/agents/pp-research-synthesizer.md +1 -1
  12. package/agents/pp-ui-auditor.md +1 -0
  13. package/agents/pp-ui-researcher.md +3 -1
  14. package/agents/pp-verifier.md +10 -2
  15. package/commands/pp-add-tests.md +1 -1
  16. package/commands/pp-debug.md +1 -0
  17. package/commands/pp-init.md +4 -3
  18. package/commands/pp-quick.md +3 -0
  19. package/commands/pp-review-backlog.md +2 -1
  20. package/gemini-extension.json +1 -1
  21. package/package.json +1 -1
  22. package/pp-core/bin/lib/capability-registry.cjs +2 -2
  23. package/pp-core/bin/lib/check-command-router.cjs +60 -20
  24. package/pp-core/bin/lib/commands.cjs +79 -4
  25. package/pp-core/bin/lib/compose-bindings.cjs +97 -14
  26. package/pp-core/bin/lib/discovery-rollup.cjs +9 -6
  27. package/pp-core/bin/lib/discovery-screens.cjs +20 -2
  28. package/pp-core/bin/lib/discovery-sweep.cjs +12 -3
  29. package/pp-core/bin/lib/discovery-vault-check.cjs +37 -3
  30. package/pp-core/bin/lib/init-contract.cjs +48 -7
  31. package/pp-core/bin/lib/vault-coordinates.cjs +1 -1
  32. package/pp-core/bin/lib/vault-index.cjs +18 -5
  33. package/pp-core/bin/lib/vault-update.cjs +4 -0
  34. package/pp-core/bin/lib/vault.cjs +2 -2
  35. package/pp-core/bin/pp-tools.cjs +10 -4
  36. package/pp-core/org-profile.json +1 -1
  37. package/pp-core/references/autonomous-best-effort.md +3 -2
  38. package/pp-core/references/autonomous-lifecycle.md +1 -0
  39. package/pp-core/references/autonomous-smart-discuss.md +5 -1
  40. package/pp-core/references/discovery-id-conventions.md +35 -2
  41. package/pp-core/references/execute-mvp-tdd.md +3 -3
  42. package/pp-core/references/git-integration.md +57 -33
  43. package/pp-core/references/git-planning-commit.md +31 -9
  44. package/pp-core/references/phase-argument-parsing.md +1 -0
  45. package/pp-core/references/planner-gap-closure.md +1 -0
  46. package/pp-core/references/planner-revision.md +3 -1
  47. package/pp-core/references/planner-tdd.md +4 -4
  48. package/pp-core/references/planning-config.md +3 -3
  49. package/pp-core/references/shell-portability.md +28 -0
  50. package/pp-core/references/tdd.md +20 -16
  51. package/pp-core/references/worktree-path-safety.md +1 -0
  52. package/pp-core/templates/milestone.md +5 -5
  53. package/pp-core/workflows/add-backlog.md +1 -1
  54. package/pp-core/workflows/add-tests.md +5 -2
  55. package/pp-core/workflows/add-todo.md +3 -1
  56. package/pp-core/workflows/ai-integration-phase.md +2 -1
  57. package/pp-core/workflows/audit-fix.md +4 -3
  58. package/pp-core/workflows/audit-milestone.md +1 -0
  59. package/pp-core/workflows/autonomous.md +6 -0
  60. package/pp-core/workflows/check-todos.md +2 -1
  61. package/pp-core/workflows/cleanup.md +3 -1
  62. package/pp-core/workflows/code-review-fix.md +12 -5
  63. package/pp-core/workflows/code-review.md +15 -1
  64. package/pp-core/workflows/complete-milestone.md +13 -3
  65. package/pp-core/workflows/debug.md +2 -0
  66. package/pp-core/workflows/diagnose-issues.md +1 -1
  67. package/pp-core/workflows/discovery-sprint/session-13-gates.md +1 -1
  68. package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +51 -7
  69. package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +5 -3
  70. package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +10 -8
  71. package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +11 -2
  72. package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +5 -5
  73. package/pp-core/workflows/discovery-sprint/sessions/06-security.md +3 -3
  74. package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +2 -2
  75. package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +14 -2
  76. package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +2 -2
  77. package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +10 -10
  78. package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +11 -8
  79. package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +21 -18
  80. package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +5 -3
  81. package/pp-core/workflows/discovery-sprint/templates/context-template.md +9 -3
  82. package/pp-core/workflows/discovery-sprint/vault-context.md +14 -3
  83. package/pp-core/workflows/discovery-sprint.md +11 -8
  84. package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +1 -0
  85. package/pp-core/workflows/discuss-phase-assumptions.md +5 -3
  86. package/pp-core/workflows/discuss-phase.md +8 -3
  87. package/pp-core/workflows/docs-update.md +1 -0
  88. package/pp-core/workflows/eval-review.md +2 -1
  89. package/pp-core/workflows/execute-phase/steps/check-query-gate.md +7 -6
  90. package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +1 -0
  91. package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -0
  92. package/pp-core/workflows/execute-phase/steps/vault-fetch.md +12 -0
  93. package/pp-core/workflows/execute-phase.md +19 -7
  94. package/pp-core/workflows/execute-plan.md +18 -7
  95. package/pp-core/workflows/explore.md +1 -1
  96. package/pp-core/workflows/forensics.md +4 -2
  97. package/pp-core/workflows/health.md +1 -0
  98. package/pp-core/workflows/import.md +2 -1
  99. package/pp-core/workflows/ingest-docs.md +1 -0
  100. package/pp-core/workflows/init/steps/client-context-capture.md +1 -0
  101. package/pp-core/workflows/init/steps/github-provisioning.md +145 -0
  102. package/pp-core/workflows/init/steps/gitlab-provisioning.md +18 -4
  103. package/pp-core/workflows/init/steps/reachability-outcome.md +3 -1
  104. package/pp-core/workflows/init.md +56 -66
  105. package/pp-core/workflows/map-codebase.md +5 -1
  106. package/pp-core/workflows/milestone-summary.md +2 -1
  107. package/pp-core/workflows/mvp-phase.md +1 -0
  108. package/pp-core/workflows/new-milestone.md +9 -5
  109. package/pp-core/workflows/new-project.md +9 -8
  110. package/pp-core/workflows/new-workspace.md +1 -0
  111. package/pp-core/workflows/next.md +2 -1
  112. package/pp-core/workflows/pause-work.md +6 -4
  113. package/pp-core/workflows/plan-milestone-gaps.md +3 -1
  114. package/pp-core/workflows/plan-phase/steps/chunked-planning.md +1 -1
  115. package/pp-core/workflows/plan-phase.md +21 -6
  116. package/pp-core/workflows/plan-review-convergence.md +2 -0
  117. package/pp-core/workflows/plant-seed.md +4 -2
  118. package/pp-core/workflows/pr-branch.md +2 -0
  119. package/pp-core/workflows/progress.md +5 -0
  120. package/pp-core/workflows/quick.md +11 -4
  121. package/pp-core/workflows/reapply-patches.md +3 -0
  122. package/pp-core/workflows/remove-phase.md +1 -1
  123. package/pp-core/workflows/resume-project.md +3 -0
  124. package/pp-core/workflows/review.md +7 -1
  125. package/pp-core/workflows/secure-phase.md +2 -1
  126. package/pp-core/workflows/session-report.md +1 -0
  127. package/pp-core/workflows/ship.md +8 -1
  128. package/pp-core/workflows/sketch-wrap-up.md +1 -1
  129. package/pp-core/workflows/sketch.md +3 -2
  130. package/pp-core/workflows/spec-phase.md +2 -1
  131. package/pp-core/workflows/spike-wrap-up.md +1 -1
  132. package/pp-core/workflows/spike.md +4 -3
  133. package/pp-core/workflows/sync-skills.md +5 -2
  134. package/pp-core/workflows/thread.md +3 -2
  135. package/pp-core/workflows/transition.md +6 -0
  136. package/pp-core/workflows/ui-phase.md +3 -1
  137. package/pp-core/workflows/ui-review.md +2 -1
  138. package/pp-core/workflows/ultraplan-phase.md +1 -0
  139. package/pp-core/workflows/undo.md +14 -10
  140. package/pp-core/workflows/update.md +5 -0
  141. package/pp-core/workflows/validate-phase.md +6 -3
  142. package/pp-core/workflows/vault-review.md +2 -1
  143. package/pp-core/workflows/vault-write-back.md +5 -0
  144. package/pp-core/workflows/verify-phase.md +5 -0
  145. package/pp-core/workflows/verify-work.md +8 -1
  146. package/scripts/prompt-injection-scan.sh +1 -1
  147. package/scripts/sync-runtime-launcher.cjs +115 -5
@@ -225,7 +225,7 @@ function isVaultEnabledInConfig(parsed) {
225
225
  // ─── buildInitQuestions ────────────────────────────────────────────────────
226
226
  /** The canonical /pp-init question set, as data; the session may answer some itself (see "Automatic answers").
227
227
  * No org-profile question in this fork (PBI 74316); provider options come from `PROVIDERS`, preselected via `resolveProvider`. */
228
- function buildInitQuestions(cwd) {
228
+ function buildInitQuestions(cwd, opts = {}) {
229
229
  let preselected = git_provider_cjs_1.DEFAULT_PROVIDER;
230
230
  try {
231
231
  preselected = (0, git_provider_cjs_1.resolveProvider)(cwd).provider;
@@ -247,6 +247,7 @@ function buildInitQuestions(cwd) {
247
247
  prompt: 'Client slug (kebab-case, e.g. "acme") — type "internal" if none',
248
248
  optionalAnswer: false,
249
249
  },
250
+ ...clientVaultQuestion(cwd, opts),
250
251
  { id: 'description', prompt: 'One-line description (what this project does)', optionalAnswer: false },
251
252
  {
252
253
  id: 'provider',
@@ -259,6 +260,23 @@ function buildInitQuestions(cwd) {
259
260
  { id: 'visibility', prompt: 'Visibility (private / public / internal)', optionalAnswer: false },
260
261
  ];
261
262
  }
263
+ /** The client-vault question, asked while no vault is bound and a binding can be composed (`clientVaultChoice`).
264
+ * Blank or "none" keeps the org profile's seeded vault, if any; an answer replaces it in the binding.
265
+ * An internal project is answered "none" automatically (see `autoAnswerFor`). */
266
+ function clientVaultQuestion(cwd, opts) {
267
+ const choice = (0, compose_bindings_cjs_1.clientVaultChoice)(cwd, opts);
268
+ if (!choice.offered)
269
+ return [];
270
+ return [
271
+ {
272
+ id: 'client_vault',
273
+ prompt: choice.defaultRepo === null
274
+ ? 'Client vault repo ("owner/name" or a full clone URL) — type "none" to bind no client vault'
275
+ : `Client vault repo ("owner/name" or a full clone URL) — type "none" to use the default vault (${choice.defaultRepo})`,
276
+ optionalAnswer: true,
277
+ },
278
+ ];
279
+ }
262
280
  // ─── Automatic answers (PBI 74299): an org profile init section fixes answers and can derive slugs ───
263
281
  /** The visibility answers the question accepts. */
264
282
  const VISIBILITY_OPTIONS = Object.freeze(['private', 'public', 'internal']);
@@ -320,6 +338,9 @@ function autoAnswerFor(id, answers, init) {
320
338
  const value = init.fixed[id];
321
339
  return value === undefined ? null : { value, source: 'org-profile' };
322
340
  }
341
+ if (id === 'client_vault' && answers.client_slug === 'internal') {
342
+ return { value: 'none', source: 'derived' };
343
+ }
323
344
  if (init.deriveSlugs && (id === 'project_slug' || id === 'client_slug')) {
324
345
  const name = answers[id === 'project_slug' ? 'project_name' : 'client_name'];
325
346
  const slug = typeof name === 'string' ? deriveSlug(name) : null;
@@ -328,10 +349,10 @@ function autoAnswerFor(id, answers, init) {
328
349
  return null;
329
350
  }
330
351
  /** The session's automatic answers, in question order. */
331
- function listAutoAnswers(cwd, session) {
352
+ function listAutoAnswers(cwd, session, opts) {
332
353
  const sources = session.autoAnswers ?? {};
333
354
  const out = [];
334
- for (const q of buildInitQuestions(cwd)) {
355
+ for (const q of buildInitQuestions(cwd, { _globalRoot: opts._globalRoot })) {
335
356
  const source = sources[q.id];
336
357
  const value = session.answers[q.id];
337
358
  if (source !== undefined && typeof value === 'string')
@@ -758,10 +779,15 @@ function normalizeYesNo(value) {
758
779
  }
759
780
  /** Decide the current step, performing its idempotent side effects once: automatic answers, the identity write,
760
781
  * the compose decision and `doneAt`. Recomputes the question set on every call. */
782
+ /** The client vault the operator named, or `undefined` to keep the seeded one. */
783
+ function answeredClientVault(session) {
784
+ const checked = (0, compose_bindings_cjs_1.validateClientVaultAnswer)(session.answers.client_vault);
785
+ return checked.ok && checked.value !== null ? checked.value : undefined;
786
+ }
761
787
  function computeCurrentStep(cwd, session, opts) {
762
788
  // Any failure degrades to a `write_failed` step instead of throwing.
763
789
  try {
764
- const questions = buildInitQuestions(cwd);
790
+ const questions = buildInitQuestions(cwd, { _globalRoot: opts._globalRoot });
765
791
  // PBI 74299: read once per call; only consulted for an unanswered question.
766
792
  let profileInit = null;
767
793
  for (const q of questions) {
@@ -784,7 +810,11 @@ function computeCurrentStep(cwd, session, opts) {
784
810
  persistSession(cwd, session);
785
811
  }
786
812
  if (session.composeDecision === null) {
787
- const dryRun = (0, compose_bindings_cjs_1.composeBindings)(cwd, { offline: opts.offline === true, _globalRoot: opts._globalRoot });
813
+ const dryRun = (0, compose_bindings_cjs_1.composeBindings)(cwd, {
814
+ offline: opts.offline === true,
815
+ _globalRoot: opts._globalRoot,
816
+ clientVaultRepo: answeredClientVault(session),
817
+ });
788
818
  // Offer compose_confirm only when approving would change something; 'unchanged' and 'skipped' are distinct end states.
789
819
  if (dryRun.composable === true && dryRun.action !== 'unchanged') {
790
820
  // VAULT-EGRESS-1: `info` is display-only (the CLI and the TTY renderer print it); approval
@@ -889,7 +919,7 @@ function initSessionNext(cwd, opts = {}) {
889
919
  }
890
920
  const session = loaded.session;
891
921
  const state = currentStepToState(computeCurrentStep(cwd, session, opts));
892
- const autoAnswered = listAutoAnswers(cwd, session);
922
+ const autoAnswered = listAutoAnswers(cwd, session, opts);
893
923
  return autoAnswered.length > 0 ? { ...state, autoAnswered } : state;
894
924
  }
895
925
  /** Record one answer for the current step only; out-of-order, blank required, unparseable or post-done answers are rejected. */
@@ -929,6 +959,12 @@ function initSessionAnswer(cwd, stepId, value, opts = {}) {
929
959
  return { ok: false, reason: 'answer_required', message: `Question "${stepId}" requires a non-blank answer.` };
930
960
  }
931
961
  const answerValue = typeof value === 'string' ? value : '';
962
+ if (stepId === 'client_vault') {
963
+ const checked = (0, compose_bindings_cjs_1.validateClientVaultAnswer)(answerValue);
964
+ if (!checked.ok) {
965
+ return { ok: false, reason: 'invalid_answer', message: `Question "client_vault": ${checked.reason}` };
966
+ }
967
+ }
932
968
  session.answers[stepId] = answerValue;
933
969
  persistSession(cwd, session);
934
970
  return { ok: true };
@@ -951,7 +987,12 @@ function initSessionAnswer(cwd, stepId, value, opts = {}) {
951
987
  }
952
988
  if (normalized === true) {
953
989
  // Check the write result before recording "approved": the file can change between the dry run and this write.
954
- const writeResult = (0, compose_bindings_cjs_1.composeBindings)(cwd, { write: true, offline: opts.offline === true, _globalRoot: opts._globalRoot });
990
+ const writeResult = (0, compose_bindings_cjs_1.composeBindings)(cwd, {
991
+ write: true,
992
+ offline: opts.offline === true,
993
+ _globalRoot: opts._globalRoot,
994
+ clientVaultRepo: answeredClientVault(session),
995
+ });
955
996
  if (writeResult.composable !== true || writeResult.wrote !== true) {
956
997
  // Leave the decision unset so the next call re-evaluates and can re-offer compose_confirm.
957
998
  return {
@@ -73,7 +73,7 @@ exports.DEFAULT_HOST_FOR_PROVIDER = Object.freeze({
73
73
  github: 'github.com',
74
74
  });
75
75
  /** The values moving out of `src/vault.cts`'s `DEFAULT_FALLBACK_REPO`/`DEFAULT_FALLBACK_REF`. */
76
- exports.DEFAULT_SHARED_REPO = 'pareto000/Pareto-Vault';
76
+ exports.DEFAULT_SHARED_REPO = 'pareto-ooo/pareto-vault-releases';
77
77
  exports.DEFAULT_SHARED_REF = 'main';
78
78
  /** Dotted `.planning/config.json` keys, named so no consumer hand-writes a key string. */
79
79
  exports.SHARED_REPO_CONFIG_KEY = 'vault.fallback_repo';
@@ -39,7 +39,7 @@
39
39
  * has already sanitised.
40
40
  */
41
41
  Object.defineProperty(exports, "__esModule", { value: true });
42
- exports.VAULT_INDEX_WITHHELD_KINDS = exports.VAULT_INDEX_UNCHECKED_CAUSES = exports.VAULT_INDEX_UNPARSEABLE_CAUSES = exports.VAULT_INDEX_REASON = exports.DISPLAY_UNSAFE_TEXT_CHAR_RE = exports.VAULT_BINDING_NOTE = exports.VAULT_PATH_REASONS = exports.VAULT_INDEX_LIST_BOUND = exports.VAULT_TEXT_MAX = exports.VAULT_INDEX_GRAMMAR = exports.VAULT_SECTION_HEADING_PREFIX = exports.VAULT_USED_WHEN_RE = exports.VAULT_USED_WHEN_MARKER = exports.VAULT_ENTRY_SEPARATOR = exports.VAULT_ENTRY_PREFIX = exports.VAULT_INDEX_BYTE_CAP = exports.VAULT_INDEX_FILENAME = void 0;
42
+ exports.VAULT_INDEX_WITHHELD_KINDS = exports.VAULT_INDEX_UNCHECKED_CAUSES = exports.VAULT_INDEX_UNPARSEABLE_CAUSES = exports.VAULT_INDEX_REASON = exports.DISPLAY_UNSAFE_TEXT_CHAR_RE = exports.VAULT_BINDING_NOTE = exports.VAULT_PATH_REASONS = exports.VAULT_INDEX_LIST_BOUND = exports.VAULT_TEXT_MAX = exports.VAULT_INDEX_GRAMMAR = exports.VAULT_CROSS_CUTTING_MARK = exports.VAULT_SECTION_HEADING_PREFIX = exports.VAULT_USED_WHEN_RE = exports.VAULT_USED_WHEN_MARKER = exports.VAULT_ENTRY_SEPARATOR = exports.VAULT_ENTRY_PREFIX = exports.VAULT_INDEX_BYTE_CAP = exports.VAULT_INDEX_FILENAME = void 0;
43
43
  exports.classifyVaultPath = classifyVaultPath;
44
44
  exports.isSafeVaultPath = isSafeVaultPath;
45
45
  exports.splitVaultEntryLine = splitVaultEntryLine;
@@ -74,6 +74,13 @@ exports.VAULT_USED_WHEN_MARKER = '; used when ';
74
74
  exports.VAULT_USED_WHEN_RE = /;\s+used when\s+/i;
75
75
  /** A section is opened by a level-2 ATX heading at column 0. */
76
76
  exports.VAULT_SECTION_HEADING_PREFIX = '## ';
77
+ /**
78
+ * The cross-cutting mark (issue #12, items 16-17): an entry whose remainder
79
+ * starts with it holds a rule every Discovery session must read — approval
80
+ * routes, folder layout, naming — whatever the session's topics are. It is
81
+ * stripped from the description and recorded as `crossCutting: true`.
82
+ */
83
+ exports.VAULT_CROSS_CUTTING_MARK = '[cross-cutting]';
77
84
  /** The six grammar values bundled, frozen, for a consumer that quotes the grammar whole. */
78
85
  exports.VAULT_INDEX_GRAMMAR = Object.freeze({
79
86
  filename: exports.VAULT_INDEX_FILENAME,
@@ -219,16 +226,22 @@ const LEVEL1_HEADING_RE = /^#(?:[ \t].*)?$/;
219
226
  // D-28: an entry prefix indented 1-3 spaces is a near-miss, not a nested list.
220
227
  const INDENTED_ENTRY_RE = /^ {1,3}- `/;
221
228
  function buildEntry(section, path, rest, line) {
222
- const m = exports.VAULT_USED_WHEN_RE.exec(rest);
229
+ // The mark is read off the front of the remainder; a mark with nothing after
230
+ // it stays text, so the description is never left empty.
231
+ const lead = rest.trimStart();
232
+ const marked = lead.startsWith(exports.VAULT_CROSS_CUTTING_MARK) && lead.slice(exports.VAULT_CROSS_CUTTING_MARK.length).trim() !== '';
233
+ const body = marked ? lead.slice(exports.VAULT_CROSS_CUTTING_MARK.length) : rest;
234
+ const flag = marked ? { crossCutting: true } : {};
235
+ const m = exports.VAULT_USED_WHEN_RE.exec(body);
223
236
  if (m) {
224
- const usedWhen = rest.slice(m.index + m[0].length).trim();
237
+ const usedWhen = body.slice(m.index + m[0].length).trim();
225
238
  if (usedWhen !== '') {
226
- return { section, path, description: rest.slice(0, m.index).trim(), usedWhen, line };
239
+ return { section, path, description: body.slice(0, m.index).trim(), usedWhen, ...flag, line };
227
240
  }
228
241
  }
229
242
  // No clause, or an empty one: the whole remainder is the description and the
230
243
  // key is omitted — a clause is never invented and text is never dropped.
231
- return { section, path, description: rest.trim(), line };
244
+ return { section, path, description: body.trim(), ...flag, line };
232
245
  }
233
246
  /**
234
247
  * Parse an INDEX.md text tolerantly. Never throws; a non-string is read as
@@ -109,6 +109,7 @@ function vaultUpdate(cwd, opts = {}) {
109
109
  from: null,
110
110
  to: null,
111
111
  latest: null,
112
+ newer: null,
112
113
  available: [],
113
114
  fetch: null,
114
115
  nextStep: null,
@@ -152,6 +153,9 @@ function vaultUpdate(cwd, opts = {}) {
152
153
  tags = [...new Set(tags)].sort((a, b) => (0, semver_compare_cjs_1.compareSemverCore)(b, a));
153
154
  result.available = tags.slice(0, exports.AVAILABLE_TAGS_CAP);
154
155
  result.latest = tags[0] ?? null;
156
+ if (result.from && result.latest && (0, semver_compare_cjs_1.isStableTripletSemver)(result.from) && (0, semver_compare_cjs_1.compareSemverCore)(result.latest, result.from) > 0) {
157
+ result.newer = result.latest;
158
+ }
155
159
  if (tags.length === 0) {
156
160
  return fail('no_tags', `The vault repo "${redactCloneTarget(repo)}" has no stable vX.Y.Z tag to pin to.`);
157
161
  }
@@ -2809,7 +2809,7 @@ function resolveRefDrift({ cwd, tier, cachedRef }) {
2809
2809
  // DEVIATION from plan 08-01's step 3, recorded so the next reader does not
2810
2810
  // "restore" it: the plan prescribed copying the miss branch's call-site-
2811
2811
  // scoped `require('./git-provider.cjs')` / `require('./vault-coordinates.cjs')`
2812
- // pair from src/check-command-router.cts:1558-1565. That shape exists THERE
2812
+ // pair from src/check-command-router.cts:1587-1594. That shape exists THERE
2813
2813
  // because the check router does not otherwise import those leaves, so the
2814
2814
  // require genuinely keeps its module graph narrow. It buys nothing HERE:
2815
2815
  // this module already imports `resolveProvider` (:40) and
@@ -2833,7 +2833,7 @@ function resolveRefDrift({ cwd, tier, cachedRef }) {
2833
2833
  // UNCONDITIONALLY, including when `repo` is null, so an ungated
2834
2834
  // comparison reports phantom drift on every project that configures no
2835
2835
  // client vault at all. The miss branch argues the same point at
2836
- // src/check-command-router.cts:1567-1570, where it keeps the truthful
2836
+ // src/check-command-router.cts:1596-1599, where it keeps the truthful
2837
2837
  // `(no ref pinned)` placeholder for the same reason.
2838
2838
  //
2839
2839
  // D-08-39(a): a BLANK or WHITESPACE-ONLY `client.vaultRepo` is not a
@@ -19,8 +19,8 @@
19
19
  * state signal-resume Remove WAITING.json signal
20
20
  * resolve-model <agent-type> Get model for agent based on profile
21
21
  * find-phase <phase> Find phase directory by number
22
- * commit <message> [--files f1 f2] [--no-verify] Commit planning docs
23
- * commit-to-subrepo <msg> --files f1 f2 Route commits to sub-repos
22
+ * commit <message> [--files f1 f2] [--phase id] [--quick id] [--no-verify] Commit planning docs
23
+ * commit-to-subrepo <msg> --files f1 f2 [--phase id] Route commits to sub-repos
24
24
  * verify-summary <path> Verify a SUMMARY.md file
25
25
  * generate-slug <text> Convert text to URL-safe slug
26
26
  * current-timestamp [format] Get timestamp (full|date|filename)
@@ -1158,6 +1158,10 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
1158
1158
  }
1159
1159
 
1160
1160
  case 'commit': {
1161
+ // --phase <id> / --quick <id> add a `Phase:` / `Quick:` trailer; pulled out
1162
+ // first so their values are never read as message words or --files paths.
1163
+ const trailerArgs = commands.extractCommitTrailerArgs(args);
1164
+ args = trailerArgs.args;
1161
1165
  const amend = args.includes('--amend');
1162
1166
  const noVerify = args.includes('--no-verify');
1163
1167
  const filesIndex = args.indexOf('--files');
@@ -1168,7 +1172,7 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
1168
1172
  const messageArgs = args.slice(1, endIndex).filter(a => !a.startsWith('--'));
1169
1173
  const message = messageArgs.join(' ') || undefined;
1170
1174
  const files = filesIndex !== -1 ? args.slice(filesIndex + 1).filter(a => !a.startsWith('--')) : [];
1171
- commands.cmdCommit(cwd, message, files, raw, amend, noVerify);
1175
+ commands.cmdCommit(cwd, message, files, raw, amend, noVerify, { phase: trailerArgs.phase, quick: trailerArgs.quick });
1172
1176
  break;
1173
1177
  }
1174
1178
 
@@ -1178,10 +1182,12 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
1178
1182
  }
1179
1183
 
1180
1184
  case 'commit-to-subrepo': {
1185
+ const trailerArgs = commands.extractCommitTrailerArgs(args);
1186
+ args = trailerArgs.args;
1181
1187
  const message = args[1];
1182
1188
  const filesIndex = args.indexOf('--files');
1183
1189
  const files = filesIndex !== -1 ? args.slice(filesIndex + 1).filter(a => !a.startsWith('--')) : [];
1184
- commands.cmdCommitToSubrepo(cwd, message, files, raw);
1190
+ commands.cmdCommitToSubrepo(cwd, message, files, raw, { phase: trailerArgs.phase, quick: trailerArgs.quick });
1185
1191
  break;
1186
1192
  }
1187
1193
 
@@ -11,7 +11,7 @@
11
11
  "repo": "pareto000/Pareto-Platform"
12
12
  },
13
13
  "client": {
14
- "vaultRepo": "pareto000/Pareto-Vault",
14
+ "vaultRepo": "pareto-ooo/pareto-vault-releases",
15
15
  "vaultHost": "github.com"
16
16
  },
17
17
  "provisioning": {
@@ -83,7 +83,7 @@ grep -q "^handled=${PHASE_NUM}:" "$RUN_STATE" 2>/dev/null ||
83
83
  printf 'handled=%s:%s\n' "$PHASE_NUM" "$OUTCOME" >> "$RUN_STATE"
84
84
  PADDED_PHASE=$(basename "$PHASE_DIR" | cut -d- -f1)
85
85
  if [ -f "$REPORT" ]; then
86
- pp_run query commit "docs(${PADDED_PHASE}): autonomous report" --files "$REPORT"
86
+ pp_run query commit "docs(planning): autonomous report" --phase "${PADDED_PHASE}" --files "$REPORT"
87
87
  fi
88
88
  ```
89
89
 
@@ -400,6 +400,7 @@ phase. Printed by the lifecycle single-phase banner, the STOPPED banner and the
400
400
  Used by `autonomous.md` §3a when `workflow.skip_discuss` is true. Get phase details:
401
401
 
402
402
  ```bash
403
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
403
404
  DETAIL=$(pp_run query roadmap.get-phase ${PHASE_NUM})
404
405
  ```
405
406
 
@@ -452,7 +453,7 @@ None — discuss phase skipped.
452
453
  Commit the minimal context:
453
454
 
454
455
  ```bash
455
- pp_run query commit "docs(${padded_phase}): auto-generated context (discuss skipped)" --files "${phase_dir}/${padded_phase}-CONTEXT.md"
456
+ pp_run query commit "docs(planning): auto-generated context (discuss skipped)" --phase "${padded_phase}" --files "${phase_dir}/${padded_phase}-CONTEXT.md"
456
457
  ```
457
458
 
458
459
  ## Banner math
@@ -109,6 +109,7 @@ Skill(skill="pp-complete-milestone", args="${milestone_version}")
109
109
  After complete-milestone returns, verify it produced output:
110
110
 
111
111
  ```bash
112
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
112
113
  ls .planning/milestones/v${milestone_version}-ROADMAP.md 2>/dev/null || true
113
114
  ```
114
115
 
@@ -5,6 +5,7 @@ Smart discuss is the autonomous-optimized variant of `pp-discuss-phase`. It prop
5
5
  **Inputs:** `PHASE_NUM` from execute_phase. Run init to get phase paths:
6
6
 
7
7
  ```bash
8
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
8
9
  PHASE_STATE=$(pp-tools query init.phase-op ${PHASE_NUM})
9
10
  ```
10
11
 
@@ -34,6 +35,7 @@ Extract from these:
34
35
  **Read all prior CONTEXT.md files:**
35
36
 
36
37
  ```bash
38
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
37
39
  (find "$(dirname "$STATE_PATH")/phases" -name "*-CONTEXT.md" 2>/dev/null || true) | sort
38
40
  ```
39
41
 
@@ -68,6 +70,7 @@ Lightweight codebase scan to inform grey area identification and proposals. Keep
68
70
  **Check for existing codebase maps:**
69
71
 
70
72
  ```bash
73
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
71
74
  ls .planning/codebase/*.md 2>/dev/null || true
72
75
  ```
73
76
 
@@ -96,6 +99,7 @@ Read the 3-5 most relevant files to understand existing patterns.
96
99
  **Get phase details:**
97
100
 
98
101
  ```bash
102
+ [ -z "${ZSH_VERSION:-}" ] || emulate -R sh
99
103
  DETAIL=$(pp-tools query roadmap.get-phase ${PHASE_NUM})
100
104
  ```
101
105
 
@@ -270,7 +274,7 @@ Write the file.
270
274
  **Commit:**
271
275
 
272
276
  ```bash
273
- pp-tools query commit "docs(${PADDED_PHASE}): smart discuss context" --files "${phase_dir}/${padded_phase}-CONTEXT.md"
277
+ pp_run query commit "docs(planning): smart discuss context" --phase "${PADDED_PHASE}" --files "${phase_dir}/${padded_phase}-CONTEXT.md"
274
278
  ```
275
279
 
276
280
  Display confirmation:
@@ -17,12 +17,13 @@ never `@`-imported.
17
17
  | Data-gathering items | `DG-<nn>` — same shape as SP. Regex: `DG-\d{2}` | `DG-07` | Open data-gathering items in `_risks.md` |
18
18
  | Research items | `RS-<nn>` — same shape as SP/DG: zero-padded 2-digit global sequence, no session component. Regex: `RS-\d{2}` | `RS-01` | Rows of `_research.md`, the research-only backlog |
19
19
  | Screens | `SCR-<nn>` — zero-padded 2-digit global sequence, no session and no role component, minted by scan (D-12). Regex: `SCR-\d{2}` | `SCR-01` | Rows of `_screens.md`, the requirement-to-screen inventory; Role is a grouping column, not part of the ID (D-50) |
20
+ | Processes | `PRC-<nn>` — zero-padded 2-digit global sequence. Regex: `PRC-\d{2}` | `PRC-03` | Rows of the Session 2 **Process Inventory**, the as-is processes Session 3 transforms |
20
21
  | Requirements | `<PREFIX><n>` — PREFIX is a registered domain prefix (uppercase letters/digits ending in `-`, e.g. a stakeholder-domain prefix, `N-` non-functional, `T-` technical); `<n>` is a numeric sequence, zero-padded to 2 digits by convention (`01`..`99`, then `100`). Regex: `[A-Z][A-Z0-9]*-\d+` | `N-01` | Rows of `_requirements.md`; the prefix carries the classification — there is no separate Type column |
21
22
  | Input-package accessions | `NN` — zero-padded 2-digit number, assigned in arrival order and never renumbered; a mid-sprint document takes the next number (D-16). Regex: `\d{2}` | `03` | Numbered documents in `.planning/discovery-input/` (including its `confidential/` subdir — one number space spans both locations, D-21). Reading order is a README column, not the number |
22
23
 
23
24
  ## Reserved Prefixes
24
25
 
25
- Six prefixes are reserved by the ID families above and MUST NOT be registered as
26
+ Seven prefixes are reserved by the ID families above and MUST NOT be registered as
26
27
  domain prefixes in the `_requirements.md` Prefix Registry:
27
28
 
28
29
  | Reserved | Owned by |
@@ -33,6 +34,7 @@ domain prefixes in the `_requirements.md` Prefix Registry:
33
34
  | `DG-` | Data-gathering items |
34
35
  | `RS-` | Research items |
35
36
  | `SCR-` | Screens |
37
+ | `PRC-` | Processes |
36
38
 
37
39
  Every one of them satisfies the requirement-ID regex `[A-Z][A-Z0-9]*-\d+`, so a
38
40
  registry that claims one mints requirement IDs indistinguishable from the family that
@@ -43,7 +45,11 @@ collide, and no reader or parser can tell them apart.
43
45
  sequence, so a registry claiming `SCR-` would make `SCR-01` ambiguous between a screen
44
46
  and a requirement — the same collision, one artifact further along the traceability ring.
45
47
 
46
- Session 1's kick-off prefix selection (D-09) must refuse all six: pick a different
48
+ `PRC-` followed with issue #12: Session 2 used to number processes `P-<nn>`, which an
49
+ engagement had also registered as a domain prefix, so `P-03` meant two things. Process IDs
50
+ moved to `PRC-` and the prefix is reserved so the clash cannot come back.
51
+
52
+ Session 1's kick-off prefix selection (D-09) must refuse all seven: pick a different
47
53
  domain prefix (any other uppercase letters/digits ending in `-`, e.g. `K-` for *Klant*).
48
54
  Non-reserved prefixes are unaffected.
49
55
 
@@ -53,6 +59,33 @@ registry, so its requirement rows also report `unknown_prefix`. The check is ret
53
59
  — an engagement that registered one of these before the rule existed turns red on
54
60
  upgrade. The remedy is the same: choose a different domain prefix.
55
61
 
62
+ ## Session-Table Row IDs
63
+
64
+ The Notes Templates number some table rows with their own short prefixes. These IDs are
65
+ local to their table: they are not requirements, never enter `_requirements.md`, and are
66
+ not reserved — but Session 1 should not propose a domain prefix that repeats one, or a
67
+ citation such as `C-01` reads two ways.
68
+
69
+ | Prefix | Table | Session |
70
+ |--------|-------|---------|
71
+ | `CON-` | Constraints and Risks Register (constraints) | 1 |
72
+ | `R-` | Constraints and Risks Register (risks), continued by the Risk Register | 1, 11 |
73
+ | `PP-` | Pain Points Register | 2 |
74
+ | `MP-` | Main Process Register | 3 |
75
+ | `BO-` | Business Objectives | 3 |
76
+ | `E-` | EPIC Register | 3 |
77
+ | `US-` | User Stories | 3 |
78
+ | `C-` | Component Inventory | 5 |
79
+ | `INT-` | Integration Points Map | 5 |
80
+ | `ADR-` | Architecture Decision Records | 5 |
81
+ | `CR-` | Compliance Requirements Register | 6 |
82
+ | `IF-` | Integration Design Register | 8 |
83
+ | `OI-` | Open Items Register | 13 |
84
+
85
+ Non-functional requirements are not in this list: they are `N-` rows of
86
+ `_requirements.md`, minted like every other requirement, and Session 3's
87
+ **Non-Functional Requirements** table cites them by that ID.
88
+
56
89
  ## Citation Rules
57
90
 
58
91
  - Requirement IDs are never renumbered and never reused (REQM-02). A gap in a sequence
@@ -15,11 +15,11 @@ If any of these is false, the gate is inactive — execution proceeds normally.
15
15
 
16
16
  For each task gated by MVP+TDD, the executor MUST verify (before running the implementation step):
17
17
 
18
- 1. **A failing-test commit exists.** Search git log on the current branch for a commit matching `test({phase}-{plan})` whose subject mentions the same plan as the current task. The commit must touch a test file (`*.test.*`, `*.spec.*`, `tests/**`).
18
+ 1. **A failing-test commit exists.** Search git log on the current branch for a commit of type `test` (any scope) that carries the trailer `Phase: {phase}-{plan}` for the same plan as the current task. The commit must touch a test file (`*.test.*`, `*.spec.*`, `tests/**`).
19
19
  2. **The test was actually red.** The commit message body or the executor's recent shell history must show the test failed when first run. Acceptable evidence:
20
20
  - Commit message contains `RED:` prefix or `(RED)` tag
21
21
  - Recent terminal output shows `FAIL` or non-zero exit on the new test before any implementation commit
22
- 3. **No implementation commit yet.** No `feat({phase}-{plan})` commit may exist for the same plan ID before the failing-test commit.
22
+ 3. **No implementation commit yet.** No `feat` commit carrying the trailer `Phase: {phase}-{plan}` may exist before the failing-test commit.
23
23
 
24
24
  If any check fails, the gate trips.
25
25
 
@@ -51,7 +51,7 @@ The executor MUST:
51
51
 
52
52
  Required next step:
53
53
  1. Write a failing test for the behavior above.
54
- 2. Commit it as: test({phase}-{plan}): {short description}
54
+ 2. Commit it as: test({area}): {short description} — with the trailer Phase: {phase}-{plan}
55
55
  3. Re-run /pp:execute-phase
56
56
  ```
57
57