@pareto4/pp-core 1.19.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.
- package/.claude-plugin/plugin.json +1 -1
- package/agents/pp-phase-researcher.md +10 -0
- package/agents/pp-planner.md +15 -0
- package/agents/pp-vault-reviewer.md +2 -0
- package/agents/pp-verifier.md +12 -8
- package/commands/pp-discovery.md +3 -2
- package/gemini-extension.json +1 -1
- package/package.json +6 -3
- package/pareto-skills/_shared/pp-claude-md-protocol.md +1 -1
- package/pareto-skills/pp-project-setup/SKILL.md +18 -343
- package/pp-core/bin/lib/capability-registry.cjs +1 -1
- package/pp-core/bin/lib/discovery-access.cjs +276 -0
- package/pp-core/bin/lib/discovery-charter.cjs +664 -0
- package/pp-core/bin/lib/discovery-deviations.cjs +264 -0
- package/pp-core/bin/lib/discovery-entities.cjs +323 -0
- package/pp-core/bin/lib/discovery-for-phase.cjs +628 -0
- package/pp-core/bin/lib/discovery-integrations.cjs +286 -0
- package/pp-core/bin/lib/discovery-migration.cjs +246 -0
- package/pp-core/bin/lib/discovery-phases.cjs +233 -0
- package/pp-core/bin/lib/discovery-processes.cjs +355 -0
- package/pp-core/bin/lib/discovery-research-track.cjs +5 -4
- package/pp-core/bin/lib/discovery-rollup.cjs +8 -7
- package/pp-core/bin/lib/discovery-rules.cjs +216 -0
- package/pp-core/bin/lib/discovery-screens.cjs +1 -1
- package/pp-core/bin/lib/discovery-session-check.cjs +264 -0
- package/pp-core/bin/lib/discovery-stories.cjs +273 -0
- package/pp-core/bin/lib/discovery-sweep.cjs +1 -1
- package/pp-core/bin/lib/discovery-table.cjs +55 -0
- package/pp-core/bin/lib/io.cjs +22 -0
- package/pp-core/bin/lib/vault-standards-review.cjs +50 -2
- package/pp-core/bin/pp-tools.cjs +143 -1
- package/pp-core/references/discovery-access.md +112 -0
- package/pp-core/references/discovery-build-consumption.md +256 -0
- package/pp-core/references/discovery-charter.md +127 -0
- package/pp-core/references/discovery-data-model.md +145 -0
- package/pp-core/references/discovery-id-conventions.md +50 -14
- package/pp-core/references/discovery-integrations.md +151 -0
- package/pp-core/references/discovery-processes.md +190 -0
- package/pp-core/references/discovery-rebaseline.md +2 -2
- package/pp-core/references/discovery-research-track.md +5 -6
- package/pp-core/references/discovery-session-output.md +70 -0
- package/pp-core/references/discovery-standards.md +107 -0
- package/pp-core/references/discovery-traceability.md +2 -2
- package/pp-core/references/planner-discovery-extract.md +35 -0
- package/pp-core/references/verifier-discovery-conformance.md +36 -0
- package/pp-core/templates/project-charter.md +161 -100
- package/pp-core/templates/project.md +3 -3
- package/pp-core/templates/requirements.md +18 -0
- package/pp-core/templates/verification-report.md +16 -0
- package/pp-core/workflows/discovery-sprint/charter.md +114 -0
- package/pp-core/workflows/discovery-sprint/prefill.md +1 -1
- package/pp-core/workflows/discovery-sprint/prototype.md +4 -4
- package/pp-core/workflows/discovery-sprint/session-13-gates.md +10 -0
- package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +5 -2
- package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +27 -5
- package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +56 -31
- package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +1 -1
- package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +42 -19
- package/pp-core/workflows/discovery-sprint/sessions/06-security.md +27 -12
- package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +41 -23
- package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +50 -23
- package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +26 -11
- package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +28 -9
- package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +21 -0
- package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +24 -9
- package/pp-core/workflows/discovery-sprint.md +12 -15
- package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +80 -29
- package/pp-core/workflows/discovery-to-build/steps/{screen-inventory-gate.md → handoff-gates.md} +111 -3
- package/pp-core/workflows/discovery-to-build/templates/discovery-references.md +12 -9
- package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +27 -26
- package/pp-core/workflows/discovery-to-build.md +70 -34
- package/pp-core/workflows/discuss-phase/discovery.md +83 -0
- package/pp-core/workflows/discuss-phase/templates/context.md +4 -0
- package/pp-core/workflows/discuss-phase.md +10 -7
- package/pp-core/workflows/new-project.md +3 -3
- package/pp-core/workflows/plan-phase/steps/discovery-extract.md +41 -0
- package/pp-core/workflows/plan-phase.md +15 -10
- package/pp-core/workflows/vault-review.md +5 -0
- package/pp-core/workflows/discovery-sprint/templates/context-template.md +0 -272
|
@@ -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
|
|
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'];
|
|
@@ -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
|
};
|
package/pp-core/bin/lib/io.cjs
CHANGED
|
@@ -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',
|
|
@@ -3,7 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.REVIEW_FILES_CAP = exports.REVIEW_VERDICTS_HEADING = exports.REVIEW_ARTIFACT_MARKER = exports.REVIEW_ARTIFACT_SUFFIX = exports.REVIEW_VERDICTS = void 0;
|
|
6
|
+
exports.DEVIATIONS_FILE = exports.REVIEW_FILES_CAP = exports.REVIEW_VERDICTS_HEADING = exports.REVIEW_ARTIFACT_MARKER = exports.REVIEW_ARTIFACT_SUFFIX = exports.REVIEW_VERDICTS = void 0;
|
|
7
|
+
exports.readDeviations = readDeviations;
|
|
7
8
|
exports.deriveStandardsReview = deriveStandardsReview;
|
|
8
9
|
exports.formatReviewCounts = formatReviewCounts;
|
|
9
10
|
exports.validateReviewArtifact = validateReviewArtifact;
|
|
@@ -59,6 +60,8 @@ exports.REVIEW_VERDICTS_HEADING = '## Standard Verdicts';
|
|
|
59
60
|
exports.REVIEW_FILES_CAP = 200;
|
|
60
61
|
/** Bound on a git query a review makes. */
|
|
61
62
|
const REVIEW_GIT_TIMEOUT_MS = 30_000;
|
|
63
|
+
/** Where the Discovery Sprint records agreed departures from the vault. */
|
|
64
|
+
exports.DEVIATIONS_FILE = '.planning/discovery/_deviations.md';
|
|
62
65
|
// ─── Derivation ──────────────────────────────────────────────────────────────
|
|
63
66
|
const PLAN_RE = /(^|-)PLAN\.md$/;
|
|
64
67
|
const SUMMARY_RE = /(^|-)SUMMARY\.md$/;
|
|
@@ -73,6 +76,47 @@ function readBounded(abs) {
|
|
|
73
76
|
return null;
|
|
74
77
|
}
|
|
75
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* The deviations on file, read by header name so Batch 3's full grammar can
|
|
81
|
+
* add or reorder columns without touching this reader. `gap` is set when the
|
|
82
|
+
* file exists but yields no readable table with both join columns.
|
|
83
|
+
*/
|
|
84
|
+
function readDeviations(root) {
|
|
85
|
+
const abs = node_path_1.default.join(root, exports.DEVIATIONS_FILE);
|
|
86
|
+
if (!node_fs_1.default.existsSync(abs))
|
|
87
|
+
return { deviations: [], gap: null };
|
|
88
|
+
const text = readBounded(abs);
|
|
89
|
+
if (text === null)
|
|
90
|
+
return { deviations: [], gap: `${exports.DEVIATIONS_FILE} could not be read (unreadable or over the size cap), so deviations on file are unknown` };
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
92
|
+
const table = require('./discovery-table.cjs');
|
|
93
|
+
const deviations = [];
|
|
94
|
+
let joinable = false;
|
|
95
|
+
for (const section of table.splitSections(text.split(/\r?\n/), 2)) {
|
|
96
|
+
for (const tbl of table.parseTables(section.body)) {
|
|
97
|
+
const names = tbl.header.map((h) => h.trim().toLowerCase());
|
|
98
|
+
const col = (name) => names.indexOf(name);
|
|
99
|
+
const standard = col('standard');
|
|
100
|
+
const status = col('status');
|
|
101
|
+
if (standard === -1 || status === -1)
|
|
102
|
+
continue;
|
|
103
|
+
joinable = true;
|
|
104
|
+
const cell = (row, i) => (i === -1 ? '' : (row.cells[i] ?? '').trim());
|
|
105
|
+
for (const row of tbl.rows) {
|
|
106
|
+
deviations.push({
|
|
107
|
+
id: cell(row, col('id')),
|
|
108
|
+
standard: cell(row, standard),
|
|
109
|
+
status: cell(row, status),
|
|
110
|
+
decision: cell(row, col('decision')),
|
|
111
|
+
approver: cell(row, col('approver')),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (!joinable)
|
|
117
|
+
return { deviations: [], gap: `${exports.DEVIATIONS_FILE} carries no table with Standard and Status columns, so deviations on file are unknown` };
|
|
118
|
+
return { deviations, gap: null };
|
|
119
|
+
}
|
|
76
120
|
function consultedText(entry, hasSummary) {
|
|
77
121
|
if (!hasSummary)
|
|
78
122
|
return 'no summary';
|
|
@@ -206,6 +250,9 @@ function deriveStandardsReview(root, cwd, positional, opts = {}) {
|
|
|
206
250
|
});
|
|
207
251
|
}
|
|
208
252
|
}
|
|
253
|
+
const onFile = readDeviations(root);
|
|
254
|
+
if (onFile.gap !== null)
|
|
255
|
+
gaps.push(onFile.gap);
|
|
209
256
|
if (plans.length === 0 || documents.size === 0) {
|
|
210
257
|
return {
|
|
211
258
|
status: 'no-records',
|
|
@@ -216,9 +263,10 @@ function deriveStandardsReview(root, cwd, positional, opts = {}) {
|
|
|
216
263
|
plans,
|
|
217
264
|
documents: [],
|
|
218
265
|
gaps,
|
|
266
|
+
deviations: onFile.deviations,
|
|
219
267
|
};
|
|
220
268
|
}
|
|
221
|
-
return { status: 'ok', phaseDir: phase.rel, plans, documents: [...documents.values()], gaps };
|
|
269
|
+
return { status: 'ok', phaseDir: phase.rel, plans, documents: [...documents.values()], gaps, deviations: onFile.deviations };
|
|
222
270
|
}
|
|
223
271
|
/** The counts line, in verdict order: `p PASS · f FAIL · n N-A · i INCONCLUSIVE (of N documents)`. */
|
|
224
272
|
function formatReviewCounts(counts) {
|
package/pp-core/bin/pp-tools.cjs
CHANGED
|
@@ -2564,13 +2564,155 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
2564
2564
|
} catch (vaultCheckErr) {
|
|
2565
2565
|
error(`discovery vault-check: ${vaultCheckErr && vaultCheckErr.message ? vaultCheckErr.message : String(vaultCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2566
2566
|
}
|
|
2567
|
+
} else if (subcommand === 'charter-check') {
|
|
2568
|
+
// discovery charter-check [--require-signed]
|
|
2569
|
+
//
|
|
2570
|
+
// Structural check of docs/discovery/PROJECT-CHARTER.md: frontmatter
|
|
2571
|
+
// enums, the twenty `<!-- charter:<key> -->` sections in order, every
|
|
2572
|
+
// cited ID resolving to a session table or _requirements.md row, the
|
|
2573
|
+
// Go / No-Go table equal to Session 13's, must-resolve open items
|
|
2574
|
+
// closed or listed as conditions, sign-off rows agreeing with status.
|
|
2575
|
+
// --require-signed also fails (discovery_charter_unsigned) unless the
|
|
2576
|
+
// status is Signed or Signed with conditions — the handoff gate's arm.
|
|
2577
|
+
const charter = require('./lib/discovery-charter.cjs');
|
|
2578
|
+
try {
|
|
2579
|
+
charter.cmdDiscoveryCharterCheck(cwd, args, raw);
|
|
2580
|
+
} catch (charterCheckErr) {
|
|
2581
|
+
error(`discovery charter-check: ${charterCheckErr && charterCheckErr.message ? charterCheckErr.message : String(charterCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2582
|
+
}
|
|
2583
|
+
} else if (subcommand === 'entity-check') {
|
|
2584
|
+
// discovery entity-check [--require-complete] — _entities.md structure
|
|
2585
|
+
// (improvement plan, Batch 2): named entities, closed lifecycle and
|
|
2586
|
+
// classification cells, attributes per entity, Mermaid cardinalities,
|
|
2587
|
+
// step-matrix data objects resolving. --require-complete also fails on
|
|
2588
|
+
// pending `—` cells (discovery_entity_check_incomplete).
|
|
2589
|
+
const entities = require('./lib/discovery-entities.cjs');
|
|
2590
|
+
try {
|
|
2591
|
+
entities.cmdDiscoveryEntityCheck(cwd, args, raw);
|
|
2592
|
+
} catch (entityCheckErr) {
|
|
2593
|
+
error(`discovery entity-check: ${entityCheckErr && entityCheckErr.message ? entityCheckErr.message : String(entityCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2594
|
+
}
|
|
2595
|
+
} else if (subcommand === 'process-check') {
|
|
2596
|
+
// discovery process-check [--require-cited] — _processes.md structure:
|
|
2597
|
+
// PRC-/MP- sequences, Replaces resolving, one swimlane and one step
|
|
2598
|
+
// matrix per main process, actors, data objects, requirement IDs.
|
|
2599
|
+
// --require-cited also fails on a step citing no requirement.
|
|
2600
|
+
const processes = require('./lib/discovery-processes.cjs');
|
|
2601
|
+
try {
|
|
2602
|
+
processes.cmdDiscoveryProcessCheck(cwd, args, raw);
|
|
2603
|
+
} catch (processCheckErr) {
|
|
2604
|
+
error(`discovery process-check: ${processCheckErr && processCheckErr.message ? processCheckErr.message : String(processCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2605
|
+
}
|
|
2606
|
+
} else if (subcommand === 'rules-check') {
|
|
2607
|
+
// discovery rules-check — _rules.md structure: BR- sequence, layer
|
|
2608
|
+
// vocabulary, every rule citing an existing requirement and an existing
|
|
2609
|
+
// entity or process.
|
|
2610
|
+
const rules = require('./lib/discovery-rules.cjs');
|
|
2611
|
+
try {
|
|
2612
|
+
rules.cmdDiscoveryRulesCheck(cwd, args, raw);
|
|
2613
|
+
} catch (rulesCheckErr) {
|
|
2614
|
+
error(`discovery rules-check: ${rulesCheckErr && rulesCheckErr.message ? rulesCheckErr.message : String(rulesCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2615
|
+
}
|
|
2616
|
+
} else if (subcommand === 'stories-check') {
|
|
2617
|
+
// discovery stories-check — _stories.md structure (improvement plan,
|
|
2618
|
+
// Batch 4): US- sequence, EPIC shape, every story citing an existing
|
|
2619
|
+
// requirement, source step resolving, every MVP domain requirement
|
|
2620
|
+
// carrying at least one story.
|
|
2621
|
+
const stories = require('./lib/discovery-stories.cjs');
|
|
2622
|
+
try {
|
|
2623
|
+
stories.cmdDiscoveryStoriesCheck(cwd, args, raw);
|
|
2624
|
+
} catch (storiesCheckErr) {
|
|
2625
|
+
error(`discovery stories-check: ${storiesCheckErr && storiesCheckErr.message ? storiesCheckErr.message : String(storiesCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2626
|
+
}
|
|
2627
|
+
} else if (subcommand === 'phases-check') {
|
|
2628
|
+
// discovery phases-check — _phases.md structure (improvement plan,
|
|
2629
|
+
// Batch 4): contiguous phases, requirement IDs resolving and not
|
|
2630
|
+
// dropped, dependencies on earlier phases, every MVP requirement in
|
|
2631
|
+
// exactly one phase.
|
|
2632
|
+
const phasesCheck = require('./lib/discovery-phases.cjs');
|
|
2633
|
+
try {
|
|
2634
|
+
phasesCheck.cmdDiscoveryPhasesCheck(cwd, args, raw);
|
|
2635
|
+
} catch (phasesCheckErr) {
|
|
2636
|
+
error(`discovery phases-check: ${phasesCheckErr && phasesCheckErr.message ? phasesCheckErr.message : String(phasesCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2637
|
+
}
|
|
2638
|
+
} else if (subcommand === 'for-phase') {
|
|
2639
|
+
// discovery for-phase <N> [--markdown | --conformance-template |
|
|
2640
|
+
// --verify <file>] — the rows of every canonical artifact that cite
|
|
2641
|
+
// the phase's requirement IDs as one extract between
|
|
2642
|
+
// <!-- discovery:auto --> markers (improvement plan, Batch 4, R1);
|
|
2643
|
+
// the verifier's conformance block and its check (R3). Fails loud on
|
|
2644
|
+
// artifact structure only; no discovery package → exit 0, empty.
|
|
2645
|
+
const forPhase = require('./lib/discovery-for-phase.cjs');
|
|
2646
|
+
try {
|
|
2647
|
+
forPhase.cmdDiscoveryForPhase(cwd, args, raw);
|
|
2648
|
+
} catch (forPhaseErr) {
|
|
2649
|
+
error(`discovery for-phase: ${forPhaseErr && forPhaseErr.message ? forPhaseErr.message : String(forPhaseErr)}`, ERROR_REASON.UNKNOWN);
|
|
2650
|
+
}
|
|
2651
|
+
} else if (subcommand === 'roles-check') {
|
|
2652
|
+
// discovery roles-check — _roles.md structure (improvement plan,
|
|
2653
|
+
// Batch 3): every role a persona, a step-matrix actor or System;
|
|
2654
|
+
// permissions naming a known role, an existing entity, lowercase
|
|
2655
|
+
// operations and at least one requirement; screen rows resolving.
|
|
2656
|
+
const roles = require('./lib/discovery-access.cjs');
|
|
2657
|
+
try {
|
|
2658
|
+
roles.cmdDiscoveryRolesCheck(cwd, args, raw);
|
|
2659
|
+
} catch (rolesCheckErr) {
|
|
2660
|
+
error(`discovery roles-check: ${rolesCheckErr && rolesCheckErr.message ? rolesCheckErr.message : String(rolesCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2661
|
+
}
|
|
2662
|
+
} else if (subcommand === 'integrations-check') {
|
|
2663
|
+
// discovery integrations-check [--require-cited] — _integrations.md
|
|
2664
|
+
// structure: IF- sequence, direction vocabulary, contract and error
|
|
2665
|
+
// rows naming a known interface, step-matrix IF- tokens resolving.
|
|
2666
|
+
// --require-cited also fails on an uncited interface and on a step
|
|
2667
|
+
// naming a system without an IF- (discovery_integrations_uncited).
|
|
2668
|
+
const integrations = require('./lib/discovery-integrations.cjs');
|
|
2669
|
+
try {
|
|
2670
|
+
integrations.cmdDiscoveryIntegrationsCheck(cwd, args, raw);
|
|
2671
|
+
} catch (integrationsCheckErr) {
|
|
2672
|
+
error(`discovery integrations-check: ${integrationsCheckErr && integrationsCheckErr.message ? integrationsCheckErr.message : String(integrationsCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2673
|
+
}
|
|
2674
|
+
} else if (subcommand === 'migration-check') {
|
|
2675
|
+
// discovery migration-check [--require-cited] — _migration.md
|
|
2676
|
+
// structure: MG- sequence, source system in Session 2's landscape,
|
|
2677
|
+
// entities resolving, requirement IDs resolving. --require-cited
|
|
2678
|
+
// also fails on an uncited row (discovery_migration_uncited).
|
|
2679
|
+
const migration = require('./lib/discovery-migration.cjs');
|
|
2680
|
+
try {
|
|
2681
|
+
migration.cmdDiscoveryMigrationCheck(cwd, args, raw);
|
|
2682
|
+
} catch (migrationCheckErr) {
|
|
2683
|
+
error(`discovery migration-check: ${migrationCheckErr && migrationCheckErr.message ? migrationCheckErr.message : String(migrationCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2684
|
+
}
|
|
2685
|
+
} else if (subcommand === 'session-check') {
|
|
2686
|
+
// discovery session-check <N> [<N>…] | --all — every Notes Template
|
|
2687
|
+
// section of the session content file present exactly once in
|
|
2688
|
+
// session-NN.md, each table with a data row or the literal
|
|
2689
|
+
// `None identified in Discovery.`; --all covers the Completed sessions.
|
|
2690
|
+
const sessionCheck = require('./lib/discovery-session-check.cjs');
|
|
2691
|
+
try {
|
|
2692
|
+
sessionCheck.cmdDiscoverySessionCheck(cwd, args, raw);
|
|
2693
|
+
} catch (sessionCheckErr) {
|
|
2694
|
+
error(`discovery session-check: ${sessionCheckErr && sessionCheckErr.message ? sessionCheckErr.message : String(sessionCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2695
|
+
}
|
|
2696
|
+
} else if (subcommand === 'deviations-check') {
|
|
2697
|
+
// discovery deviations-check [--require-approved] — _deviations.md
|
|
2698
|
+
// structure: DEV- sequence, a [vault: path] standard present in the
|
|
2699
|
+
// cache, reversible and status vocabularies, approver in Session 1's
|
|
2700
|
+
// Approval Matrix, decision ID shape. --require-approved also fails
|
|
2701
|
+
// on a hard-to-reverse deviation still Proposed
|
|
2702
|
+
// (discovery_deviations_unapproved).
|
|
2703
|
+
const deviations = require('./lib/discovery-deviations.cjs');
|
|
2704
|
+
try {
|
|
2705
|
+
deviations.cmdDiscoveryDeviationsCheck(cwd, args, raw);
|
|
2706
|
+
} catch (deviationsCheckErr) {
|
|
2707
|
+
error(`discovery deviations-check: ${deviationsCheckErr && deviationsCheckErr.message ? deviationsCheckErr.message : String(deviationsCheckErr)}`, ERROR_REASON.UNKNOWN);
|
|
2708
|
+
}
|
|
2567
2709
|
} else {
|
|
2568
2710
|
// WIRING, NOT HELP TEXT: tests/discovery-artifacts.test.cjs derives each
|
|
2569
2711
|
// subcommand spelling by regexing `<namespace>_<subcommand>_failed` out of
|
|
2570
2712
|
// the engine's exported exit identity, splits this string on commas and
|
|
2571
2713
|
// asserts membership. A spelling missing here is a command the engine
|
|
2572
2714
|
// claims to be and the router does not dispatch.
|
|
2573
|
-
error('Unknown discovery subcommand. Available: rollup, input-check, research-check, screen-check, element-sweep, rebaseline-check, vault-check', ERROR_REASON.SDK_UNKNOWN_COMMAND);
|
|
2715
|
+
error('Unknown discovery subcommand. Available: rollup, input-check, research-check, screen-check, element-sweep, rebaseline-check, vault-check, charter-check, entity-check, process-check, rules-check, stories-check, phases-check, for-phase, roles-check, integrations-check, migration-check, deviations-check, session-check', ERROR_REASON.SDK_UNKNOWN_COMMAND);
|
|
2574
2716
|
}
|
|
2575
2717
|
break;
|
|
2576
2718
|
}
|