@pareto4/pp-core 1.18.0 → 1.20.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 (194) 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 +13 -1
  9. package/agents/pp-plan-checker.md +3 -0
  10. package/agents/pp-planner.md +20 -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-vault-reviewer.md +2 -0
  15. package/agents/pp-verifier.md +22 -10
  16. package/commands/pp-add-tests.md +1 -1
  17. package/commands/pp-debug.md +1 -0
  18. package/commands/pp-discovery.md +3 -2
  19. package/commands/pp-init.md +4 -3
  20. package/commands/pp-quick.md +3 -0
  21. package/commands/pp-review-backlog.md +2 -1
  22. package/gemini-extension.json +1 -1
  23. package/package.json +6 -3
  24. package/pareto-skills/_shared/pp-claude-md-protocol.md +1 -1
  25. package/pareto-skills/pp-project-setup/SKILL.md +18 -343
  26. package/pp-core/bin/lib/capability-registry.cjs +3 -3
  27. package/pp-core/bin/lib/check-command-router.cjs +60 -20
  28. package/pp-core/bin/lib/commands.cjs +79 -4
  29. package/pp-core/bin/lib/compose-bindings.cjs +97 -14
  30. package/pp-core/bin/lib/discovery-access.cjs +276 -0
  31. package/pp-core/bin/lib/discovery-charter.cjs +664 -0
  32. package/pp-core/bin/lib/discovery-deviations.cjs +264 -0
  33. package/pp-core/bin/lib/discovery-entities.cjs +323 -0
  34. package/pp-core/bin/lib/discovery-for-phase.cjs +628 -0
  35. package/pp-core/bin/lib/discovery-integrations.cjs +286 -0
  36. package/pp-core/bin/lib/discovery-migration.cjs +246 -0
  37. package/pp-core/bin/lib/discovery-phases.cjs +233 -0
  38. package/pp-core/bin/lib/discovery-processes.cjs +355 -0
  39. package/pp-core/bin/lib/discovery-research-track.cjs +5 -4
  40. package/pp-core/bin/lib/discovery-rollup.cjs +12 -8
  41. package/pp-core/bin/lib/discovery-rules.cjs +216 -0
  42. package/pp-core/bin/lib/discovery-screens.cjs +21 -3
  43. package/pp-core/bin/lib/discovery-session-check.cjs +264 -0
  44. package/pp-core/bin/lib/discovery-stories.cjs +273 -0
  45. package/pp-core/bin/lib/discovery-sweep.cjs +13 -4
  46. package/pp-core/bin/lib/discovery-table.cjs +55 -0
  47. package/pp-core/bin/lib/discovery-vault-check.cjs +37 -3
  48. package/pp-core/bin/lib/init-contract.cjs +48 -7
  49. package/pp-core/bin/lib/io.cjs +22 -0
  50. package/pp-core/bin/lib/vault-coordinates.cjs +1 -1
  51. package/pp-core/bin/lib/vault-index.cjs +18 -5
  52. package/pp-core/bin/lib/vault-standards-review.cjs +50 -2
  53. package/pp-core/bin/lib/vault-update.cjs +4 -0
  54. package/pp-core/bin/lib/vault.cjs +2 -2
  55. package/pp-core/bin/pp-tools.cjs +153 -5
  56. package/pp-core/org-profile.json +1 -1
  57. package/pp-core/references/autonomous-best-effort.md +3 -2
  58. package/pp-core/references/autonomous-lifecycle.md +1 -0
  59. package/pp-core/references/autonomous-smart-discuss.md +5 -1
  60. package/pp-core/references/discovery-access.md +112 -0
  61. package/pp-core/references/discovery-build-consumption.md +256 -0
  62. package/pp-core/references/discovery-charter.md +127 -0
  63. package/pp-core/references/discovery-data-model.md +145 -0
  64. package/pp-core/references/discovery-id-conventions.md +77 -8
  65. package/pp-core/references/discovery-integrations.md +151 -0
  66. package/pp-core/references/discovery-processes.md +190 -0
  67. package/pp-core/references/discovery-rebaseline.md +2 -2
  68. package/pp-core/references/discovery-research-track.md +5 -6
  69. package/pp-core/references/discovery-session-output.md +70 -0
  70. package/pp-core/references/discovery-standards.md +107 -0
  71. package/pp-core/references/discovery-traceability.md +2 -2
  72. package/pp-core/references/execute-mvp-tdd.md +3 -3
  73. package/pp-core/references/git-integration.md +57 -33
  74. package/pp-core/references/git-planning-commit.md +31 -9
  75. package/pp-core/references/phase-argument-parsing.md +1 -0
  76. package/pp-core/references/planner-discovery-extract.md +35 -0
  77. package/pp-core/references/planner-gap-closure.md +1 -0
  78. package/pp-core/references/planner-revision.md +3 -1
  79. package/pp-core/references/planner-tdd.md +4 -4
  80. package/pp-core/references/planning-config.md +3 -3
  81. package/pp-core/references/shell-portability.md +28 -0
  82. package/pp-core/references/tdd.md +20 -16
  83. package/pp-core/references/verifier-discovery-conformance.md +36 -0
  84. package/pp-core/references/worktree-path-safety.md +1 -0
  85. package/pp-core/templates/milestone.md +5 -5
  86. package/pp-core/templates/project-charter.md +161 -100
  87. package/pp-core/templates/project.md +3 -3
  88. package/pp-core/templates/requirements.md +18 -0
  89. package/pp-core/templates/verification-report.md +16 -0
  90. package/pp-core/workflows/add-backlog.md +1 -1
  91. package/pp-core/workflows/add-tests.md +5 -2
  92. package/pp-core/workflows/add-todo.md +3 -1
  93. package/pp-core/workflows/ai-integration-phase.md +2 -1
  94. package/pp-core/workflows/audit-fix.md +4 -3
  95. package/pp-core/workflows/audit-milestone.md +1 -0
  96. package/pp-core/workflows/autonomous.md +6 -0
  97. package/pp-core/workflows/check-todos.md +2 -1
  98. package/pp-core/workflows/cleanup.md +3 -1
  99. package/pp-core/workflows/code-review-fix.md +12 -5
  100. package/pp-core/workflows/code-review.md +15 -1
  101. package/pp-core/workflows/complete-milestone.md +13 -3
  102. package/pp-core/workflows/debug.md +2 -0
  103. package/pp-core/workflows/diagnose-issues.md +1 -1
  104. package/pp-core/workflows/discovery-sprint/charter.md +114 -0
  105. package/pp-core/workflows/discovery-sprint/prefill.md +1 -1
  106. package/pp-core/workflows/discovery-sprint/prototype.md +4 -4
  107. package/pp-core/workflows/discovery-sprint/session-13-gates.md +11 -1
  108. package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +54 -7
  109. package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +31 -7
  110. package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +64 -37
  111. package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +12 -3
  112. package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +45 -22
  113. package/pp-core/workflows/discovery-sprint/sessions/06-security.md +30 -15
  114. package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +43 -25
  115. package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +63 -24
  116. package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +28 -13
  117. package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +35 -16
  118. package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +32 -8
  119. package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +21 -18
  120. package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +29 -12
  121. package/pp-core/workflows/discovery-sprint/vault-context.md +14 -3
  122. package/pp-core/workflows/discovery-sprint.md +23 -23
  123. package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +81 -29
  124. package/pp-core/workflows/discovery-to-build/steps/{screen-inventory-gate.md → handoff-gates.md} +111 -3
  125. package/pp-core/workflows/discovery-to-build/templates/discovery-references.md +12 -9
  126. package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +27 -26
  127. package/pp-core/workflows/discovery-to-build.md +70 -34
  128. package/pp-core/workflows/discuss-phase/discovery.md +83 -0
  129. package/pp-core/workflows/discuss-phase/templates/context.md +4 -0
  130. package/pp-core/workflows/discuss-phase-assumptions.md +5 -3
  131. package/pp-core/workflows/discuss-phase.md +18 -10
  132. package/pp-core/workflows/docs-update.md +1 -0
  133. package/pp-core/workflows/eval-review.md +2 -1
  134. package/pp-core/workflows/execute-phase/steps/check-query-gate.md +7 -6
  135. package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +1 -0
  136. package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -0
  137. package/pp-core/workflows/execute-phase/steps/vault-fetch.md +12 -0
  138. package/pp-core/workflows/execute-phase.md +19 -7
  139. package/pp-core/workflows/execute-plan.md +18 -7
  140. package/pp-core/workflows/explore.md +1 -1
  141. package/pp-core/workflows/forensics.md +4 -2
  142. package/pp-core/workflows/health.md +1 -0
  143. package/pp-core/workflows/import.md +2 -1
  144. package/pp-core/workflows/ingest-docs.md +1 -0
  145. package/pp-core/workflows/init/steps/client-context-capture.md +1 -0
  146. package/pp-core/workflows/init/steps/github-provisioning.md +145 -0
  147. package/pp-core/workflows/init/steps/gitlab-provisioning.md +18 -4
  148. package/pp-core/workflows/init/steps/reachability-outcome.md +3 -1
  149. package/pp-core/workflows/init.md +56 -66
  150. package/pp-core/workflows/map-codebase.md +5 -1
  151. package/pp-core/workflows/milestone-summary.md +2 -1
  152. package/pp-core/workflows/mvp-phase.md +1 -0
  153. package/pp-core/workflows/new-milestone.md +9 -5
  154. package/pp-core/workflows/new-project.md +12 -11
  155. package/pp-core/workflows/new-workspace.md +1 -0
  156. package/pp-core/workflows/next.md +2 -1
  157. package/pp-core/workflows/pause-work.md +6 -4
  158. package/pp-core/workflows/plan-milestone-gaps.md +3 -1
  159. package/pp-core/workflows/plan-phase/steps/chunked-planning.md +1 -1
  160. package/pp-core/workflows/plan-phase/steps/discovery-extract.md +41 -0
  161. package/pp-core/workflows/plan-phase.md +36 -16
  162. package/pp-core/workflows/plan-review-convergence.md +2 -0
  163. package/pp-core/workflows/plant-seed.md +4 -2
  164. package/pp-core/workflows/pr-branch.md +2 -0
  165. package/pp-core/workflows/progress.md +5 -0
  166. package/pp-core/workflows/quick.md +11 -4
  167. package/pp-core/workflows/reapply-patches.md +3 -0
  168. package/pp-core/workflows/remove-phase.md +1 -1
  169. package/pp-core/workflows/resume-project.md +3 -0
  170. package/pp-core/workflows/review.md +7 -1
  171. package/pp-core/workflows/secure-phase.md +2 -1
  172. package/pp-core/workflows/session-report.md +1 -0
  173. package/pp-core/workflows/ship.md +8 -1
  174. package/pp-core/workflows/sketch-wrap-up.md +1 -1
  175. package/pp-core/workflows/sketch.md +3 -2
  176. package/pp-core/workflows/spec-phase.md +2 -1
  177. package/pp-core/workflows/spike-wrap-up.md +1 -1
  178. package/pp-core/workflows/spike.md +4 -3
  179. package/pp-core/workflows/sync-skills.md +5 -2
  180. package/pp-core/workflows/thread.md +3 -2
  181. package/pp-core/workflows/transition.md +6 -0
  182. package/pp-core/workflows/ui-phase.md +3 -1
  183. package/pp-core/workflows/ui-review.md +2 -1
  184. package/pp-core/workflows/ultraplan-phase.md +1 -0
  185. package/pp-core/workflows/undo.md +14 -10
  186. package/pp-core/workflows/update.md +5 -0
  187. package/pp-core/workflows/validate-phase.md +6 -3
  188. package/pp-core/workflows/vault-review.md +7 -1
  189. package/pp-core/workflows/vault-write-back.md +5 -0
  190. package/pp-core/workflows/verify-phase.md +5 -0
  191. package/pp-core/workflows/verify-work.md +8 -1
  192. package/scripts/prompt-injection-scan.sh +1 -1
  193. package/scripts/sync-runtime-launcher.cjs +115 -5
  194. package/pp-core/workflows/discovery-sprint/templates/context-template.md +0 -266
