@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.
- package/.claude-plugin/plugin.json +1 -1
- package/agents/pp-code-fixer.md +9 -5
- package/agents/pp-code-reviewer.md +1 -0
- package/agents/pp-codebase-mapper.md +3 -0
- package/agents/pp-debugger.md +3 -2
- package/agents/pp-executor.md +16 -7
- package/agents/pp-integration-checker.md +1 -0
- package/agents/pp-phase-researcher.md +13 -1
- package/agents/pp-plan-checker.md +3 -0
- package/agents/pp-planner.md +20 -1
- package/agents/pp-research-synthesizer.md +1 -1
- package/agents/pp-ui-auditor.md +1 -0
- package/agents/pp-ui-researcher.md +3 -1
- package/agents/pp-vault-reviewer.md +2 -0
- package/agents/pp-verifier.md +22 -10
- package/commands/pp-add-tests.md +1 -1
- package/commands/pp-debug.md +1 -0
- package/commands/pp-discovery.md +3 -2
- package/commands/pp-init.md +4 -3
- package/commands/pp-quick.md +3 -0
- package/commands/pp-review-backlog.md +2 -1
- 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 +3 -3
- package/pp-core/bin/lib/check-command-router.cjs +60 -20
- package/pp-core/bin/lib/commands.cjs +79 -4
- package/pp-core/bin/lib/compose-bindings.cjs +97 -14
- 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 +12 -8
- package/pp-core/bin/lib/discovery-rules.cjs +216 -0
- package/pp-core/bin/lib/discovery-screens.cjs +21 -3
- 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 +13 -4
- package/pp-core/bin/lib/discovery-table.cjs +55 -0
- package/pp-core/bin/lib/discovery-vault-check.cjs +37 -3
- package/pp-core/bin/lib/init-contract.cjs +48 -7
- package/pp-core/bin/lib/io.cjs +22 -0
- package/pp-core/bin/lib/vault-coordinates.cjs +1 -1
- package/pp-core/bin/lib/vault-index.cjs +18 -5
- package/pp-core/bin/lib/vault-standards-review.cjs +50 -2
- package/pp-core/bin/lib/vault-update.cjs +4 -0
- package/pp-core/bin/lib/vault.cjs +2 -2
- package/pp-core/bin/pp-tools.cjs +153 -5
- package/pp-core/org-profile.json +1 -1
- package/pp-core/references/autonomous-best-effort.md +3 -2
- package/pp-core/references/autonomous-lifecycle.md +1 -0
- package/pp-core/references/autonomous-smart-discuss.md +5 -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 +77 -8
- 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/execute-mvp-tdd.md +3 -3
- package/pp-core/references/git-integration.md +57 -33
- package/pp-core/references/git-planning-commit.md +31 -9
- package/pp-core/references/phase-argument-parsing.md +1 -0
- package/pp-core/references/planner-discovery-extract.md +35 -0
- package/pp-core/references/planner-gap-closure.md +1 -0
- package/pp-core/references/planner-revision.md +3 -1
- package/pp-core/references/planner-tdd.md +4 -4
- package/pp-core/references/planning-config.md +3 -3
- package/pp-core/references/shell-portability.md +28 -0
- package/pp-core/references/tdd.md +20 -16
- package/pp-core/references/verifier-discovery-conformance.md +36 -0
- package/pp-core/references/worktree-path-safety.md +1 -0
- package/pp-core/templates/milestone.md +5 -5
- 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/add-backlog.md +1 -1
- package/pp-core/workflows/add-tests.md +5 -2
- package/pp-core/workflows/add-todo.md +3 -1
- package/pp-core/workflows/ai-integration-phase.md +2 -1
- package/pp-core/workflows/audit-fix.md +4 -3
- package/pp-core/workflows/audit-milestone.md +1 -0
- package/pp-core/workflows/autonomous.md +6 -0
- package/pp-core/workflows/check-todos.md +2 -1
- package/pp-core/workflows/cleanup.md +3 -1
- package/pp-core/workflows/code-review-fix.md +12 -5
- package/pp-core/workflows/code-review.md +15 -1
- package/pp-core/workflows/complete-milestone.md +13 -3
- package/pp-core/workflows/debug.md +2 -0
- package/pp-core/workflows/diagnose-issues.md +1 -1
- 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 +11 -1
- package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +54 -7
- package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +31 -7
- package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +64 -37
- package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +12 -3
- package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +45 -22
- package/pp-core/workflows/discovery-sprint/sessions/06-security.md +30 -15
- package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +43 -25
- package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +63 -24
- package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +28 -13
- package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +35 -16
- package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +32 -8
- package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +21 -18
- package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +29 -12
- package/pp-core/workflows/discovery-sprint/vault-context.md +14 -3
- package/pp-core/workflows/discovery-sprint.md +23 -23
- package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +81 -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-assumptions.md +5 -3
- package/pp-core/workflows/discuss-phase.md +18 -10
- package/pp-core/workflows/docs-update.md +1 -0
- package/pp-core/workflows/eval-review.md +2 -1
- package/pp-core/workflows/execute-phase/steps/check-query-gate.md +7 -6
- package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +1 -0
- package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -0
- package/pp-core/workflows/execute-phase/steps/vault-fetch.md +12 -0
- package/pp-core/workflows/execute-phase.md +19 -7
- package/pp-core/workflows/execute-plan.md +18 -7
- package/pp-core/workflows/explore.md +1 -1
- package/pp-core/workflows/forensics.md +4 -2
- package/pp-core/workflows/health.md +1 -0
- package/pp-core/workflows/import.md +2 -1
- package/pp-core/workflows/ingest-docs.md +1 -0
- package/pp-core/workflows/init/steps/client-context-capture.md +1 -0
- package/pp-core/workflows/init/steps/github-provisioning.md +145 -0
- package/pp-core/workflows/init/steps/gitlab-provisioning.md +18 -4
- package/pp-core/workflows/init/steps/reachability-outcome.md +3 -1
- package/pp-core/workflows/init.md +56 -66
- package/pp-core/workflows/map-codebase.md +5 -1
- package/pp-core/workflows/milestone-summary.md +2 -1
- package/pp-core/workflows/mvp-phase.md +1 -0
- package/pp-core/workflows/new-milestone.md +9 -5
- package/pp-core/workflows/new-project.md +12 -11
- package/pp-core/workflows/new-workspace.md +1 -0
- package/pp-core/workflows/next.md +2 -1
- package/pp-core/workflows/pause-work.md +6 -4
- package/pp-core/workflows/plan-milestone-gaps.md +3 -1
- package/pp-core/workflows/plan-phase/steps/chunked-planning.md +1 -1
- package/pp-core/workflows/plan-phase/steps/discovery-extract.md +41 -0
- package/pp-core/workflows/plan-phase.md +36 -16
- package/pp-core/workflows/plan-review-convergence.md +2 -0
- package/pp-core/workflows/plant-seed.md +4 -2
- package/pp-core/workflows/pr-branch.md +2 -0
- package/pp-core/workflows/progress.md +5 -0
- package/pp-core/workflows/quick.md +11 -4
- package/pp-core/workflows/reapply-patches.md +3 -0
- package/pp-core/workflows/remove-phase.md +1 -1
- package/pp-core/workflows/resume-project.md +3 -0
- package/pp-core/workflows/review.md +7 -1
- package/pp-core/workflows/secure-phase.md +2 -1
- package/pp-core/workflows/session-report.md +1 -0
- package/pp-core/workflows/ship.md +8 -1
- package/pp-core/workflows/sketch-wrap-up.md +1 -1
- package/pp-core/workflows/sketch.md +3 -2
- package/pp-core/workflows/spec-phase.md +2 -1
- package/pp-core/workflows/spike-wrap-up.md +1 -1
- package/pp-core/workflows/spike.md +4 -3
- package/pp-core/workflows/sync-skills.md +5 -2
- package/pp-core/workflows/thread.md +3 -2
- package/pp-core/workflows/transition.md +6 -0
- package/pp-core/workflows/ui-phase.md +3 -1
- package/pp-core/workflows/ui-review.md +2 -1
- package/pp-core/workflows/ultraplan-phase.md +1 -0
- package/pp-core/workflows/undo.md +14 -10
- package/pp-core/workflows/update.md +5 -0
- package/pp-core/workflows/validate-phase.md +6 -3
- package/pp-core/workflows/vault-review.md +7 -1
- package/pp-core/workflows/vault-write-back.md +5 -0
- package/pp-core/workflows/verify-phase.md +5 -0
- package/pp-core/workflows/verify-work.md +8 -1
- package/scripts/prompt-injection-scan.sh +1 -1
- package/scripts/sync-runtime-launcher.cjs +115 -5
- package/pp-core/workflows/discovery-sprint/templates/context-template.md +0 -266
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Discovery business-rules check (Discovery Sprint improvement plan, Batch 2).
|
|
4
|
+
*
|
|
5
|
+
* `.planning/discovery/_rules.md` is the register of business rules —
|
|
6
|
+
* validations, calculations, invariants — Session 3 Step 5D′ mints from the
|
|
7
|
+
* step matrices: `BR-nn`, the rule, what it applies to (an entity of
|
|
8
|
+
* `_entities.md` or a process of `_processes.md`), where it is checked
|
|
9
|
+
* (`UI`, `service`, `DB` — the vault's rule that a check has one owning
|
|
10
|
+
* layer), the requirements it realises, and its source. The build's planner
|
|
11
|
+
* and tests read it by requirement ID. This module checks the structure:
|
|
12
|
+
*
|
|
13
|
+
* - the `## Rules` table exists with the template's columns;
|
|
14
|
+
* - `BR-` IDs are well-formed, unique, contiguous;
|
|
15
|
+
* - `Checked at` is a comma list from the closed layer vocabulary;
|
|
16
|
+
* - every rule cites at least one requirement, each existing in
|
|
17
|
+
* `_requirements.md` when it is on file and parseable;
|
|
18
|
+
* - every `Applies to` token is an entity name that `_entities.md` has or a
|
|
19
|
+
* `PRC-`/`MP-` ID that `_processes.md` has, when those artifacts are on
|
|
20
|
+
* file (an absent artifact leaves the token shape-checked only).
|
|
21
|
+
*
|
|
22
|
+
* `_glossary.md` (`Term | Definition | Do not use | Source`) lives beside it
|
|
23
|
+
* and is deliberately not checked: a definition has no grammar.
|
|
24
|
+
*
|
|
25
|
+
* Posture (D-43/D-53): reports at Sessions 8 and 13; hard-blocks at
|
|
26
|
+
* `/pp-discovery-to-build`. Absent artifact → exit 0.
|
|
27
|
+
*/
|
|
28
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
29
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
30
|
+
};
|
|
31
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
32
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
34
|
+
const io = require("./io.cjs");
|
|
35
|
+
const { output, error, ERROR_REASON } = io;
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
37
|
+
const table = require("./discovery-table.cjs");
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
39
|
+
const rollup = require("./discovery-rollup.cjs");
|
|
40
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
41
|
+
const DISCOVERY_DIR = '.planning/discovery';
|
|
42
|
+
const RULES_FILE = `${DISCOVERY_DIR}/_rules.md`;
|
|
43
|
+
const REQUIREMENTS_FILE = `${DISCOVERY_DIR}/_requirements.md`;
|
|
44
|
+
const ENTITIES_FILE = `${DISCOVERY_DIR}/_entities.md`;
|
|
45
|
+
const PROCESSES_FILE = `${DISCOVERY_DIR}/_processes.md`;
|
|
46
|
+
const RULES_SECTION = 'Rules';
|
|
47
|
+
const RULES_HEADER = Object.freeze(['ID', 'Rule', 'Applies to', 'Checked at', 'Requirements', 'Source']);
|
|
48
|
+
const BR_RE = /^BR-(\d{2})$/;
|
|
49
|
+
const LAYERS = Object.freeze(['UI', 'service', 'DB']);
|
|
50
|
+
const ENTITY_NAME_RE = /^[A-Z][A-Za-z0-9]*$/;
|
|
51
|
+
const PROCESS_ID_RE = /^(PRC|MP)-\d{2}$/;
|
|
52
|
+
const REQUIREMENT_ID_RE = /^[A-Z][A-Z0-9]*-\d+$/;
|
|
53
|
+
const USAGE = 'Usage: pp-tools discovery rules-check';
|
|
54
|
+
function readIfPresent(abs) {
|
|
55
|
+
if (!node_fs_1.default.existsSync(abs))
|
|
56
|
+
return { kind: 'absent' };
|
|
57
|
+
try {
|
|
58
|
+
return { kind: 'ok', content: node_fs_1.default.readFileSync(abs, 'utf-8') };
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return { kind: 'unreadable' };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function sameHeader(actual, expected) {
|
|
65
|
+
return actual.length === expected.length && actual.every((c, i) => c.trim().toLowerCase() === expected[i].toLowerCase());
|
|
66
|
+
}
|
|
67
|
+
function splitList(cell) {
|
|
68
|
+
return cell.split(',').map((t) => t.trim()).filter(Boolean);
|
|
69
|
+
}
|
|
70
|
+
/** First-column IDs/names of the first table under a `## <heading>` of another artifact. */
|
|
71
|
+
function firstColumnOf(content, heading) {
|
|
72
|
+
const section = table.splitSections(content.split(/\r?\n/), 2).find((s) => s.heading === heading);
|
|
73
|
+
if (!section)
|
|
74
|
+
return new Set();
|
|
75
|
+
const tables = table.parseTables(section.body);
|
|
76
|
+
if (tables.length === 0)
|
|
77
|
+
return new Set();
|
|
78
|
+
return new Set(tables[0].rows.map((r) => (r.cells[0] ?? '').trim()).filter(Boolean));
|
|
79
|
+
}
|
|
80
|
+
// ─── Check ────────────────────────────────────────────────────────────────────
|
|
81
|
+
function checkRules(content, sources) {
|
|
82
|
+
const violations = [];
|
|
83
|
+
const sections = table.splitSections(content.split(/\r?\n/), 2);
|
|
84
|
+
const section = sections.find((s) => s.heading === RULES_SECTION);
|
|
85
|
+
if (!section) {
|
|
86
|
+
return { ok: false, rules: 0, violations: [{ artifact: RULES_FILE, reason: 'missing_section', id: RULES_SECTION }] };
|
|
87
|
+
}
|
|
88
|
+
const tables = table.parseTables(section.body);
|
|
89
|
+
if (tables.length === 0) {
|
|
90
|
+
return { ok: false, rules: 0, violations: [{ artifact: RULES_FILE, reason: 'missing_section', line: section.line, id: RULES_SECTION }] };
|
|
91
|
+
}
|
|
92
|
+
if (!sameHeader(tables[0].header, RULES_HEADER)) {
|
|
93
|
+
return { ok: false, rules: 0, violations: [{ artifact: RULES_FILE, reason: 'bad_table_shape', line: tables[0].line, id: RULES_SECTION }] };
|
|
94
|
+
}
|
|
95
|
+
const ids = new Set();
|
|
96
|
+
const seqs = [];
|
|
97
|
+
for (const row of tables[0].rows) {
|
|
98
|
+
const [id, rule, appliesTo, checkedAt, requirements] = row.cells.map((c) => c.trim());
|
|
99
|
+
const m = BR_RE.exec(id ?? '');
|
|
100
|
+
if (!m) {
|
|
101
|
+
violations.push({ artifact: RULES_FILE, reason: 'bad_id', line: row.line });
|
|
102
|
+
}
|
|
103
|
+
else if (ids.has(id)) {
|
|
104
|
+
violations.push({ artifact: RULES_FILE, reason: 'duplicate_id', line: row.line, id });
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
ids.add(id);
|
|
108
|
+
seqs.push(Number(m[1]));
|
|
109
|
+
}
|
|
110
|
+
if ((rule ?? '') === '')
|
|
111
|
+
violations.push({ artifact: RULES_FILE, reason: 'empty_cell', line: row.line, id: 'Rule' });
|
|
112
|
+
const layers = splitList(checkedAt ?? '');
|
|
113
|
+
if (layers.length === 0 || layers.some((l) => !LAYERS.includes(l)))
|
|
114
|
+
violations.push({ artifact: RULES_FILE, reason: 'bad_checked_at', line: row.line });
|
|
115
|
+
const subjects = splitList(appliesTo ?? '');
|
|
116
|
+
if (subjects.length === 0)
|
|
117
|
+
violations.push({ artifact: RULES_FILE, reason: 'bad_subject', line: row.line });
|
|
118
|
+
for (const s of subjects) {
|
|
119
|
+
if (ENTITY_NAME_RE.test(s)) {
|
|
120
|
+
if (sources.entities !== null && !sources.entities.has(s))
|
|
121
|
+
violations.push({ artifact: RULES_FILE, reason: 'unknown_subject', line: row.line, id: s });
|
|
122
|
+
}
|
|
123
|
+
else if (PROCESS_ID_RE.test(s)) {
|
|
124
|
+
if (sources.processes !== null && !sources.processes.has(s))
|
|
125
|
+
violations.push({ artifact: RULES_FILE, reason: 'unknown_subject', line: row.line, id: s });
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
violations.push({ artifact: RULES_FILE, reason: 'bad_subject', line: row.line });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const reqs = splitList(requirements ?? '');
|
|
132
|
+
if (reqs.length === 0)
|
|
133
|
+
violations.push({ artifact: RULES_FILE, reason: 'no_requirement', line: row.line });
|
|
134
|
+
for (const r of reqs) {
|
|
135
|
+
if (!REQUIREMENT_ID_RE.test(r))
|
|
136
|
+
violations.push({ artifact: RULES_FILE, reason: 'unknown_requirement_id', line: row.line, id: r.slice(0, 40) });
|
|
137
|
+
else if (sources.requirementIds instanceof Set && !sources.requirementIds.has(r))
|
|
138
|
+
violations.push({ artifact: RULES_FILE, reason: 'unknown_requirement_id', line: row.line, id: r });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const max = table.boundedSequenceMax(seqs);
|
|
142
|
+
if (max !== null) {
|
|
143
|
+
const have = new Set(seqs);
|
|
144
|
+
for (let n = 1; n <= max; n += 1) {
|
|
145
|
+
if (!have.has(n))
|
|
146
|
+
violations.push({ artifact: RULES_FILE, reason: 'sequence_gap', id: `BR-${String(n).padStart(2, '0')}` });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (sources.requirementIds === 'unparseable')
|
|
150
|
+
violations.push({ artifact: REQUIREMENTS_FILE, reason: 'requirements_unparseable' });
|
|
151
|
+
return { ok: violations.length === 0, rules: ids.size, violations };
|
|
152
|
+
}
|
|
153
|
+
function checkProjectRules(cwd) {
|
|
154
|
+
const read = readIfPresent(node_path_1.default.join(cwd, RULES_FILE));
|
|
155
|
+
if (read.kind === 'absent')
|
|
156
|
+
return null;
|
|
157
|
+
if (read.kind === 'unreadable')
|
|
158
|
+
return { ok: false, file: RULES_FILE, rules: 0, violations: [{ artifact: RULES_FILE, reason: 'artifact_unreadable' }] };
|
|
159
|
+
const extra = [];
|
|
160
|
+
const sources = { requirementIds: null, entities: null, processes: null };
|
|
161
|
+
const req = readIfPresent(node_path_1.default.join(cwd, REQUIREMENTS_FILE));
|
|
162
|
+
if (req.kind === 'unreadable')
|
|
163
|
+
extra.push({ artifact: REQUIREMENTS_FILE, reason: 'artifact_unreadable' });
|
|
164
|
+
if (req.kind === 'ok') {
|
|
165
|
+
const parsed = rollup.parseRequirementsArtifact(req.content);
|
|
166
|
+
sources.requirementIds = parsed.ok ? new Set(parsed.rows.map((r) => r.id)) : 'unparseable';
|
|
167
|
+
}
|
|
168
|
+
const ent = readIfPresent(node_path_1.default.join(cwd, ENTITIES_FILE));
|
|
169
|
+
if (ent.kind === 'unreadable')
|
|
170
|
+
extra.push({ artifact: ENTITIES_FILE, reason: 'artifact_unreadable' });
|
|
171
|
+
if (ent.kind === 'ok')
|
|
172
|
+
sources.entities = firstColumnOf(ent.content, 'Entities');
|
|
173
|
+
const proc = readIfPresent(node_path_1.default.join(cwd, PROCESSES_FILE));
|
|
174
|
+
if (proc.kind === 'unreadable')
|
|
175
|
+
extra.push({ artifact: PROCESSES_FILE, reason: 'artifact_unreadable' });
|
|
176
|
+
if (proc.kind === 'ok')
|
|
177
|
+
sources.processes = new Set([...firstColumnOf(proc.content, 'As-is'), ...firstColumnOf(proc.content, 'To-be')]);
|
|
178
|
+
const result = checkRules(read.content, sources);
|
|
179
|
+
result.violations.push(...extra);
|
|
180
|
+
result.ok = result.violations.length === 0;
|
|
181
|
+
return { file: RULES_FILE, ...result };
|
|
182
|
+
}
|
|
183
|
+
function formatViolation(v) {
|
|
184
|
+
const where = v.line !== undefined ? `:${v.line}` : '';
|
|
185
|
+
const what = v.id !== undefined ? ` (${v.id})` : '';
|
|
186
|
+
return ` - ${v.artifact}${where}: ${v.reason}${what}`;
|
|
187
|
+
}
|
|
188
|
+
// ─── CLI adapter ──────────────────────────────────────────────────────────────
|
|
189
|
+
/** `pp-tools discovery rules-check` (dotted `query discovery.rules-check`). Absent artifact → exit 0, `present: false`. */
|
|
190
|
+
function cmdDiscoveryRulesCheck(cwd, args, raw) {
|
|
191
|
+
if (args.length > 2) {
|
|
192
|
+
error(USAGE, ERROR_REASON.USAGE);
|
|
193
|
+
return; // unreachable
|
|
194
|
+
}
|
|
195
|
+
const result = checkProjectRules(cwd);
|
|
196
|
+
if (result === null) {
|
|
197
|
+
output({ ok: true, present: false, file: RULES_FILE }, raw);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const payload = { ok: result.ok, present: true, file: result.file, rules: result.rules };
|
|
201
|
+
if (!result.ok) {
|
|
202
|
+
output({ ...payload, violations: result.violations.length }, raw);
|
|
203
|
+
error(`discovery rules-check: ${result.violations.length} violation(s):\n` + result.violations.map(formatViolation).join('\n'), ERROR_REASON.DISCOVERY_RULES_CHECK_FAILED);
|
|
204
|
+
return; // unreachable
|
|
205
|
+
}
|
|
206
|
+
output(payload, raw);
|
|
207
|
+
}
|
|
208
|
+
module.exports = {
|
|
209
|
+
RULES_FILE,
|
|
210
|
+
RULES_HEADER,
|
|
211
|
+
LAYERS,
|
|
212
|
+
RULES_CHECK_FAILED: ERROR_REASON.DISCOVERY_RULES_CHECK_FAILED,
|
|
213
|
+
checkRules,
|
|
214
|
+
checkProjectRules,
|
|
215
|
+
cmdDiscoveryRulesCheck,
|
|
216
|
+
};
|
|
@@ -355,7 +355,7 @@ const VERDICTS = Object.freeze(['change', 'keep', 'drop', 'question']);
|
|
|
355
355
|
const PROCESSING = Object.freeze({ OPEN: 'Open', NO_CHANGE: 'No change' });
|
|
356
356
|
// D-57: the three citation shapes a Processing cell may take. Anchored,
|
|
357
357
|
// quantifier-free, applied to a SINGLE already-split cell (T-04-13).
|
|
358
|
-
const DECISION_CITE_RE = /^D
|
|
358
|
+
const DECISION_CITE_RE = /^D-(?:\d{2}|P\d{2})-\d+$/;
|
|
359
359
|
// D-57: a data-gathering item in `_risks.md`.
|
|
360
360
|
const DG_CITE_RE = /^DG-\d{2}$/;
|
|
361
361
|
// D-57: a research-backlog item in `_research.md`.
|
|
@@ -1287,6 +1287,23 @@ const SCAN_SKIP_DIRS = Object.freeze(new Set([
|
|
|
1287
1287
|
'.next', '.nuxt', '.turbo', '.cache', 'coverage',
|
|
1288
1288
|
'.venv', 'venv', '__pycache__', 'vendor', 'target',
|
|
1289
1289
|
]));
|
|
1290
|
+
/**
|
|
1291
|
+
* True when `dir` holds a pp-core install (`<dir>/pp-core/VERSION`, the marker
|
|
1292
|
+
* `update-context` reads): a project-local runtime directory such as
|
|
1293
|
+
* `.claude` or `.codex`. The walk never enters one. Its engine files, agents
|
|
1294
|
+
* and commands spell the instrumentation literals out, and none of it ships
|
|
1295
|
+
* in the engagement's build, so walking it reports pp-core itself as a leak
|
|
1296
|
+
* (issue #12, item 7). Keyed on the marker rather than on directory names so
|
|
1297
|
+
* every runtime's install directory is covered without a third list of them.
|
|
1298
|
+
*/
|
|
1299
|
+
function isPpCoreInstallDir(dir) {
|
|
1300
|
+
try {
|
|
1301
|
+
return node_fs_1.default.statSync(node_path_1.default.join(dir, 'pp-core', 'VERSION')).isFile();
|
|
1302
|
+
}
|
|
1303
|
+
catch {
|
|
1304
|
+
return false;
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1290
1307
|
/**
|
|
1291
1308
|
* The file extensions the scan actually READS. An allowlist rather than a
|
|
1292
1309
|
* denylist, because the bound has to hold for file types nobody has thought of
|
|
@@ -1475,7 +1492,8 @@ function loadScanAllowlist(loaded) {
|
|
|
1475
1492
|
* - every entry is resolved and confirmed strictly beneath the resolved
|
|
1476
1493
|
* project root before any stat or read, the same posture the roll-up
|
|
1477
1494
|
* traversal guard takes (T-04-16);
|
|
1478
|
-
* - `SCAN_SKIP_DIRS` keeps the walk out of the dependency tree
|
|
1495
|
+
* - `SCAN_SKIP_DIRS` keeps the walk out of the dependency tree, and
|
|
1496
|
+
* `isPpCoreInstallDir` out of a project-local pp-core install;
|
|
1479
1497
|
* - `SCAN_EXTENSIONS` bounds what is read;
|
|
1480
1498
|
* - the file and byte caps stop the walk.
|
|
1481
1499
|
*
|
|
@@ -1536,7 +1554,7 @@ function scanBuildTree(cwd, options = {}) {
|
|
|
1536
1554
|
if (stat.isSymbolicLink())
|
|
1537
1555
|
continue;
|
|
1538
1556
|
if (stat.isDirectory()) {
|
|
1539
|
-
if (!SCAN_SKIP_DIRS.has(name))
|
|
1557
|
+
if (!SCAN_SKIP_DIRS.has(name) && !isPpCoreInstallDir(full))
|
|
1540
1558
|
stack.push(full);
|
|
1541
1559
|
continue;
|
|
1542
1560
|
}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Discovery session output check (Discovery Sprint improvement plan, Batch 5).
|
|
4
|
+
*
|
|
5
|
+
* A session's Notes Template is the contract for what the session produces:
|
|
6
|
+
* every whole-line bold title under `**Notes Template:**` in
|
|
7
|
+
* `workflows/discovery-sprint/sessions/NN-*.md` names a section the session
|
|
8
|
+
* file `.planning/discovery/session-NN.md` must carry exactly once, and a
|
|
9
|
+
* section that is a table in the template must hold at least one data row
|
|
10
|
+
* (or say, on one line under its title, `None identified in Discovery.`).
|
|
11
|
+
* A prose section must hold at least one line of prose or that literal.
|
|
12
|
+
* The score formula in `discovery-sprint.md` Step 7 counts a section this
|
|
13
|
+
* check reports empty as not covered, so a session can no longer close at
|
|
14
|
+
* 100 with an empty Entity Register or an empty Roles table (review F-6).
|
|
15
|
+
*
|
|
16
|
+
* The template is read from the pp-core install itself (two directories up
|
|
17
|
+
* from the compiled engine), so the required-section list cannot drift from
|
|
18
|
+
* the session file the facilitator reads.
|
|
19
|
+
*
|
|
20
|
+
* - `session-check <N>` checks one session whatever its status;
|
|
21
|
+
* - `session-check --all` checks every `session-NN.md` on disk whose
|
|
22
|
+
* frontmatter says `status: "Completed"` — Session 8's Closing Step and
|
|
23
|
+
* the Session 13 gates run it over the sessions closed so far.
|
|
24
|
+
*
|
|
25
|
+
* Posture: reports at every session close, at Session 8 and at Session 13;
|
|
26
|
+
* hard-blocks at the handoff like the other artifact checks.
|
|
27
|
+
*/
|
|
28
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
29
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
30
|
+
};
|
|
31
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
32
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
34
|
+
const io = require("./io.cjs");
|
|
35
|
+
const { output, error, ERROR_REASON } = io;
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
37
|
+
const table = require("./discovery-table.cjs");
|
|
38
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
39
|
+
const DISCOVERY_DIR = '.planning/discovery';
|
|
40
|
+
const SESSION_COUNT = 13;
|
|
41
|
+
/** The literal a section carries when the session found nothing to record. */
|
|
42
|
+
const NONE_LITERAL = 'None identified in Discovery.';
|
|
43
|
+
/** A parked topic's marker (Flow 2 Step 5.3) — the section is accounted for, the score is not. */
|
|
44
|
+
const PARKED_LITERAL = 'PARKED — needs business/senior input';
|
|
45
|
+
const NOTES_TEMPLATE_MARKER = /^\*\*Notes Template:\*\*$/;
|
|
46
|
+
const BOLD_TITLE_RE = /^\*\*([^*]+)\*\*$/;
|
|
47
|
+
const HEADING_RE = /^#{1,6}\s/;
|
|
48
|
+
const HEADING_TITLE_RE = /^#{1,6}\s+(.+?)\s*$/;
|
|
49
|
+
const RULE_RE = /^---$/;
|
|
50
|
+
const TABLE_ROW_RE = /^\|/;
|
|
51
|
+
const HTML_COMMENT_RE = /^<!--.*-->$/;
|
|
52
|
+
const COMPLETED_RE = /^status:\s*"?Completed"?\s*$/m;
|
|
53
|
+
const SESSIONS_DIR = node_path_1.default.resolve(__dirname, '..', '..', 'workflows', 'discovery-sprint', 'sessions');
|
|
54
|
+
const USAGE = 'Usage: pp-tools discovery session-check <N> [<N>…] | --all';
|
|
55
|
+
function readIfPresent(abs) {
|
|
56
|
+
if (!node_fs_1.default.existsSync(abs))
|
|
57
|
+
return { kind: 'absent' };
|
|
58
|
+
try {
|
|
59
|
+
return { kind: 'ok', content: node_fs_1.default.readFileSync(abs, 'utf-8') };
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return { kind: 'unreadable' };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function pad(n) {
|
|
66
|
+
return String(n).padStart(2, '0');
|
|
67
|
+
}
|
|
68
|
+
function sessionFile(n) {
|
|
69
|
+
return `${DISCOVERY_DIR}/session-${pad(n)}.md`;
|
|
70
|
+
}
|
|
71
|
+
/** The session content file `NN-*.md` under the install's sessions directory, or null. */
|
|
72
|
+
function templatePath(n, sessionsDir) {
|
|
73
|
+
let names;
|
|
74
|
+
try {
|
|
75
|
+
names = node_fs_1.default.readdirSync(sessionsDir);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
const prefix = `${pad(n)}-`;
|
|
81
|
+
const found = names.find((f) => f.startsWith(prefix) && f.endsWith('.md'));
|
|
82
|
+
return found ? node_path_1.default.join(sessionsDir, found) : null;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Split a run of lines into titled sections. In the template (`stopAtHeading`)
|
|
86
|
+
* a title is a whole-line `**Title**` and the run ends at the first heading or
|
|
87
|
+
* rule. In a session file a facilitator writes the same titles either as
|
|
88
|
+
* `**Title**` or as a heading `## Title` (both are found in the field), so
|
|
89
|
+
* there every whole-line bold and every heading opens a section.
|
|
90
|
+
*/
|
|
91
|
+
function titledSections(lines, from, stopAtHeading) {
|
|
92
|
+
const out = [];
|
|
93
|
+
let current = null;
|
|
94
|
+
for (let i = from; i < lines.length; i += 1) {
|
|
95
|
+
const text = lines[i].trim();
|
|
96
|
+
if (stopAtHeading && (HEADING_RE.test(text) || RULE_RE.test(text)))
|
|
97
|
+
break;
|
|
98
|
+
const bold = BOLD_TITLE_RE.exec(text);
|
|
99
|
+
const heading = HEADING_TITLE_RE.exec(text);
|
|
100
|
+
const title = bold ? bold[1].trim() : heading ? heading[1].trim() : null;
|
|
101
|
+
if (title !== null) {
|
|
102
|
+
current = { title, line: i + 1, body: [] };
|
|
103
|
+
out.push(current);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (current)
|
|
107
|
+
current.body.push({ n: i + 1, text: lines[i] });
|
|
108
|
+
}
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
/** The required sections of a session content file, in template order; null without a Notes Template. */
|
|
112
|
+
function requiredSectionsOf(templateContent) {
|
|
113
|
+
const lines = templateContent.split(/\r?\n/);
|
|
114
|
+
const at = lines.findIndex((l) => NOTES_TEMPLATE_MARKER.test(l.trim()));
|
|
115
|
+
if (at === -1)
|
|
116
|
+
return null;
|
|
117
|
+
return titledSections(lines, at + 1, true).map((s) => {
|
|
118
|
+
const tables = table.parseTables(s.body);
|
|
119
|
+
return tables.length > 0
|
|
120
|
+
? { title: s.title, kind: 'table', header: tables[0].header.map((c) => c.trim()) }
|
|
121
|
+
: { title: s.title, kind: 'prose', header: null };
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/** A table body row holds data when at least two cells are filled (one, for a one-column table). */
|
|
125
|
+
function isDataRow(cells) {
|
|
126
|
+
const filled = cells.filter((c) => c.trim() !== '').length;
|
|
127
|
+
return cells.length <= 1 ? filled >= 1 : filled >= 2;
|
|
128
|
+
}
|
|
129
|
+
function hasLiteral(body) {
|
|
130
|
+
return body.some((l) => {
|
|
131
|
+
const t = l.text.trim();
|
|
132
|
+
if (t === NONE_LITERAL || t === PARKED_LITERAL || t.startsWith(`${PARKED_LITERAL}`))
|
|
133
|
+
return true;
|
|
134
|
+
return TABLE_ROW_RE.test(t) && table.splitTableCells(t).some((c) => c.trim() === NONE_LITERAL);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function hasProse(body) {
|
|
138
|
+
return body.some((l) => {
|
|
139
|
+
const t = l.text.trim();
|
|
140
|
+
return t !== '' && !TABLE_ROW_RE.test(t) && !HTML_COMMENT_RE.test(t);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
// ─── Check ────────────────────────────────────────────────────────────────────
|
|
144
|
+
function checkSession(n, sessionContent, required) {
|
|
145
|
+
const file = sessionFile(n);
|
|
146
|
+
const violations = [];
|
|
147
|
+
const empty = [];
|
|
148
|
+
const sections = titledSections(sessionContent.split(/\r?\n/), 0, false);
|
|
149
|
+
let filled = 0;
|
|
150
|
+
for (const req of required) {
|
|
151
|
+
const found = sections.filter((s) => s.title === req.title);
|
|
152
|
+
if (found.length === 0) {
|
|
153
|
+
violations.push({ artifact: file, reason: 'missing_section', id: req.title });
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (found.length > 1) {
|
|
157
|
+
violations.push({ artifact: file, reason: 'duplicate_section', line: found[1].line, id: req.title });
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const s = found[0];
|
|
161
|
+
if (hasLiteral(s.body)) {
|
|
162
|
+
filled += 1;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (req.kind === 'table') {
|
|
166
|
+
const tables = table.parseTables(s.body);
|
|
167
|
+
if (tables.length === 0 || tables[0].header.length !== (req.header ?? []).length) {
|
|
168
|
+
violations.push({ artifact: file, reason: 'bad_table_shape', line: tables.length > 0 ? tables[0].line : s.line, id: req.title });
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (tables[0].rows.some((r) => isDataRow(r.cells)))
|
|
172
|
+
filled += 1;
|
|
173
|
+
else {
|
|
174
|
+
empty.push(req.title);
|
|
175
|
+
violations.push({ artifact: file, reason: 'empty_section', line: s.line, id: req.title });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
else if (hasProse(s.body) || table.parseTables(s.body).some((t) => t.rows.some((r) => isDataRow(r.cells))))
|
|
179
|
+
filled += 1;
|
|
180
|
+
else {
|
|
181
|
+
empty.push(req.title);
|
|
182
|
+
violations.push({ artifact: file, reason: 'empty_section', line: s.line, id: req.title });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return { required: required.length, filled, empty, violations };
|
|
186
|
+
}
|
|
187
|
+
function checkProjectSession(cwd, n, sessionsDir = SESSIONS_DIR) {
|
|
188
|
+
const file = sessionFile(n);
|
|
189
|
+
const base = { session: n, file, required: 0, filled: 0, empty: [] };
|
|
190
|
+
const tpl = templatePath(n, sessionsDir);
|
|
191
|
+
const tplRead = tpl ? readIfPresent(tpl) : { kind: 'absent' };
|
|
192
|
+
const required = tplRead.kind === 'ok' ? requiredSectionsOf(tplRead.content) : null;
|
|
193
|
+
if (required === null)
|
|
194
|
+
return { ...base, violations: [{ artifact: `sessions/${pad(n)}-*.md`, reason: 'template_missing', id: `session ${n}` }] };
|
|
195
|
+
const read = readIfPresent(node_path_1.default.join(cwd, file));
|
|
196
|
+
if (read.kind === 'absent')
|
|
197
|
+
return { ...base, required: required.length, violations: [{ artifact: file, reason: 'session_missing' }] };
|
|
198
|
+
if (read.kind === 'unreadable')
|
|
199
|
+
return { ...base, required: required.length, violations: [{ artifact: file, reason: 'artifact_unreadable' }] };
|
|
200
|
+
return { session: n, file, ...checkSession(n, read.content, required) };
|
|
201
|
+
}
|
|
202
|
+
/** The sessions `--all` covers: every session file on disk whose frontmatter says Completed. */
|
|
203
|
+
function completedSessions(cwd) {
|
|
204
|
+
const out = [];
|
|
205
|
+
for (let n = 1; n <= SESSION_COUNT; n += 1) {
|
|
206
|
+
const read = readIfPresent(node_path_1.default.join(cwd, sessionFile(n)));
|
|
207
|
+
if (read.kind === 'ok' && COMPLETED_RE.test(read.content.slice(0, 2000)))
|
|
208
|
+
out.push(n);
|
|
209
|
+
}
|
|
210
|
+
return out;
|
|
211
|
+
}
|
|
212
|
+
function checkProjectSessions(cwd, numbers, sessionsDir = SESSIONS_DIR) {
|
|
213
|
+
const sessions = numbers.map((n) => checkProjectSession(cwd, n, sessionsDir));
|
|
214
|
+
return { ok: sessions.every((s) => s.violations.length === 0), sessions };
|
|
215
|
+
}
|
|
216
|
+
function formatViolation(v) {
|
|
217
|
+
const where = v.line !== undefined ? `:${v.line}` : '';
|
|
218
|
+
const what = v.id !== undefined ? ` (${v.id})` : '';
|
|
219
|
+
return ` - ${v.artifact}${where}: ${v.reason}${what}`;
|
|
220
|
+
}
|
|
221
|
+
// ─── CLI adapter ──────────────────────────────────────────────────────────────
|
|
222
|
+
/**
|
|
223
|
+
* `pp-tools discovery session-check <N> [<N>…] | --all` (dotted
|
|
224
|
+
* `query discovery.session-check`). Faults exit via
|
|
225
|
+
* `DISCOVERY_SESSION_CHECK_FAILED`; `--all` over a project with no completed
|
|
226
|
+
* session exits 0 with `sessions: []`.
|
|
227
|
+
*/
|
|
228
|
+
function cmdDiscoverySessionCheck(cwd, args, raw) {
|
|
229
|
+
const rest = args.slice(2);
|
|
230
|
+
let numbers;
|
|
231
|
+
if (rest.length === 1 && rest[0] === '--all')
|
|
232
|
+
numbers = completedSessions(cwd);
|
|
233
|
+
else {
|
|
234
|
+
if (rest.length === 0 || rest.some((a) => !/^(?:[1-9]|1[0-3])$/.test(a))) {
|
|
235
|
+
error(USAGE, ERROR_REASON.USAGE);
|
|
236
|
+
return; // unreachable
|
|
237
|
+
}
|
|
238
|
+
numbers = [...new Set(rest.map(Number))];
|
|
239
|
+
}
|
|
240
|
+
const result = checkProjectSessions(cwd, numbers);
|
|
241
|
+
const payload = {
|
|
242
|
+
ok: result.ok,
|
|
243
|
+
sessions: result.sessions.map((s) => ({ session: s.session, file: s.file, required: s.required, filled: s.filled, empty: s.empty })),
|
|
244
|
+
};
|
|
245
|
+
if (!result.ok) {
|
|
246
|
+
const violations = result.sessions.flatMap((s) => s.violations);
|
|
247
|
+
output({ ...payload, violations: violations.length }, raw);
|
|
248
|
+
error(`discovery session-check: ${violations.length} violation(s):\n` + violations.map(formatViolation).join('\n'), ERROR_REASON.DISCOVERY_SESSION_CHECK_FAILED);
|
|
249
|
+
return; // unreachable
|
|
250
|
+
}
|
|
251
|
+
output(payload, raw);
|
|
252
|
+
}
|
|
253
|
+
module.exports = {
|
|
254
|
+
NONE_LITERAL,
|
|
255
|
+
PARKED_LITERAL,
|
|
256
|
+
SESSIONS_DIR,
|
|
257
|
+
SESSION_CHECK_FAILED: ERROR_REASON.DISCOVERY_SESSION_CHECK_FAILED,
|
|
258
|
+
requiredSectionsOf,
|
|
259
|
+
checkSession,
|
|
260
|
+
checkProjectSession,
|
|
261
|
+
checkProjectSessions,
|
|
262
|
+
completedSessions,
|
|
263
|
+
cmdDiscoverySessionCheck,
|
|
264
|
+
};
|