@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,276 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Discovery access-model check (Discovery Sprint improvement plan, Batch 3).
|
|
4
|
+
*
|
|
5
|
+
* `.planning/discovery/_roles.md` is the register of roles and what each may
|
|
6
|
+
* do — Session 7 writes it in place of the old preset RBAC matrix. Three
|
|
7
|
+
* tables: `## Roles` (the role, what it is, its data scope, how it is
|
|
8
|
+
* recovered when its last holder leaves — the vault's role-recovery rule —
|
|
9
|
+
* and its source), `## Permissions` (role × operation × entity, with the
|
|
10
|
+
* condition that narrows it and the requirements it realises; one row per
|
|
11
|
+
* role-and-entity, operations as a list — never CRUD columns) and
|
|
12
|
+
* `## Screens` (which role may open which `SCR-` screen, the join the
|
|
13
|
+
* forbidden-path tests are written from). The build's planner reads the
|
|
14
|
+
* permissions by requirement ID. This module checks the structure:
|
|
15
|
+
*
|
|
16
|
+
* - the three sections exist with the template's columns;
|
|
17
|
+
* - every role is a Session 2 persona, an actor a step matrix names, or
|
|
18
|
+
* `System`, when those sources are on file (a role nobody plays is a
|
|
19
|
+
* role the build cannot test), and is listed once;
|
|
20
|
+
* - every permission and screen row names a role of `## Roles`;
|
|
21
|
+
* - every entity resolves against `_entities.md`, every screen against
|
|
22
|
+
* `_screens.md`, every requirement against `_requirements.md`, when
|
|
23
|
+
* those artifacts are on file and parseable;
|
|
24
|
+
* - operations are a comma list of lowercase verbs.
|
|
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
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
42
|
+
const processes = require("./discovery-processes.cjs");
|
|
43
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
44
|
+
const DISCOVERY_DIR = '.planning/discovery';
|
|
45
|
+
const ROLES_FILE = `${DISCOVERY_DIR}/_roles.md`;
|
|
46
|
+
const REQUIREMENTS_FILE = `${DISCOVERY_DIR}/_requirements.md`;
|
|
47
|
+
const ENTITIES_FILE = `${DISCOVERY_DIR}/_entities.md`;
|
|
48
|
+
const PROCESSES_FILE = `${DISCOVERY_DIR}/_processes.md`;
|
|
49
|
+
const SCREENS_FILE = `${DISCOVERY_DIR}/_screens.md`;
|
|
50
|
+
const SESSION_02_FILE = `${DISCOVERY_DIR}/session-02.md`;
|
|
51
|
+
const ROLES_SECTION = 'Roles';
|
|
52
|
+
const PERMISSIONS_SECTION = 'Permissions';
|
|
53
|
+
const SCREENS_SECTION = 'Screens';
|
|
54
|
+
const ROLES_HEADER = Object.freeze(['Role', 'Description', 'Scope', 'Recovery path', 'Source']);
|
|
55
|
+
const PERMISSIONS_HEADER = Object.freeze(['Role', 'Entity', 'Operation', 'Condition', 'Requirements']);
|
|
56
|
+
const SCREENS_HEADER = Object.freeze(['Role', 'Screen']);
|
|
57
|
+
const SYSTEM_ACTOR = 'System';
|
|
58
|
+
const ENTITY_NAME_RE = /^[A-Z][A-Za-z0-9]*$/;
|
|
59
|
+
const OPERATION_RE = /^[a-z][a-z-]*$/;
|
|
60
|
+
const SCREEN_ID_RE = /^SCR-\d{2}$/;
|
|
61
|
+
const REQUIREMENT_ID_RE = /^[A-Z][A-Z0-9]*-\d+$/;
|
|
62
|
+
const STEPS_ACTOR_COLUMN = 'Actor';
|
|
63
|
+
const USAGE = 'Usage: pp-tools discovery roles-check';
|
|
64
|
+
function readIfPresent(abs) {
|
|
65
|
+
if (!node_fs_1.default.existsSync(abs))
|
|
66
|
+
return { kind: 'absent' };
|
|
67
|
+
try {
|
|
68
|
+
return { kind: 'ok', content: node_fs_1.default.readFileSync(abs, 'utf-8') };
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return { kind: 'unreadable' };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function sameHeader(actual, expected) {
|
|
75
|
+
return actual.length === expected.length && actual.every((c, i) => c.trim().toLowerCase() === expected[i].toLowerCase());
|
|
76
|
+
}
|
|
77
|
+
function splitList(cell) {
|
|
78
|
+
return cell.split(',').map((t) => t.trim()).filter(Boolean);
|
|
79
|
+
}
|
|
80
|
+
/** First-column values of the first table under a `## <heading>` of another artifact. */
|
|
81
|
+
function firstColumnOf(content, heading) {
|
|
82
|
+
const section = table.splitSections(content.split(/\r?\n/), 2).find((s) => s.heading === heading);
|
|
83
|
+
if (!section)
|
|
84
|
+
return new Set();
|
|
85
|
+
const tables = table.parseTables(section.body);
|
|
86
|
+
if (tables.length === 0)
|
|
87
|
+
return new Set();
|
|
88
|
+
return new Set(tables[0].rows.map((r) => (r.cells[0] ?? '').trim()).filter(Boolean));
|
|
89
|
+
}
|
|
90
|
+
/** Every actor a step matrix of `_processes.md` § Steps names, by the `Actor` column. */
|
|
91
|
+
function stepActors(content) {
|
|
92
|
+
const out = new Set();
|
|
93
|
+
const steps = table.splitSections(content.split(/\r?\n/), 2).find((s) => s.heading === 'Steps');
|
|
94
|
+
if (!steps)
|
|
95
|
+
return out;
|
|
96
|
+
for (const t of table.parseTables(steps.body)) {
|
|
97
|
+
const col = t.header.findIndex((h) => h.trim().toLowerCase() === STEPS_ACTOR_COLUMN.toLowerCase());
|
|
98
|
+
if (col === -1)
|
|
99
|
+
continue;
|
|
100
|
+
for (const r of t.rows)
|
|
101
|
+
for (const a of splitList(r.cells[col] ?? ''))
|
|
102
|
+
out.add(a);
|
|
103
|
+
}
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
/** The first table under `## <heading>`, header-checked; faults are pushed and null returned. */
|
|
107
|
+
function sectionTable(content, heading, expected, violations) {
|
|
108
|
+
const section = table.splitSections(content.split(/\r?\n/), 2).find((s) => s.heading === heading);
|
|
109
|
+
if (!section) {
|
|
110
|
+
violations.push({ artifact: ROLES_FILE, reason: 'missing_section', id: heading });
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
const tables = table.parseTables(section.body);
|
|
114
|
+
if (tables.length === 0) {
|
|
115
|
+
violations.push({ artifact: ROLES_FILE, reason: 'missing_section', line: section.line, id: heading });
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
if (!sameHeader(tables[0].header, expected)) {
|
|
119
|
+
violations.push({ artifact: ROLES_FILE, reason: 'bad_table_shape', line: tables[0].line, id: heading });
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
return tables[0];
|
|
123
|
+
}
|
|
124
|
+
// ─── Check ────────────────────────────────────────────────────────────────────
|
|
125
|
+
function checkRoles(content, sources) {
|
|
126
|
+
const violations = [];
|
|
127
|
+
const roles = sectionTable(content, ROLES_SECTION, ROLES_HEADER, violations);
|
|
128
|
+
const permissions = sectionTable(content, PERMISSIONS_SECTION, PERMISSIONS_HEADER, violations);
|
|
129
|
+
const screens = sectionTable(content, SCREENS_SECTION, SCREENS_HEADER, violations);
|
|
130
|
+
const known = new Set();
|
|
131
|
+
if (roles) {
|
|
132
|
+
for (const row of roles.rows) {
|
|
133
|
+
const [role, description, scope, recovery] = row.cells.map((c) => c.trim());
|
|
134
|
+
if ((role ?? '') === '') {
|
|
135
|
+
violations.push({ artifact: ROLES_FILE, reason: 'empty_cell', line: row.line, id: 'Role' });
|
|
136
|
+
}
|
|
137
|
+
else if (known.has(role)) {
|
|
138
|
+
violations.push({ artifact: ROLES_FILE, reason: 'duplicate_role', line: row.line, id: role.slice(0, 40) });
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
known.add(role);
|
|
142
|
+
if (sources.actors !== null && role !== SYSTEM_ACTOR && !sources.actors.has(role))
|
|
143
|
+
violations.push({ artifact: ROLES_FILE, reason: 'unknown_role', line: row.line, id: role.slice(0, 40) });
|
|
144
|
+
}
|
|
145
|
+
if ((description ?? '') === '')
|
|
146
|
+
violations.push({ artifact: ROLES_FILE, reason: 'empty_cell', line: row.line, id: 'Description' });
|
|
147
|
+
if ((scope ?? '') === '')
|
|
148
|
+
violations.push({ artifact: ROLES_FILE, reason: 'empty_cell', line: row.line, id: 'Scope' });
|
|
149
|
+
if ((recovery ?? '') === '')
|
|
150
|
+
violations.push({ artifact: ROLES_FILE, reason: 'empty_cell', line: row.line, id: 'Recovery path' });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const roleRef = (role, line) => {
|
|
154
|
+
if (role === '')
|
|
155
|
+
violations.push({ artifact: ROLES_FILE, reason: 'empty_cell', line, id: 'Role' });
|
|
156
|
+
else if (roles && !known.has(role))
|
|
157
|
+
violations.push({ artifact: ROLES_FILE, reason: 'unknown_role', line, id: role.slice(0, 40) });
|
|
158
|
+
};
|
|
159
|
+
if (permissions) {
|
|
160
|
+
for (const row of permissions.rows) {
|
|
161
|
+
const [role, entity, operation, , requirements] = row.cells.map((c) => c.trim());
|
|
162
|
+
roleRef(role ?? '', row.line);
|
|
163
|
+
if (!ENTITY_NAME_RE.test(entity ?? ''))
|
|
164
|
+
violations.push({ artifact: ROLES_FILE, reason: 'bad_entity', line: row.line });
|
|
165
|
+
else if (sources.entities !== null && !sources.entities.has(entity))
|
|
166
|
+
violations.push({ artifact: ROLES_FILE, reason: 'unknown_entity', line: row.line, id: entity });
|
|
167
|
+
const ops = splitList(operation ?? '');
|
|
168
|
+
if (ops.length === 0 || ops.some((o) => !OPERATION_RE.test(o)))
|
|
169
|
+
violations.push({ artifact: ROLES_FILE, reason: 'bad_operation', line: row.line });
|
|
170
|
+
const reqs = splitList(requirements ?? '');
|
|
171
|
+
if (reqs.length === 0)
|
|
172
|
+
violations.push({ artifact: ROLES_FILE, reason: 'no_requirement', line: row.line });
|
|
173
|
+
for (const r of reqs) {
|
|
174
|
+
if (!REQUIREMENT_ID_RE.test(r))
|
|
175
|
+
violations.push({ artifact: ROLES_FILE, reason: 'unknown_requirement_id', line: row.line, id: r.slice(0, 40) });
|
|
176
|
+
else if (sources.requirementIds instanceof Set && !sources.requirementIds.has(r))
|
|
177
|
+
violations.push({ artifact: ROLES_FILE, reason: 'unknown_requirement_id', line: row.line, id: r });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (screens) {
|
|
182
|
+
for (const row of screens.rows) {
|
|
183
|
+
const [role, screen] = row.cells.map((c) => c.trim());
|
|
184
|
+
roleRef(role ?? '', row.line);
|
|
185
|
+
if (!SCREEN_ID_RE.test(screen ?? ''))
|
|
186
|
+
violations.push({ artifact: ROLES_FILE, reason: 'bad_screen', line: row.line });
|
|
187
|
+
else if (sources.screens !== null && !sources.screens.has(screen))
|
|
188
|
+
violations.push({ artifact: ROLES_FILE, reason: 'unknown_screen', line: row.line, id: screen });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (sources.requirementIds === 'unparseable')
|
|
192
|
+
violations.push({ artifact: REQUIREMENTS_FILE, reason: 'requirements_unparseable' });
|
|
193
|
+
return {
|
|
194
|
+
ok: violations.length === 0,
|
|
195
|
+
roles: known.size,
|
|
196
|
+
permissions: permissions ? permissions.rows.length : 0,
|
|
197
|
+
screens: screens ? screens.rows.length : 0,
|
|
198
|
+
violations,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function checkProjectRoles(cwd) {
|
|
202
|
+
const read = readIfPresent(node_path_1.default.join(cwd, ROLES_FILE));
|
|
203
|
+
if (read.kind === 'absent')
|
|
204
|
+
return null;
|
|
205
|
+
if (read.kind === 'unreadable')
|
|
206
|
+
return { ok: false, file: ROLES_FILE, roles: 0, permissions: 0, screens: 0, violations: [{ artifact: ROLES_FILE, reason: 'artifact_unreadable' }] };
|
|
207
|
+
const extra = [];
|
|
208
|
+
const sources = { actors: null, entities: null, screens: null, requirementIds: null };
|
|
209
|
+
const s2 = readIfPresent(node_path_1.default.join(cwd, SESSION_02_FILE));
|
|
210
|
+
if (s2.kind === 'unreadable')
|
|
211
|
+
extra.push({ artifact: SESSION_02_FILE, reason: 'artifact_unreadable' });
|
|
212
|
+
const personas = s2.kind === 'ok' ? processes.personasFrom(s2.content) : null;
|
|
213
|
+
const proc = readIfPresent(node_path_1.default.join(cwd, PROCESSES_FILE));
|
|
214
|
+
if (proc.kind === 'unreadable')
|
|
215
|
+
extra.push({ artifact: PROCESSES_FILE, reason: 'artifact_unreadable' });
|
|
216
|
+
const actors = proc.kind === 'ok' ? stepActors(proc.content) : null;
|
|
217
|
+
if (personas !== null || actors !== null)
|
|
218
|
+
sources.actors = new Set([...(personas ?? []), ...(actors ?? [])]);
|
|
219
|
+
const ent = readIfPresent(node_path_1.default.join(cwd, ENTITIES_FILE));
|
|
220
|
+
if (ent.kind === 'unreadable')
|
|
221
|
+
extra.push({ artifact: ENTITIES_FILE, reason: 'artifact_unreadable' });
|
|
222
|
+
if (ent.kind === 'ok')
|
|
223
|
+
sources.entities = firstColumnOf(ent.content, 'Entities');
|
|
224
|
+
const scr = readIfPresent(node_path_1.default.join(cwd, SCREENS_FILE));
|
|
225
|
+
if (scr.kind === 'unreadable')
|
|
226
|
+
extra.push({ artifact: SCREENS_FILE, reason: 'artifact_unreadable' });
|
|
227
|
+
if (scr.kind === 'ok')
|
|
228
|
+
sources.screens = firstColumnOf(scr.content, 'Screens');
|
|
229
|
+
const req = readIfPresent(node_path_1.default.join(cwd, REQUIREMENTS_FILE));
|
|
230
|
+
if (req.kind === 'unreadable')
|
|
231
|
+
extra.push({ artifact: REQUIREMENTS_FILE, reason: 'artifact_unreadable' });
|
|
232
|
+
if (req.kind === 'ok') {
|
|
233
|
+
const parsed = rollup.parseRequirementsArtifact(req.content);
|
|
234
|
+
sources.requirementIds = parsed.ok ? new Set(parsed.rows.map((r) => r.id)) : 'unparseable';
|
|
235
|
+
}
|
|
236
|
+
const result = checkRoles(read.content, sources);
|
|
237
|
+
result.violations.push(...extra);
|
|
238
|
+
result.ok = result.violations.length === 0;
|
|
239
|
+
return { file: ROLES_FILE, ...result };
|
|
240
|
+
}
|
|
241
|
+
function formatViolation(v) {
|
|
242
|
+
const where = v.line !== undefined ? `:${v.line}` : '';
|
|
243
|
+
const what = v.id !== undefined ? ` (${v.id})` : '';
|
|
244
|
+
return ` - ${v.artifact}${where}: ${v.reason}${what}`;
|
|
245
|
+
}
|
|
246
|
+
// ─── CLI adapter ──────────────────────────────────────────────────────────────
|
|
247
|
+
/** `pp-tools discovery roles-check` (dotted `query discovery.roles-check`). Absent artifact → exit 0, `present: false`. */
|
|
248
|
+
function cmdDiscoveryRolesCheck(cwd, args, raw) {
|
|
249
|
+
if (args.length > 2) {
|
|
250
|
+
error(USAGE, ERROR_REASON.USAGE);
|
|
251
|
+
return; // unreachable
|
|
252
|
+
}
|
|
253
|
+
const result = checkProjectRoles(cwd);
|
|
254
|
+
if (result === null) {
|
|
255
|
+
output({ ok: true, present: false, file: ROLES_FILE }, raw);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
const payload = { ok: result.ok, present: true, file: result.file, roles: result.roles, permissions: result.permissions, screens: result.screens };
|
|
259
|
+
if (!result.ok) {
|
|
260
|
+
output({ ...payload, violations: result.violations.length }, raw);
|
|
261
|
+
error(`discovery roles-check: ${result.violations.length} violation(s):\n` + result.violations.map(formatViolation).join('\n'), ERROR_REASON.DISCOVERY_ROLES_CHECK_FAILED);
|
|
262
|
+
return; // unreachable
|
|
263
|
+
}
|
|
264
|
+
output(payload, raw);
|
|
265
|
+
}
|
|
266
|
+
module.exports = {
|
|
267
|
+
ROLES_FILE,
|
|
268
|
+
ROLES_HEADER,
|
|
269
|
+
PERMISSIONS_HEADER,
|
|
270
|
+
SCREENS_HEADER,
|
|
271
|
+
ROLES_CHECK_FAILED: ERROR_REASON.DISCOVERY_ROLES_CHECK_FAILED,
|
|
272
|
+
stepActors,
|
|
273
|
+
checkRoles,
|
|
274
|
+
checkProjectRoles,
|
|
275
|
+
cmdDiscoveryRolesCheck,
|
|
276
|
+
};
|