@pareto4/pp-core 1.13.0 → 1.18.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 (204) hide show
  1. package/.claude-plugin/plugin.json +3 -3
  2. package/README.md +31 -12
  3. package/agents/pp-code-fixer.md +0 -11
  4. package/agents/pp-code-reviewer.md +1 -77
  5. package/agents/pp-debug-session-manager.md +9 -6
  6. package/agents/pp-executor.md +12 -29
  7. package/agents/pp-plan-checker.md +4 -4
  8. package/agents/pp-planner.md +34 -20
  9. package/agents/pp-vault-reviewer.md +100 -23
  10. package/agents/pp-verifier.md +1 -3
  11. package/bin/install.js +10 -3
  12. package/commands/pp-execute-phase.md +1 -3
  13. package/commands/pp-ns-manage.md +1 -1
  14. package/commands/pp-ns-review.md +1 -1
  15. package/commands/pp-plan-phase.md +1 -2
  16. package/commands/pp-vault-promote.md +7 -8
  17. package/commands/pp-vault-review.md +6 -5
  18. package/commands/pp-verify-work.md +1 -2
  19. package/gemini-extension.json +1 -1
  20. package/hooks/dist/pp-check-update.js +0 -0
  21. package/hooks/dist/pp-cursor-session-start.js +1 -1
  22. package/hooks/dist/pp-statusline.js +1 -1
  23. package/hooks/dist/pp-update-banner.js +0 -0
  24. package/hooks/pp-check-update.js +0 -0
  25. package/hooks/pp-cursor-session-start.js +1 -1
  26. package/hooks/pp-statusline.js +1 -1
  27. package/hooks/pp-update-banner.js +0 -0
  28. package/package.json +10 -18
  29. package/pareto-skills/_shared/create-project-board.sh +0 -0
  30. package/pareto-skills/pp-project-setup/SKILL.md +1 -1
  31. package/pp-core/bin/check-latest-version.cjs +0 -0
  32. package/pp-core/bin/detect-build-commands.sh +2 -2
  33. package/pp-core/bin/lib/capability-activation.cjs +83 -9
  34. package/pp-core/bin/lib/capability-registry.cjs +201 -229
  35. package/pp-core/bin/lib/capability-state.cjs +93 -1
  36. package/pp-core/bin/lib/capability-writer.cjs +3 -3
  37. package/pp-core/bin/lib/check-command-router.cjs +1906 -1514
  38. package/pp-core/bin/lib/clusters.cjs +3 -0
  39. package/pp-core/bin/lib/command-aliases.cjs +0 -35
  40. package/pp-core/bin/lib/commands.cjs +9 -4
  41. package/pp-core/bin/lib/compose-bindings.cjs +251 -88
  42. package/pp-core/bin/lib/config-loader.cjs +7 -103
  43. package/pp-core/bin/lib/config-schema.cjs +1 -1
  44. package/pp-core/bin/lib/config.cjs +151 -62
  45. package/pp-core/bin/lib/decisions.cjs +4 -4
  46. package/pp-core/bin/lib/discovery-vault-check.cjs +246 -0
  47. package/pp-core/bin/lib/frontmatter.cjs +1 -38
  48. package/pp-core/bin/lib/git-base-branch.cjs +11 -12
  49. package/pp-core/bin/lib/git-provider.cjs +9 -10
  50. package/pp-core/bin/lib/init-command-router.cjs +5 -8
  51. package/pp-core/bin/lib/init-contract.cjs +620 -738
  52. package/pp-core/bin/lib/init-interactive.cjs +62 -4
  53. package/pp-core/bin/lib/init.cjs +24 -51
  54. package/pp-core/bin/lib/install-profiles.cjs +1 -1
  55. package/pp-core/bin/lib/io.cjs +4 -2
  56. package/pp-core/bin/lib/loop-resolver.cjs +165 -129
  57. package/pp-core/bin/lib/org-profile.cjs +402 -161
  58. package/pp-core/bin/lib/package-identity.cjs +3 -3
  59. package/pp-core/bin/lib/phase-command-router.cjs +1 -29
  60. package/pp-core/bin/lib/phase-scope.cjs +1 -1
  61. package/pp-core/bin/lib/phase.cjs +11 -253
  62. package/pp-core/bin/lib/platform-bindings.cjs +40 -19
  63. package/pp-core/bin/lib/profile-output.cjs +1 -1
  64. package/pp-core/bin/lib/shell-command-projection.cjs +104 -1
  65. package/pp-core/bin/lib/state.cjs +11 -85
  66. package/pp-core/bin/lib/template.cjs +1 -1
  67. package/pp-core/bin/lib/vault-command-router.cjs +1682 -477
  68. package/pp-core/bin/lib/vault-coordinates.cjs +16 -7
  69. package/pp-core/bin/lib/vault-index.cjs +656 -0
  70. package/pp-core/bin/lib/vault-seal.cjs +1851 -0
  71. package/pp-core/bin/lib/vault-standards-promote.cjs +577 -0
  72. package/pp-core/bin/lib/vault-standards-review.cjs +293 -0
  73. package/pp-core/bin/lib/vault-standards.cjs +1504 -0
  74. package/pp-core/bin/lib/vault-trace.cjs +155 -2778
  75. package/pp-core/bin/lib/vault-update.cjs +10 -59
  76. package/pp-core/bin/lib/vault.cjs +2570 -950
  77. package/pp-core/bin/lib/verify.cjs +0 -11
  78. package/pp-core/bin/portable-timeout.sh +1 -1
  79. package/pp-core/bin/pp-tools.cjs +157 -54
  80. package/pp-core/bin/shared/config-defaults.manifest.json +0 -7
  81. package/pp-core/bin/shared/config-schema.manifest.json +0 -3
  82. package/pp-core/bin/shared/model-catalog.json +2 -2
  83. package/pp-core/bin/verify-reapply-patches.cjs +0 -0
  84. package/pp-core/org-profile.json +26 -0
  85. package/pp-core/references/autonomous-best-effort.md +8 -7
  86. package/pp-core/references/loop-hook-dispatch.md +0 -25
  87. package/pp-core/references/planner-gap-closure.md +0 -1
  88. package/pp-core/references/planner-tdd.md +0 -1
  89. package/pp-core/references/planner-vault-standards.md +311 -0
  90. package/pp-core/references/planning-config.md +0 -8
  91. package/pp-core/references/tdd.md +0 -1
  92. package/pp-core/templates/README.md +1 -0
  93. package/pp-core/templates/VAULT-REVIEW.md +56 -0
  94. package/pp-core/templates/phase-prompt.md +2 -6
  95. package/pp-core/workflows/_runtime-launcher.snippet.sh +0 -0
  96. package/pp-core/workflows/add-tests.md +55 -4
  97. package/pp-core/workflows/audit-fix.md +63 -2
  98. package/pp-core/workflows/audit-milestone.md +1 -1
  99. package/pp-core/workflows/autonomous.md +85 -11
  100. package/pp-core/workflows/check-todos.md +13 -2
  101. package/pp-core/workflows/code-review-fix.md +59 -3
  102. package/pp-core/workflows/code-review.md +10 -57
  103. package/pp-core/workflows/complete-milestone.md +1 -1
  104. package/pp-core/workflows/debug.md +54 -4
  105. package/pp-core/workflows/discovery-sprint/prefill.md +19 -7
  106. package/pp-core/workflows/discovery-sprint/prototype.md +46 -49
  107. package/pp-core/workflows/discovery-sprint/session-13-gates.md +2 -0
  108. package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +1 -3
  109. package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +1 -3
  110. package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +1 -3
  111. package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +20 -24
  112. package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +1 -3
  113. package/pp-core/workflows/discovery-sprint/sessions/06-security.md +1 -3
  114. package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +1 -3
  115. package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +1 -3
  116. package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +1 -3
  117. package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +1 -6
  118. package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +1 -3
  119. package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +1 -3
  120. package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +1 -3
  121. package/pp-core/workflows/discovery-sprint/templates/context-template.md +1 -1
  122. package/pp-core/workflows/discovery-sprint/vault-context.md +207 -0
  123. package/pp-core/workflows/discovery-sprint.md +7 -3
  124. package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +4 -4
  125. package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +43 -1
  126. package/pp-core/workflows/discovery-to-build.md +10 -2
  127. package/pp-core/workflows/discuss-phase.md +3 -10
  128. package/pp-core/workflows/execute-phase/steps/check-query-gate.md +327 -0
  129. package/pp-core/workflows/execute-phase/steps/client-context-gate.md +3 -2
  130. package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +1 -1
  131. package/pp-core/workflows/execute-phase/steps/execute-pre-dispatch.md +78 -0
  132. package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
  133. package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +6 -8
  134. package/pp-core/workflows/execute-phase/steps/vault-fetch.md +115 -14
  135. package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +219 -20
  136. package/pp-core/workflows/execute-phase/steps/verify-post-gate.md +147 -0
  137. package/pp-core/workflows/execute-phase.md +152 -128
  138. package/pp-core/workflows/execute-plan.md +54 -6
  139. package/pp-core/workflows/fast.md +53 -4
  140. package/pp-core/workflows/help/modes/full.md +3 -3
  141. package/pp-core/workflows/init/steps/client-context-capture.md +101 -0
  142. package/pp-core/workflows/init/steps/gitlab-provisioning.md +130 -0
  143. package/pp-core/workflows/init/steps/project-md-deferral-narration.md +66 -0
  144. package/pp-core/workflows/init/steps/reachability-outcome.md +70 -0
  145. package/pp-core/workflows/init.md +232 -272
  146. package/pp-core/workflows/new-project.md +31 -23
  147. package/pp-core/workflows/plan-phase.md +98 -45
  148. package/pp-core/workflows/quick.md +117 -3
  149. package/pp-core/workflows/secure-phase.md +1 -1
  150. package/pp-core/workflows/ship.md +57 -28
  151. package/pp-core/workflows/undo.md +1 -1
  152. package/pp-core/workflows/update.md +3 -3
  153. package/pp-core/workflows/validate-phase.md +58 -3
  154. package/pp-core/workflows/vault-review.md +161 -23
  155. package/pp-core/workflows/vault-write-back.md +73 -110
  156. package/pp-core/workflows/verify-work.md +12 -14
  157. package/scripts/changeset/cli.cjs +6 -5
  158. package/scripts/changeset/lint.cjs +0 -0
  159. package/scripts/changeset/new.cjs +33 -50
  160. package/scripts/dev-env.cjs +25 -85
  161. package/scripts/gen-capability-registry.cjs +120 -13
  162. package/scripts/lib/allowlist-ratchet.cjs +1 -103
  163. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -2
  164. package/scripts/lint-docs-required.cjs +0 -0
  165. package/scripts/lint-pr-check-project-dir.cjs +1 -3
  166. package/scripts/lint-test-file-count.allowlist.json +3 -19
  167. package/scripts/lint-test-file-count.cjs +5 -6
  168. package/scripts/prompt-injection-scan.sh +19 -17
  169. package/scripts/release-channel.cjs +0 -0
  170. package/scripts/sync-manifest-versions.cjs +5 -4
  171. package/scripts/sync-rulesets.sh +1 -1
  172. package/scripts/update-size-baseline.cjs +30 -274
  173. package/scripts/verify-opengsd-sweep.cjs +3 -5
  174. package/scripts/workflow-size.cjs +9 -61
  175. package/pp-core/bin/lib/dispatch-plan.cjs +0 -483
  176. package/pp-core/bin/lib/install-run.cjs +0 -536
  177. package/pp-core/bin/lib/loop-gates.cjs +0 -213
  178. package/pp-core/bin/lib/phase-manifest.cjs +0 -527
  179. package/pp-core/bin/lib/phase-tier.cjs +0 -480
  180. package/pp-core/bin/lib/plan-graph.cjs +0 -368
  181. package/pp-core/bin/lib/practices-review-check.cjs +0 -938
  182. package/pp-core/bin/lib/vault-audit.cjs +0 -2070
  183. package/pp-core/bin/lib/vault-practices.cjs +0 -1990
  184. package/pp-core/bin/lib/vault-review.cjs +0 -2359
  185. package/pp-core/bin/lib/vault-write-back.cjs +0 -877
  186. package/pp-core/references/executor-vault-trace.md +0 -47
  187. package/pp-core/references/planner-plan-graph.md +0 -66
  188. package/pp-core/references/planner-vault-practices.md +0 -112
  189. package/pp-core/templates/practices/README.md +0 -251
  190. package/pp-core/templates/practices/error-handling.md +0 -18
  191. package/pp-core/templates/practices/minimal-comments.md +0 -14
  192. package/pp-core/templates/practices/naming-convention.md +0 -18
  193. package/pp-core/templates/practices/practice-template.md +0 -23
  194. package/pp-core/workflows/discovery-sprint/vault-fetch.md +0 -37
  195. package/pp-core/workflows/execute-phase/steps/checkpoint-handling.md +0 -45
  196. package/pp-core/workflows/execute-phase/steps/install-run.md +0 -29
  197. package/pp-core/workflows/execute-phase/steps/phase-close-rebaseline.md +0 -23
  198. package/pp-core/workflows/execute-phase/steps/serial-group-dispatch.md +0 -73
  199. package/pp-core/workflows/execute-phase/steps/vault-door-gate.md +0 -19
  200. package/pp-core/workflows/plan-phase/steps/phase-tier.md +0 -43
  201. package/scripts/lib/size-budget.cjs +0 -311
  202. package/scripts/lint-neutrality-names.cjs +0 -694
  203. package/scripts/lint-org-coordinates.allowlist.json +0 -35
  204. package/scripts/lint-org-coordinates.cjs +0 -426
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+ /**
3
+ * Discovery vault-context check (PBI 75336).
4
+ *
5
+ * Each Discovery session selects the client vault documents that bear on its
6
+ * topics from the vault's own `INDEX.md` (falling back to the engine-written
7
+ * `SUMMARY.md`), and records the selection in a `## Vault Context` section of
8
+ * `.planning/discovery/session-NN.md` — one line per document, naming what was
9
+ * selected and why. This module checks that record at session close:
10
+ *
11
+ * - the section exists and names the index the selection used;
12
+ * - every entry is one well-formed line (`- `<path>` — <why>`, or
13
+ * `- (none) — <why>` when nothing was selected);
14
+ * - every selected document was USED: cited `[vault: <path>]` somewhere in
15
+ * the session notes, or marked `— not used: <reason>` on its own line.
16
+ *
17
+ * The selection itself is the facilitator's judgement and is not re-derived
18
+ * here; what is checked is that the record exists, is complete, and that what
19
+ * was loaded either reached the notes or was explicitly set aside.
20
+ *
21
+ * Parsing: the entry head grammar (prefix, closing backtick, separator) and the
22
+ * vault-path safety rule belong to the vault-index leaf (`src/vault-index.cts`,
23
+ * D-06/D-07), shared with the INDEX.md parser; only the tail grammar — the
24
+ * `(none)` entry, `why` and the `— not used:` mark — lives here.
25
+ *
26
+ * Content-free contract (the T-02-05 precedent): output names the session
27
+ * file, vault paths, structured reasons and counts — never note contents.
28
+ *
29
+ * Traversal: the only argument is a session NUMBER, resolved against the fixed
30
+ * convention path `.planning/discovery/session-NN.md`. No caller string is
31
+ * ever joined into a path (the T-02-08 precedent).
32
+ */
33
+ var __importDefault = (this && this.__importDefault) || function (mod) {
34
+ return (mod && mod.__esModule) ? mod : { "default": mod };
35
+ };
36
+ const node_fs_1 = __importDefault(require("node:fs"));
37
+ const node_path_1 = __importDefault(require("node:path"));
38
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
39
+ const io = require("./io.cjs");
40
+ const { output, error, ERROR_REASON } = io;
41
+ // The entry head split and the vault-path rule are the vault-index leaf's
42
+ // (D-06/D-07). The leaf imports nothing, so this cannot close a cycle.
43
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
44
+ const vaultIndexLeaf = require("./vault-index.cjs");
45
+ // ─── Constants ────────────────────────────────────────────────────────────────
46
+ const DISCOVERY_DIR = '.planning/discovery';
47
+ const SECTION_HEADING = '## Vault Context';
48
+ const INDEX_PREFIX = 'Index: ';
49
+ /**
50
+ * `INDEX.md` — the vault-owned index (preferred). `SUMMARY.md` — the
51
+ * engine-written fallback when the vault ships no `INDEX.md`. `none` — no
52
+ * cache index at all, so nothing could be selected.
53
+ */
54
+ const INDEX_VALUES = Object.freeze(['INDEX.md', 'SUMMARY.md', 'none']);
55
+ // Discovery's tail grammar, built on the leaf's separator (one spelling in src/).
56
+ // No trailing space: section lines are trimEnd()-ed, so a mark with an empty
57
+ // reason must still be recognised as a mark (and refused), not read as `why`.
58
+ const NOT_USED_MARKER = vaultIndexLeaf.VAULT_ENTRY_SEPARATOR + 'not used:';
59
+ const NONE_ENTRY_PREFIX = '- (none)' + vaultIndexLeaf.VAULT_ENTRY_SEPARATOR;
60
+ const SESSION_MIN = 1;
61
+ const SESSION_MAX = 13;
62
+ const USAGE = 'Usage: pp-tools discovery vault-check <session 1-13>';
63
+ // ─── Parsing ──────────────────────────────────────────────────────────────────
64
+ /** The `## Vault Context` section's lines (with 1-based file line numbers), or null. */
65
+ function extractSection(content) {
66
+ const all = content.split(/\r?\n/);
67
+ const start = all.findIndex((l) => l.trimEnd() === SECTION_HEADING);
68
+ if (start === -1)
69
+ return null;
70
+ const out = [];
71
+ for (let i = start + 1; i < all.length; i += 1) {
72
+ if (/^#{1,2} /.test(all[i]))
73
+ break;
74
+ out.push({ n: i + 1, text: all[i].trimEnd() });
75
+ }
76
+ return out;
77
+ }
78
+ /** Parse one entry line. Returns the entry, or a violation for the line. */
79
+ function parseEntry(n, text) {
80
+ if (text.startsWith(NONE_ENTRY_PREFIX)) {
81
+ const why = text.slice(NONE_ENTRY_PREFIX.length).trim();
82
+ return why === '' ? { reason: 'entry_malformed', line: n } : { line: n, path: null, notUsed: false };
83
+ }
84
+ const head = vaultIndexLeaf.splitVaultEntryLine(text);
85
+ if (head === null || 'malformed' in head)
86
+ return { reason: 'entry_malformed', line: n };
87
+ const p = head.path;
88
+ // The leaf's `rest` starts AFTER the separator, but the not-used marker
89
+ // begins with that same separator: a remainder that opens `not used:` is a
90
+ // mark with an empty `why`, and must stay one. So the tail is searched with
91
+ // the separator re-joined in front, exactly as before the split moved.
92
+ const sep = vaultIndexLeaf.VAULT_ENTRY_SEPARATOR;
93
+ const rest = sep + head.rest;
94
+ const notUsedAt = rest.indexOf(NOT_USED_MARKER);
95
+ const why = (notUsedAt === -1 ? rest.slice(sep.length) : rest.slice(sep.length, notUsedAt)).trim();
96
+ if (why === '')
97
+ return { reason: 'entry_malformed', line: n };
98
+ if (notUsedAt !== -1 && rest.slice(notUsedAt + NOT_USED_MARKER.length).trim() === '') {
99
+ return { reason: 'entry_malformed', line: n };
100
+ }
101
+ if (!vaultIndexLeaf.isSafeVaultPath(p))
102
+ return { reason: 'path_unsafe', line: n, path: p };
103
+ return { line: n, path: p, notUsed: notUsedAt !== -1 };
104
+ }
105
+ // ─── Check ────────────────────────────────────────────────────────────────────
106
+ /**
107
+ * Check one session file's `## Vault Context` record. Pure over `content`.
108
+ *
109
+ * A citation counts only OUTSIDE the section: the record naming a path is not
110
+ * evidence that the path was used.
111
+ */
112
+ function checkVaultContext(content) {
113
+ const violations = [];
114
+ const section = extractSection(content);
115
+ if (section === null) {
116
+ return { ok: false, index: null, documents: 0, cited: 0, not_used: 0, violations: [{ reason: 'section_missing' }] };
117
+ }
118
+ let index = null;
119
+ const entries = [];
120
+ for (const { n, text } of section) {
121
+ if (text.trim() === '')
122
+ continue;
123
+ if (text.startsWith(INDEX_PREFIX)) {
124
+ const value = text.slice(INDEX_PREFIX.length).trim();
125
+ if (!INDEX_VALUES.includes(value))
126
+ violations.push({ reason: 'index_invalid', line: n });
127
+ else
128
+ index = value;
129
+ continue;
130
+ }
131
+ const parsed = parseEntry(n, text);
132
+ if ('reason' in parsed)
133
+ violations.push(parsed);
134
+ else
135
+ entries.push(parsed);
136
+ }
137
+ if (index === null && !violations.some((v) => v.reason === 'index_invalid')) {
138
+ violations.push({ reason: 'index_line_missing' });
139
+ }
140
+ if (entries.length === 0 && !violations.some((v) => v.reason === 'entry_malformed' || v.reason === 'path_unsafe')) {
141
+ violations.push({ reason: 'no_entries' });
142
+ }
143
+ const docs = entries.filter((e) => e.path !== null);
144
+ const nones = entries.filter((e) => e.path === null);
145
+ if (nones.length > 0 && docs.length > 0) {
146
+ violations.push({ reason: 'none_mixed', line: nones[0].line });
147
+ }
148
+ // Citations are searched in the file with the section's own lines removed.
149
+ const sectionLines = new Set(section.map((s) => s.n));
150
+ const notes = content
151
+ .split(/\r?\n/)
152
+ .filter((_, i) => !sectionLines.has(i + 1))
153
+ .join('\n');
154
+ let cited = 0;
155
+ let notUsed = 0;
156
+ for (const doc of docs) {
157
+ const isCited = notes.includes(`[vault: ${doc.path}]`);
158
+ if (isCited)
159
+ cited += 1;
160
+ if (doc.notUsed)
161
+ notUsed += 1;
162
+ if (!isCited && !doc.notUsed) {
163
+ violations.push({ reason: 'document_unused', line: doc.line, path: doc.path });
164
+ }
165
+ }
166
+ return { ok: violations.length === 0, index, documents: docs.length, cited, not_used: notUsed, violations };
167
+ }
168
+ /** Parse the session argument: a bare integer 1–13, leading zeros allowed (`03` is session 3). */
169
+ function parseSessionArg(raw) {
170
+ if (typeof raw !== 'string' || !/^\d{1,3}$/.test(raw))
171
+ return null;
172
+ const n = Number(raw);
173
+ return n >= SESSION_MIN && n <= SESSION_MAX ? n : null;
174
+ }
175
+ function sessionFileRel(n) {
176
+ return `${DISCOVERY_DIR}/session-${String(n).padStart(2, '0')}.md`;
177
+ }
178
+ /** Check the record of session `n` in the project at `cwd`. */
179
+ function checkSessionVaultContext(cwd, n) {
180
+ const rel = sessionFileRel(n);
181
+ const abs = node_path_1.default.join(cwd, rel);
182
+ let content;
183
+ try {
184
+ content = node_fs_1.default.readFileSync(abs, 'utf-8');
185
+ }
186
+ catch {
187
+ return {
188
+ ok: false, session: n, file: rel, index: null, documents: 0, cited: 0, not_used: 0,
189
+ violations: [{ reason: 'session_file_missing' }],
190
+ };
191
+ }
192
+ return { session: n, file: rel, ...checkVaultContext(content) };
193
+ }
194
+ function formatViolation(v) {
195
+ const where = v.line !== undefined ? `line ${v.line}: ` : '';
196
+ // The path is facilitator-written text echoed to a terminal: it goes through
197
+ // the leaf's path display (D-27, T-14-04; 14-REVIEW WR-02), so a control or
198
+ // bidi character in a refused path is shown as an escape, never emitted raw,
199
+ // and a path is never echoed under a plausible different name.
200
+ const what = v.path !== undefined ? ` (${vaultIndexLeaf.displayVaultPath(v.path)})` : '';
201
+ return ` - ${where}${v.reason}${what}`;
202
+ }
203
+ // ─── CLI adapter ──────────────────────────────────────────────────────────────
204
+ /**
205
+ * CLI adapter for `pp-tools discovery vault-check <session>` (dotted form
206
+ * `pp-tools query discovery.vault-check <session>`).
207
+ *
208
+ * Exit semantics: any violation exits non-zero via
209
+ * `ERROR_REASON.DISCOVERY_VAULT_CHECK_FAILED`, naming each by line, reason and
210
+ * vault path. The payload is emitted on stdout first on the failure path too,
211
+ * so the counts survive alongside the error.
212
+ */
213
+ function cmdDiscoveryVaultCheck(cwd, args, raw) {
214
+ // args arrive shaped ['discovery', 'vault-check', <session>].
215
+ const rest = args.slice(2);
216
+ const n = rest.length === 1 ? parseSessionArg(rest[0]) : null;
217
+ if (n === null) {
218
+ error(USAGE, ERROR_REASON.USAGE);
219
+ return; // unreachable — error() exits
220
+ }
221
+ const result = checkSessionVaultContext(cwd, n);
222
+ const payload = {
223
+ ok: result.ok,
224
+ session: result.session,
225
+ file: result.file,
226
+ index: result.index,
227
+ documents: result.documents,
228
+ cited: result.cited,
229
+ not_used: result.not_used,
230
+ };
231
+ if (!result.ok) {
232
+ output({ ...payload, violations: result.violations.length }, raw);
233
+ error(`discovery vault-check: ${result.violations.length} violation(s) in ${result.file}:\n` +
234
+ result.violations.map(formatViolation).join('\n'), ERROR_REASON.DISCOVERY_VAULT_CHECK_FAILED);
235
+ return; // unreachable — error() exits
236
+ }
237
+ output(payload, raw);
238
+ }
239
+ module.exports = {
240
+ SECTION_HEADING,
241
+ INDEX_VALUES,
242
+ checkVaultContext,
243
+ checkSessionVaultContext,
244
+ parseSessionArg,
245
+ cmdDiscoveryVaultCheck,
246
+ };
@@ -216,24 +216,6 @@ function reconstructFrontmatter(obj) {
216
216
  }
217
217
  return lines.join('\n');
218
218
  }
