@transitrix/diagrams 1.9.18 → 1.11.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/dist/activities/resolver.d.ts +3 -0
- package/dist/activities/resolver.js +39 -18
- package/dist/activities/validate.js +59 -0
- package/dist/canon-resolver-utils.d.ts +3 -0
- package/dist/canon-resolver-utils.js +16 -0
- package/dist/codex/index.d.ts +1 -1
- package/dist/codex/index.js +1 -1
- package/dist/codex/types.d.ts +9 -2
- package/dist/codex/types.js +10 -3
- package/dist/codex/validate.js +29 -3
- package/dist/compliance/classify.d.ts +11 -1
- package/dist/compliance/classify.js +31 -17
- package/dist/compliance/impact.d.ts +3 -1
- package/dist/compliance/impact.js +41 -13
- package/dist/compliance/types.d.ts +6 -0
- package/dist/compliance-matrix/build.js +0 -0
- package/dist/compliance-verification-matrix/build.d.ts +17 -0
- package/dist/compliance-verification-matrix/build.js +65 -0
- package/dist/compliance-verification-matrix/csv.d.ts +6 -0
- package/dist/compliance-verification-matrix/csv.js +41 -0
- package/dist/compliance-verification-matrix/index.d.ts +3 -0
- package/dist/compliance-verification-matrix/index.js +2 -0
- package/dist/compliance-verification-matrix/types.d.ts +75 -0
- package/dist/compliance-verification-matrix/types.js +4 -0
- package/dist/fgca/action-change-ids.d.ts +7 -0
- package/dist/fgca/action-change-ids.js +11 -0
- package/dist/fgca/parse-canonical.d.ts +3 -1
- package/dist/fgca/parse-canonical.js +13 -8
- package/dist/fgca/resolver.d.ts +6 -2
- package/dist/fgca/resolver.js +12 -6
- package/dist/fgca/validate.d.ts +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/process-blueprint/index.d.ts +3 -1
- package/dist/process-blueprint/index.js +1 -0
- package/dist/process-blueprint/layout.js +33 -29
- package/dist/process-blueprint/resolve-columns.d.ts +52 -0
- package/dist/process-blueprint/resolve-columns.js +138 -0
- package/dist/process-blueprint/types.d.ts +23 -5
- package/dist/process-blueprint/validate.d.ts +19 -1
- package/dist/process-blueprint/validate.js +73 -21
- package/dist/repo-validate/check-candidate-fields.d.ts +6 -0
- package/dist/repo-validate/check-candidate-fields.js +87 -0
- package/dist/repo-validate/check-process-parent.d.ts +8 -0
- package/dist/repo-validate/check-process-parent.js +172 -0
- package/dist/repo-validate/index.d.ts +1 -0
- package/dist/repo-validate/index.js +1 -0
- package/dist/repo-validate/validate-repo.js +7 -0
- package/dist/requirement/types.d.ts +1 -1
- package/dist/requirement/types.js +1 -1
- package/dist/webview/entry.js +11 -4
- package/dist/webview/render-fgca.js +1 -1
- package/dist/webview/render-process.d.ts +19 -1
- package/dist/webview/render-process.js +75 -42
- package/package.json +1 -1
- package/src/activities/resolver.ts +40 -20
- package/src/activities/validate.ts +58 -0
- package/src/canon-resolver-utils.ts +19 -0
- package/src/codex/index.ts +1 -1
- package/src/codex/types.ts +11 -3
- package/src/codex/validate.ts +30 -2
- package/src/compliance/classify.ts +40 -17
- package/src/compliance/impact.ts +41 -12
- package/src/compliance/types.ts +6 -0
- package/src/compliance-matrix/build.ts +0 -0
- package/src/compliance-verification-matrix/build.ts +84 -0
- package/src/compliance-verification-matrix/csv.ts +47 -0
- package/src/compliance-verification-matrix/index.ts +7 -0
- package/src/compliance-verification-matrix/types.ts +85 -0
- package/src/fgca/action-change-ids.ts +13 -0
- package/src/fgca/parse-canonical.ts +12 -8
- package/src/fgca/resolver.ts +15 -6
- package/src/fgca/validate.ts +2 -0
- package/src/index.ts +1 -0
- package/src/process-blueprint/index.ts +11 -0
- package/src/process-blueprint/layout.ts +48 -29
- package/src/process-blueprint/resolve-columns.ts +147 -0
- package/src/process-blueprint/types.ts +19 -5
- package/src/process-blueprint/validate.ts +107 -21
- package/src/repo-validate/check-candidate-fields.ts +93 -0
- package/src/repo-validate/check-process-parent.ts +184 -0
- package/src/repo-validate/index.ts +5 -0
- package/src/repo-validate/validate-repo.ts +7 -0
- package/src/requirement/types.ts +1 -1
- package/src/webview/entry.ts +13 -6
- package/src/webview/render-fgca.ts +1 -1
- package/src/webview/render-process.ts +94 -49
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Requirement -> verification matrix builder.
|
|
2
|
+
import { buildGapReport } from '../compliance/gap-report.js';
|
|
3
|
+
function byId(a, b) {
|
|
4
|
+
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Builds the repository-wide requirement -> verification matrix.
|
|
8
|
+
*
|
|
9
|
+
* Pure projection over an already-built `ComplianceIndex`
|
|
10
|
+
* (`compliance/reverse-index.ts`) — no I/O, no revalidation. Reuses
|
|
11
|
+
* `buildGapReport`'s `REQ-VERIF-COVERAGE-001`/`-002` verdicts verbatim rather
|
|
12
|
+
* than recomputing a competing coverage status.
|
|
13
|
+
*
|
|
14
|
+
* Ordering: requirements sorted by id; within a requirement, its verification
|
|
15
|
+
* rows sorted by verification id. Both are explicit sorts over the index's
|
|
16
|
+
* values, so the result is independent of the Map's insertion order (which —
|
|
17
|
+
* for a caller that built the index from a filesystem walk — would otherwise
|
|
18
|
+
* track directory enumeration order).
|
|
19
|
+
*/
|
|
20
|
+
export function buildRequirementVerificationMatrix(index, options = {}) {
|
|
21
|
+
const gapReport = buildGapReport(index, options);
|
|
22
|
+
const noVerification = new Set(gapReport.requirementsWithoutVerification.map(r => r.id));
|
|
23
|
+
const unresolvedVerification = new Set(gapReport.requirementsWithUnresolvedVerification.map(r => r.id));
|
|
24
|
+
const requirements = [...index.requirementById.values()].sort(byId);
|
|
25
|
+
const rows = [];
|
|
26
|
+
let verificationRowCount = 0;
|
|
27
|
+
let gapRequirementCount = 0;
|
|
28
|
+
for (const requirement of requirements) {
|
|
29
|
+
const parentId = requirement.parent;
|
|
30
|
+
const parentLabel = parentId ? index.requirementById.get(parentId)?.name : undefined;
|
|
31
|
+
const base = {
|
|
32
|
+
requirementId: requirement.id,
|
|
33
|
+
requirementLabel: requirement.name,
|
|
34
|
+
...(parentId !== undefined ? { parentId } : {}),
|
|
35
|
+
...(parentLabel !== undefined ? { parentLabel } : {}),
|
|
36
|
+
};
|
|
37
|
+
if (noVerification.has(requirement.id)) {
|
|
38
|
+
gapRequirementCount++;
|
|
39
|
+
rows.push({ ...base, coverageGap: 'REQ-VERIF-COVERAGE-001' });
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const gap = unresolvedVerification.has(requirement.id) ? 'REQ-VERIF-COVERAGE-002' : undefined;
|
|
43
|
+
if (gap)
|
|
44
|
+
gapRequirementCount++;
|
|
45
|
+
const verifications = [...(index.verificationsByRequirement.get(requirement.id) ?? [])].sort(byId);
|
|
46
|
+
for (const verification of verifications) {
|
|
47
|
+
verificationRowCount++;
|
|
48
|
+
rows.push({
|
|
49
|
+
...base,
|
|
50
|
+
verificationId: verification.id,
|
|
51
|
+
verificationLabel: verification.protocol ?? verification.id,
|
|
52
|
+
verificationOutcome: verification.outcome,
|
|
53
|
+
...(gap ? { coverageGap: gap } : {}),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
rows,
|
|
59
|
+
summary: {
|
|
60
|
+
requirements: requirements.length,
|
|
61
|
+
verifications: verificationRowCount,
|
|
62
|
+
gaps: gapRequirementCount,
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { RequirementVerificationMatrix } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Serialise the matrix to CSV. Identical input yields byte-identical output;
|
|
4
|
+
* the row order is the builder's stable order.
|
|
5
|
+
*/
|
|
6
|
+
export declare function renderRequirementVerificationMatrixCsv(matrix: RequirementVerificationMatrix): string;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Deterministic CSV serialiser for the requirement-verification matrix.
|
|
2
|
+
// Same row order as `buildRequirementVerificationMatrix` (requirement id,
|
|
3
|
+
// then verification id). CRLF line endings, RFC-4180 quoting — the same
|
|
4
|
+
// convention as the Gap Dashboard export.
|
|
5
|
+
const HEADER = [
|
|
6
|
+
'requirement_id',
|
|
7
|
+
'requirement_label',
|
|
8
|
+
'parent_id',
|
|
9
|
+
'parent_label',
|
|
10
|
+
'related_test_result_id',
|
|
11
|
+
'related_test_result_label',
|
|
12
|
+
'outcome',
|
|
13
|
+
'coverage_gap',
|
|
14
|
+
];
|
|
15
|
+
function csvCell(value) {
|
|
16
|
+
const v = value ?? '';
|
|
17
|
+
return `"${v.replace(/"/g, '""')}"`;
|
|
18
|
+
}
|
|
19
|
+
function rowCells(row) {
|
|
20
|
+
return [
|
|
21
|
+
row.requirementId,
|
|
22
|
+
row.requirementLabel,
|
|
23
|
+
row.parentId ?? '',
|
|
24
|
+
row.parentLabel ?? '',
|
|
25
|
+
row.verificationId ?? '',
|
|
26
|
+
row.verificationLabel ?? '',
|
|
27
|
+
row.verificationOutcome ?? '',
|
|
28
|
+
row.coverageGap ?? '',
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Serialise the matrix to CSV. Identical input yields byte-identical output;
|
|
33
|
+
* the row order is the builder's stable order.
|
|
34
|
+
*/
|
|
35
|
+
export function renderRequirementVerificationMatrixCsv(matrix) {
|
|
36
|
+
const lines = [HEADER.map(h => csvCell(h)).join(',')];
|
|
37
|
+
for (const row of matrix.rows) {
|
|
38
|
+
lines.push(rowCells(row).map(csvCell).join(','));
|
|
39
|
+
}
|
|
40
|
+
return lines.join('\r\n') + '\r\n';
|
|
41
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { VerificationOutcome } from '../verification/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* One row of the requirement-verification matrix.
|
|
4
|
+
*
|
|
5
|
+
* A requirement with N admitted VERIFICATION records produces N rows, one per
|
|
6
|
+
* verification. A requirement with none still produces exactly one row,
|
|
7
|
+
* carrying the `REQ-VERIF-COVERAGE-001` gap. A requirement whose
|
|
8
|
+
* verifications exist but never closed (pass/fail) produces one row per
|
|
9
|
+
* verification — each carrying the `REQ-VERIF-COVERAGE-002` gap alongside its
|
|
10
|
+
* (still-open) outcome; the open verification is real data and is never
|
|
11
|
+
* replaced by a placeholder gap row.
|
|
12
|
+
*
|
|
13
|
+
* Coverage comes only from `VERIFICATION.verifies` (27-verification.md §2). A
|
|
14
|
+
* child requirement's verification never covers its `parent`
|
|
15
|
+
* (15-requirement.md §2.4 — `parent` records structure only, no roll-up).
|
|
16
|
+
*/
|
|
17
|
+
export interface RequirementVerificationRow {
|
|
18
|
+
requirementId: string;
|
|
19
|
+
requirementLabel: string;
|
|
20
|
+
/**
|
|
21
|
+
* Direct `REQUIREMENT.parent` id, verbatim, present whenever the field is
|
|
22
|
+
* set — whether or not it resolves. `parent` resolution is not currently a
|
|
23
|
+
* validator concern (15-requirement.md §2.4); a dangling parent must still
|
|
24
|
+
* be visible on the row, not silently dropped.
|
|
25
|
+
*/
|
|
26
|
+
parentId?: string;
|
|
27
|
+
/**
|
|
28
|
+
* The resolved parent's `name`, present only when `parentId` names an
|
|
29
|
+
* admitted requirement in the index. Absent when `parentId` is set but
|
|
30
|
+
* unresolved (dangling), or when the requirement has no parent.
|
|
31
|
+
*/
|
|
32
|
+
parentLabel?: string;
|
|
33
|
+
/** The verification filling this row, when one exists. */
|
|
34
|
+
verificationId?: string;
|
|
35
|
+
/**
|
|
36
|
+
* VERIFICATION carries no `name`/label field of its own
|
|
37
|
+
* (27-verification.md §2); `protocol` — the procedure description — is the
|
|
38
|
+
* closest human-readable text and is what this row shows as its label.
|
|
39
|
+
*/
|
|
40
|
+
verificationLabel?: string;
|
|
41
|
+
verificationOutcome?: VerificationOutcome;
|
|
42
|
+
/**
|
|
43
|
+
* `REQ-VERIF-COVERAGE-001` / `-002` (`compliance/gap-report.ts`), reused
|
|
44
|
+
* verbatim — this builder never recomputes a competing coverage verdict.
|
|
45
|
+
* Absent once the requirement has at least one closed (pass/fail)
|
|
46
|
+
* verification.
|
|
47
|
+
*/
|
|
48
|
+
coverageGap?: 'REQ-VERIF-COVERAGE-001' | 'REQ-VERIF-COVERAGE-002';
|
|
49
|
+
}
|
|
50
|
+
export interface RequirementVerificationMatrixOptions {
|
|
51
|
+
/**
|
|
52
|
+
* Forwarded to `buildGapReport`. Neither coverage gap this matrix surfaces
|
|
53
|
+
* (`REQ-VERIF-COVERAGE-001`/`-002`) depends on `today` — accepted only for
|
|
54
|
+
* call-site symmetry with the other compliance-view builders.
|
|
55
|
+
*/
|
|
56
|
+
today?: string;
|
|
57
|
+
}
|
|
58
|
+
export interface RequirementVerificationMatrix {
|
|
59
|
+
/**
|
|
60
|
+
* One row per (requirement, verification) pair, plus one gap row per
|
|
61
|
+
* requirement with zero verifications.
|
|
62
|
+
*
|
|
63
|
+
* Ordering (stable, independent of filesystem/Map enumeration order):
|
|
64
|
+
* requirements sorted by id; within a requirement, its verification rows
|
|
65
|
+
* sorted by verification id.
|
|
66
|
+
*/
|
|
67
|
+
rows: RequirementVerificationRow[];
|
|
68
|
+
summary: {
|
|
69
|
+
requirements: number;
|
|
70
|
+
/** Rows carrying a real verification (excludes -001 gap-only rows). */
|
|
71
|
+
verifications: number;
|
|
72
|
+
/** Requirements carrying a REQ-VERIF-COVERAGE-001 or -002 gap. */
|
|
73
|
+
gaps: number;
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Action → Change linkage on an element record.
|
|
3
|
+
*
|
|
4
|
+
* Canonical field is `delivers_changes` (Action element spec). `changes` is
|
|
5
|
+
* the pre-rename alias still present on older fixtures and inline docs.
|
|
6
|
+
*/
|
|
7
|
+
export declare function actionChangeLinkField(el: Record<string, unknown>): 'delivers_changes' | 'changes';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Action → Change linkage on an element record.
|
|
3
|
+
*
|
|
4
|
+
* Canonical field is `delivers_changes` (Action element spec). `changes` is
|
|
5
|
+
* the pre-rename alias still present on older fixtures and inline docs.
|
|
6
|
+
*/
|
|
7
|
+
export function actionChangeLinkField(el) {
|
|
8
|
+
return Object.prototype.hasOwnProperty.call(el, 'delivers_changes')
|
|
9
|
+
? 'delivers_changes'
|
|
10
|
+
: 'changes';
|
|
11
|
+
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* - Typed string IDs per `IDS_AND_REFERENCES.md` (`FACTOR-1`,
|
|
9
9
|
* `GOAL-RET-1`, `CHANGE-1`, `ACTION-ONBOARD-1` (legacy `ACTIVITY-*` accepted).
|
|
10
10
|
* - Plural canonical-direction cross-references: `goal.factors`,
|
|
11
|
-
* `change.goals`, `
|
|
11
|
+
* `change.goals`, `action.delivers_changes`, `action.goals`.
|
|
12
12
|
* - Per-notation validation codes `FGCA-001 … FGCA-015`.
|
|
13
13
|
*
|
|
14
14
|
* Returns the validation result plus, on success, an internal `FGCADoc`
|
|
@@ -29,6 +29,8 @@ export interface CanonicalFGCAResult extends ValidationResult {
|
|
|
29
29
|
/** On success, the internal-form FGCADoc derived from the canonical input. */
|
|
30
30
|
parsed?: FGCADoc;
|
|
31
31
|
}
|
|
32
|
+
/** DGA mode (`view_config.layers.changes: off`) omits the Changes column. */
|
|
33
|
+
export declare function isDgcaChangesLayerOff(input: unknown): boolean;
|
|
32
34
|
/**
|
|
33
35
|
* Validate canonical FGCA YAML and, on success, return an internal-form
|
|
34
36
|
* `FGCADoc` ready for `buildFGCALayout`.
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* - Typed string IDs per `IDS_AND_REFERENCES.md` (`FACTOR-1`,
|
|
9
9
|
* `GOAL-RET-1`, `CHANGE-1`, `ACTION-ONBOARD-1` (legacy `ACTIVITY-*` accepted).
|
|
10
10
|
* - Plural canonical-direction cross-references: `goal.factors`,
|
|
11
|
-
* `change.goals`, `
|
|
11
|
+
* `change.goals`, `action.delivers_changes`, `action.goals`.
|
|
12
12
|
* - Per-notation validation codes `FGCA-001 … FGCA-015`.
|
|
13
13
|
*
|
|
14
14
|
* Returns the validation result plus, on success, an internal `FGCADoc`
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* unify the internal types with the canonical form across all sister
|
|
24
24
|
* modules.
|
|
25
25
|
*/
|
|
26
|
+
import { actionChangeLinkField } from './action-change-ids.js';
|
|
26
27
|
// Canonical ID grammars per IDS_AND_REFERENCES.md §1. The TYPE prefix
|
|
27
28
|
// is fixed per layer; middle segments are optional; the terminal is a
|
|
28
29
|
// positive integer with no leading zeros.
|
|
@@ -43,9 +44,11 @@ function asObjectArray(v) {
|
|
|
43
44
|
return null;
|
|
44
45
|
return v.map((el) => (el && typeof el === 'object' ? el : null));
|
|
45
46
|
}
|
|
46
|
-
/** DGA mode (`view_config.layers.changes: off`)
|
|
47
|
-
function
|
|
48
|
-
|
|
47
|
+
/** DGA mode (`view_config.layers.changes: off`) omits the Changes column. */
|
|
48
|
+
export function isDgcaChangesLayerOff(input) {
|
|
49
|
+
if (!input || typeof input !== 'object')
|
|
50
|
+
return false;
|
|
51
|
+
const vc = input['view_config'];
|
|
49
52
|
if (!vc || typeof vc !== 'object')
|
|
50
53
|
return false;
|
|
51
54
|
const layers = vc['layers'];
|
|
@@ -99,7 +102,7 @@ export function parseCanonicalFGCA(input) {
|
|
|
99
102
|
const factorsRaw = asObjectArray(raw['factors']);
|
|
100
103
|
const goalsRaw = asObjectArray(raw['goals']);
|
|
101
104
|
let changesRaw = asObjectArray(raw['changes']);
|
|
102
|
-
if (changesRaw === null && raw['changes'] === undefined &&
|
|
105
|
+
if (changesRaw === null && raw['changes'] === undefined && isDgcaChangesLayerOff(raw)) {
|
|
103
106
|
changesRaw = [];
|
|
104
107
|
}
|
|
105
108
|
const activitiesRaw = asObjectArray(raw['actions']);
|
|
@@ -212,11 +215,12 @@ export function parseCanonicalFGCA(input) {
|
|
|
212
215
|
factor: refIds.map((r) => ({ id: r })),
|
|
213
216
|
};
|
|
214
217
|
});
|
|
215
|
-
// Build a reverse mapping: which changes does each
|
|
216
|
-
// canonical
|
|
218
|
+
// Build a reverse mapping: which changes does each action deliver (via
|
|
219
|
+
// canonical `delivers_changes`, or the pre-rename `changes` alias)?
|
|
217
220
|
const activityChangesByCanonicalActivityId = new Map();
|
|
218
221
|
activities.forEach((el, i) => {
|
|
219
|
-
const
|
|
222
|
+
const field = actionChangeLinkField(el);
|
|
223
|
+
const refIds = checkRefArray(el, field, changeIds, CHANGE_ID_RE, 'FGCA-010', `activities[${i}]`);
|
|
220
224
|
activityChangesByCanonicalActivityId.set(String(el['id']), refIds);
|
|
221
225
|
});
|
|
222
226
|
// For each change, find the activities that reference it.
|
|
@@ -286,6 +290,7 @@ export function parseCanonicalFGCA(input) {
|
|
|
286
290
|
goals: internalGoals,
|
|
287
291
|
changes: internalChanges,
|
|
288
292
|
activities: internalActivities,
|
|
293
|
+
hideChanges: isDgcaChangesLayerOff(raw),
|
|
289
294
|
};
|
|
290
295
|
return { valid: true, errors, warnings, parsed };
|
|
291
296
|
}
|
package/dist/fgca/resolver.d.ts
CHANGED
|
@@ -18,6 +18,10 @@ export interface FGCAViewConfig {
|
|
|
18
18
|
changes?: {
|
|
19
19
|
surface?: 'derived' | 'all';
|
|
20
20
|
};
|
|
21
|
+
/** Canonical projection key. `activities` remains a deprecated alias. */
|
|
22
|
+
actions?: {
|
|
23
|
+
surface?: 'derived' | 'all';
|
|
24
|
+
};
|
|
21
25
|
activities?: {
|
|
22
26
|
surface?: 'derived' | 'all';
|
|
23
27
|
};
|
|
@@ -43,7 +47,7 @@ export declare function isFGCAViewDoc(parsed: unknown): boolean;
|
|
|
43
47
|
* to pass to `parseCanonicalFGCA`.
|
|
44
48
|
*
|
|
45
49
|
* Cross-reference fields (`goal.factors[]`, `change.goals[]`,
|
|
46
|
-
* `action.
|
|
47
|
-
* resolver decides which elements to include, not how they are shaped.
|
|
50
|
+
* `action.delivers_changes[]`) are carried from the canon elements unchanged;
|
|
51
|
+
* the resolver decides which elements to include, not how they are shaped.
|
|
48
52
|
*/
|
|
49
53
|
export declare function resolveFGCA(viewDoc: unknown, sources: FGCACanonSources): Record<string, unknown>;
|
package/dist/fgca/resolver.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* top-level `factors[]`, `goals[]`, `changes[]`, `actions[]` with typed
|
|
7
7
|
* string IDs. Filesystem-free — callable from unit tests without vscode.
|
|
8
8
|
*/
|
|
9
|
+
import { actionChangeLinkField } from './action-change-ids.js';
|
|
9
10
|
function isObject(v) {
|
|
10
11
|
return !!v && typeof v === 'object' && !Array.isArray(v);
|
|
11
12
|
}
|
|
@@ -49,8 +50,8 @@ export function isFGCAViewDoc(parsed) {
|
|
|
49
50
|
* to pass to `parseCanonicalFGCA`.
|
|
50
51
|
*
|
|
51
52
|
* Cross-reference fields (`goal.factors[]`, `change.goals[]`,
|
|
52
|
-
* `action.
|
|
53
|
-
* resolver decides which elements to include, not how they are shaped.
|
|
53
|
+
* `action.delivers_changes[]`) are carried from the canon elements unchanged;
|
|
54
|
+
* the resolver decides which elements to include, not how they are shaped.
|
|
54
55
|
*/
|
|
55
56
|
export function resolveFGCA(viewDoc, sources) {
|
|
56
57
|
if (!isObject(viewDoc)) {
|
|
@@ -60,7 +61,11 @@ export function resolveFGCA(viewDoc, sources) {
|
|
|
60
61
|
const goalsConf = isObject(vc['goals']) ? vc['goals'] : {};
|
|
61
62
|
const factorsConf = isObject(vc['factors']) ? vc['factors'] : {};
|
|
62
63
|
const changesConf = isObject(vc['changes']) ? vc['changes'] : {};
|
|
63
|
-
const
|
|
64
|
+
const actionsConf = isObject(vc['actions'])
|
|
65
|
+
? vc['actions']
|
|
66
|
+
: isObject(vc['activities'])
|
|
67
|
+
? vc['activities']
|
|
68
|
+
: {};
|
|
64
69
|
const factorElemsLegacy = collectByNotation(sources.elements, 'factor');
|
|
65
70
|
const factorElemsNew = collectByNotation(sources.elements, 'driver');
|
|
66
71
|
const factorElems = new Map([...factorElemsLegacy, ...factorElemsNew]);
|
|
@@ -108,13 +113,13 @@ export function resolveFGCA(viewDoc, sources) {
|
|
|
108
113
|
selectedChanges = [...changeElems.values()].filter((c) => strArray(c['goals']).some((gid) => selectedGoalIds.has(gid)));
|
|
109
114
|
}
|
|
110
115
|
const selectedChangeIds = new Set(selectedChanges.map((c) => str(c['id'])).filter((x) => x !== undefined));
|
|
111
|
-
// 4. Select
|
|
116
|
+
// 4. Select actions: derived = delivers ≥1 selected change, or (DGA) a selected goal
|
|
112
117
|
let selectedActivities;
|
|
113
|
-
if (str(
|
|
118
|
+
if (str(actionsConf['surface']) === 'all') {
|
|
114
119
|
selectedActivities = [...activityElems.values()];
|
|
115
120
|
}
|
|
116
121
|
else {
|
|
117
|
-
selectedActivities = [...activityElems.values()].filter((a) => strArray(a[
|
|
122
|
+
selectedActivities = [...activityElems.values()].filter((a) => strArray(a[actionChangeLinkField(a)]).some((cid) => selectedChangeIds.has(cid)) ||
|
|
118
123
|
strArray(a['goals']).some((gid) => selectedGoalIds.has(gid)));
|
|
119
124
|
}
|
|
120
125
|
return {
|
|
@@ -126,5 +131,6 @@ export function resolveFGCA(viewDoc, sources) {
|
|
|
126
131
|
goals: selectedGoals,
|
|
127
132
|
changes: selectedChanges,
|
|
128
133
|
actions: selectedActivities,
|
|
134
|
+
view_config: viewDoc['view_config'],
|
|
129
135
|
};
|
|
130
136
|
}
|
package/dist/fgca/validate.d.ts
CHANGED
|
@@ -10,5 +10,7 @@ export interface FGCADoc {
|
|
|
10
10
|
goals: GoalItem[];
|
|
11
11
|
changes: BdnChangeWithActivities[];
|
|
12
12
|
activities: ActivityItem[];
|
|
13
|
+
/** DGA mode: omit the Changes column (`view_config.layers.changes: off`). */
|
|
14
|
+
hideChanges?: boolean;
|
|
13
15
|
}
|
|
14
16
|
export declare function validateFGCADoc(input: unknown): FGCAValidationResult;
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export { validateCapabilityMap, layoutCapabilityTree, TREE_NODE_WIDTH, TREE_NODE
|
|
|
11
11
|
export * from "./change/index";
|
|
12
12
|
export * from "./compliance/index";
|
|
13
13
|
export * from "./compliance-matrix/index";
|
|
14
|
+
export * from "./compliance-verification-matrix/index";
|
|
14
15
|
export * from "./confidence/index";
|
|
15
16
|
export * from "./factor/index";
|
|
16
17
|
export * from "./fgca/index";
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,7 @@ export { validateCapabilityMap, layoutCapabilityTree, TREE_NODE_WIDTH, TREE_NODE
|
|
|
10
10
|
export * from "./change/index.js";
|
|
11
11
|
export * from "./compliance/index.js";
|
|
12
12
|
export * from "./compliance-matrix/index.js";
|
|
13
|
+
export * from "./compliance-verification-matrix/index.js";
|
|
13
14
|
export * from "./confidence/index.js";
|
|
14
15
|
export * from "./factor/index.js";
|
|
15
16
|
export * from "./fgca/index.js";
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export type { AspectCategory, RowId, Stage, AspectEntry, LaneConfig, ProcessBlueprintHeader, ProcessBlueprintFile, ProcessBlueprintLayoutOptions, LegendCell, StageHeaderCell, StageTextCell, AspectPill, AspectRow, ProcessBlueprintLayout, ComplianceDecoration, ComplianceChip, ComplianceRow, ComplianceLaneConfig, ComplianceLaneAssertion, ComplianceLaneRequirement, ComplianceLaneInput, } from './types.js';
|
|
2
2
|
export { validateProcessBlueprint } from './validate.js';
|
|
3
|
-
export type { ValidationError as ProcessBlueprintValidationError, ValidationWarning as ProcessBlueprintValidationWarning, ValidationResult as ProcessBlueprintValidationResult, } from './validate.js';
|
|
3
|
+
export type { ValidationError as ProcessBlueprintValidationError, ValidationWarning as ProcessBlueprintValidationWarning, ValidationResult as ProcessBlueprintValidationResult, ProcessBlueprintValidateOptions, ProcessParentEdge, } from './validate.js';
|
|
4
4
|
export { layoutProcessBlueprint } from './layout.js';
|
|
5
|
+
export { collectProcessColumnRecords, collectStepHomeProcess, columnIndexesForRealisedVia, isProcessColumnId, isStageColumnId, resolveColumnDisplay, } from './resolve-columns.js';
|
|
6
|
+
export type { ProcessColumnRecord } from './resolve-columns.js';
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export { validateProcessBlueprint } from './validate.js';
|
|
2
2
|
export { layoutProcessBlueprint } from './layout.js';
|
|
3
|
+
export { collectProcessColumnRecords, collectStepHomeProcess, columnIndexesForRealisedVia, isProcessColumnId, isStageColumnId, resolveColumnDisplay, } from './resolve-columns.js';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { wrapTextLines } from '../webview/entity-text-layout.js';
|
|
2
|
+
import { columnIndexesForRealisedVia, resolveColumnDisplay } from './resolve-columns.js';
|
|
2
3
|
const ASPECT_CATEGORIES = ['systems', 'actors', 'equipment', 'information_entities'];
|
|
3
4
|
const ASPECT_LABEL = {
|
|
4
5
|
systems: 'Systems',
|
|
@@ -181,29 +182,31 @@ function deriveComplianceRow(stages, stageIndexById, input, opts, yStart) {
|
|
|
181
182
|
for (const stageRef of realisedVia) {
|
|
182
183
|
if (typeof stageRef !== 'string')
|
|
183
184
|
continue;
|
|
184
|
-
const
|
|
185
|
-
if (
|
|
185
|
+
const idxs = columnIndexesForRealisedVia(stageRef, stageIndexById, opts.stepHomeProcess);
|
|
186
|
+
if (idxs.length === 0)
|
|
186
187
|
continue;
|
|
187
|
-
for (const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
stageAccum.
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
existing.deadline
|
|
188
|
+
for (const idx of idxs) {
|
|
189
|
+
for (const lawId of lawIds) {
|
|
190
|
+
// Apply jurisdiction filter.
|
|
191
|
+
if (filterJurisdictions) {
|
|
192
|
+
const jur = input.codexJurisdictions?.[lawId];
|
|
193
|
+
if (!jur || !filterJurisdictions.has(jur))
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (!stageAccum.has(idx))
|
|
197
|
+
stageAccum.set(idx, new Map());
|
|
198
|
+
const lawMap = stageAccum.get(idx);
|
|
199
|
+
const existing = lawMap.get(lawId);
|
|
200
|
+
const isGap = a.status === 'non_compliant' || a.status === 'partial';
|
|
201
|
+
if (!existing) {
|
|
202
|
+
lawMap.set(lawId, { gap: isGap, deadline: req.deadline });
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
existing.gap = existing.gap || isGap;
|
|
206
|
+
// Keep the earlier deadline (more urgent).
|
|
207
|
+
if (req.deadline && (!existing.deadline || req.deadline < existing.deadline)) {
|
|
208
|
+
existing.deadline = req.deadline;
|
|
209
|
+
}
|
|
207
210
|
}
|
|
208
211
|
}
|
|
209
212
|
}
|
|
@@ -253,6 +256,7 @@ export function layoutProcessBlueprint(file, options) {
|
|
|
253
256
|
? allStages.filter(s => s?.id != null && opts.visibleStages.includes(s.id))
|
|
254
257
|
: allStages;
|
|
255
258
|
const stageIndexById = buildStageIndex(stages);
|
|
259
|
+
const display = stages.map(s => resolveColumnDisplay(s, opts.processCatalog));
|
|
256
260
|
// Row visibility from visibleRows
|
|
257
261
|
const visRows = opts.visibleRows;
|
|
258
262
|
const showGoal = !visRows || visRows.includes('goal');
|
|
@@ -266,7 +270,7 @@ export function layoutProcessBlueprint(file, options) {
|
|
|
266
270
|
const stageHeaders = stages.map((s, i) => ({
|
|
267
271
|
stageIndex: i,
|
|
268
272
|
id: s?.id ?? '',
|
|
269
|
-
name:
|
|
273
|
+
name: display[i].name,
|
|
270
274
|
x: opts.legendColumnWidth + i * opts.stageColumnWidth,
|
|
271
275
|
y: 0,
|
|
272
276
|
width: opts.stageColumnWidth,
|
|
@@ -280,8 +284,8 @@ export function layoutProcessBlueprint(file, options) {
|
|
|
280
284
|
const maxLines = Math.max(1, ...lineCounts);
|
|
281
285
|
return Math.max(minHeight, maxLines * opts.textLineHeight + 2 * opts.cellTextPadY);
|
|
282
286
|
};
|
|
283
|
-
const goalLines =
|
|
284
|
-
const resultLines =
|
|
287
|
+
const goalLines = display.map(d => wrap(d.goal));
|
|
288
|
+
const resultLines = display.map(d => wrap(d.result));
|
|
285
289
|
let cursorY = opts.stageHeaderHeight;
|
|
286
290
|
let goalRowY = 0, goalRowHeight = 0;
|
|
287
291
|
let resultRowY = 0, resultRowHeight = 0;
|
|
@@ -290,8 +294,8 @@ export function layoutProcessBlueprint(file, options) {
|
|
|
290
294
|
if (showGoal) {
|
|
291
295
|
goalRowY = cursorY;
|
|
292
296
|
goalRowHeight = rowHeightFor(goalLines.map(l => l.length), opts.goalRowHeight);
|
|
293
|
-
goalCells.push(...
|
|
294
|
-
stageIndex: i, text:
|
|
297
|
+
goalCells.push(...display.map((d, i) => ({
|
|
298
|
+
stageIndex: i, text: d.goal, lines: goalLines[i],
|
|
295
299
|
x: opts.legendColumnWidth + i * opts.stageColumnWidth, y: goalRowY,
|
|
296
300
|
width: opts.stageColumnWidth, height: goalRowHeight,
|
|
297
301
|
})));
|
|
@@ -300,8 +304,8 @@ export function layoutProcessBlueprint(file, options) {
|
|
|
300
304
|
if (showResult) {
|
|
301
305
|
resultRowY = cursorY;
|
|
302
306
|
resultRowHeight = rowHeightFor(resultLines.map(l => l.length), opts.resultRowHeight);
|
|
303
|
-
resultCells.push(...
|
|
304
|
-
stageIndex: i, text:
|
|
307
|
+
resultCells.push(...display.map((d, i) => ({
|
|
308
|
+
stageIndex: i, text: d.result, lines: resultLines[i],
|
|
305
309
|
x: opts.legendColumnWidth + i * opts.stageColumnWidth, y: resultRowY,
|
|
306
310
|
width: opts.stageColumnWidth, height: resultRowHeight,
|
|
307
311
|
})));
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render-time resolution of Process Blueprint column headers and the
|
|
3
|
+
* compliance-lane join key.
|
|
4
|
+
*
|
|
5
|
+
* A `PROCESS-…` column is the process: `name` / `goal` / `result` come from
|
|
6
|
+
* the child PROCESS element (ELEMENT_PRIMITIVES §7.5), never from restated
|
|
7
|
+
* view fields. A `STAGE-…` column keeps the authored sketch copy.
|
|
8
|
+
*
|
|
9
|
+
* Pure: no I/O. The host (VS Code preview, JCEF, CLI) already owns loading
|
|
10
|
+
* canon/elements/** — this module only knows how to pick PROCESS / STEP
|
|
11
|
+
* records out of that pool.
|
|
12
|
+
*
|
|
13
|
+
* This is a read model, never storage. Callers render an ephemeral display
|
|
14
|
+
* copy and must never serialise it back onto the view.
|
|
15
|
+
*/
|
|
16
|
+
import type { Stage } from './types.js';
|
|
17
|
+
export declare function isProcessColumnId(id: string): boolean;
|
|
18
|
+
export declare function isStageColumnId(id: string): boolean;
|
|
19
|
+
/** Display fields taken from a `notation: process` element. */
|
|
20
|
+
export interface ProcessColumnRecord {
|
|
21
|
+
name: string;
|
|
22
|
+
goal?: string;
|
|
23
|
+
result?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Collect admitted PROCESS elements from a raw canon/elements/** doc pool,
|
|
27
|
+
* keyed by id. Malformed documents are skipped.
|
|
28
|
+
*/
|
|
29
|
+
export declare function collectProcessColumnRecords(docs: readonly unknown[]): Map<string, ProcessColumnRecord>;
|
|
30
|
+
/**
|
|
31
|
+
* Map each STEP id onto the PROCESS that contains it — either inline in
|
|
32
|
+
* `PROCESS.flow.steps[]` or a promoted `{ notation: step, process }` element.
|
|
33
|
+
* First writer wins; a later duplicate is ignored.
|
|
34
|
+
*/
|
|
35
|
+
export declare function collectStepHomeProcess(docs: readonly unknown[]): Map<string, string>;
|
|
36
|
+
/**
|
|
37
|
+
* Display copy for one column. PROCESS- columns ignore any restated view
|
|
38
|
+
* fields; missing catalogue falls back to the id (header) and empty
|
|
39
|
+
* goal/result. STAGE- columns keep authored name/goal/result.
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveColumnDisplay(stage: Stage, catalog?: ReadonlyMap<string, ProcessColumnRecord>): {
|
|
42
|
+
name: string;
|
|
43
|
+
goal: string;
|
|
44
|
+
result: string;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Column indexes a `realised_via` token pins on this blueprint.
|
|
48
|
+
*
|
|
49
|
+
* `STAGE-…` is never a join key (ASSERT-004). A `PROCESS-…` token pins that
|
|
50
|
+
* column. A STEP whose home process is a column pins that column.
|
|
51
|
+
*/
|
|
52
|
+
export declare function columnIndexesForRealisedVia(ref: string, stageIndexById: ReadonlyMap<string, number>, stepHomeProcess?: ReadonlyMap<string, string>): number[];
|