@@ -0,0 +1,273 @@
1
+ "use strict";
2
+ /**
3
+ * Discovery user-stories check (Discovery Sprint improvement plan, Batch 4).
4
+ *
5
+ * `.planning/discovery/_stories.md` is the register of user stories Session 3
6
+ * Step 5E mints from the step matrices: `US-nn`, the EPIC it belongs to, the
7
+ * requirements it realises, the story itself, its acceptance criteria and the
8
+ * step it was derived from. Before Batch 4 the stories lived only in Session
9
+ * 3's Notes Template, where no build agent read them (review F-4); the
10
+ * verifier now takes its UAT from this register through `for-phase`. This
11
+ * module checks the structure:
12
+ *
13
+ * - the `## Stories` table exists with the template's columns;
14
+ * - `US-` IDs are well-formed, unique, contiguous;
15
+ * - `EPIC` is one `E-nn`;
16
+ * - every story cites at least one requirement, each existing in
17
+ * `_requirements.md` when it is on file and parseable;
18
+ * - `Story` and `Acceptance criteria` are non-empty;
19
+ * - `Acceptance criteria` is a criterion, not a pointer (`As in F-01`);
20
+ * - `Source step` is `—` or a comma list of `MP-nn #<cell>`, the main process
21
+ * and the step row existing in `_processes.md` when it is on file;
22
+ * - every MVP-disposition requirement that a persona-actor step cites has at
23
+ * least one story (the `N-`/`T-` prefixes never need one; a requirement
24
+ * only `System` steps realise, or no step at all, has no user to tell it).
25
+ *
26
+ * Posture (D-43/D-53): reports at Sessions 8 and 13; hard-blocks at
27
+ * `/pp-discovery-to-build`. Absent artifact → exit 0.
28
+ */
29
+ var __importDefault = (this && this.__importDefault) || function (mod) {
30
+ return (mod && mod.__esModule) ? mod : { "default": mod };
31
+ };
32
+ const node_fs_1 = __importDefault(require("node:fs"));
33
+ const node_path_1 = __importDefault(require("node:path"));
34
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
35
+ const io = require("./io.cjs");
36
+ const { output, error, ERROR_REASON } = io;
37
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
38
+ const table = require("./discovery-table.cjs");
39
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
40
+ const rollup = require("./discovery-rollup.cjs");
41
+ // ─── Constants ────────────────────────────────────────────────────────────────
42
+ const DISCOVERY_DIR = '.planning/discovery';
43
+ const STORIES_FILE = `${DISCOVERY_DIR}/_stories.md`;
44
+ const REQUIREMENTS_FILE = `${DISCOVERY_DIR}/_requirements.md`;
45
+ const PROCESSES_FILE = `${DISCOVERY_DIR}/_processes.md`;
46
+ const STORIES_SECTION = 'Stories';
47
+ const STORIES_HEADER = Object.freeze(['ID', 'EPIC', 'Requirements', 'Story', 'Acceptance criteria', 'Source step']);
48
+ const US_RE = /^US-(\d{2})$/;
49
+ const EPIC_RE = /^E-\d{2}$/;
50
+ const REQUIREMENT_ID_RE = /^[A-Z][A-Z0-9]*-\d+$/;
51
+ /** `MP-nn #<cell>` — the main process and the step matrix's `#` cell, copied verbatim (`01.3`, `5`). */
52
+ const SOURCE_STEP_RE = /^(MP-\d{2}) #(\S+)$/;
53
+ const MP_HEADING_RE = /^(MP-\d{2})\s+—\s+\S/;
54
+ const SYSTEM_ACTOR = 'System';
55
+ /** An acceptance-criteria cell that only points elsewhere is no criterion the verifier can run. */
56
+ const POINTER_AC_RE = /^(as in|see|zie|idem)\b/i;
57
+ const PENDING = '—';
58
+ /** Requirement prefixes that need no user story: non-functional and technical rows have no actor. */
59
+ const STORYLESS_PREFIXES = Object.freeze(['N-', 'T-']);
60
+ const USAGE = 'Usage: pp-tools discovery stories-check';
61
+ function readIfPresent(abs) {
62
+ if (!node_fs_1.default.existsSync(abs))
63
+ return { kind: 'absent' };
64
+ try {
65
+ return { kind: 'ok', content: node_fs_1.default.readFileSync(abs, 'utf-8') };
66
+ }
67
+ catch {
68
+ return { kind: 'unreadable' };
69
+ }
70
+ }
71
+ function sameHeader(actual, expected) {
72
+ return actual.length === expected.length && actual.every((c, i) => c.trim().toLowerCase() === expected[i].toLowerCase());
73
+ }
74
+ function splitList(cell) {
75
+ return cell.split(',').map((t) => t.trim()).filter(Boolean);
76
+ }
77
+ /** First-column IDs of the first table under a `## <heading>` of another artifact. */
78
+ function firstColumnOf(content, heading) {
79
+ const section = table.splitSections(content.split(/\r?\n/), 2).find((s) => s.heading === heading);
80
+ if (!section)
81
+ return new Set();
82
+ const tables = table.parseTables(section.body);
83
+ if (tables.length === 0)
84
+ return new Set();
85
+ return new Set(tables[0].rows.map((r) => (r.cells[0] ?? '').trim()).filter(Boolean));
86
+ }
87
+ /**
88
+ * Every step row of `_processes.md` § Steps: its key (`MP-nn #<cell>`), its
89
+ * actors and the requirements it cites. `null` when the section is absent —
90
+ * the step rule then cannot run and only the prefix exemption applies.
91
+ */
92
+ function stepsOf(content) {
93
+ const steps = table.splitSections(content.split(/\r?\n/), 2).find((s) => s.heading === 'Steps');
94
+ if (!steps)
95
+ return null;
96
+ const out = [];
97
+ for (const sec of table.splitSections(steps.body.map((l) => l.text), 3)) {
98
+ const m = MP_HEADING_RE.exec(sec.heading);
99
+ const tbl = table.parseTables(sec.body)[0];
100
+ if (!m || !tbl)
101
+ continue;
102
+ for (const r of tbl.rows) {
103
+ out.push({ key: `${m[1]} #${(r.cells[0] ?? '').trim()}`, actors: splitList(r.cells[2] ?? ''), requirements: splitList(r.cells[7] ?? '') });
104
+ }
105
+ }
106
+ return out;
107
+ }
108
+ /**
109
+ * Does this MVP requirement need a user story? Step 5E derives stories from the
110
+ * step rows where the actor is a persona, so the check mirrors that: a
111
+ * requirement needs one when at least one step citing it has an actor other
112
+ * than `System`. Without a step matrix the prefix exemption is all there is.
113
+ */
114
+ function needsStory(r, steps) {
115
+ if (STORYLESS_PREFIXES.includes(r.prefix))
116
+ return false;
117
+ if (steps === null)
118
+ return true;
119
+ return steps.some((s) => s.requirements.includes(r.id) && s.actors.some((a) => a !== SYSTEM_ACTOR));
120
+ }
121
+ // ─── Check ────────────────────────────────────────────────────────────────────
122
+ function checkStories(content, sources) {
123
+ const violations = [];
124
+ const sections = table.splitSections(content.split(/\r?\n/), 2);
125
+ const section = sections.find((s) => s.heading === STORIES_SECTION);
126
+ if (!section) {
127
+ return { ok: false, stories: 0, violations: [{ artifact: STORIES_FILE, reason: 'missing_section', id: STORIES_SECTION }] };
128
+ }
129
+ const tables = table.parseTables(section.body);
130
+ if (tables.length === 0) {
131
+ return { ok: false, stories: 0, violations: [{ artifact: STORIES_FILE, reason: 'missing_section', line: section.line, id: STORIES_SECTION }] };
132
+ }
133
+ if (!sameHeader(tables[0].header, STORIES_HEADER)) {
134
+ return { ok: false, stories: 0, violations: [{ artifact: STORIES_FILE, reason: 'bad_table_shape', line: tables[0].line, id: STORIES_SECTION }] };
135
+ }
136
+ const knownIds = sources.requirements instanceof Array ? new Set(sources.requirements.map((r) => r.id)) : null;
137
+ const ids = new Set();
138
+ const seqs = [];
139
+ const storied = new Set();
140
+ for (const row of tables[0].rows) {
141
+ const [id, epic, requirements, story, acceptance, sourceStep] = row.cells.map((c) => c.trim());
142
+ const m = US_RE.exec(id ?? '');
143
+ if (!m) {
144
+ violations.push({ artifact: STORIES_FILE, reason: 'bad_id', line: row.line });
145
+ }
146
+ else if (ids.has(id)) {
147
+ violations.push({ artifact: STORIES_FILE, reason: 'duplicate_id', line: row.line, id });
148
+ }
149
+ else {
150
+ ids.add(id);
151
+ seqs.push(Number(m[1]));
152
+ }
153
+ if (!EPIC_RE.test(epic ?? ''))
154
+ violations.push({ artifact: STORIES_FILE, reason: 'bad_epic', line: row.line });
155
+ if ((story ?? '') === '')
156
+ violations.push({ artifact: STORIES_FILE, reason: 'empty_cell', line: row.line, id: 'Story' });
157
+ if ((acceptance ?? '') === '')
158
+ violations.push({ artifact: STORIES_FILE, reason: 'empty_cell', line: row.line, id: 'Acceptance criteria' });
159
+ else if (POINTER_AC_RE.test(acceptance))
160
+ violations.push({ artifact: STORIES_FILE, reason: 'acceptance_is_pointer', line: row.line, id });
161
+ const reqs = splitList(requirements ?? '');
162
+ if (reqs.length === 0)
163
+ violations.push({ artifact: STORIES_FILE, reason: 'no_requirement', line: row.line });
164
+ for (const r of reqs) {
165
+ if (!REQUIREMENT_ID_RE.test(r))
166
+ violations.push({ artifact: STORIES_FILE, reason: 'unknown_requirement_id', line: row.line, id: r.slice(0, 40) });
167
+ else if (knownIds !== null && !knownIds.has(r))
168
+ violations.push({ artifact: STORIES_FILE, reason: 'unknown_requirement_id', line: row.line, id: r });
169
+ else
170
+ storied.add(r);
171
+ }
172
+ const src = sourceStep ?? '';
173
+ if (src !== PENDING) {
174
+ const refs = splitList(src);
175
+ if (refs.length === 0)
176
+ violations.push({ artifact: STORIES_FILE, reason: 'bad_source_step', line: row.line });
177
+ for (const ref of refs) {
178
+ const sm = SOURCE_STEP_RE.exec(ref);
179
+ if (!sm)
180
+ violations.push({ artifact: STORIES_FILE, reason: 'bad_source_step', line: row.line });
181
+ else if (sources.processes !== null && !sources.processes.has(sm[1]))
182
+ violations.push({ artifact: STORIES_FILE, reason: 'unknown_process_id', line: row.line, id: sm[1] });
183
+ else if (sources.steps !== null && !sources.steps.some((st) => st.key === ref))
184
+ violations.push({ artifact: STORIES_FILE, reason: 'unknown_process_step', line: row.line, id: ref.slice(0, 40) });
185
+ }
186
+ }
187
+ }
188
+ const max = table.boundedSequenceMax(seqs);
189
+ if (max !== null) {
190
+ const have = new Set(seqs);
191
+ for (let n = 1; n <= max; n += 1) {
192
+ if (!have.has(n))
193
+ violations.push({ artifact: STORIES_FILE, reason: 'sequence_gap', id: `US-${String(n).padStart(2, '0')}` });
194
+ }
195
+ }
196
+ if (sources.requirements instanceof Array) {
197
+ for (const r of sources.requirements) {
198
+ if (r.disposition !== rollup.DISPOSITIONS.MVP)
199
+ continue;
200
+ if (!needsStory(r, sources.steps))
201
+ continue;
202
+ if (!storied.has(r.id))
203
+ violations.push({ artifact: REQUIREMENTS_FILE, reason: 'mvp_requirement_without_story', id: r.id });
204
+ }
205
+ }
206
+ else if (sources.requirements === 'unparseable') {
207
+ violations.push({ artifact: REQUIREMENTS_FILE, reason: 'requirements_unparseable' });
208
+ }
209
+ return { ok: violations.length === 0, stories: ids.size, violations };
210
+ }
211
+ function checkProjectStories(cwd) {
212
+ const read = readIfPresent(node_path_1.default.join(cwd, STORIES_FILE));
213
+ if (read.kind === 'absent')
214
+ return null;
215
+ if (read.kind === 'unreadable')
216
+ return { ok: false, file: STORIES_FILE, stories: 0, violations: [{ artifact: STORIES_FILE, reason: 'artifact_unreadable' }] };
217
+ const extra = [];
218
+ const sources = { requirements: null, processes: null, steps: null };
219
+ const req = readIfPresent(node_path_1.default.join(cwd, REQUIREMENTS_FILE));
220
+ if (req.kind === 'unreadable')
221
+ extra.push({ artifact: REQUIREMENTS_FILE, reason: 'artifact_unreadable' });
222
+ if (req.kind === 'ok') {
223
+ const parsed = rollup.parseRequirementsArtifact(req.content);
224
+ sources.requirements = parsed.ok ? parsed.rows.map((r) => ({ id: r.id, prefix: r.prefix, disposition: r.disposition })) : 'unparseable';
225
+ }
226
+ const proc = readIfPresent(node_path_1.default.join(cwd, PROCESSES_FILE));
227
+ if (proc.kind === 'unreadable')
228
+ extra.push({ artifact: PROCESSES_FILE, reason: 'artifact_unreadable' });
229
+ if (proc.kind === 'ok') {
230
+ sources.processes = firstColumnOf(proc.content, 'To-be');
231
+ sources.steps = stepsOf(proc.content);
232
+ }
233
+ const result = checkStories(read.content, sources);
234
+ result.violations.push(...extra);
235
+ result.ok = result.violations.length === 0;
236
+ return { file: STORIES_FILE, ...result };
237
+ }
238
+ function formatViolation(v) {
239
+ const where = v.line !== undefined ? `:${v.line}` : '';
240
+ const what = v.id !== undefined ? ` (${v.id})` : '';
241
+ return ` - ${v.artifact}${where}: ${v.reason}${what}`;
242
+ }
243
+ // ─── CLI adapter ──────────────────────────────────────────────────────────────
244
+ /** `pp-tools discovery stories-check` (dotted `query discovery.stories-check`). Absent artifact → exit 0, `present: false`. */
245
+ function cmdDiscoveryStoriesCheck(cwd, args, raw) {
246
+ if (args.length > 2) {
247
+ error(USAGE, ERROR_REASON.USAGE);
248
+ return; // unreachable
249
+ }
250
+ const result = checkProjectStories(cwd);
251
+ if (result === null) {
252
+ output({ ok: true, present: false, file: STORIES_FILE }, raw);
253
+ return;
254
+ }
255
+ const payload = { ok: result.ok, present: true, file: result.file, stories: result.stories };
256
+ if (!result.ok) {
257
+ output({ ...payload, violations: result.violations.length }, raw);
258
+ error(`discovery stories-check: ${result.violations.length} violation(s):\n` + result.violations.map(formatViolation).join('\n'), ERROR_REASON.DISCOVERY_STORIES_CHECK_FAILED);
259
+ return; // unreachable
260
+ }
261
+ output(payload, raw);
262
+ }
263
+ module.exports = {
264
+ STORIES_FILE,
265
+ STORIES_HEADER,
266
+ STORYLESS_PREFIXES,
267
+ SYSTEM_ACTOR,
268
+ needsStory,
269
+ STORIES_CHECK_FAILED: ERROR_REASON.DISCOVERY_STORIES_CHECK_FAILED,
270
+ checkStories,
271
+ checkProjectStories,
272
+ cmdDiscoveryStoriesCheck,
273
+ };
@@ -40,7 +40,7 @@ const VOID_TAGS = new Set([
40
40
  const RAW_TEXT_TAGS = new Set(['script', 'style', 'noscript']);
41
41
  const ID_RE = /^[A-Za-z][A-Za-z0-9_:.-]{0,79}$/;
42
42
  const REQ_ID_RE = /^[A-Z][A-Z0-9]*-\d+$/;
43
- const DECISION_RE = /^D-\d{2}-\d+$/;
43
+ const DECISION_RE = /^D-(?:\d{2}|P\d{2})-\d+$/;
44
44
  const ELEMENT_RE = /^(\*|[A-Za-z0-9_.-]+\.html?)#([A-Za-z][A-Za-z0-9_:.-]{0,79})$/;
45
45
  const ROUND_RE = /^## Sweep (\d{1,2}) — \d{4}-\d{2}-\d{2} — .+$/;
46
46
  const TABLE_HEADER = ['Element', 'Outcome', 'Reference'];
@@ -63,12 +63,21 @@ function parseAttrs(src) {
63
63
  }
64
64
  return attrs;
65
65
  }
66
- function isFunctional(tag, attrs) {
66
+ /**
67
+ * A multi-word PascalCase name (`UiButton`, `UiSelect`): the component spelling
68
+ * of a kebab-case custom element (`ui-button`), which a string template keeps
69
+ * and lowercasing would hide (issue #12, item 8). A single word (`Div`) or an
70
+ * all-caps tag (`BUTTON`) is plain HTML.
71
+ */
72
+ function isPascalComponent(rawTag) {
73
+ return /^[A-Z][a-z0-9]+[A-Z]/.test(rawTag);
74
+ }
75
+ function isFunctional(tag, attrs, rawTag = tag) {
67
76
  if (attrs.has('data-req'))
68
77
  return true;
69
78
  if (tag === 'input' && (attrs.get('type') || '').toLowerCase() === 'hidden')
70
79
  return false;
71
- if (FUNCTIONAL_TAGS.has(tag) || tag.includes('-'))
80
+ if (FUNCTIONAL_TAGS.has(tag) || tag.includes('-') || isPascalComponent(rawTag))
72
81
  return true;
73
82
  const role = (attrs.get('role') || '').toLowerCase();
74
83
  return FUNCTIONAL_ROLES.has(role);
@@ -107,7 +116,7 @@ function scanPage(page, html, violations) {
107
116
  const parentScreen = stack.length ? stack[stack.length - 1].screen : null;
108
117
  const screen = screenFrom(tag, attrs) ?? parentScreen;
109
118
  const line = lineAt(text, m.index);
110
- if (isFunctional(tag, attrs)) {
119
+ if (isFunctional(tag, attrs, m[2])) {
111
120
  const id = attrs.get('id') ?? null;
112
121
  let reqs = [];
113
122
  if (attrs.has('data-req')) {
@@ -90,9 +90,64 @@ function boundedSequenceMax(seqs) {
90
90
  const max = seqs.reduce((a, b) => (b > a ? b : a), 0);
91
91
  return max > MAX_SEQUENCE_SPAN ? null : max;
92
92
  }
93
+ /**
94
+ * Every pipe table in a run of numbered lines (Discovery Sprint improvement
95
+ * plan, Batch 2). The header is the row before the first separator; data rows
96
+ * follow until the first line that is not a pipe row; a second separator
97
+ * starts the next table. Cells go through `splitTableCells`, so an escaped pipe
98
+ * inside a free-text cell never shifts a column.
99
+ */
100
+ function parseTables(body) {
101
+ const tables = [];
102
+ for (let i = 1; i < body.length; i += 1) {
103
+ if (!SEPARATOR_ROW_RE.test(body[i].text) || !body[i - 1].text.trim().startsWith('|'))
104
+ continue;
105
+ const table = { line: body[i - 1].n, header: splitTableCells(body[i - 1].text), rows: [] };
106
+ let j = i + 1;
107
+ for (; j < body.length; j += 1) {
108
+ const text = body[j].text.trim();
109
+ if (!text.startsWith('|') || SEPARATOR_ROW_RE.test(text))
110
+ break;
111
+ table.rows.push({ line: body[j].n, cells: splitTableCells(text) });
112
+ }
113
+ tables.push(table);
114
+ i = j - 1;
115
+ }
116
+ return tables;
117
+ }
118
+ /**
119
+ * Split a file into the sections opened by headings of exactly `level` hashes
120
+ * (`## ` for 2). A section's body runs to the next heading of the same or a
121
+ * higher level; lower-level headings stay inside the body, so a caller can
122
+ * split a body again at the next level. Lines before the first heading belong
123
+ * to no section.
124
+ */
125
+ function splitSections(lines, level) {
126
+ const open = new RegExp(`^#{${level}}\\s+(.*)$`);
127
+ const close = new RegExp(`^#{1,${level}}\\s`);
128
+ const out = [];
129
+ let current = null;
130
+ for (let i = 0; i < lines.length; i += 1) {
131
+ const m = open.exec(lines[i]);
132
+ if (m) {
133
+ current = { heading: m[1].trim(), line: i + 1, body: [] };
134
+ out.push(current);
135
+ continue;
136
+ }
137
+ if (close.test(lines[i])) {
138
+ current = null;
139
+ continue;
140
+ }
141
+ if (current)
142
+ current.body.push({ n: i + 1, text: lines[i] });
143
+ }
144
+ return out;
145
+ }
93
146
  module.exports = {
94
147
  splitTableCells,
95
148
  SEPARATOR_ROW_RE,
96
149
  MAX_SEQUENCE_SPAN,
97
150
  boundedSequenceMax,
151
+ parseTables,
152
+ splitSections,
98
153
  };
@@ -12,7 +12,11 @@
12
12
  * - every entry is one well-formed line (`- `<path>` — <why>`, or
13
13
  * `- (none) — <why>` when nothing was selected);
14
14
  * - every selected document was USED: cited `[vault: <path>]` somewhere in
15
- * the session notes, or marked `— not used: <reason>` on its own line.
15
+ * the session notes, or marked `— not used: <reason>` on its own line;
16
+ * - when the selection used `INDEX.md`, every entry the vault marks
17
+ * `[cross-cutting]` has a record line (issue #12, items 16-17): those rules
18
+ * bind every session, so leaving one out is how an approval route went
19
+ * unseen until Session 12.
16
20
  *
17
21
  * The selection itself is the facilitator's judgement and is not re-derived
18
22
  * here; what is checked is that the record exists, is complete, and that what
@@ -109,7 +113,7 @@ function parseEntry(n, text) {
109
113
  * A citation counts only OUTSIDE the section: the record naming a path is not
110
114
  * evidence that the path was used.
111
115
  */
112
- function checkVaultContext(content) {
116
+ function checkVaultContext(content, crossCutting = []) {
113
117
  const violations = [];
114
118
  const section = extractSection(content);
115
119
  if (section === null) {
@@ -163,8 +167,38 @@ function checkVaultContext(content) {
163
167
  violations.push({ reason: 'document_unused', line: doc.line, path: doc.path });
164
168
  }
165
169
  }
170
+ // The cross-cutting entries are owed a line only when the selection read the
171
+ // vault's own index: SUMMARY.md carries no mark, and `none` read nothing.
172
+ if (index === 'INDEX.md') {
173
+ const recorded = new Set(docs.map((d) => d.path));
174
+ for (const p of crossCutting) {
175
+ if (!recorded.has(p))
176
+ violations.push({ reason: 'cross_cutting_missing', path: p });
177
+ }
178
+ }
166
179
  return { ok: violations.length === 0, index, documents: docs.length, cited, not_used: notUsed, violations };
167
180
  }
181
+ /**
182
+ * The paths the cached `INDEX.md` marks `[cross-cutting]`, read through the
183
+ * vault's one index-state function so the bounded read, the withheld filter
184
+ * and the path rule are the engine's own. Withheld entries never reach it;
185
+ * unsafe ones are dropped, since no record line could name them. A vault that
186
+ * is off, uncached or unreadable owes nothing here — `vault status` reports
187
+ * that state.
188
+ */
189
+ function crossCuttingPaths(cwd) {
190
+ try {
191
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
192
+ const vault = require('./vault.cjs');
193
+ const state = vault.computeVaultIndexState(cwd);
194
+ if (state === null)
195
+ return [];
196
+ return state.entries.filter((e) => e.crossCutting === true && e.resolution !== 'unsafe').map((e) => e.path);
197
+ }
198
+ catch {
199
+ return [];
200
+ }
201
+ }
168
202
  /** Parse the session argument: a bare integer 1–13, leading zeros allowed (`03` is session 3). */
169
203
  function parseSessionArg(raw) {
170
204
  if (typeof raw !== 'string' || !/^\d{1,3}$/.test(raw))
@@ -189,7 +223,7 @@ function checkSessionVaultContext(cwd, n) {
189
223
  violations: [{ reason: 'session_file_missing' }],
190
224
  };
191
225
  }
192
- return { session: n, file: rel, ...checkVaultContext(content) };
226
+ return { session: n, file: rel, ...checkVaultContext(content, crossCuttingPaths(cwd)) };
193
227
  }
194
228
  function formatViolation(v) {
195
229
  const where = v.line !== undefined ? `line ${v.line}: ` : '';
@@ -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 {
@@ -203,6 +203,28 @@ const ERROR_REASON = Object.freeze({
203
203
  DISCOVERY_REBASELINE_CHECK_FAILED: 'discovery_rebaseline_check_failed',
204
204
  // discovery vault context record (PBI 75336)
205
205
  DISCOVERY_VAULT_CHECK_FAILED: 'discovery_vault_check_failed',
206
+ // discovery project charter (Discovery Sprint improvement plan, Batch 1)
207
+ DISCOVERY_CHARTER_CHECK_FAILED: 'discovery_charter_check_failed',
208
+ DISCOVERY_CHARTER_UNSIGNED: 'discovery_charter_unsigned',
209
+ // discovery data model, processes, business rules (improvement plan, Batch 2)
210
+ DISCOVERY_ENTITY_CHECK_FAILED: 'discovery_entity_check_failed',
211
+ DISCOVERY_ENTITY_CHECK_INCOMPLETE: 'discovery_entity_check_incomplete',
212
+ DISCOVERY_PROCESS_CHECK_FAILED: 'discovery_process_check_failed',
213
+ DISCOVERY_PROCESS_STEPS_UNCITED: 'discovery_process_steps_uncited',
214
+ DISCOVERY_RULES_CHECK_FAILED: 'discovery_rules_check_failed',
215
+ DISCOVERY_STORIES_CHECK_FAILED: 'discovery_stories_check_failed',
216
+ DISCOVERY_PHASES_CHECK_FAILED: 'discovery_phases_check_failed',
217
+ DISCOVERY_FOR_PHASE_FAILED: 'discovery_for_phase_failed',
218
+ // discovery roles, integrations, migration, deviations (improvement plan, Batch 3)
219
+ DISCOVERY_ROLES_CHECK_FAILED: 'discovery_roles_check_failed',
220
+ DISCOVERY_INTEGRATIONS_CHECK_FAILED: 'discovery_integrations_check_failed',
221
+ DISCOVERY_INTEGRATIONS_UNCITED: 'discovery_integrations_uncited',
222
+ DISCOVERY_MIGRATION_CHECK_FAILED: 'discovery_migration_check_failed',
223
+ DISCOVERY_MIGRATION_UNCITED: 'discovery_migration_uncited',
224
+ // discovery session output gate (improvement plan, Batch 5)
225
+ DISCOVERY_SESSION_CHECK_FAILED: 'discovery_session_check_failed',
226
+ DISCOVERY_DEVIATIONS_CHECK_FAILED: 'discovery_deviations_check_failed',
227
+ DISCOVERY_DEVIATIONS_UNAPPROVED: 'discovery_deviations_unapproved',
206
228
  // generic
207
229
  USAGE: 'usage',
208
230
  UNKNOWN: 'unknown',
@@ -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';