@transitrix/diagrams 1.8.21 → 1.9.18
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/activity-card/types.js +1 -1
- package/dist/agreement.d.ts +15 -0
- package/dist/agreement.js +61 -0
- package/dist/applications/index.d.ts +2 -0
- package/dist/applications/index.js +1 -0
- package/dist/applications/resolve-maturity.d.ts +16 -0
- package/dist/applications/resolve-maturity.js +57 -0
- package/dist/applications/validate.js +20 -0
- package/dist/capability-map/CapabilityCardNode.d.ts +1 -1
- package/dist/capability-map/CapabilityRootNode.d.ts +1 -1
- package/dist/capability-map/index.d.ts +2 -0
- package/dist/capability-map/index.js +1 -0
- package/dist/capability-map/resolve-maturity.d.ts +20 -0
- package/dist/capability-map/resolve-maturity.js +72 -0
- package/dist/capability-map/validate.js +1 -2
- package/dist/compliance/classify.d.ts +10 -1
- package/dist/compliance/classify.js +44 -2
- package/dist/compliance/confidence.js +1 -1
- package/dist/compliance/gap-report.d.ts +31 -1
- package/dist/compliance/gap-report.js +55 -3
- package/dist/compliance/html.js +14 -6
- package/dist/compliance/impact.js +1 -1
- package/dist/compliance/index.d.ts +3 -1
- package/dist/compliance/index.js +2 -0
- package/dist/compliance/markdown.js +26 -4
- package/dist/compliance/reverse-index.d.ts +6 -1
- package/dist/compliance/reverse-index.js +27 -3
- package/dist/compliance/trace.js +5 -1
- package/dist/compliance/types.d.ts +70 -0
- package/dist/compliance/types.js +1 -2
- package/dist/compliance/views.js +1 -1
- package/dist/compliance-matrix/types.d.ts +1 -1
- package/dist/compliance-matrix/types.js +1 -1
- package/dist/confidence/score.js +1 -1
- package/dist/constraint/validate.js +3 -0
- package/dist/edge-path.js +1 -1
- package/dist/factor/validate.js +2 -2
- package/dist/fgca/chain-table.js +1 -1
- package/dist/fgca/nodeTypes.d.ts +4 -4
- package/dist/fgca/nodes/FGCAActivityNode.d.ts +1 -1
- package/dist/fgca/nodes/FGCAChangeNode.d.ts +1 -1
- package/dist/fgca/nodes/FGCAFactorNode.d.ts +1 -1
- package/dist/fgca/nodes/FGCAGoalNode.d.ts +1 -1
- package/dist/fgca/parse-canonical.d.ts +2 -2
- package/dist/fgca/parse-canonical.js +2 -2
- package/dist/fgca/preview-layout.d.ts +1 -1
- package/dist/goals/GoalNode.d.ts +1 -1
- package/dist/goals/layout.d.ts +1 -1
- package/dist/goals/layout.js +1 -1
- package/dist/goals/parse-canonical.d.ts +1 -1
- package/dist/goals/parse-canonical.js +1 -1
- package/dist/goals/types.d.ts +1 -1
- package/dist/goals/validate.js +2 -2
- package/dist/link-suspicion.d.ts +43 -0
- package/dist/link-suspicion.js +156 -0
- package/dist/metric/index.d.ts +4 -0
- package/dist/metric/index.js +2 -0
- package/dist/metric/types.d.ts +24 -0
- package/dist/metric/types.js +7 -0
- package/dist/metric/validate.d.ts +7 -0
- package/dist/metric/validate.js +90 -0
- package/dist/need/index.d.ts +3 -0
- package/dist/need/index.js +1 -0
- package/dist/need/types.d.ts +21 -0
- package/dist/need/types.js +3 -0
- package/dist/need/validate.d.ts +7 -0
- package/dist/need/validate.js +60 -0
- package/dist/repo-validate/check-versioned-attributes.d.ts +4 -0
- package/dist/repo-validate/check-versioned-attributes.js +105 -0
- package/dist/repo-validate/validate-repo.js +3 -0
- package/dist/requirement/index.d.ts +2 -2
- package/dist/requirement/index.js +1 -1
- package/dist/requirement/types.d.ts +18 -0
- package/dist/requirement/types.js +2 -0
- package/dist/requirement/validate.js +22 -1
- package/dist/risk/index.d.ts +4 -0
- package/dist/risk/index.js +2 -0
- package/dist/risk/types.d.ts +25 -0
- package/dist/risk/types.js +3 -0
- package/dist/risk/validate.d.ts +7 -0
- package/dist/risk/validate.js +108 -0
- package/dist/schema-version.d.ts +9 -6
- package/dist/schema-version.js +9 -6
- package/dist/theme/themes.js +3 -2
- package/dist/validation/index.d.ts +4 -0
- package/dist/validation/index.js +2 -0
- package/dist/validation/types.d.ts +31 -0
- package/dist/validation/types.js +21 -0
- package/dist/validation/validate.d.ts +7 -0
- package/dist/validation/validate.js +106 -0
- package/dist/verification/index.d.ts +4 -0
- package/dist/verification/index.js +2 -0
- package/dist/verification/types.d.ts +31 -0
- package/dist/verification/types.js +12 -0
- package/dist/verification/validate.d.ts +7 -0
- package/dist/verification/validate.js +104 -0
- package/dist/versioned-attribute/index.d.ts +4 -0
- package/dist/versioned-attribute/index.js +2 -0
- package/dist/versioned-attribute/resolve.d.ts +15 -0
- package/dist/versioned-attribute/resolve.js +37 -0
- package/dist/versioned-attribute/types.d.ts +12 -0
- package/dist/versioned-attribute/types.js +6 -0
- package/dist/versioned-attribute/validate.d.ts +17 -0
- package/dist/versioned-attribute/validate.js +87 -0
- package/dist/webview/render-applications.d.ts +15 -1
- package/dist/webview/render-applications.js +7 -2
- package/dist/webview/render-blocks.js +6 -4
- package/dist/webview/render-capability-map.d.ts +16 -1
- package/dist/webview/render-capability-map.js +4 -1
- package/dist/webview/render-process.d.ts +1 -1
- package/dist/webview/render-process.js +5 -5
- package/package.json +9 -6
- package/src/activity-card/types.ts +1 -1
- package/src/agreement.ts +71 -0
- package/src/applications/index.ts +3 -0
- package/src/applications/resolve-maturity.ts +66 -0
- package/src/applications/validate.ts +23 -0
- package/src/capability-map/index.ts +7 -0
- package/src/capability-map/resolve-maturity.ts +84 -0
- package/src/capability-map/validate.ts +1 -2
- package/src/compliance/classify.ts +54 -3
- package/src/compliance/confidence.ts +1 -1
- package/src/compliance/gap-report.ts +91 -4
- package/src/compliance/html.ts +18 -6
- package/src/compliance/impact.ts +1 -1
- package/src/compliance/index.ts +6 -0
- package/src/compliance/markdown.ts +18 -4
- package/src/compliance/reverse-index.ts +35 -4
- package/src/compliance/trace.ts +7 -1
- package/src/compliance/types.ts +75 -2
- package/src/compliance/views.ts +1 -1
- package/src/compliance-matrix/types.ts +2 -2
- package/src/confidence/score.ts +1 -1
- package/src/constraint/validate.ts +4 -0
- package/src/edge-path.ts +1 -1
- package/src/factor/validate.ts +2 -2
- package/src/fgca/chain-table.ts +1 -1
- package/src/fgca/parse-canonical.ts +2 -2
- package/src/fgca/preview-layout.ts +1 -1
- package/src/goals/layout.ts +1 -1
- package/src/goals/parse-canonical.ts +1 -1
- package/src/goals/types.ts +1 -1
- package/src/goals/validate.ts +2 -2
- package/src/link-suspicion.ts +181 -0
- package/src/metric/index.ts +4 -0
- package/src/metric/types.ts +38 -0
- package/src/metric/validate.ts +102 -0
- package/src/need/index.ts +3 -0
- package/src/need/types.ts +31 -0
- package/src/need/validate.ts +71 -0
- package/src/repo-validate/check-versioned-attributes.ts +112 -0
- package/src/repo-validate/validate-repo.ts +3 -0
- package/src/requirement/index.ts +2 -2
- package/src/requirement/types.ts +25 -0
- package/src/requirement/validate.ts +25 -1
- package/src/risk/index.ts +4 -0
- package/src/risk/types.ts +35 -0
- package/src/risk/validate.ts +121 -0
- package/src/schema-version.ts +9 -6
- package/src/theme/themes.ts +3 -2
- package/src/validation/index.ts +4 -0
- package/src/validation/types.ts +57 -0
- package/src/validation/validate.ts +113 -0
- package/src/verification/index.ts +4 -0
- package/src/verification/types.ts +48 -0
- package/src/verification/validate.ts +111 -0
- package/src/versioned-attribute/index.ts +4 -0
- package/src/versioned-attribute/resolve.ts +45 -0
- package/src/versioned-attribute/types.ts +19 -0
- package/src/versioned-attribute/validate.ts +109 -0
- package/src/webview/render-applications.ts +25 -2
- package/src/webview/render-blocks.ts +8 -4
- package/src/webview/render-capability-map.ts +23 -1
- package/src/webview/render-process.ts +5 -5
- package/src/webview/styles.css +6 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Activity Card notation — single-project narrative view.
|
|
2
2
|
//
|
|
3
3
|
// Spec: transitrix/methodology `notations/views/18-activity-card.md` (v0.1).
|
|
4
|
-
//
|
|
4
|
+
// Studio track #134.
|
|
5
5
|
//
|
|
6
6
|
// The Activity Card is the first MULTI-DOCUMENT Studio notation. The card
|
|
7
7
|
// YAML itself holds only `id`, `project` (an Activity ID), `description`,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ValidationError } from './validation-types.js';
|
|
2
|
+
export declare const AGREEMENT_VALUES: readonly ["draft", "agreed", "disputed"];
|
|
3
|
+
export type AgreementValue = (typeof AGREEMENT_VALUES)[number];
|
|
4
|
+
export declare function looksLikeTool(id: unknown): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* AGREE-001..003 over an element's already-extracted `agreement` /
|
|
7
|
+
* `agreed_by` fields. Returns an empty list when `agreement` is absent
|
|
8
|
+
* (back-compat — absent ⇒ `agreed`, nothing to check) or when the fields
|
|
9
|
+
* are well-formed.
|
|
10
|
+
*
|
|
11
|
+
* AGREE-002 and AGREE-003 can both describe an `agreement: agreed` record
|
|
12
|
+
* with no `agreed_by` — this reports AGREE-003 only, the same choice
|
|
13
|
+
* `check-agreement.mjs` makes (CONTRACT.md §6.3.1 permits either or both).
|
|
14
|
+
*/
|
|
15
|
+
export declare function checkAgreement(record: Record<string, unknown>): ValidationError[];
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Agreement axis — methodology CONTRACT.md §6.3 / §6.3.1 (AGREE-001..003).
|
|
2
|
+
//
|
|
3
|
+
// A closed three-value axis (`draft` | `agreed` | `disputed`) carried by
|
|
4
|
+
// REQUIREMENT, CONSTRAINT, and NEED — independent of admission (§6.1),
|
|
5
|
+
// recording whether the accountable party has committed to the statement.
|
|
6
|
+
// Absent ⇒ `agreed` (back-compat; a human-authored element that predates
|
|
7
|
+
// this axis needs no change).
|
|
8
|
+
//
|
|
9
|
+
// Ported from methodology's reference implementation
|
|
10
|
+
// (`scripts/check-agreement.mjs`'s `checkAgreement`/`looksLikeTool`) so a
|
|
11
|
+
// repo carrying the field validates identically here — same closed
|
|
12
|
+
// vocabulary, same tool-id heuristic, same "AGREE-003 wins over AGREE-002
|
|
13
|
+
// when both would fire" precedence.
|
|
14
|
+
export const AGREEMENT_VALUES = ['draft', 'agreed', 'disputed'];
|
|
15
|
+
/** Same convention as `ADMIT-007`'s footgun-catcher: an npm-scoped name, or
|
|
16
|
+
* a hyphenated `*-cli` / `*-reviewer[-…]` / `*-bot` / `*-scanner` id, reads
|
|
17
|
+
* as a tool; anything else reads as a human handle. Not a security
|
|
18
|
+
* boundary — catches the common mistake of a tool defaulting or
|
|
19
|
+
* copy-pasting `agreed`. */
|
|
20
|
+
const TOOL_ID_RE = /^@|-cli$|-reviewer(?:-\w+)?$|-bot$|-scanner$/i;
|
|
21
|
+
export function looksLikeTool(id) {
|
|
22
|
+
return TOOL_ID_RE.test(String(id ?? ''));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* AGREE-001..003 over an element's already-extracted `agreement` /
|
|
26
|
+
* `agreed_by` fields. Returns an empty list when `agreement` is absent
|
|
27
|
+
* (back-compat — absent ⇒ `agreed`, nothing to check) or when the fields
|
|
28
|
+
* are well-formed.
|
|
29
|
+
*
|
|
30
|
+
* AGREE-002 and AGREE-003 can both describe an `agreement: agreed` record
|
|
31
|
+
* with no `agreed_by` — this reports AGREE-003 only, the same choice
|
|
32
|
+
* `check-agreement.mjs` makes (CONTRACT.md §6.3.1 permits either or both).
|
|
33
|
+
*/
|
|
34
|
+
export function checkAgreement(record) {
|
|
35
|
+
const agreement = record.agreement;
|
|
36
|
+
if (agreement === undefined)
|
|
37
|
+
return [];
|
|
38
|
+
if (typeof agreement !== 'string' || !AGREEMENT_VALUES.includes(agreement)) {
|
|
39
|
+
return [{
|
|
40
|
+
code: 'AGREE-001',
|
|
41
|
+
message: `agreement "${String(agreement)}" must be one of ${AGREEMENT_VALUES.join(', ')}.`,
|
|
42
|
+
path: 'agreement',
|
|
43
|
+
}];
|
|
44
|
+
}
|
|
45
|
+
const agreedBy = record.agreed_by;
|
|
46
|
+
if (typeof agreedBy !== 'string' || agreedBy.trim() === '') {
|
|
47
|
+
return [{
|
|
48
|
+
code: 'AGREE-003',
|
|
49
|
+
message: 'agreed_by is required whenever agreement is present.',
|
|
50
|
+
path: 'agreed_by',
|
|
51
|
+
}];
|
|
52
|
+
}
|
|
53
|
+
if (agreement === 'agreed' && looksLikeTool(agreedBy)) {
|
|
54
|
+
return [{
|
|
55
|
+
code: 'AGREE-002',
|
|
56
|
+
message: `agreement: agreed but agreed_by "${agreedBy}" identifies a tool — a tool must never write "agreed".`,
|
|
57
|
+
path: 'agreed_by',
|
|
58
|
+
}];
|
|
59
|
+
}
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export type { ApplicationType, ApplicationStatus, IntegrationDirection, ApplicationIntegration, Application, ApplicationsCatalogueHeader, ApplicationsCatalogueFile, } from './types.js';
|
|
2
2
|
export { validateApplicationsCatalogue } from './validate.js';
|
|
3
|
+
export type { ResolvedApplicationAttributes } from './resolve-maturity.js';
|
|
4
|
+
export { resolveApplicationAttributes, withResolvedAttributes } from './resolve-maturity.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Application } from './types.js';
|
|
2
|
+
export interface ResolvedApplicationAttributes {
|
|
3
|
+
owner_role?: string;
|
|
4
|
+
vendor?: string;
|
|
5
|
+
maturity?: number;
|
|
6
|
+
}
|
|
7
|
+
/** Pick every sidecar out of a raw canon/elements/** doc pool and resolve
|
|
8
|
+
* owner_role/vendor/maturity at `atDate`, keyed by the sidecar's target id
|
|
9
|
+
* (== an application's `app_id`, per the notation's element-file pairing). */
|
|
10
|
+
export declare function resolveApplicationAttributes(rawElementDocs: unknown[], atDate: string): Map<string, ResolvedApplicationAttributes>;
|
|
11
|
+
/** Merge resolved sidecar values into a rendering copy of an application row.
|
|
12
|
+
* An inline value would win if present, but VERSIONED-004 already forbids
|
|
13
|
+
* that on an admitted document — this is a display fallback, not a
|
|
14
|
+
* migration tool. The returned object is a plain copy; nothing here writes
|
|
15
|
+
* it anywhere. */
|
|
16
|
+
export declare function withResolvedAttributes(app: Application, resolved: Map<string, ResolvedApplicationAttributes>): Application;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Render-time resolution of the applications-catalogue's sidecar-bound
|
|
2
|
+
// time_varying fields (owner_role, vendor, maturity — notations/views/
|
|
3
|
+
// 10-applications.md §5a, CONTRACT.md §9.4). VERSIONED-004 rejects these
|
|
4
|
+
// inline on an admitted catalogue entry, so a compliant document never
|
|
5
|
+
// carries them — a renderer that only reads the document straight shows
|
|
6
|
+
// blank cells for every field the maturity decision moved out.
|
|
7
|
+
//
|
|
8
|
+
// Pure: no I/O, no repo walk. The host (VS Code extension, IntelliJ/JCEF,
|
|
9
|
+
// CLI) already owns loading canon/elements/** (canon-loader.ts's shared FS
|
|
10
|
+
// layer) — this module only knows how to pick sidecars out of that pool and
|
|
11
|
+
// resolve them at a date.
|
|
12
|
+
//
|
|
13
|
+
// Per the 2026-08-05 hub decision (proposal #1022): this is a read model,
|
|
14
|
+
// never storage. Callers render an augmented, ephemeral copy of the
|
|
15
|
+
// application row and must never serialise it back — the sidecar stays the
|
|
16
|
+
// only place these fields are written.
|
|
17
|
+
import { parseSidecar, resolveAttributes } from '../versioned-attribute/index.js';
|
|
18
|
+
/** Pick every sidecar out of a raw canon/elements/** doc pool and resolve
|
|
19
|
+
* owner_role/vendor/maturity at `atDate`, keyed by the sidecar's target id
|
|
20
|
+
* (== an application's `app_id`, per the notation's element-file pairing). */
|
|
21
|
+
export function resolveApplicationAttributes(rawElementDocs, atDate) {
|
|
22
|
+
const byAppId = new Map();
|
|
23
|
+
for (const doc of rawElementDocs) {
|
|
24
|
+
if (!doc || typeof doc !== 'object' || Array.isArray(doc))
|
|
25
|
+
continue;
|
|
26
|
+
const sidecar = parseSidecar(doc);
|
|
27
|
+
if (!sidecar)
|
|
28
|
+
continue;
|
|
29
|
+
const resolved = resolveAttributes(sidecar, atDate);
|
|
30
|
+
const entry = {};
|
|
31
|
+
if (typeof resolved['owner_role'] === 'string')
|
|
32
|
+
entry.owner_role = resolved['owner_role'];
|
|
33
|
+
if (typeof resolved['vendor'] === 'string')
|
|
34
|
+
entry.vendor = resolved['vendor'];
|
|
35
|
+
if (typeof resolved['maturity'] === 'number')
|
|
36
|
+
entry.maturity = resolved['maturity'];
|
|
37
|
+
if (Object.keys(entry).length > 0)
|
|
38
|
+
byAppId.set(sidecar.target, entry);
|
|
39
|
+
}
|
|
40
|
+
return byAppId;
|
|
41
|
+
}
|
|
42
|
+
/** Merge resolved sidecar values into a rendering copy of an application row.
|
|
43
|
+
* An inline value would win if present, but VERSIONED-004 already forbids
|
|
44
|
+
* that on an admitted document — this is a display fallback, not a
|
|
45
|
+
* migration tool. The returned object is a plain copy; nothing here writes
|
|
46
|
+
* it anywhere. */
|
|
47
|
+
export function withResolvedAttributes(app, resolved) {
|
|
48
|
+
const r = resolved.get(app.app_id);
|
|
49
|
+
if (!r)
|
|
50
|
+
return app;
|
|
51
|
+
return {
|
|
52
|
+
...app,
|
|
53
|
+
owner_role: app.owner_role ?? r.owner_role,
|
|
54
|
+
vendor: app.vendor ?? r.vendor,
|
|
55
|
+
maturity: app.maturity ?? r.maturity,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -5,6 +5,16 @@ const VALID_SENSITIVITY = new Set(['public', 'internal', 'confidential', 'restri
|
|
|
5
5
|
const VALID_DIRECTIONALITY = new Set(['producer', 'consumer', 'request_reply', 'bidirectional_stream']);
|
|
6
6
|
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
7
7
|
const IFACE_CONDITIONAL = ['protocol', 'payload_class', 'sensitivity', 'directionality'];
|
|
8
|
+
// VERSIONED-004 (CONTRACT.md §9.3) — `owner_role`, `vendor`, `maturity` are
|
|
9
|
+
// declared `time_varying` by notations/views/10-applications.md §5/§5a and
|
|
10
|
+
// belong in the sidecar `<app_id>.history.yaml`, not inline on the catalogue
|
|
11
|
+
// entry. Unlike capability's VERSIONED-004 (packages/diagrams/src/
|
|
12
|
+
// repo-validate/check-versioned-attributes.ts), which walks standalone
|
|
13
|
+
// element files under canon/elements/**, an applications-catalogue entry has
|
|
14
|
+
// no file of its own to check inline-vs-sidecar on — the entry lives in this
|
|
15
|
+
// view document — so the check runs here, at the per-file notation
|
|
16
|
+
// validator, rather than in the repo-scope element sweep.
|
|
17
|
+
const TIME_VARYING_FIELDS = ['owner_role', 'vendor', 'maturity'];
|
|
8
18
|
export function validateApplicationsCatalogue(input) {
|
|
9
19
|
const errors = [];
|
|
10
20
|
const warnings = [];
|
|
@@ -92,6 +102,16 @@ export function validateApplicationsCatalogue(input) {
|
|
|
92
102
|
errors.push({ code: 'APP-006', message: `${idx}: maturity must be an integer 1–5, got "${m}"` });
|
|
93
103
|
}
|
|
94
104
|
}
|
|
105
|
+
// VERSIONED-004: time_varying fields must live in the sidecar, not inline.
|
|
106
|
+
for (const field of TIME_VARYING_FIELDS) {
|
|
107
|
+
if (a[field] !== undefined) {
|
|
108
|
+
errors.push({
|
|
109
|
+
code: 'VERSIONED-004',
|
|
110
|
+
message: `${idx}: '${field}' is time_varying (CONTRACT.md §9) and belongs in the sidecar ` +
|
|
111
|
+
`'${typeof a['app_id'] === 'string' ? a['app_id'] : idx}.history.yaml', not inline on the catalogue entry.`,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
95
115
|
// APP-009: integrations[].direction enum
|
|
96
116
|
if (Array.isArray(a['integrations'])) {
|
|
97
117
|
const integrations = a['integrations'];
|
|
@@ -15,5 +15,5 @@ export interface CapabilityCardNodeData {
|
|
|
15
15
|
}
|
|
16
16
|
declare const CapabilityCardNode: React.MemoExoticComponent<({ data }: {
|
|
17
17
|
data: CapabilityCardNodeData;
|
|
18
|
-
}) =>
|
|
18
|
+
}) => React.JSX.Element>;
|
|
19
19
|
export default CapabilityCardNode;
|
|
@@ -8,5 +8,5 @@ export interface CapabilityRootNodeData {
|
|
|
8
8
|
* add/delete/collapse affordances (it isn't a mutable Capability). */
|
|
9
9
|
declare const CapabilityRootNode: React.MemoExoticComponent<({ data }: {
|
|
10
10
|
data: CapabilityRootNodeData;
|
|
11
|
-
}) =>
|
|
11
|
+
}) => React.JSX.Element>;
|
|
12
12
|
export default CapabilityRootNode;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export type { CapabilityType, CapabilityNode, CapabilityMapHeader, CapabilityMapFile, } from './types.js';
|
|
2
2
|
export { validateCapabilityMap } from './validate.js';
|
|
3
|
+
export type { ResolvedCapabilityAttributes } from './resolve-maturity.js';
|
|
4
|
+
export { resolveCapabilityAttributes, withResolvedAttributes, withResolvedCapabilityMap, } from './resolve-maturity.js';
|
|
3
5
|
export type { CapabilityTreeNode, CapabilityTreeEdge, CapabilityTreeLevelCounts, CapabilityTreeLayout, } from './layout-tree.js';
|
|
4
6
|
export { layoutCapabilityTree, TREE_NODE_WIDTH, TREE_NODE_HEIGHT, TREE_RANK_SEP, TREE_NODE_SEP } from './layout-tree.js';
|
|
5
7
|
export type { RenderCapabilityTreeOptions } from './render-capability-tree.js';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { validateCapabilityMap } from './validate.js';
|
|
2
|
+
export { resolveCapabilityAttributes, withResolvedAttributes, withResolvedCapabilityMap, } from './resolve-maturity.js';
|
|
2
3
|
export { layoutCapabilityTree, TREE_NODE_WIDTH, TREE_NODE_HEIGHT, TREE_RANK_SEP, TREE_NODE_SEP } from './layout-tree.js';
|
|
3
4
|
export { renderCapabilityTreeSvg } from './render-capability-tree.js';
|
|
4
5
|
export { parseAddress, formatAddress, getLevel, getParentAddress, getFirstFreeAddress, isAddressTaken, } from './address.js';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { CapabilityMapHeader, CapabilityNode } from './types.js';
|
|
2
|
+
export interface ResolvedCapabilityAttributes {
|
|
3
|
+
current_maturity?: number;
|
|
4
|
+
target_maturity?: number;
|
|
5
|
+
owner_role?: string;
|
|
6
|
+
target_date?: string;
|
|
7
|
+
}
|
|
8
|
+
/** Pick every sidecar out of a raw canon/elements/** doc pool and resolve
|
|
9
|
+
* current_maturity/target_maturity/owner_role/target_date at `atDate`,
|
|
10
|
+
* keyed by the sidecar's target id (== a capability entry's `id`, per the
|
|
11
|
+
* CAPABILITY-* primitive that entry references). */
|
|
12
|
+
export declare function resolveCapabilityAttributes(rawElementDocs: unknown[], atDate: string): Map<string, ResolvedCapabilityAttributes>;
|
|
13
|
+
/** Merge resolved sidecar values into a rendering copy of a capability node
|
|
14
|
+
* and its descendants. An inline value always wins if present — this is a
|
|
15
|
+
* display fallback for fields the document doesn't carry inline, not a
|
|
16
|
+
* migration tool. Returns a new node tree; nothing here writes it
|
|
17
|
+
* anywhere. */
|
|
18
|
+
export declare function withResolvedAttributes(node: CapabilityNode, resolved: Map<string, ResolvedCapabilityAttributes>): CapabilityNode;
|
|
19
|
+
/** Same merge, applied across a capability map's whole top-level tree. */
|
|
20
|
+
export declare function withResolvedCapabilityMap(map: CapabilityMapHeader, resolved: Map<string, ResolvedCapabilityAttributes>): CapabilityMapHeader;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Render-time resolution of a nested capability-map entry's sidecar-bound
|
|
2
|
+
// time_varying fields (current_maturity, target_maturity, owner_role,
|
|
3
|
+
// target_date — 05-capability-map.md §13/§14, CONTRACT.md §9.4).
|
|
4
|
+
// check-versioned-attributes.ts's VERSIONED-004 already rejects these inline
|
|
5
|
+
// on the standalone CAPABILITY-*.yaml primitive; a nested capability-map
|
|
6
|
+
// entry references that same primitive by id (the 2026-08-05 packages
|
|
7
|
+
// decision — no separate per-nested-entry sidecar convention), so this
|
|
8
|
+
// resolves the same way applications/resolve-maturity.ts does for the
|
|
9
|
+
// applications catalogue.
|
|
10
|
+
//
|
|
11
|
+
// Pure: no I/O, no repo walk. The host (VS Code extension, IntelliJ/JCEF,
|
|
12
|
+
// CLI) already owns loading canon/elements/** — this module only knows how
|
|
13
|
+
// to pick sidecars out of that pool and resolve them at a date.
|
|
14
|
+
//
|
|
15
|
+
// Per the 2026-08-05 packages decision: this is a read model, never
|
|
16
|
+
// storage. Callers render an augmented, ephemeral copy of the capability
|
|
17
|
+
// tree and must never serialise it back — the sidecar stays the only place
|
|
18
|
+
// these fields are written.
|
|
19
|
+
import { parseSidecar, resolveAttributes } from '../versioned-attribute/index.js';
|
|
20
|
+
/** Pick every sidecar out of a raw canon/elements/** doc pool and resolve
|
|
21
|
+
* current_maturity/target_maturity/owner_role/target_date at `atDate`,
|
|
22
|
+
* keyed by the sidecar's target id (== a capability entry's `id`, per the
|
|
23
|
+
* CAPABILITY-* primitive that entry references). */
|
|
24
|
+
export function resolveCapabilityAttributes(rawElementDocs, atDate) {
|
|
25
|
+
const byId = new Map();
|
|
26
|
+
for (const doc of rawElementDocs) {
|
|
27
|
+
if (!doc || typeof doc !== 'object' || Array.isArray(doc))
|
|
28
|
+
continue;
|
|
29
|
+
const sidecar = parseSidecar(doc);
|
|
30
|
+
if (!sidecar)
|
|
31
|
+
continue;
|
|
32
|
+
const resolved = resolveAttributes(sidecar, atDate);
|
|
33
|
+
const entry = {};
|
|
34
|
+
if (typeof resolved['current_maturity'] === 'number')
|
|
35
|
+
entry.current_maturity = resolved['current_maturity'];
|
|
36
|
+
if (typeof resolved['target_maturity'] === 'number')
|
|
37
|
+
entry.target_maturity = resolved['target_maturity'];
|
|
38
|
+
if (typeof resolved['owner_role'] === 'string')
|
|
39
|
+
entry.owner_role = resolved['owner_role'];
|
|
40
|
+
if (typeof resolved['target_date'] === 'string')
|
|
41
|
+
entry.target_date = resolved['target_date'];
|
|
42
|
+
if (Object.keys(entry).length > 0)
|
|
43
|
+
byId.set(sidecar.target, entry);
|
|
44
|
+
}
|
|
45
|
+
return byId;
|
|
46
|
+
}
|
|
47
|
+
/** Merge resolved sidecar values into a rendering copy of a capability node
|
|
48
|
+
* and its descendants. An inline value always wins if present — this is a
|
|
49
|
+
* display fallback for fields the document doesn't carry inline, not a
|
|
50
|
+
* migration tool. Returns a new node tree; nothing here writes it
|
|
51
|
+
* anywhere. */
|
|
52
|
+
export function withResolvedAttributes(node, resolved) {
|
|
53
|
+
const r = resolved.get(node.id);
|
|
54
|
+
const merged = r
|
|
55
|
+
? {
|
|
56
|
+
...node,
|
|
57
|
+
current_maturity: node.current_maturity ?? r.current_maturity,
|
|
58
|
+
target_maturity: node.target_maturity ?? r.target_maturity,
|
|
59
|
+
owner_role: node.owner_role ?? r.owner_role,
|
|
60
|
+
target_date: node.target_date ?? r.target_date,
|
|
61
|
+
}
|
|
62
|
+
: node;
|
|
63
|
+
if (!node.children || node.children.length === 0)
|
|
64
|
+
return merged;
|
|
65
|
+
return { ...merged, children: node.children.map((c) => withResolvedAttributes(c, resolved)) };
|
|
66
|
+
}
|
|
67
|
+
/** Same merge, applied across a capability map's whole top-level tree. */
|
|
68
|
+
export function withResolvedCapabilityMap(map, resolved) {
|
|
69
|
+
if (resolved.size === 0)
|
|
70
|
+
return map;
|
|
71
|
+
return { ...map, capabilities: map.capabilities.map((c) => withResolvedAttributes(c, resolved)) };
|
|
72
|
+
}
|
|
@@ -3,8 +3,7 @@ const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
|
3
3
|
// Canonical form per `IDS_AND_REFERENCES.md` §2: `CAPABILITY-V…` /
|
|
4
4
|
// `CAPABILITY-H…` with the dotted address (`V1`, `V1.2.1`, `H1`). The bare
|
|
5
5
|
// legacy form (`V1`, `H1`) is still accepted so existing maps without the
|
|
6
|
-
// canonical prefix do not break
|
|
7
|
-
// vkgeorgia/strategy#36.
|
|
6
|
+
// canonical prefix do not break; migration is tracked separately.
|
|
8
7
|
const CAP_ID_RE = /^(CAPABILITY-)?(V|H)\d+(\.\d+)*$/;
|
|
9
8
|
export function validateCapabilityMap(input) {
|
|
10
9
|
const errors = [];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IndexAssertion, IndexRequirement } from './types.js';
|
|
1
|
+
import type { IndexAssertion, IndexRequirement, IndexVerification, IndexNeed, IndexValidation } from './types.js';
|
|
2
2
|
export interface ComplianceProduct {
|
|
3
3
|
id: string;
|
|
4
4
|
name: string;
|
|
@@ -14,6 +14,8 @@ export interface ComplianceCanon {
|
|
|
14
14
|
products: ComplianceProduct[];
|
|
15
15
|
requirements: IndexRequirement[];
|
|
16
16
|
assertions: IndexAssertion[];
|
|
17
|
+
/** VERIFICATION artefacts (27-verification.md) — the engineering V&V peer of `assertions`. */
|
|
18
|
+
verifications: IndexVerification[];
|
|
17
19
|
codex: ComplianceCodexDoc[];
|
|
18
20
|
/**
|
|
19
21
|
* Named elements that can serve as assertion subjects beyond `products`:
|
|
@@ -21,6 +23,13 @@ export interface ComplianceCanon {
|
|
|
21
23
|
* `ingestComplianceDoc` for the corresponding notation values.
|
|
22
24
|
*/
|
|
23
25
|
subjects: ComplianceProduct[];
|
|
26
|
+
/** NEED elements (ELEMENT_PRIMITIVES.md §7.28) — upstream of `requirements`.
|
|
27
|
+
* Ingested so NEED-COVERAGE-001 / NEED-VALIDATION-COVERAGE-001..002 can
|
|
28
|
+
* compute coverage over the full needs catalogue. */
|
|
29
|
+
needs: IndexNeed[];
|
|
30
|
+
/** VALIDATION artefacts (28-validation.md) — the validation-domain peer of
|
|
31
|
+
* `verifications`, anchored on NEED instead of REQUIREMENT. */
|
|
32
|
+
validations: IndexValidation[];
|
|
24
33
|
}
|
|
25
34
|
export declare function emptyCanon(): ComplianceCanon;
|
|
26
35
|
/**
|
|
@@ -1,9 +1,18 @@
|
|
|
1
|
-
// Canon-artefact classification
|
|
1
|
+
// Canon-artefact classification. The single authority
|
|
2
2
|
// for "what is a compliance artefact" — used by both the Studio extension scan
|
|
3
3
|
// (webview previews) and the CLI scan (`export-compliance`), so the recognition
|
|
4
4
|
// rules live once. Pure: takes a parsed YAML document, no IO.
|
|
5
5
|
export function emptyCanon() {
|
|
6
|
-
return {
|
|
6
|
+
return {
|
|
7
|
+
products: [],
|
|
8
|
+
requirements: [],
|
|
9
|
+
assertions: [],
|
|
10
|
+
verifications: [],
|
|
11
|
+
codex: [],
|
|
12
|
+
subjects: [],
|
|
13
|
+
needs: [],
|
|
14
|
+
validations: [],
|
|
15
|
+
};
|
|
7
16
|
}
|
|
8
17
|
const str = (v) => (typeof v === 'string' ? v : undefined);
|
|
9
18
|
const strArray = (v) => Array.isArray(v) ? v.filter((x) => typeof x === 'string') : undefined;
|
|
@@ -46,6 +55,25 @@ export function ingestComplianceDoc(canon, doc) {
|
|
|
46
55
|
element_kind: d.notation,
|
|
47
56
|
description: str(d.description),
|
|
48
57
|
next_review_at: str(d.next_review_at),
|
|
58
|
+
serves: str(d.serves),
|
|
59
|
+
});
|
|
60
|
+
return id;
|
|
61
|
+
}
|
|
62
|
+
if (d.notation === 'need') {
|
|
63
|
+
canon.needs.push({ id, name: str(d.name) ?? id });
|
|
64
|
+
return id;
|
|
65
|
+
}
|
|
66
|
+
if (d.notation === 'validation') {
|
|
67
|
+
const validates = str(d.validates);
|
|
68
|
+
const method = str(d.method);
|
|
69
|
+
const outcome = str(d.outcome);
|
|
70
|
+
if (!validates || !method || !outcome)
|
|
71
|
+
return null;
|
|
72
|
+
canon.validations.push({
|
|
73
|
+
id, validates, method, outcome,
|
|
74
|
+
performed_at: str(d.performed_at),
|
|
75
|
+
evidenceCount: Array.isArray(d.evidence) ? d.evidence.length : 0,
|
|
76
|
+
admitted_at: str(d.admitted_at),
|
|
49
77
|
});
|
|
50
78
|
return id;
|
|
51
79
|
}
|
|
@@ -66,6 +94,20 @@ export function ingestComplianceDoc(canon, doc) {
|
|
|
66
94
|
});
|
|
67
95
|
return id;
|
|
68
96
|
}
|
|
97
|
+
if (d.notation === 'verification') {
|
|
98
|
+
const verifies = str(d.verifies);
|
|
99
|
+
const method = str(d.method);
|
|
100
|
+
const outcome = str(d.outcome);
|
|
101
|
+
if (!verifies || !method || !outcome)
|
|
102
|
+
return null;
|
|
103
|
+
canon.verifications.push({
|
|
104
|
+
id, verifies, method, outcome,
|
|
105
|
+
performed_at: str(d.performed_at),
|
|
106
|
+
evidenceCount: Array.isArray(d.evidence) ? d.evidence.length : 0,
|
|
107
|
+
admitted_at: str(d.admitted_at),
|
|
108
|
+
});
|
|
109
|
+
return id;
|
|
110
|
+
}
|
|
69
111
|
if (d.zone === 'codex') {
|
|
70
112
|
canon.codex.push({ id, name: str(d.name) ?? id, type: str(d.type), jurisdiction: str(d.jurisdiction) });
|
|
71
113
|
return id;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Confidence bridge for the compliance views (
|
|
1
|
+
// Confidence bridge for the compliance views (DQ-2).
|
|
2
2
|
// Maps the compliance index projections (IndexRequirement / IndexAssertion) onto
|
|
3
3
|
// the DQ-1 ScoringElement shape, scores the view, and renders the §11.6 header
|
|
4
4
|
// line. Lives in @transitrix/diagrams so Studio and DSM share one bridge and
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ComplianceIndex, IndexAssertion, IndexRequirement } from './types.js';
|
|
1
|
+
import type { ComplianceIndex, IndexAssertion, IndexRequirement, IndexNeed } from './types.js';
|
|
2
2
|
export interface GapReport {
|
|
3
3
|
/** Requirements with no assertion about them, severity-sorted then id. */
|
|
4
4
|
requirementsWithoutAssertions: IndexRequirement[];
|
|
@@ -12,6 +12,36 @@ export interface GapReport {
|
|
|
12
12
|
* (oldest first). Empty when `today` is not supplied.
|
|
13
13
|
*/
|
|
14
14
|
pastDeadlineRequirements: IndexRequirement[];
|
|
15
|
+
/**
|
|
16
|
+
* REQ-VERIF-COVERAGE-001: requirements with no VERIFICATION targeting them,
|
|
17
|
+
* severity-sorted then id — the engineering V&V analogue of
|
|
18
|
+
* `requirementsWithoutAssertions`.
|
|
19
|
+
*/
|
|
20
|
+
requirementsWithoutVerification: IndexRequirement[];
|
|
21
|
+
/**
|
|
22
|
+
* REQ-VERIF-COVERAGE-002: requirements with one or more VERIFICATIONs
|
|
23
|
+
* targeting them, but none closed (`pass`/`fail`) — every one is still
|
|
24
|
+
* `not_yet_run`/`inconclusive`. Severity-sorted then id.
|
|
25
|
+
*/
|
|
26
|
+
requirementsWithUnresolvedVerification: IndexRequirement[];
|
|
27
|
+
/**
|
|
28
|
+
* NEED-COVERAGE-001: needs with no REQUIREMENT whose `serves` names them —
|
|
29
|
+
* the NEED-side analogue of `requirementsWithoutAssertions`, id-sorted.
|
|
30
|
+
*/
|
|
31
|
+
needsWithoutRequirements: IndexNeed[];
|
|
32
|
+
/**
|
|
33
|
+
* NEED-VALIDATION-COVERAGE-001: needs with no VALIDATION targeting them,
|
|
34
|
+
* id-sorted — the validation-side analogue of
|
|
35
|
+
* `requirementsWithoutVerification`.
|
|
36
|
+
*/
|
|
37
|
+
needsWithoutValidation: IndexNeed[];
|
|
38
|
+
/**
|
|
39
|
+
* NEED-VALIDATION-COVERAGE-002: needs with one or more VALIDATIONs
|
|
40
|
+
* targeting them, but none closed (`pass`/`fail`) — every one is still
|
|
41
|
+
* `not_yet_run`/`inconclusive`. Mutually exclusive with
|
|
42
|
+
* `needsWithoutValidation` by construction. Id-sorted.
|
|
43
|
+
*/
|
|
44
|
+
needsWithUnresolvedValidation: IndexNeed[];
|
|
15
45
|
}
|
|
16
46
|
export interface GapReportOptions {
|
|
17
47
|
/** Today as ISO `YYYY-MM-DD`; required for the stale-assertion list. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
// Gap dashboard report (
|
|
1
|
+
// Gap dashboard report (Phase 4 / CV-5; REQ-VERIF-COVERAGE-001/002 per
|
|
2
|
+
// 15-requirement.md §4).
|
|
2
3
|
//
|
|
3
|
-
//
|
|
4
|
+
// Operational gap lists computed from the shared reverse-index (Phase 3):
|
|
4
5
|
// 1. Requirements with no Assertion targeting them (severity-sorted).
|
|
5
6
|
// 2. Assertions without evidence — the ASSERT-007 case: status ∈
|
|
6
7
|
// {compliant, partial} AND no evidence. `under_review` / `non_compliant` /
|
|
@@ -9,7 +10,16 @@
|
|
|
9
10
|
// 3. Stale Assertions — the ASSERT-008 case: `next_review_at` is in the past.
|
|
10
11
|
// 4. Past-deadline requirements (CV-5) — REQUIREMENT.deadline < today AND
|
|
11
12
|
// the requirement has no fully-compliant assertion. Deadline-missed gaps.
|
|
13
|
+
// 5. Requirements with no Verification targeting them — REQ-VERIF-COVERAGE-001
|
|
14
|
+
// (27-verification.md §5 / 15-requirement.md §4). Independent of #1 — the
|
|
15
|
+
// ASSERTION and VERIFICATION catalogues never disagree with each other by
|
|
16
|
+
// construction; a requirement may have one, both, or neither.
|
|
17
|
+
// 6. Requirements whose verifications exist but never closed — every one is
|
|
18
|
+
// still `not_yet_run` / `inconclusive` — REQ-VERIF-COVERAGE-002. Mutually
|
|
19
|
+
// exclusive with #5 by construction.
|
|
12
20
|
import { computeDeadlineStatus } from './impact.js';
|
|
21
|
+
import { CLOSED_VERIFICATION_OUTCOMES } from '../verification/types.js';
|
|
22
|
+
import { CLOSED_VALIDATION_OUTCOMES } from '../validation/types.js';
|
|
13
23
|
const SEVERITY_RANK = { high: 0, medium: 1, low: 2 };
|
|
14
24
|
function severityRank(s) {
|
|
15
25
|
return s !== undefined && s in SEVERITY_RANK ? SEVERITY_RANK[s] : 3;
|
|
@@ -60,5 +70,47 @@ export function buildGapReport(index, options = {}) {
|
|
|
60
70
|
return da < db ? -1 : da > db ? 1 : a.id < b.id ? -1 : 1;
|
|
61
71
|
})
|
|
62
72
|
: [];
|
|
63
|
-
|
|
73
|
+
// REQ-VERIF-COVERAGE-001 — no VERIFICATION targets the requirement at all.
|
|
74
|
+
const requirementsWithoutVerification = [...index.requirementById.values()]
|
|
75
|
+
.filter(r => (index.verificationsByRequirement.get(r.id) ?? []).length === 0)
|
|
76
|
+
.sort((a, b) => {
|
|
77
|
+
const d = severityRank(a.severity) - severityRank(b.severity);
|
|
78
|
+
return d !== 0 ? d : a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
79
|
+
});
|
|
80
|
+
// REQ-VERIF-COVERAGE-002 — has verifications, but none closed (pass/fail).
|
|
81
|
+
// Mutually exclusive with -001 by construction (only requirements with at
|
|
82
|
+
// least one verification are considered here).
|
|
83
|
+
const requirementsWithUnresolvedVerification = [...index.requirementById.values()]
|
|
84
|
+
.filter(r => {
|
|
85
|
+
const verifications = index.verificationsByRequirement.get(r.id) ?? [];
|
|
86
|
+
return verifications.length > 0
|
|
87
|
+
&& !verifications.some(v => CLOSED_VERIFICATION_OUTCOMES.includes(v.outcome));
|
|
88
|
+
})
|
|
89
|
+
.sort((a, b) => {
|
|
90
|
+
const d = severityRank(a.severity) - severityRank(b.severity);
|
|
91
|
+
return d !== 0 ? d : a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
92
|
+
});
|
|
93
|
+
// NEED-COVERAGE-001 — no REQUIREMENT.serves names this need.
|
|
94
|
+
const needsWithoutRequirements = [...index.needById.values()]
|
|
95
|
+
.filter(n => (index.requirementsByNeed.get(n.id) ?? []).length === 0)
|
|
96
|
+
.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
97
|
+
// NEED-VALIDATION-COVERAGE-001 — no VALIDATION targets the need at all.
|
|
98
|
+
const needsWithoutValidation = [...index.needById.values()]
|
|
99
|
+
.filter(n => (index.validationsByNeed.get(n.id) ?? []).length === 0)
|
|
100
|
+
.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
101
|
+
// NEED-VALIDATION-COVERAGE-002 — has validations, but none closed (pass/fail).
|
|
102
|
+
// Mutually exclusive with -001 by construction (only needs with at least
|
|
103
|
+
// one validation are considered here).
|
|
104
|
+
const needsWithUnresolvedValidation = [...index.needById.values()]
|
|
105
|
+
.filter(n => {
|
|
106
|
+
const validations = index.validationsByNeed.get(n.id) ?? [];
|
|
107
|
+
return validations.length > 0
|
|
108
|
+
&& !validations.some(v => CLOSED_VALIDATION_OUTCOMES.includes(v.outcome));
|
|
109
|
+
})
|
|
110
|
+
.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
111
|
+
return {
|
|
112
|
+
requirementsWithoutAssertions, assertionsWithoutEvidence, staleAssertions, pastDeadlineRequirements,
|
|
113
|
+
requirementsWithoutVerification, requirementsWithUnresolvedVerification,
|
|
114
|
+
needsWithoutRequirements, needsWithoutValidation, needsWithUnresolvedValidation,
|
|
115
|
+
};
|
|
64
116
|
}
|
package/dist/compliance/html.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// HTML renderers for the compliance views (
|
|
1
|
+
// HTML renderers for the compliance views (Phase 5,
|
|
2
2
|
// PDF follow-on). Pure string builders — fed to WeasyPrint by the
|
|
3
3
|
// `transitrix export-compliance --format pdf` path in src/export-compliance.ts.
|
|
4
4
|
//
|
|
@@ -163,11 +163,13 @@ export function renderComplianceHtml(canon, scope, options = {}) {
|
|
|
163
163
|
return htmlDoc(title, options.today, `${summary}<table><thead>${head}</thead><tbody>${rows}</tbody></table>`);
|
|
164
164
|
}
|
|
165
165
|
case 'gap': {
|
|
166
|
-
const index = buildComplianceIndex({
|
|
166
|
+
const index = buildComplianceIndex({
|
|
167
|
+
requirements: canon.requirements,
|
|
168
|
+
assertions: canon.assertions,
|
|
169
|
+
verifications: canon.verifications,
|
|
170
|
+
});
|
|
167
171
|
const report = buildGapReport(index, { today: options.today });
|
|
168
172
|
const title = options.title ?? 'Compliance Gap Dashboard';
|
|
169
|
-
const total = report.requirementsWithoutAssertions.length + report.assertionsWithoutEvidence.length + report.staleAssertions.length;
|
|
170
|
-
const summary = `<p class="cmp-summary">${total} gap(s)</p>`;
|
|
171
173
|
const section = (heading, count, items) => {
|
|
172
174
|
const body = items.length === 0
|
|
173
175
|
? '<p class="cmp-ok">✓ none</p>'
|
|
@@ -178,13 +180,19 @@ export function renderComplianceHtml(canon, scope, options = {}) {
|
|
|
178
180
|
const noEvidence = report.assertionsWithoutEvidence.map(a => (`<li><code>${escHtml(a.id)}</code> <span class="cmp-meta">about <code>${escHtml(a.about)}</code>, subject <code>${escHtml(a.subject)}</code>, status ${escHtml(a.status)}</span></li>`));
|
|
179
181
|
const stale = report.staleAssertions.map(a => (`<li><code>${escHtml(a.id)}</code> <span class="cmp-meta">review due ${a.next_review_at ? escHtml(a.next_review_at) : '—'}, subject <code>${escHtml(a.subject)}</code></span></li>`));
|
|
180
182
|
const pastDl = report.pastDeadlineRequirements.map(r => (`<li><code>${escHtml(r.id)}</code> ${escHtml(r.name)} <span class="cmp-meta">deadline ${r.deadline ? escHtml(r.deadline) : '—'}${r.severity ? `, severity ${escHtml(r.severity)}` : ''}</span></li>`));
|
|
181
|
-
const
|
|
183
|
+
const noVerif = report.requirementsWithoutVerification.map(r => (`<li><code>${escHtml(r.id)}</code> ${escHtml(r.name)}${r.severity ? ` <span class="cmp-meta">severity ${escHtml(r.severity)}</span>` : ''}</li>`));
|
|
184
|
+
const unresolvedVerif = report.requirementsWithUnresolvedVerification.map(r => (`<li><code>${escHtml(r.id)}</code> ${escHtml(r.name)}${r.severity ? ` <span class="cmp-meta">severity ${escHtml(r.severity)}</span>` : ''}</li>`));
|
|
185
|
+
const total6 = report.requirementsWithoutAssertions.length + report.assertionsWithoutEvidence.length
|
|
186
|
+
+ report.staleAssertions.length + report.pastDeadlineRequirements.length
|
|
187
|
+
+ report.requirementsWithoutVerification.length + report.requirementsWithUnresolvedVerification.length;
|
|
182
188
|
const body = [
|
|
183
|
-
`<p class="cmp-summary">${
|
|
189
|
+
`<p class="cmp-summary">${total6} gap(s) across 6 checks</p>`,
|
|
184
190
|
section('Requirements without assertions', report.requirementsWithoutAssertions.length, reqs),
|
|
185
191
|
section('Assertions without evidence — ASSERT-007', report.assertionsWithoutEvidence.length, noEvidence),
|
|
186
192
|
section('Stale assertions — ASSERT-008', report.staleAssertions.length, stale),
|
|
187
193
|
section('Past-deadline requirements (CV-5)', report.pastDeadlineRequirements.length, pastDl),
|
|
194
|
+
section('Requirements without verification — REQ-VERIF-COVERAGE-001', report.requirementsWithoutVerification.length, noVerif),
|
|
195
|
+
section('Requirements with unresolved verification — REQ-VERIF-COVERAGE-002', report.requirementsWithUnresolvedVerification.length, unresolvedVerif),
|
|
188
196
|
].join('');
|
|
189
197
|
return htmlDoc(title, options.today, body);
|
|
190
198
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Compliance-impact matrix derivation + markdown renderer
|
|
2
|
-
// (
|
|
2
|
+
// (interim renderer; CV-1 view-config wiring
|
|
3
3
|
// per strategy#84 decomposition refresh).
|
|
4
4
|
//
|
|
5
5
|
// Implements the render contract from
|