219
- /**
220
- * The raw lines of a top-level `key:` block inside a `---` frontmatter match —
221
- * the `key:` line plus every following line that is blank or indented, up to
222
- * the next column-0 key — with trailing blank lines dropped, or `null` when the
223
- * frontmatter has no such top-level block.
224
- */
225
- function rawTopLevelBlock(frontmatterMatch, key) {
226
- const lines = frontmatterMatch.split(/\r?\n/).slice(1, -1);
227
- const start = lines.findIndex((l) => l === `${key}:` || new RegExp(`^${key}:\\s*$`).test(l));
228
- if (start === -1)
229
- return null;
230
- let end = start + 1;
231
- while (end < lines.length && (lines[end].trim() === '' || /^\s/.test(lines[end])))
232
- end += 1;
233
- while (end > start + 1 && lines[end - 1].trim() === '')
234
- end -= 1;
235
- return lines.slice(start, end).join('\n');
236
- }
237
219
  function spliceFrontmatter(content, newObj) {
238
220
  const match = content.match(/^---\r?\n[\s\S]+?\r?\n---/);
239
221
  if (match) {
@@ -256,25 +238,6 @@ function spliceFrontmatter(content, newObj) {
256
238
  catch {
257
239
  /* fall through to regeneration on any comparison hiccup */
258
240
  }
259
- // The practice-trace ledger (`vault:`, docs/vault-trace-format.md) is a list
260
- // of maps the lossy serializer cannot re-emit. When the caller left it
261
- // untouched — `frontmatter set <plan> wave 2`, the plan-graph write-back —
262
- // its raw lines are carried over verbatim instead of being regenerated.
263
- const ledger = rawTopLevelBlock(match[0], 'vault');
264
- if (ledger !== null) {
265
- try {
266
- const original = extractFrontmatter(content);
267
- if (frontmatterDeepEqual(original.vault, newObj.vault)) {
268
- const rest = { ...newObj };
269
- delete rest.vault;
270
- const restStr = reconstructFrontmatter(rest);
271
- return `---\n${restStr}${restStr === '' ? '' : '\n'}${ledger}\n---` + content.slice(match[0].length);
272
- }
273
- }
274
- catch {
275
- /* fall through to regeneration */
276
- }
277
- }
278
241
  const yamlStr = reconstructFrontmatter(newObj);
279
242
  return `---\n${yamlStr}\n---` + content.slice(match[0].length);
280
243
  }
@@ -427,7 +390,7 @@ function parseMustHavesBlock(content, blockName) {
427
390
  }
428
391
  // ─── Frontmatter CRUD commands ────────────────────────────────────────────────
429
392
  const FRONTMATTER_SCHEMAS = {
430
- plan: { required: ['phase', 'plan', 'title', 'type', 'wave', 'depends_on', 'files_modified', 'autonomous', 'must_haves'] },
393
+ plan: { required: ['phase', 'plan', 'type', 'wave', 'depends_on', 'files_modified', 'autonomous', 'must_haves'] },
431
394
  summary: { required: ['phase', 'plan', 'subsystem', 'tags', 'duration', 'completed'] },
432
395
  verification: { required: ['phase', 'verified', 'status', 'score'] },
433
396
  };
@@ -83,7 +83,7 @@ const MAX_REF_LENGTH = 255;
83
83
  * leading `-` refused on top. Re-implemented rather than shelled out to: this
84
84
  * function is pure, total and called on a path that must never spawn a
85
85
  * subprocess to decide whether it is safe to spawn a subprocess. The equivalence
86
- * is not asserted here in prose — `tests/git-base-branch.test.cjs` drives
86
+ * is not asserted here in prose — `tests/vault-review.security.test.cjs` drives
87
87
  * the admit half from `git check-ref-format --branch` AS THE ORACLE, so git
88
88
  * decides what must be admitted and this comment cannot quietly stop being true.
89
89
  *
@@ -118,11 +118,11 @@ function isPlausibleBranchRef(value) {
118
118
  }
119
119
  /**
120
120
  * WHAT A CALLER SAYS WHEN THE OVERRIDE IS REFUSED. One sentence, exported, for
121
- * every channel — three call sites state it and none of them spells it.
121
+ * every channel — a caller states it and never spells it.
122
122
  *
123
123
  * A second spelling of this sentence would be a second definition of what the
124
- * mechanism did. The `check vault.audit` notes and the `check ship.practices`
125
- * message carry these exact bytes.
124
+ * mechanism did, so every channel that states the refusal carries these exact
125
+ * bytes.
126
126
  *
127
127
  * The reason carries NO copy of the refused value: the value is client-supplied
128
128
  * and one of its shapes is an absolute filesystem path, so echoing it back onto
@@ -295,14 +295,13 @@ function resolveBaseBranchResult(cwd, deps) {
295
295
  * Consults the full precedence ladder and always returns a non-empty string.
296
296
  * Never throws.
297
297
  *
298
- * The bare-string form. A consumer that computes a range and has a channel to
299
- * speak on takes `resolveBaseBranchResult` instead and states the refusal
300
- * (`vault-audit.cts`, the blocking audit gate, and `cmdShipPractices`, the
301
- * ship:pre check): a gate that ran over a range the project never configured
302
- * and said nothing would report success on the wrong input. This form stays
303
- * for callers with nowhere to put a sentence, such as `cmdGitBaseBranch`,
304
- * whose contract is one branch name on stdout. Either form refuses an
305
- * option-shaped override.
298
+ * The bare-string form. A consumer that computes a range takes
299
+ * `resolveBaseBranchResult` instead and states the refusal (W16): a gate that
300
+ * refused in silence would run over a range the project never configured and
301
+ * report success on it. What remains here is `cmdGitBaseBranch`, whose entire
302
+ * contract is one branch name on stdout and which therefore genuinely has
303
+ * nowhere to put a sentence. A new caller reaching for this form should ask
304
+ * whether that is true of it as well; if it has a channel, it owes the operator the reason.
306
305
  */
307
306
  function resolveBaseBranch(cwd, deps) {
308
307
  return resolveBaseBranchResult(cwd, deps).branch;
@@ -14,9 +14,9 @@
14
14
  * provider, what environment a clone needs, and whether a host CLI (`glab`)
15
15
  * may be spawned at all. It imports node builtins plus `./org-profile.cjs` —
16
16
  * still never `./vault.cjs`, `./vault-coordinates.cjs`, `./io.cjs` or
17
- * `./shell-command-projection.cjs` — so that both `src/vault.cts` and
18
- * `src/vault-write-back.cts` can import it (since 30-02 and 30-03) without
19
- * forming a require cycle in the CommonJS graph `require()` loads.
17
+ * `./shell-command-projection.cjs` — so that `src/vault.cts`, the vault write
18
+ * path and the other vault modules can import it without forming a require
19
+ * cycle in the CommonJS graph `require()` loads.
20
20
  * `./org-profile.cjs` is the one exception: it is itself a zero-import leaf
21
21
  * (node builtins only, see its own header), so importing it here creates no
22
22
  * load-time cycle even though it lazily requires this module back at
@@ -43,12 +43,12 @@ const node_path_1 = __importDefault(require("node:path"));
43
43
  const org_profile_cjs_1 = require("./org-profile.cjs");
44
44
  // ─── Constants ───────────────────────────────────────────────────────────────
45
45
  /** The complete, ordered set of supported provider ids. */
46
- exports.PROVIDERS = Object.freeze(['gitlab', 'azure']);
46
+ exports.PROVIDERS = Object.freeze(['gitlab', 'azure', 'github']);
47
47
  /**
48
48
  * Every project that has never heard of this phase resolves to this provider
49
49
  * (PROV-04): an absent config key is the normal case, not an error.
50
50
  */
51
- exports.DEFAULT_PROVIDER = 'gitlab';
51
+ exports.DEFAULT_PROVIDER = 'github';
52
52
  /** The dotted `.planning/config.json` key that selects the provider. */
53
53
  exports.PROVIDER_CONFIG_KEY = 'vault.provider';
54
54
  // ─── normalizeProvider ───────────────────────────────────────────────────────
@@ -176,9 +176,8 @@ const compiledLiteralCloneTargetPatterns = exports.LITERAL_CLONE_TARGET_PATTERNS
176
176
  * absolute path, a scheme-qualified URL, or scp-style `user@host:path` —
177
177
  * rather than a `group/project` slug that must be composed onto a host.
178
178
  *
179
- * This is now the SINGLE definition. `src/vault.cts` and
180
- * `src/vault-write-back.cts` import it from here (30-02, 30-03) rather than
181
- * each keeping a copy — a read/write divergence in this predicate was defect
179
+ * This is now the SINGLE definition. `src/vault.cts` imports it from here
180
+ * (30-02) rather than keeping a copy — a read/write divergence in this predicate was defect
182
181
  * P-1 in Phase 29 (the write path used the value verbatim while the read path
183
182
  * always composed a URL, so a slug broke writing and a path broke reading).
184
183
  *
@@ -244,8 +243,8 @@ function composeCloneTarget(provider, repo, host) {
244
243
  }
245
244
  return { url, warnings: [] };
246
245
  }
247
- // gitlab (and, structurally, any future provider that composes the same
248
- // shape) — byte-identical to the template this replaces.
246
+ // gitlab and github compose the same `{host}/{slug}.git` shape —
247
+ // byte-identical to the template this replaces.
249
248
  return { url: `https://${host}/${trimmed}.git`, warnings: [] };
250
249
  }
251
250
  // ─── cloneEnv ────────────────────────────────────────────────────────────────
@@ -30,22 +30,19 @@ function routeInitCommand({ init, args, cwd, raw, error }) {
30
30
  unknownMessage: (_subcommand, available) => `Unknown init workflow: ${_subcommand}\nAvailable: ${available.join(', ')}`,
31
31
  handlers: {
32
32
  'execute-phase': () => {
33
- const namedArgs = (0, command_arg_projection_cjs_1.parseNamedArgs)(args, ['tier'], ['validate', 'tdd']);
34
- init.cmdInitExecutePhase(cwd, args[2], raw, { validate: namedArgs['validate'], tdd: namedArgs['tdd'], tier: namedArgs['tier'] });
33
+ const namedArgs = (0, command_arg_projection_cjs_1.parseNamedArgs)(args, [], ['validate', 'tdd']);
34
+ init.cmdInitExecutePhase(cwd, args[2], raw, { validate: namedArgs['validate'], tdd: namedArgs['tdd'] });
35
35
  },
36
36
  'plan-phase': () => {
37
- const namedArgs = (0, command_arg_projection_cjs_1.parseNamedArgs)(args, ['granularity', 'tier'], ['validate', 'tdd']);
38
- init.cmdInitPlanPhase(cwd, args[2], raw, { validate: namedArgs['validate'], tdd: namedArgs['tdd'], granularity: namedArgs['granularity'], tier: namedArgs['tier'] });
37
+ const namedArgs = (0, command_arg_projection_cjs_1.parseNamedArgs)(args, ['granularity'], ['validate', 'tdd']);
38
+ init.cmdInitPlanPhase(cwd, args[2], raw, { validate: namedArgs['validate'], tdd: namedArgs['tdd'], granularity: namedArgs['granularity'] });
39
39
  },
40
40
  'new-project': () => init.cmdInitNewProject(cwd, raw),
41
41
  'new-milestone': () => init.cmdInitNewMilestone(cwd, raw),
42
42
  quick: () => init.cmdInitQuick(cwd, args.slice(2).join(' '), raw),
43
43
  'ingest-docs': () => init.cmdInitIngestDocs(cwd, raw),
44
44
  resume: () => init.cmdInitResume(cwd, raw),
45
- 'verify-work': () => {
46
- const namedArgs = (0, command_arg_projection_cjs_1.parseNamedArgs)(args, ['tier'], []);
47
- init.cmdInitVerifyWork(cwd, args[2], raw, { tier: namedArgs['tier'] });
48
- },
45
+ 'verify-work': () => init.cmdInitVerifyWork(cwd, args[2], raw),
49
46
  'phase-op': () => init.cmdInitPhaseOp(cwd, args[2], raw),
50
47
  todos: () => init.cmdInitTodos(cwd, args[2], raw),
51
48
  'milestone-op': () => init.cmdInitMilestoneOp(cwd, raw),